Makefile 작성 매뉴얼
http://www.gnu.org/software/make/manual/make.html
프로젝트 과제를 진행하면서 Makefile을 수동으로 작성해야 될 일이 생겨서
잊어먹지 않으려고 작성해둔다!
Makefile의 정의
사실 요즘은 Makefile을 일일이 수동으로 만들지 않고, CMake같은 빌드 툴을 사용해서 Makefile을 자동으로 생성하여 사용하는 추세이지만,
build option이 static하고 소스파일이 몇 개 되지 않는 경우에는
CMake보다 Makefile을 직접 만들어서 쓰는 것이 더 편한 케이스도 있으므로
알아두면 나쁠 것 없다고 생각한다.😁
세 개의 소스파일을 각각 컴파일하여 Object 파일(*.o)를 생성하고,
이들을 한 데 묶는 링크 과정을 통해 실행파일인 app.out을 생성하는 빌드 예제를 생각해보자.
위 그림에서는 main에서 foo.c, bar.c에 정의된 함수를 호출하고 있다.
이를 Makefile로 깔끔하게 빌드해보자!
app.out: main.o foo.o bar.o
gcc -o app.out main.o foo.o bar.o
main.o: foo.h bar.h main.c
gcc -c -o main.o main.c
foo.o: foo.h foo.c
gcc -c -o foo.o foo.c
bar.o: bar.h bar.c
gcc -c -o bar.o bar.c
소스코드가 위치하는 디렉토리에 위와 같이 Makefile을 작성하고
make
터미널에 make 명령을 치면 한번에 실행 파일(app.out)을 만들어낸다.
이 때, Object 파일들(main.o, foo.o, bar.o)도 함께 생성된다.
해당 Target만 따로 빌드하고 싶을 때는 make 명령 뒤에 Target을 명시하면 된다.
예를 들어, 아래의 명령을 치면 foo.o 파일만 생성되는 것을 확인할 수 있다.
make foo.o
// gcc -c -o foo.o foo.c
사실 make 명령은 자주 사용되는 빌드 규칙은 내장이 되어 있어 굳이 기술하지 않아도 자동으로 처리해준다.
소스 파일(*.c)를 컴파일해서 Object 파일(*.o)로 만들어 주는 규칙도 여기에 해당한다.
그렇다면 Makefile에 이렇게만 써도 실행 파일(app.out)이 빌드될까?
app.out: main.o foo.o bar.o
gcc -o app.out main.o foo.o bar.o
위처럼 작성하고 make명령을 실행해도 정상적으로 실행파일이 빌드된다.
하지만 이렇게만 작성하면 Incremental build를 위한 의존성 검사에서
헤더 파일의 변경을 감지하지 못하는 문제가 발생한다.
make는 소스 파일의 마지막 변경 시점만 확인할 뿐이지,
소스코드를 일일이 들여다보고 어떤 헤더파일이 포함되었는지까지는 추적하지 않기 때문이다.
따라서, 다음과 같이 각 target에 대한 dependencies까지는 꼭 명시를 해 줘야 한다.
이렇게 작성하면 헤더 파일만 변경된 경우에도 의존성 검사에서 올바르게 탐지할 수 있다.
물론 해당 소스파일에 헤더 파일이 추가될 때마다(#include) 이 부분도 함께 업데이트가 필요하다.
app.out: main.o foo.o bar.o
gcc -o app.out main.o foo.o bar.o
main.o: foo.h bar.h main.c
foo.o: foo.h foo.c
bar.o: bar.h bar.c
변수 사용하기
변수를 사용하면 Makefile을 보다 깔끔하고 확장성 있게 작성할 수 있다.
Makefile에 자주 사용되는 변수들은 다음과 같다.
- CC : 컴파일러
- CFLAGS : 컴파일 옵션
- OBJS : 중간 산물 Object 파일 목록
- TARGET : 빌드 대상 이름(실행 파일 이름)
- LDFLAGS : 링커 옵션
- LDLIBS : 링크 라이브러리
Makefile의 내장 변수 말고도 자동 변수도 존재하는데, 자동 변수는 위치한 곳의 맥락에 맞도록 치환되는 변수이다.
이렇게 하면 Target 이름을 수정할 때 Recipe까지 일일이 찾아서 수정하는 수고를 덜 수 있다.
Make에서 지원하는 주요 자동 변수들은 다음과 같다.
- $@ : 현재 Target 이름
- $^ : 현재 Target이 의존하는 대상들의 전체 목록
- $? : 현재 Target이 의존하는 대상들 중 변경된 것들의 목록
이 외에도 사용 가능한 자동 변수들의 목록과 설명은 아래 사이트에서 확인할 수 있다.
http://www.gnu.org/software/make/manual/html_node/Automatic-Variables.html
Clean Rule 추가
clean은 빌드 결과물(app.out)과 중간 산물(*.o)을 모두 삭제하여 깨끗한 상태에서 다시 빌드할 수 있는 환경을 만들어 주는 매크로이다.
이렇게 하고 수행하는 빌드를 clean build라고 한다.
Makefile에 다음 빌드 규칙을 추가하고
clean:
rm -f *.o
rm -f $(TARGET)
make 명령 뒤에 clean을 붙여주면 clean 매크로를 사용할 수 있다.
make clean
Makefile 기본 패턴
CC=<컴파일러>
CFLAGS=<컴파일 옵션>
LDFLAGS=<링크 옵션>
LDLIBS=<링크 라이브러리 목록>
OBJS=<Object 파일 목록>
TARGET=<빌드 대상 이름>
all: $(TARGET)
clean:
rm -f *.o
rm -f $(TARGET)
$(TARGET): $(OBJS)
$(CC) -o $@ $(OBJS)