make
파일관리유틸리티로 각파일간의 종속관계를 파악하고 Makefile에 기술된대로 컴파일명령이나 쉘명령을 순차적으로 실행해주는것으로 빌드를 자동화하여 개발자의 수고를 덜어준다.
프로그램의 종속구조를 파악하고 관리가 용이해진다.
all : diary
diary : main.o calendar.o memo.o
gcc -W -Wall -o diary memo.o calendar.o main.o
main.o : main.c
gcc -W -Wall -c -o main.o main.c
calendar.o : calendar.c
gcc -W -Wall -c -o calendar.o calendar.c
memo.o : memo.c
gcc -W -Wall -c -o memo.o memo.c
첫번째 타겟 의 dependency인 diary로 이동 diary: 는 main, calendar, memo 디펜던시가 있는데 main.o calendar.o main.o가 생성이 되었는지 확인하고 안되어 있으면 해당 타겟을 먼저 실행한다.
해당 타겟이 모두 준비되면 gcc -W -Wall -o diary memo.o calendar.o main.o 가 실행되어 diary라는 실행파일을 만들어낸다.
빌드 및 실행하면..
leeyosam$ make
gcc -W -Wall -c -o main.o main.c
gcc -W -Wall -c -o calendar.o calendar.c
gcc -W -Wall -c -o memo.o memo.c
gcc -W -Wall -o diary memo.o calendar.o main.o
leeyosam$ ./diary
function memo.
function calendar.
위 상태에서 calendar.c파일을 수정하면 이 파일만 다시 빌드 한 후 링크한다.
leeyosam$ make
gcc -W -Wall -c -o calendar.o calendar.c
gcc -W -Wall -o diary memo.o calendar.o main.o
Makefile기본 구조 및 규칙
CC = gcc
target1 : dependency1 dependency2
command1
command2
1) 명령의 시작은 반드시 탭으로 시작된다.
2) 비어 있는 행은 무시된다.
3) #을 만나면 개행을 만날때까지 무시한다.
4) 행이 길어지면 \ 문자로 다음행에 이어서 기술이 가능하다.
diary : memo.o calendar.o\
main.o
==> diary : memo.o calendar.o main.o
5) ;는 명령행을 나눌때 사용한다.
target1 : dependency1 dependency2; command1
6) 종속항목이 없는 타겟도 가능하다
clean :
rm -rf *.0 target1 target2
매크로 규칙
1) 매크로명 = 매크로내용 형식
2) #은 주석의 시작
3) 여러행 기술시 \ 사용
4) 매크로 참조시에는 소괄호나 중괄호사용 가능하고 $를 앞에 붙이는데 쉘의 환경변수를 사용할때 중괄호를 사용하므로 해깔리지 않도록 소괄호사용이 권장된다.
NAME = string
$(NAME)
5) 정의되지 않은 매크로는 빈문자(null)로 치환된다.
6) 중복정의하게 되면 마지막 값이 사용된다.
7) 매크로 정의시 이전에 정의된 매크로참조가 가능하다.
8) =, :=, +=, ?= 등 대입이 가능하다.
= 는 매크로정의 순서와 상관없이 매크로들을 모두 반영한다.
:= 는 매크로정의 순서 그대로 반영한다.
+= 는 기존 매크로에 추가한다.
?= 는 정의되지 않았으면 새로 반영하고 정의되어 있으면 반영하지 않는다.
A = $(B) BB
B = $(C) CC
C = D
==> 이 경우 A는 D CC BB 가 된다.
a := $(b) bb
b := $(c) cc
c := d
==> 이 경우 a 는 bb가 된다. 순차적으로 반영이 되므로 a를 정의할때의 bb가 b의 값이 새로 정의되더라도 a에 반영되지 않는다.
내부적으로 정의되어 있는 매크로의 사용
make -p명령으로 내부 매크로리스트를 확인할 수 있다. 미리 정의된 매크로들을 추가로 정의할 필요가 없다.
많이 사용하는 매크로들은 다음 명령어로 확인 가능하다
make -p | grep ^[[:alpha:]]*[[:space:]]*=[[:space:]]
매크로 이름 | 설명 | 기본값 |
AR | 아카이브 관리 프로그램 | ar |
AS | 어셈블러 | as |
CC | C 컴파일러 | cc |
CXX | C++ 컴파일러 | g++ |
CO | RCS checkout 프로그램 | co |
CPP | C 전처리기 | cc –E |
FC | 포트란 컴파일러 | f77 |
GET | SCCS 관련 프로그램 | get |
LD | 링크 | ld |
LEX | 스캐너 코드 생성기 | lex |
PC | 파스칼 컴파일러 | pc |
YACC | 파서 코드 생성기 | yacc |
MAKEINFO | Texinfo 파일을 Info 파일로 변환 | makeinfo |
TeX 문서로부터 TeX DVI 생성 | ||
TEXI2DVI | Texinfo 파일을 dvi 파일로 변환 프로그램 | texi2dvi |
WEAVE | Web을 TeX로 변환 | weave |
CWEAVE | C Web을 TeX로 변환 | cweave |
TANGLE | Web을 파스칼로 변환 | tangle |
CTANGLE | C Web을 C로 변환 | ctangle |
RM | 파일을 지우는 명령 | rm –f |
ARFLAGS | ar 플래그 | rv |
ASFLAGS | 어셈블러 플래그 | |
CFLAGS | C 컴파일러 플래그 | |
CXXFLAGS | C++ 컴파일러 플래그 | |
COFLAGS | RCS co 플래그 | |
CPPFLAGS | C 전처리기 플래그 | |
FFLAGS | 포트란 컴파일러 플래그 | |
GFLAGS | SCCS get 플래그 | |
LDFLAGS | 링크 플래그 | |
LFLAGS | Lex 플래그 | |
PFLAGS | 파스칼 컴파일러 플래그 | |
YFLAGS | Yacc 플래그 |
$? | 현재의 타겟보다 최근에 변경된 종속 항목 리스트 (확장자 규칙에서 사용 불가) |
$^ | 현재 타겟의 종속 항목 리스트 (확장자 규칙에서 사용 불가) |
$@ | 현재 타겟의 이름 |
$< | 현재 타겟보다 최근에 변경된 종속 항목 리스트 (확장자 규칙에서만 사용 가능) |
$* | 현재 타겟보다 최근에 변경된 현재 종속 항목의 이름(확장자 제외) (확장자 규칙에서만 사용 가능) |
$% | 현재의 타겟이 라이브러리 모듈일 때 .o 파일에 대응되는 이름 |
${@F}: 현재 타겟의 파일 부분(target) ${<F}: 현재 디펜던시항목의 파일 부분(test.c) ${@D}: 현재 타겟의 디렉토리 부분(/temp) ${<D}: 현재 디펜던시항목의 디렉토리 부분(/usr/local/c) |
$@와 $^는 다음과 같이 사용될 수 있다.
target : dependency1.c dependency2.c
gcc -o target dependency1.c dependency2.c
==>
target : dependency1.c dependency2.c
gcc -o $@ $^
${@F}와 ${<F}, 그리고 ${@D}와 ${<D}는 다음 타겟과 디펜던시가 있을 경우
/temp/target : /usr/local/c/test.c
==>
${@F} : target
${<F} : test.c
${@D} : /temp
${<D} : /usr/local/c
확장자 규칙의 사용
make -p로 출력되는 내용 중에 %.o : %c 가 있다.
%o : %c
$(COMPILE.c) $(OUTPUT_OPTION) $<
이는 *.o타겟이고 확장자가 c인 c 코드파일을 만나면 c -o $@ $<를 수행하라는것이다.
다음 Makefile은 맨처음 이 문서에서 작성한 Makefile과 동일한 동작을 수행한다.
OBJECTS = memo.o calendar.o main.o
all : diary
diary : $(OBJECTS)
$(CC) -o $@ $^
OBJECTS에 정의된 dependency들을 처리하는데 타겟이 *.o이고 확장자가 *.c인것들을 만나게 된다. 이 때 make는 다음 순서로 처리하게 된다.
1) Makefile에서 diary를 생성하기 위해 make는 dependency들을 살펴보고 각 각을 타겟으로 설정한다.
2) diary는 memo.o에 의존하고 있고 memo.o는 만들어져 있지 않으면 memo.o를 만들어야 하는게 룰이 정의되어 있지 않다.
3) 따라서 make는 내부 확장자 규칙 .o를 참조하여 다음 기준으로 현재 디렉토리에서 memo.o를 생성할 파일을 찾는다
- 확장자를 제외하고 memo.0와 같은 이름이 있어야 한다. memo.c가 해당된다.
- 중요확장자를 가지고 있어야 한다. .c, .cc, .cpp, .s, .S, ... 등
- 내부확장자 규칙에 따라 memo.o를 만드는데 사용할 수 있어야 한다. make -p로 확인해본바 %.o : %.c가 있기 떄문에 가능하다.
4) 내부정의 확장자 규칙에 따라 memo.o를 생성한다. cc -c -o memo.o memo.c 가 수행된다.
내부확장자 규칙을 변경하고자 하는 경우에는 .SUFFIXES 타겟으로 가능하다. 다음 Makefile예는 -DDEBUG 옵션을 추가하는 예제이다.
OBJECTS = memo.o calendar.o main.o
.SUFFIXES : .o .c
$(CC) -DDEBUG -c -o $@ $<
all : diary
diary : $(OBJECTS)
$(CC) -o $@ $^
leeyosam$ tree
.
├── Makefile
├── calendar
│ ├── Makefile
│ └── calendar.c
├── include
│ └── diary.h
├── main
│ ├── Makefile
│ └── main.c
└── memo
├── Makefile
└── memo.c
함수의 사용
make는 Makefile에서 사용할 수 있는 여러 함수들을 제공한다.
$(shell 쉘명령어) : 쉘명령어를 수행하고 결과를 리턴한다.
SRCS = $(shell ls *.c)
echo :
@echo $(SRCS)
$(subst 찾을문자열, 변경할문자열, 목표문자열) : 목표문자열에서 찾을문자열을 찾아서 변경할문자열로 변경한다.
STR = $(subst like, LIKE, I like you)
==> I LIKE you 를 찍게 된다.
$(patsubst 패턴, 변경문자열, 목표문자열) : subst와 다른점은 패턴을 발견하면 변경문자열로 변경한다. %기호가 사용될 수 있다.
STR = $(patsubst %.c, %.o, memo.c main.c ABCD)
==> memo.o main.o ABCD
$(매크로명: 패턴=치환할문자열) : 매크로에서 패턴을 발견하면 변경한다.
MACRO = memo.c main.c ABCD
STR = $(MACRO:%.c=%.o)
$(sort 문자열) : 문자열의 인자들을 정렬하며 중복된것은 하나로 인식한다.
MACRO = bbb aaa ccc aaa ddd
STR = $(sort $(MACRO))
==> aaa bbb ccc ddd
$(strip 문자열) : 인자로 오는 문자열의 앞뒤의 공백을 제거하고 문자열내의 공백은 한칸으로 줄인다. 가령 'I love you. ' 같은 문자열은 'I love you.'로 변경된다.
$(filter 패턴, 문자열) : 패턴과 일치하는 문자열만 걸러준다. 소스와 헤더파일이 섞여 있는 경우 소스와 헤더파일을 각각 읽어올때 유용하다.
FILES = memo.c head.h mein.c asm.S diary.h
SRCS = $(filter %c %S, $(FILES))
HEADERS = $(filter %h, $(FILES))
$(filter-out 패턴, 문자열) : filter 의 반대
$(findstring 찾을 문자열, 대상문자열)
$(words 문자열) : 문자열내에서 사용된 단어들의 개수를 리턴한다.
$(word [n], 문자열) : 문자열내의 n번째 문자열을 리턴한다.
$(wordlist 시작번호, 끝번호, 문자열) : 문자열의 시작번호와 끝번호사이의 문자열을 리턴한다.
$(firstword 문자열) : 문자열에서 첫번째 단어를 리턴한다.
$(join 문자열, 문자열) : 앞뒤의 문자열을 합친다.
$(dir 문자열) : 문자열에서 디렉토리부분만 리턴한다.
PATHS = /bin/ls /sbin/ifconfig memo/memo.c Makefile
echo :
@echo $(dir $(PATHS))
==> /bin/ /sbin/ memo/ ./
$(notdir 문자열) : 문자열에서 디렉토리부분만 제외하고 리턴한다.
==> ls i fconfig memo.c Makefile
$(suffix 문자열) : 문자열에서 확장자만 리턴한다.
FILES = src/memo.c asm/asm.S for.f ls /etc/resolv.conf
echo :
@echo $(suffix $(FILES))
==> .c .S .f .conf
$(basename 문자열) : 문자열에서 점과 확장자를 제외한 순수한 파일명만 리턴한다.
==> src/memo asm/asm for ls /etc/resolv
$(addsuffix 접미사, 문자열) : 문자열에 접미사를 추가한다.
$(addprefix 접두사, 문자열) : 문자열에 접두사를 추가한다.
$(wildcard 패턴) : wildcard 패턴결과를 리턴한다.
$(foreach 변수명, 대입문자열, 확장문자열) : 변수명에 대입문자열을 단어별로 대입하여 넣고 그 변수를 ㅎ ㅘㄱ장문자열에서 사용한다.
SRCS = $(foreach str, a b c, $(str).c)
echo :
@echo $(SRCS)
==> a.c b.c c.c
주로 다음과 같이 반복확장시 유용하다.
SRCS = $(foreach dir, . memo main calendar, $(wildcard $(dir)/*.c))
echo :
@echo $(SRCS)