02. Makefile (0)

지수호·2023년 2월 9일
0

C언어 공부

목록 보기
3/4
post-thumbnail

이전내용

  • 이전 글에선 GCC와 컴파일 과정에 대하여 알아보았다.
  • 하지만 과정도 복잡하고 소스가 많아지면 입력해야할 명령어도 많아진다.
  • 일련의 과정들을 명령어 하나로 간편하게 컴파일하고 빌드하는 방법을 알아보겠다.

Makefile

make는 소프트웨어 개발을 위해 유닉스 계열 운영 체제에서 주로 사용되는 프로그램 빌드 도구이다.
여러 파일들끼리의 의존성과 각 파일에 필요한 명령을 정의함으로써 프로그램을 컴파일할 수 있다.
최종 프로그램을 만들 수 있는 과정을 서술할 수 있는 표준적인 문법을 가지고 있다.
위의 구조로 기술된 파일(주로 Makefile이라는 파일명)을 make가 해석하여 프로그램 빌드를 수행하게 된다.

사용예시

우선 사용하는 파일은 아래 4개를 쓰겠다

// main.h
#include <stdio.h>

void printLine();
void printLines(int length);
// main.c
#include "main.h"

int main(){
    printf("Print Line\n");
    printLine();
    
    printf("Print Lines 10\n");
    printLines(10);
    return 0;
}
// printLine.c
#include "main.h"

void printLine(){
    printf("---------------------------\n");
}
// printLines.c
#include "main.h"

void printLine(){
    printf("---------------------------\n");
}

GCC의 경우

$ gcc -c .\main.c
$ gcc -c .\printLine.c
$ gcc -c .\printLines.c
$ gcc -o .\main.exe .\main.o .\printLine.o .\printLines.o
$ .\main.exe
Print Line
---------------------------
Print Lines 10
---------------------------
---------------------------
---------------------------
---------------------------
---------------------------
---------------------------
---------------------------
---------------------------
---------------------------
---------------------------
  • 지금은 겨우 3개의 의존성만 가지고 있을 경우이다.
  • 이 경우 간단해보이기도 하고 아래의 명령어로 간단하게 빌드할 수도 있다.

$ gcc -o .\main.exe .\main.c .\printLine.c .\printLines.c
$ .\main.exe
Print Line
---------------------------
Print Lines 10
---------------------------
---------------------------
---------------------------
---------------------------
---------------------------
---------------------------
---------------------------
---------------------------
---------------------------
---------------------------
  • 하지만 소스파일이 수십에서 수백개가 된다면? 끔찍하다,,,

Makefile 사용시

// Makefile
main.exe: main.o printLine.o printLines.o
	gcc -o main.exe main.o printLine.o printLines.o

main.o: main.h main.c
	gcc -c main.c
    
printLine.o: main.h printLine.c
	gcc -c printLine.c
   
printLines.o: main.h printLines.c
	gcc -c printLines.c

굉장히 복잡해보인다.
위 파일을 소스파일이 있는 폴더에 만들고 make 명령어를 입력해보자! (Window의 경우 mingw32-make or mingw64-make)

$ mingw32-make 
gcc -c main.c
gcc -c printLine.c
gcc -c printLines.c
gcc -o main.exe main.o printLine.o printLines.o
$ .\main.exe
Print Line
---------------------------
Print Lines 10
---------------------------
---------------------------
---------------------------
---------------------------
---------------------------
---------------------------
---------------------------
---------------------------
---------------------------
---------------------------

gcc -c main.c
gcc -c printLine.c
gcc -c printLines.c
gcc -o main.exe main.o printLine.o printLines.o

  • 이렇게 gcc로 입력했던 명령어들이 똑같이 나온다.
  • 그러면 아까랑 별 다른게 없잖아? 싶을거다,,,

하지만 printLine.c 를 변경해보고 다시 make 실행해보면 다른 점을 알 수 있다.

// printLine.c
#include "main.h"

void printLine(){
    printf("***************************\n");
}
$ mingw32-make
gcc -c -o .\printLine.o .\printLine.c
gcc -o .\main.exe .\main.o .\printLine.o .\printLines.o
$ .\main.exe
Print Line
***************************
Print Lines 10
***************************
***************************
***************************
***************************
***************************
***************************
***************************
***************************
***************************
***************************

똑같은 make 명령어를 입력했을 뿐인데 printLine.o 파일만 다시 생성한 후 링크를 한다.

알아서 바뀐 파일을 찾아주고 그 부분만 실행하게 도와준다.


이렇게만 봐도 한 번 만들어두면 두고두고 쓰기 좋다고 생각할 수 있다.

하지만 이걸 더 간결하게 작성하고 수정하기 쉽도록 한다면? 더더욱 좋을 것이다.

Makefile 작성법

Makefile은 기본적으로 아래와 같이 목표(target), 의존 관계(dependency), 명령(command)의 세개로 이루어진 기분적인 규칙(rule)들이 계속적으로 나열되어 있다고 봐도 무방하다.
make가 지능적으로 파일을 갱신하는 것도 모두 이 간단한 규칙에 의하기 때문이다.

기본 규칙

target ... : dependency ...
(one tab)command
                ...

target에 생성될 파일을 depency에 의존 관계를
     command 에는 사용될 명령어를 적는 부분이다.

위에서 사용했던 Makefile을 다시 보자

# Makefile
main.exe: main.o printLine.o printLines.o
	gcc -o main.exe main.o printLine.o printLines.o

main.o: main.h main.c
	gcc -c main.c
    
printLine.o: main.h printLine.c
	gcc -c printLine.c
   
printLines.o: main.h printLines.c
	gcc -c printLines.c

이제 Makefile의 구조는 눈에 보이기 시작할 것이다.

만약에 printBox.c라는 파일을 작성해서 추가해야한다면?

# Makefile
main.exe: main.o printLine.o printLines.o printBox.o # edit
	gcc -o main.exe main.o printLine.o printLines.o printBox.o # edit

main.o: main.h main.c
	gcc -c main.c
    
printLine.o: main.h printLine.c
	gcc -c printLine.c
   
printLines.o: main.h printLines.c
	gcc -c printLines.c
    
printBox.o: main.h printBox.c 					    		   # add
	gcc -c printBox.c 								    	   # add

하지만 이것도 귀찮다.
소스파일이 추가될 때마다 최소 2줄씩은 추가해주고 2줄을 수정해야한다.
게다가 이걸 작성하다가 간단하게 철자나 확장자를 잘못 작성하는 실수라도 하면? 끔찍하다,,,

매크로

하지만 우리가 프로그램을 개발하는 이유가 무엇인가?
매크로 기능을 사용하면 간결하게 작성이 가능하고 실수도 줄일 수 있다.

대입은 MACRO = ...
사용은 $(MACRO)로 사용한다.

# Makefile
OBJS = main.o printLine.o printLines.o # add

main.exe: $(OBJS) # edit
	gcc -o main.exe $(OBJS)			   # edit

main.o: main.h main.c
	gcc -c main.c
    
printLine.o: main.h printLine.c
	gcc -c printLine.c
   
printLines.o: main.h printLines.c
	gcc -c printLines.c

아까와 동일한 동작을 한다.
$(OBJS)가 main.o printLine.o printLines.o 로 치환이 돼서 실행된다.

확장자 규칙 (Suffix rule)

확장자 규칙이란, 간단히 말해서 파일의 확장자를 보고, 그에 따라 적절한 연산을 수행시키는 규칙이라고 말할 수 있다. 가령 .c 파일은 일반적으로 C 소스 코드를 가리키며, .o 파일은 목적 파일(Object file)을 말하고 있다. 그리고 당연히 .c 파일은 컴파일되어서 .o 파일이 되어야 하는 것이다. 여기서 한가지 변수가 등장하게 된다. .SUFFIXES 라고 하는 변수인데 우리가 make 파일에게 주의 깊게 처리할 파일들의 확장자를 등록해 준다고 이해하면 될 것이다. .SUFFIXES : .c .o 위의 표현은 ‘.c’ 와 ‘.o’ 확장자를 가진 파일들을 확장자 규칙에 의거해서 처리될 수 있도록 해준다.

이제 확장자 규칙도 추가하고 다른 매크로도 추가해보겠다.

# Makefile
.SUFFIXEX = .c .o 						# add
CC = gcc 								# add
TARGET = main.exe
OBJS = main.o printLine.o printLines.o 

$(TARGET): $(OBJS)				       
	$(CC) -o $(TARGET) $(OBJS)
    
# delete

물론 아까와 똑같은 동작을 한다.
하지만 이제 새로운 소스파일이 생겨도 OBJS에 하나 추가해주면 끝이다.
그리고 파일이 수정되어서 다시 컴파일을 해야한다고 해도 make 명령어 하나면 된다.

다음 글에서는 아래 내용들을 공부할 것이다.

  • 레이블
  • 미리 정해져 있는 매크로 (Pre-defined macro)
  • 내부 매크로 (Internal macro)
    ... 등등

마무리

쓰다보니까 생각보다 점점 양이 많아져서 글을 나눠야 할 것 같다.
여러 블로그와 문서들을 찾아보면서 이제 기본적인 문법과 사용은 꽤 익은 것 같다.
공부하는 것보다 글쓰는게 더 어렵다,,,ㅠㅠ

참조
http://developinghappiness.com/?p=26
http://doc.kldp.org/KoreanDoc/html/GNU-Make/GNU-Make-2.html

profile
지수호

0개의 댓글