Makefile 개념 및 사용

timothy jeong·2021년 5월 12일
1

42seoul 교육과정중에는 Makefile 을 굉장히 많이 사용하게 될것 같다. 이에 Makefile을 공부하고자한다.

(1) Makefile 이란?

  • make 는 파일 관리 유틸리티이다.
  • 각 파일의 종속 관계를 파악해 Makefile(기술 파일)에 기술된 대로 컴파일 명령이나 shell 명령을 순차적으로 수행한다.
  • 각 파일에 대한 반복적 명령을 자동화시켜 개발자의 수고를 덜고, 시간을 절약할 수 있게 해준다.

[의문점] 컴파일 명령을 시행할거면 관련 셸 스크립트를 작성하면 되는게 아닐까?
왜 Makefile을 사용할까?
[해답] 하나의 소스파일에 변화가 생긴경우 makefile은 변화가 생긴 소스파일만 컴파일 하지만 셸 스크립트는 전체를 다 컴파일 하게되므로 Makefile로 관리하는게 효율적이다.

(2) Makefile의 구성요소

Makefile은 다음과 같은 형태로 작성되며, 그 의미는 아래와 같다.

target1: dependency1 dependency2
	command1
    	command2
        
target2: dependency3 dependency4
	command3
    	command4

target : 미리 기술되어 있는 일련의 과정에 대응되는 것이다.
make를 시행하기 위해서는 make [target] 과 같이 셸에 입력해야 한다.

실행 명령어(command) : target을 make할때 실제로 시행되는 명령어들의 집합이다. commnad는 반드시 tab으로 들여쓰기하고 써야한다.

의존 파일(dependency) : command들이 시행될때 건드려야 하는 파일들의 집합이다.

foo.c, bar.c, main.c 를 이용해서 pros.exe를 만들려고 한다면 다음과 같이 Makefile을 작성할 수 있다.

foo.o: foo.c
	gcc -c -Wall -Wextra -Werror foo.c
        
bar.o: bar.c
	gcc -c -Wall -Wextra -Werror bar.c
    
main.o: main.c
	gcc -c -Wall -Wextra -Werror main.c
    
pros: main.o foo.o bar.o
	gcc -Wall -Wextra -Werror main.o foo.o bar.o -o pros.exe

그리고 shell에서 make pros 를 입력하면 정상적으로 pros.exe가 만들어지는 것을 알 수 있다.

(3) Makefile의 작동

위의 작성 예시에서 foo.o target절을 제일 밑으로 내리면 어떻게 될까?
위에서부터 순차적으로 명령어가 시행되는 shell 스크립트라면 문제가 되겠지만, Makefile에서는 문제가 되지 않는다.

make pros라고 입력하면

  1. pros명령을 시행하기 위해서는 main.o, foo.o, bar.o가 필요하다.
  2. main.o, foo.o, bar.o 라는 타겟을 찾아본다.
  3. .o 타겟을 를 시행하기 위해서는 각각의 .c 파일이 필요하다.
  4. .c 파일들이 정상적으로 존재하는지 확인했고, 명령어에 따라 .o파일을 컴파일 한다.
  5. pros의 의존 파일이 모두 생성되었으므로 pros.exe를 컴파일한다.

만약 foo.c를 변경하고 다시 make pros를 입력한다면 .o파일과 .c 파일의 생성시간, 수정시간을 비교한다.

  1. pros명령을 시행하기 위해서는 main.o, foo.o, bar.o가 필요하다.
  2. main.o, foo.o, bar.o 라는 타겟이 모두 존재한다.
  3. 하지만 foo.o의 생성시간보다 foo.c의 수정시간이 더 나중이므로 foo.o 타겟을 다시 시행한다.
  4. pros의 의존 파일이 모두 생성되었으므로 pros.exe를 컴파일한다.

이렇듯 make 명령어의 target을 시행하기 위해 필요한 절차를 순차적으로 시행하는 것이기 때문에 Makefile에 target들이 나열된 순서는 상관이 없다.

(4) Makefile 변수

Makefile은 일련의 절차를 지정할 수 있을 뿐만 아니라 변수를 설정할 수 있다.
gcc 에 flag를 유동적으로 사용해줘야하는 상황에서 모두 손으로 작성해준다면 실수가 나올 수도 있고 무엇보다 귀찮다.
이를 다음과 같이 하나의 변수로 설정해두면 손쉽게 쓸 수 있다.


CC = gcc
CFLAG = -Wall -Wextra -Werror

foo.o: foo.c
	$(CC) $(CFLAG) -c foo.c
        
bar.o: bar.c
	$(CC) $(CFLAG) bar.c
    
main.o: main.c
	$(CC) $(CFLAG) main.c
    
pros: main.o foo.o bar.o
	$(CC) $(CFLAG) main.o foo.o bar.o -o pros.exe

(5) 의존 파일이 없는 타겟

지금까지 작성한 Makefile에 따르면 pros를 시행하면 pros.exe 뿐만 아니라 main.o foo.o bar.o 목적 파일들도 작업 디렉터리에 남게됨.

일반적으로 이를 제거하는 명령어도 함께 두는데, Makefile을 다음과 같이 작성할 수 있음.


CC = gcc
CFLAG = -Wall -Wextra -Werror
OBJS = main.o foo.o bar.o

foo.o: foo.c
	$(CC) $(CFLAG) -c foo.c
        
bar.o: bar.c
	$(CC) $(CFLAG) bar.c
    
main.o: main.c
	$(CC) $(CFLAG) main.c
    
pros: main.o foo.o bar.o
	$(CC) $(CFLAG) main.o foo.o bar.o -o pros.exe
    
clean:
	rm -f $(OBJS)

make clean 명령어를 시행하면 생성된 목적 파일들이 모두 삭제됨.

이때 clean은 rm 명령어를 시행하는 역할을 하기 때문에 의존 파일이 굳이 필요하지 않음.

그런데 '(3) Makefile의 작동' 에서 보았듯이, make는 target의 의존파일에 의거하여 최신 상태인지 판단후 이후 명령절을 시행합니다. 그렇다면 만약 clean이라는 파일이 작업 디렉터리에 있다면 어떻게 될까?

make clean은 "clean은 의존파일이 없는데, clean파일은 최신이니까 굳이 명령절을 시행할 필요가 없네" 인식하여 명령어를 무시하게 됨.

위와 같은 만약의 사태를 막기위해 PHONY 라는 것을 이용하는데,

[omited...]

pros: main.o foo.o bar.o
	$(CC) $(CFLAG) main.o foo.o bar.o -o pros.exe

.PHONY: clean

clean:
	rm -f $(OBJS)

이제 make clean 하게 되면 clean이라는 파일의 유무와 관계없이 타겟의 명령절이 시행된다.

(6) Makefile 패턴&자동변수

위의 예제에서는 pros.exe 를 만들기 위한 파일이 3개만 존재한다. 하지만 프로젝트 빌드를 위한 파일이 굉장히 많아진다면 Makefile이 너무 커져서 관리가 어려울 것이다.

이러한 이유때문에 패턴룰(Pattern Rule)이라는 것이 존재하는데, % 이 그것이다. %과 다른 문자를 같이 쓰는데, 예를 들어 %.c 와 같은 식이다. %는 셸에서 사용하는 wildcard *와 유사한 기능을 수행한다. 이때 %는 하위문자열(substring)과 일치함을 의미하고, 이렇게 일치한 하위 문자열을 stem이라고 부른다.

가령 이런식으로 사용된다.

%.o: %.c
	$(CC) $(CFLAGS) -c $@ $<

해석하자면, 주어진 경로(이 경우는 Makefile이 있는 경로)의 .c로 끝나는 파일을 의존 파일로 하여 목적 파일을 컴파일한다는 것이다.

이때 사용된 $@, $< 는 자동변수라고 하는데, 다음과 같은 것들이 있다.

1.$@ 현재 rule에 있는 타겟을 참조한다.
2.$^현재 rule에 있는 의존 파일 전체를 참조한다.
3.$<현재 rule에 있는 첫째 의존파일을 참조한다.
4.$?현재 rule의 타겟보다 최신인 의존파일을 참조한다.
5.$+$^와 비슷하지만, 중복된 파일 이름들 까지 모두 포함한다.

(7) 참고자료

씹어먹는 C++
Software Capentry
GNU.org

profile
개발자

0개의 댓글