1. 개발 도구
1.1. 다중 소스 파일의 문제점
큰 프로그램을 만들 때 조그만 변경 사항 때문에 전체를 다시 빌드하는 것은 시간 낭비가 심하다. 또한 소스 파일이 꼬여 있을 경우 수정된 부분에 영향을 받는 모든 모듈을 재 컴파일 해야 하는데 이를 새로 컴파일 하지 않는다면, 결과적으로 만들어지는 프로그램은 올바르게 작동하지 않을 수 있다. make 유틸리티는 이런 경우 변경 사항에 의해 영향을 받는 모든 파일들을 필요할 때 새로 컴파일 하도록 만들어 줌으로써 이 두가지 문제를 모두 해결한다.
1.2. make 명령과 makefile
make 명령은 스스로 응용프로그램을 빌드하는 방법을 알지는 못한다. make에게 응용프로그램을 어떻게 만드는지 반드시 알려주어야 하는데, 이 파일을 makefile이라고 한다. makefile은 종속성과 법칙으로 구성된다. 종속성은 대상(만들 파일)과 대상이 종속되는 소스 파일의 집합을 가진다. 법칙은 종속 파일로부터 대상을 만드는 방법을 설명한다.
make 명령은 makefile을 읽고 대상 파일 혹은 만들어야 할 파일을 결정한다. 그런 다음 소스 파일의 날짜와 시간을 비교해 대상을 만들기 위해 어떤 법칙을 호출해야 할지 결정한다.
옵 션 |
기 능 |
-k |
에러를 발견하더라도 멈추지 않고 계속 진행한다 |
-n |
실제로 작업을 수행하지는 않고 어떤 작업을 수행할 것인지 출력한다 |
-f <filename> |
메이크파일을 지정한다. 지정하지 않으면 makefile, 없으면 Makefile을 사용한다. |
<make의 옵션>
make가 특정 대상을 빌드하도록 지시하려면, 대상 이름을 make의 매개변수로 전달하면 된다. 그렇지 않으면 make는 makefile에 나열된 첫 번째 대상을 메이크한다. 보통 makefile의 첫 번째 대상으로 all을 지정하고, 다른 대상을 all의 종속 요소로 나열한다.
1.2.1. 종속성(Dependency)
종속속성은 최종 응용프로그램의 각 파일이 소스 파일에 대해 어떤 관련이 있는지를 지정한다. 즉, 아래와 같은 코드는 좌측의 결과물(파일)을 만들기 위해 우측의 파일들이 필요하다는 것을 의미한다.
myapp: main.o 2.o 3.o main.o: main.c a.h 2.o: 2.c a.h b.h 3.o: 3.c b.h c.h |
<makefile에서 종속성 예제>
여러개의 파일을 한꺼번에 메이크하고 싶다면 Phony Target "all"을 사용할 수 있다. 응용프로그램이 바이너리 파일 myapp와 매뉴얼 페이지 myapp.1로 이루어진다고 하면 다음과 같은 코드로 이를 지정할 수 있다.
all: myapp myapp.1 |
<Phony Target 예제>
1.2.2. 법칙(Rule)
makefile의 두 번째 부분은 대상을 어떻게 만드는지 설명하는 법칙을 지정한다. 법칙은 반드시 탭으로 시작해야 한다. 위에서 나왔던 종속성을 지정했던 코드에 아래와 같이 법칙을 추가해 파일을 만드는 방법을 지정할 수 있다.
myapp: main.o 2.o 3.o gcc -o myapp main.o 2.o 3.o main.o: main.c a.h gcc -c main.c 2.o: 2.c a.h b.h gcc -c 2.c 3.o: 3.c b.h c.h gcc -c 3.c |
<makefile에 종속성과 Rule을 추가한 모습>
1.2.3. makefile의 주석과 매크로
makefile
의 주석은 #으로 시작하고 줄의 끝까지 계속된다. MACRONAME=value와 같이 작성해 매크로를 정의할 수 있다. 그런 다음
$(MACRONAME) 또는 ${MACRONAME}을 사용해 액세스 한다.
$MACRONAME과 같은 형식을 쓸 수 있는 make도 있다. value뒤쪽으로 공백이 있으면 공백도 매크로 값으로 적용된다. make cc=c89 또는 make "cc = c89"와 같은 방법으로 매크로 인자를 직접 지정할 수 있다. 이렇게 지정된 매크로는 makefile 내부에서 지정된 매크로보다 우선한다.
매크로 이름 |
기 능 |
$? |
현재 대상보다 더 최근에 변경된 파일 이름 |
$@ |
현재 대상의 이름 |
$< |
현재 파일 이름 |
$* |
접미사(suffix)를 제거한 현재 파일 이름 |
<make의 내장 매크로>
1.2.4. 내장 법칙(Inference Rule)
특별히 기술하지 않아도 make는 기본적으로 동작하는 법칙을 가지고 있다. make -p 명령으로 내장 법칙을 확인할 수 있다.
1.2.5. 접미사(Suffix)와 패턴 법칙
이전 접미사를 가진 파일로부터 다른 접미사를 가진 파일로 변환하는 방법으로 다음의 두 가지 방법을 사용할 수 있다.
.cpp.o: $(CC) -xc++ $(CFLAGS) -I$(INCLUDE) -c $< |
%.cpp: %.o $(CC) -xc++ $(CFLAGS) -I$(INCLUDE) -c $< |
<접미사에 따라 파일 형식을 변환하는 패턴 법칙>
1.2.6. make로 라이브러리 관리하기
make 명령은 라이브러리를 쉽게 관리할 수 있는 특별한 문법을 가지고 있다. lib(file.o)인데, 객체 파일 file.o를 라이브러리 lib.a에 포함시킨다는 의미이다. make는 다음과 같은 유사한 내장 라이브러리 관리 법칙을 가지고 있다.
.c.a: $(CC) -c $(CFLAGS) $< $(AR) $(ARFLAGS) $@ $*.o |
<내장 라이브러리 관리 법칙>
1.1. Makefile 요약
프로젝트의 소스 파일이 많을 경우, make를 이용해 정의된 시퀀스와 조건에 따라 빌드 과정을 자동으로 수행할 수 있다. 또한 빌드 시 재 컴파일과 링킹이 필요한 파일을 선별해 작업을 수행할 수 있으므로 빌드 시간이 단축되고, 컴파일 해야 하는 파일을 빼먹는 일도 막아준다.
make는 기본적으로 makefile을 참고해 빌드 과정을 수행한다. makefile은 크게 Dependency와 Rule로 구성되며, 추가적으로 매크로를 정의해 사용할 수 있다. Rule은 반드시 맨 앞이 탭으로 시작되어야 한다.
make를 실행할 때 어떤 대상(Dependency Name)에 대해 작업을 수행할 지 지정할 수 있으며, 지정하지 않으면 makefile의 첫 번째 대상을 수행한다.
1.2. 실습
make를 이용해 빌드를 하기 위해 다음과 같은 C 소스 파일을 사용한다. main() 함수에서 두 함수를 부르고 있으며, 각 함수는 각자 다른 C 소스 파일에 정의되어 있다. 따라서, 세 소스 파일을 컴파일 한 후 빌드하는 과정이 필요하다.
또한 "a.h", "b.h", "c.h" 세 대의 빈 헤더 파일을 생성했다. 이는 무의미한 파일이지만 makefile의 Dependency를 설명하기 위해 사용되었다.
<세 개의 C 소스 파일>
<빈 헤더 파일 세 개를 생성>
이제 소스 파일 간의 종속 관계와 소스 파일과 헤더 파일 간의 종속 관계를 고려해 아래와 같이 makefile을 만든 후, make를 이용해 빌드 작업을 수행한다.
<Makefile1 이라는 이름을 가진 메이크파일>
메이크파일의 이름이 makefile이나 Makefile인 경우에는 그냥 make를 실행하면 자동으로 찾아서 적용하지만, 그 외 다른 이름의 메이크파일을 사용하기 위해서는 -f 옵션으로 파일명을 명시해 주어야 한다.
<make를 수행한 후 최종 실행 파일 myapp가 생성된 모습>
1.3. 작동 원리
Makefile1
에는 4개의 종속성이 있다. make시 따로 대상을 지정하지 않았으므로, 첫 번째 대상인 myapp에 대해 작업을 수행한다.
myapp는 main.o 2.o 3.o에 송족되어있다. 따라서 make는 종속성을 판단해 main.o 2.o 3.o를 우선 처리한
뒤 myapp를 처리한다. 이 때, main.o 2.o 3.o 파일의 최종 갱신 일자를 확인해 myapp의 최종 갱신 일자
이후인 경우에만 작업을 수행한다.
위 경우에는 myapp파일이 아예 없는 상태이므로 세 개의 종속성을 모두 선 처리해야 한다.
main.o
2.o 3.o는 각각 자신의 소스 코드 파일과 소스 코드 파일에서 사용한 헤더 파일에 종속되어있다. 역시 파일들의 최종 갱신일을
비교한다. 이 때, 소스 코드 파일과 헤더 파일은 Makefile1에 종속성과 룰이 지정되어 있지 않으므로, 최종 갱신일 비교
후에도 종속성 처리를 할 수가 없다.
다만, 최종 갱신일을 기준으로 Rule을 수행해야 하는지 여부만 판단한다. 만약 종속성을 가지고 있는 모든 파일의 최종 갱신일이 작업 대상 파일의 갱신일보다 이전이라면 이 단계에서 Rule은 수행될 필요가 없다.
2. 메이크파일에 매크로 사용하기
2.1. 주석 & 매크로 요약
makefile에서 주석은 #로 시작하며 그 위치부터 줄의 끝까지를 주석으로 인식한다.
MACRONAME=value 와 같은 방식으로 매크로를 정의할 수 있다. 사용할 때는 $(MACRONAME) 또는 ${MACRONAME} 둘 중 한 가지 방법으로 액세스한다. 만약 =뒤쪽으로 공백이 있다면 공백도 매크로의 값에 포함된다.
make를 실행할 때 인자를 넘기는 방식으로 매크로를 지정할 수도 있다. make CC=c89또는 make "CC=c89"와 같은 방법으로 매크로를 정의한다. 이렇게 인수로 지정한 매크로는 makefile에 있는 매크로를 덮어쓴다.
2.2. 실습
앞에서 사용된 Makefile1을 다음과 같이 수정해 Makefile2를 만들었다.
<매크로와 주석을 사용한 Makefile2>
<다시 빌드를 수행한 모습>
2.3. 작동원리
CC는
컴파일러 파일 이름을 지정한 매크로 변수이다. 수행한 모습을 보면 CC의 값인 gcc로 대체되어 실행된 것을 알 수 있다.
INCLUDE는 인클루드 파일들이 위치한 경로가 지정되어 있다. 즉, gcc의 -I옵션 뒷 부분에 들어간다. -I 옵션은 기본
인클루드 경로 이외에 추가적인 인클루드 경로를 지정하는 옵션이다. CFLAGS는 컴파일 과정에서 공통적으로 수행될 옵션이다.
CFLAG를 두 개 선언하고 하나는 주석 처리를 해 놓았는데, 경우에 따라 디버그용과 릴리즈용을 선택해 빌드할 수 있다. 디버그 모드용 CFLAG에 있는 -g는 GDB를 위한 디버깅 정보를 실행 파일에 포함시키라는 의미이다. 릴리즈 모드용 CFLAG에 있는 -O 옵션은 결과물의 실행 속도를 우선으로 최적화 하여 컴파일 하는 옵션이다.
3. 다중 대상
3.1. 다중 대상 요약
하나 이상의 최종 결과 파일을 만드는 경우나, 프로젝트에 관련된 각각 다른 작업들을 수행해야 하는 경우에 명령의 몇 가지 그룹을 한 자리에 모으면 편리하다. 즉, 대상을 여러개 만들어 make 실행 시 인자로 넘겨 원하는 작업을 수행하도록 할 수 있다.
3.2. 실습
앞에서 만든 Makefile2를 수정해 불필요한 객체 파일을 삭제하는 clean 대상과 응용프로그램을 다른 디렉터리로 복사(설치)하는 install 대상을 추가했다.
<clean과 install을 추가한 Makefile3>
<clean과 install을 대상으로 make를 수행한 모습>
3.3. 작동원리
clean을 대상으로 make를 실행하면 모든 오브젝트 파일을 삭제한다.
install 을 대상으로 make를 실행하면 myapp를 만든 후, 이를 지정된 디렉터리 INSTDIR로 복사하고 실행할 수 있도록 파일 권한 설정을 변경한다. 디렉터리가 없다면 설치는 실패하고 메시지를 출력한다. install이 myapp에 종속된 것이 중요한데, 이는 설치 전에 일단 설치할 파일이 만들어져 있어야 하기 때문이다.
install의 룰에 사용된 쉘 스크립트는 보기에는 여러 줄이지만 실제로는 한 줄이다. 여러 줄에 걸쳐 쓰게 되면 에러가 난다. \ 기호는 앞 뒤의 줄을 하나로 간주하도록 한다.
4. 라이브러리 관리
4.1. 라이브러리 관리 요약
make는 라이브러리를 쉽게 관리하기 위한 특별한 문법을 가지고 있다. lib(file.o)와 같은 표현은 file.o를 라이브러리 lib.a에 포함시킨다는 의미이다. 이 표현은 make를 수행하면 라이브러리 관리 명령인 ar을 호출한다. 즉, ar rv lib.a file.o로 대체된다.
4.2. 실습
오브젝트 파일을 생성하는 과정에서 추론법칙을 사용해 Rule을 명시하지 않았다.
<라이브러리 관리 기능을 추가한 Makefile4>
<실행 결과>
[ar 옵션]
t : library 내용보기
p : library 소스보기
r : library insert or replace
s : Index 생성
x : 묶은 파일 풀기
d : 삭제
4.3. 동작원리
MYLIB는
생성하거나 업데이트 할 라이브러리 이름을 지정하고 있다. myapp는 main.o와 mylib.a를 합쳐서 생성된다. 따라서
myapp를 빌드하면 mylib.a가 자동으로 빌드된다. mylib Dependency를 보면 $(MYLIB)(2.o)와 같은
표현이 있는데 이는 mylib.a(2.o)와 같고 이는 2.o를 mylib.a에 추가하라는 make의 문법이다.
이 문법은 실행 결과를 보면 ar rv mylib.a 2.o로 대체된 것을 알 수 있다. 즉, 라이브러리를 관리할 수 있는 간결한 표현이다.
[출처] make (2/2)|작성자 화무
'Academy I > Tech Academy' 카테고리의 다른 글
[Linux]man 활용 (0) | 2014.12.31 |
---|---|
[Linux]설치된 Command의 경로, 정보를 얻는 명령들 (0) | 2014.12.31 |
Linux Shell Programming (0) | 2014.12.22 |
Linux gdb 사용법 [II] (0) | 2014.12.22 |
VirtualBox의 Linux(CentOS)에서 공유폴더 설정 (0) | 2014.12.22 |
Linux gdb 사용법 [I] (0) | 2014.12.19 |
Linux make 사용법 [I] (0) | 2014.12.18 |
Unix System Programming 10 (0) | 2014.12.16 |