저자: 제니퍼 베스퍼만, 역 서성용
Make는 코드를 컴파일하기 위해 구축된 시스템으로 고안되었다. 현재는 많은 파일과 디렉터리들에 변경사항을 적용하기 위한 시스템으로 사용되기도 한다. Make는 한 파일의 변경사항이 다른 파일의 변경이 필요할 때 유용하다.
Make는 개발자뿐만 아니라 시스템 관리자에게도 유용하다. 여기서는 먼저 컴파일 도구로서의 Make를 소개하지만, 프로그램 설치나 시스템 환경 설정 변경사항을 적용하는데도 효율적이다.
Make 실행하기
Make를 사용하려면 설정 파일이 필요하다. 프로젝트에 대한 설정 파일을 한 번 만들어두면 변경된 파일을 구축할 때 make만 입력하면 된다.
이 설정 파일 이름은 보통 Makefile이다. README 파일이나 다른 특별한 파일들과 같이 makefile의 첫번째 문자는 대문자로 사용한다.
아무 인자없이 make를 실행하면 GNU Make는 현재 작업 디렉터리에서 설정 파일 GNUmakefile, Makefile, makefile를 찾는다. 만약에 설정 파일에 대해서 다른 이름을 사용한다면 make -f filename을 사용해야한다.
다음 예제에서 make 파일은 make complete만을 출력하며 다른 작업은 아무것도 하지 않는다. 기본적으로 Make는 makefile에 있는 명령들을 실행하는 목록을 보여준다. 명령들을 화면에 표시하지 않고 조용히 실행하려면 make -s를 사용한다.
$ls
makefile renamed_makefile
$make
echo make complete
make complete
$make -f renamed_makefile
echo make complete
make complete
$make -s
make complete
간단한 Makefiles
여기서 사용하는 예제들은 C로 작성되었으며 sample 목적 파일을 생성한다. Make는 어떤 목적 파일을 생성할 수 있으며, 어떤 쉘 명령도 실행할 수 있으며, 어떤 소스 파일과도 작업할 수 있다. Make는 컴파일러가 의존성을 스스로 해결하지 못하는 언어에서도 잘 동작한다.
숙력된 Make 사용자는 다음 예제 makefile에서 중복된 라인이 있다는 것을 알 수 있을 것이다. Make는 어떤 종류의 파일을 컴파일하는 방법을 알고 있으며, 이러한 파일에 대한 규칙을 생략할 수도 있다.(이것을 암시적 규칙(implicit rules)이라한다) 편의상, 다음 예제는 이러한 규칙을 보여준다.
# Linking object files
sample: main.o example.o
cc -o sample main.o example.o
echo sample: make complete
# Compiling source files
main.o: main.c main.h
cc -c main.c
example.o: example.c defs.h
cc -c example.c
Make 규칙은 다음과 같이 구성된다.
target: prerequisites
commands
target(목적)은 prerequisites(준비사항)에 있거나 이것보다 새로운 파일이 있으면 "최신의 것"으로 간주된다. Make는 반대로도 동작하며, 설정 파일에 있는 첫번째 규칙의 목적에서 시작할 수 있다. 이 예제에서 목적은 sample이다. Make는 sample에 필요한 준비사항 -- main.o와 example.o -- 이 규칙을 갖고 있는지 알아보기 위해 확인다. 규칙이 있다면 이러한 규칙들을 연속적으로 확인한다.
참고. target은 컴파일하여 최종적으로 생성되는 목적 파일을 뜻하며, 이 목적 파일은 라이브러리나 실행파일일 수 있으며, 그외 다른 것도 될 수 있다. prerequisites는 target을 생성하는데 필요한 것들을 뜻한다. Make는 필요한 파일이 없으면, 필요한 파일에 대한 규칙이 있는지 확인하게 되며, 이 과정은 재귀적으로 일어난다. commands는 prerequisites가 모두 있을 때 target을 위해 실행될 명령들이며, 이 명령은 프로그램에 따라 한 줄에서 수백, 수천줄이 될 수 있다.
Make는 이러한 준비사항이 없는 target을 찾거나 준비사항에 아무런 규칙도 없는 target을 찾을 때까지 재귀적 연결을 따라 다닌다. 이러한 target을 찾게되면, 지금까지의 재귀적 연결의 반대 순서로 처리되며 필요한 명령들을 실행한다. Make는 규칙을 갖고 있는 target의 준비사항을 만날때마다 재귀적 연결을 만든다.
모든 준비사항에 대한 규칙을 실행하고 나면 최종적으로 sample에 대한 규칙으로 돌아온다. sample 파일이 없거나 지금 있는 준비사항보다 오래된 것이라면(규칙들이 테스트된 이후에) sample을 생성하는 명령을 실행한다.
makefile 예제에서 Make는 다음과 같이 동작한다.
- Make는 첫번째 규칙을 실행한다 -- sample.
- sample의 준비사항에 대한 규칙이 있는지 알아보기 위해 검사한다. 규칙이 있으므로 규칙들을 확인한다.
- 첫번째 준비사항에 대한 규칙을 실행한다 -- main.o
- main.o의 준비사항들이 규칙을 갖고 있는지 확인한다. 규칙이 없으므로 규칙들을 확인하지 않는다.
- main.o가 최근에 생성된 것인지 확인한다. 최근에 생성된게 아니라면 main.o를 생성하기 위한 명령들을 실행한다.
- 두번째 준비사항에 대한 규칙을 실행한다 -- example.o
- example.o의 준비사항이 규칙을 갖고 있는지 확인한다. 규칙이 없으므로 규칙들을 확인하지 않는다.
- example.o가 최근에 생성된 것인지 확인한다. 최근에 생성된 것이 아니라면 example.o에 대한 명령을 실행한다.
- sample에 대한 규칙으로 돌아온다.
- sample이 최근에 생성된 것인지 확인한다. 최근에 생성된 것이 아니라면 sample을 갱신한다.
Make는 순서에 관계없이 준비사항들을 실행할 수 있다. 이 절차에서 중요한 부분은 Make가 첫번째 target에 대해 거슬러 올라가며 재귀적으로 실행되며, 준비사항 사슬에서 마주치는 규칙에 대해서만 테스트한다는 것이다.
Make는 에러가 발생하면 컴파일 과정을 중단한다. 이것은 일반적으로 유용한 동작이다. -- 컴파일 & 테스트 주기동안에 컴파일러가 발견한 문제들을 수정할 수 있도록 해준다. 에러를 무시하려면 Make에 -i 옵션을 사용한다.
Phony Targets
소프트웨어 개발에서 다음 빌드에서 모든 것을 다시 컴파일할 수 있도록 예전에 컴파일한 코드들을 제거하는 스크립트를 작성하는 것은 매우 편리하다. 마찬가지로 코드를 설치하기 위한 스크립트를 작성하는 것도 편리하다. Make는 makefile에 이와 같은 기능을 phony target으로 포함시킬 수 있다. phony target은 준비사항들을 가질 수 있으며, 자기 자신을 준비사항으로 할 수도 있다.
.PHONY는 특별한 규칙으로 target이 파일이 아니라는 것을 지시한다. 이 규칙을 사용하여 같은 이름을 갖는 파일과 충돌하는 것을 피하고, 수행성능을 개선한다.
phony target을 다른 target의 준비사항에 포함시키면 다른 target이 필요할 때마다 실행된다. phony target은 절대로 최신의 것이 되지 않는다.
명령줄에서 phony target을 실행하려면 make clean과 같이 Make에 phony target을 같이 사용한다.
참고. Phony Target이라하면 "가짜 목적"으로 해석할 수 있으며, .PHONY와 같은 특별한 키워드에서 정의된다.
# Naming our phony targets
.PHONY: clean install
# Removing the executable and the object files
clean:
rm sample main.o example.o
echo clean: make complete
# Installing the final product
install:
cp sample /usr/local
echo install: make complete
Makefile 변수
프로젝트가 커질수록 더 많은 파일을 추가하게된다. 파일 목록에 대해서 반복하다보면 실수로 목록에서 파일을 누락시킬 수도 있다. 이런 경우에는 보다 간단히 하기 위해 목록을 확장하는 변수를 사용한다.
makefile 변수를 선언하고 설정하는 구문은 varname = variable contents와 같으며, 변수를 호출하려면 $(varname)을 사용한다.
# Defining the object files
objects = main.o example.o
# Linking object files
sample: $(objects)
cc -o sample $(objects)
echo sample: make complete
# Compiling source files
main.o: main.c main.h
cc -c main.c
example.o: example.c defs.h
cc -c example.c
# Removing the executable and the object files
clean:
rm sample $(objects)
echo clean: make complete
최종 마무리(Final Touches)
사용하기 편리한 makefile과 전문적인 makefile의 차이를 만드는 몇 가지 특징이 있다. 다음 예제는 몇 가지 특징을 추가한 것이다.
# 1
# Defining the compiler:
CC=gcc
# Defining the object files:
objects = main.o example.o
# 2
# The default rule - compiling our main program:
all: sample
echo all: make complete
# 3
sample: $(objects)
# If we get here, all the dependencies are now built.
# Link it:
$(CC) -o $@ $+
# 4
# Tell make how to build .o files from .c files:
%.o:%.c
$(CC) -c $+
# 5
#Now make sure that make rebuilds files if included headers change:
main.o: main.h defs.h
example.o: example.h defs.h
- 다른 컴파일러에서 동일한 makefile을 이용하도록 하는 경우에 컴파일러에 대한 변수를 사용한다.
- 규칙 매개변수 없이 호출될 때, Make는 첫번째로 만나는 규칙을 실행한다. 첫번째 규칙을 명시적으로 설명하면 사람들이 보다 쉽게 읽을 수 있다. all은 첫번째 규칙에 사용하는 일반적인 이름이다.
- 자동 변수 $@는 "target의 이름"을 뜻한다. 자동 변수 $+는 "공백으로 구분된 모든 준비사항"을 뜻한다. 자동 변수는 Make에서 미리 정의되어 있는 변수다.
- 패턴 규칙은 target에 대한 준비사항을 변환하는 방법을 지시한다. 패턴에서 %는 "하나 이상의 문자"를 뜻하며 준비사항과 target에 있는 동일한 문자열을 가리킨다. 여기서 사용된 패턴은 같은 파일 이름을 갖고 있는 *.c 파일을 *.o 파일로 변환하는 방법을 지시한다. 자동 변수 $+는 "공백으로 구분된 모든 준비사항"을 뜻한다.
- 이러한 규칙들은 "암시적인 규칙"에 의존한다. Make는 *.h 파일을 의존하는 *.o 파일로 변환하는 내장 패턴을 갖고 있다. 이러한 규칙은 적절한 *.o 파일을 위한 준비사항에 대한 정의를 포함한다.
문제점과 알아둘점
- 이 글은 GNU Make에 관해서 쓴 것이다. 다른 Unix 시스템에서는 다른 버전의 Make를 사용하며, 다소 변경된 것일 수도 있다.
- Make 규칙은 각 명령 앞에 탭을 필요로한다. 일련의 공백은 동작하지 않는다.
- Make는 target에서 준비사항으로 뒤로 동작한다.
마지막으로
Make는 여기서 언급하지 않은 많은 기능들을 갖고 있으며, 파일이 다른 변경된 파일에 의존하는 경우에 유용하다. Make의 기능들을 탐험하고, 자신만의 makefile들을 만들도록 한다.
관련자료