[Linux] Makefile 소개
Introduction
Software로 과제를 할 때 makefile은 땔 수 없는 프로그램이다. Makefile은 빌드 도구로, 각 파일에 대한 의존성과 명령을 정의함으로써 최종 목적 프로그램을 빌드해서 만든다. 또한 불필요한 compile을 줄일 수 있으며 command log를 쉽게 확인 할 수 있다. 기본적인 구조는 아래와 같다.
Target: Dependencies [TAB] Command
어떤 Target을 만들기 위해 필요한 Dependencies들을 준비하고 난 뒤, Command를 실행하는 방식이다. 따라서 dependencies가 준비가 되지 않으면 dependencies에 해당하는 구문으로 이동해서 수행하게 된다.
# all is default target all: main.o read.o write.o gcc main.o read.o write.o -o output main.o: main.c gcc -c main.c -o main.o read.o: read.c gcc -c read.c -o read.o write.o: write.c gcc -c write.c -o write.o clean: rm -rf main.o read.o write.o output
Makefile은 초단위로 target과 dependencies들의 update 시간을 비교하게 되는데, 만약 target보다 dependencies의 update 시간이 더 최신이라면 새롭게 target을 만든다. 참고로 Makefile에서 all
은 default target이다.
위 예시 Makefile 기준으로 설명하자면, main.c
가 main.o
보다 더 최신으로 업데이트 되었다고 한다면 자동으로 main.o
를 새롭게 만든다.
$ make # make all $ make -f Makefile1
기본적으로 makefile 수행 방법은 make
이며, 해당 directory에 Makefile
또는 makefile
을 default로 읽으며 그 외 다른 이름으로 수행할 경우 -f
옵션을 통해 수행 가능하다.
Automatic Variables
Makefile에서 target과 dependencies를 자주 가리키며 사용하게 되는데 이를 위한 automatic variable을 makefile에선 다음과 같이 제공한다. (그 외는 아래 출처를 참고)
링크
Option | Description |
---|---|
$< | Dependencies 중에 첫 번째로 대치됨 |
$^ | Dependencies 중 전체로 대치됨 |
$@ | Target으로 대치됨 |
$* | 확장자가 없는 Target으로 대치됨 |
$? | Dependencies 중에 Target보다 새로운 파일들 목록으로 대치됨 |
위 내용을 통해 기존 처음 Makefile을 수정해보면 다음과 같이 automatic variable을 적용해 만들 수 있다.
# all is default target all: main.o read.o write.o gcc $^ -o output main.o: main.c gcc -c $^ -o $@ read.o: read.c gcc -c $^ -o $@ write.o: write.c gcc -c $^ -o $@ clean: rm -rf main.o read.o write.o output
Fancy Rules
Implicit rules & macro
Makefile에서 암묵적으로 사용되는 변수들은 다양하게 존재하는데, 대표적으로 사용되는 변수들은 다음과 같다.
Variables | Description |
---|---|
CC | C compiler, cc 또는 gcc |
CXX | C++ compiler, g++ |
CFLAGS | Extra flags to give to the C compiler |
CXXFLAGS | Extra flags to give to the C++ compiler |
CPPFLAGS | Extra flags to give to the C preprocessor |
LDFLAGS | Extra flags to give to compilers when they are supposed to invoke the linker |
SRCS | Source codes |
OBJECTS | Object codes |
TARGET | Target file |
.SUFFIXES | 확장자 규칙 특정 확장자를 가진 file들에 대해 규칙에 의거에 수행되도록 한다. Ex.) .SUFFIXES : .c .o 위 예시는 *.c file을 *.o 로 만드는 루틴이 자동으로 동작하게 된다. 따라서 target이 .c.o 로 된 부분을 수행하게 된다. |
INC | Include path |
그 외에 makefile에 정의된 매크로를 확인하는 방법은 다음과 같다.
$ make -p
좀 더 자세한 내용은 아래 링크를 참고 바란다.
링크
최종적으로 fancy rule을 적용하게 되면 다음과 같이 업데이트 할 수 있다.
CC = gcc SRCS = main.c read.c write.c OBJS = main.o read.o write.o TARGET = output INC = CFLAGS = -Wall # all is default target all: $(OBJS) $(CC) $(CFLAGS) $^ -o $(TARGET) # all is default target all: $(OBJS) $(CC) $(CFLAGS) $^ -o $(TARGET) clean: rm -rf $(OBJS) $(TARGET)
Simple Type 변수
Makefile 내부에선 변수를 활용해서 반복되는 단어를 변수로 정의해서 사용한다. 만약 동일한 변수를 두 번 정의하게 되면 에러가 발생하기 때문에, 이 때는 simple type 변수를 정의해줘야 한다.
CC = gcc CC = $(CC) -Wall all: main.o func.o $(CC) main.o func.o -o a.out main.o: main.c $(CC) -c main.c func.o: func.c $(CC) -c func.c
$ make Makefile:2: *** Recursive variable 'CC' references itself (eventually). Stop.
이럴 때는 :=
를 통해서 simple type 변수로 선언해줘야 한다. 또는 +=
연산자를 통해서 추가 가능하다.
CC := gcc # CC += Wall CC := $(CC) -Wall all: main.o func.o $(CC) main.o func.o -o a.out main.o: main.c $(CC) -c main.c func.o: func.c $(CC) -c func.c
CC := gcc CFLAGS = -Wall -c OBJS = main.o func.o TARGET = a.out $(TARGET): $(OBJS) $(CC) $(OBJS) -o $(TARGET) main.o: main.c $(CC) $(CFLAGS) $< func.o: func.c $(CC) $(CFLAGS) $<
$<
가 뜻하는 것은, 현재의 dependencies 중 첫 번째 이름을 대체한다는 의미다.
Verbose
Makefile을 통해 명령을 수행하다 문제가 발생해 debugging을 할 때 verbose option을 통해 요약된 명령어를 자세하게 살펴볼 수 있다.
$ make test VERBOSE=2
Verbose Level | Description |
---|---|
VERBOSE=0 | 이 모드에서는 Make에서 실행되는 명령어의 출력이 숨겨진다. 대신 실행되는 명령어의 이름만 표시됩니다. |
VERBOSE=1 | 이 모드에서는 Make에서 실행되는 명령어의 출력이 자세하게 나타난다. 이 모드에서는 실행되는 명령어와 해당 명령어가 출력하는 결과 모두가 터미널에 표시된다. |
VERBOSE=2 | 가장 높은 verbose level로, 이 모드에서는 Make에서 실행되는 명령어의 출력이 매우 자세하게 나타난다. 이 모드에서는 실행되는 명령어와 해당 명령어가 출력하는 모든 결과가 터미널에 표시된다. |
Reference
- https://makefiletutorial.com/
- http://doc.kldp.org/KoreanDoc/html/GNU-Make/GNU-Make-7.html
- http://doc.kldp.org/KoreanDoc/html/GNU-Make/GNU-Make-3.html