책에서는 build 를 작성이라 번역했는데 아무리 봐도 문맥상 너무 그 내용이 맞질 않아서 전부 다 빌드로 번역했다. 작성이라는 단어가 코드를 write 한건지 프로그램을 build 한건지 도통 감이 안 잡힌다. 아마 1990년대 책이라 그런 것일 수도 있겠다. 아직 IT 용어 자체가 정립되지 않은 시기여서 작성이라 번역한 것이라 생각된다
단말기라는 용어도 요즈음엔 잘 이해하기 어려울 것 같다는 생각이 들어서 그냥 터미널이라고 번역을 했다.
다음은 program
을 빌드하는 경우에 따라 서로 다른 버전을 생성할 수 있는 명령이다.
$ make program
위 명령어를 입력하게 되면 해당 파일(program
)을 생성하는 데 필요한 모든 컴파일과 링크 작업을 수행할 수 있다. make
유틸리티를 사용하면 수많은 cc
컴파일러 명령들을 일일이 손으로 입력하지 않고도 자동으로 수행할 수 있다.
위 예제에 나오는 program
같은 파일을 작업할 타깃(target)
이라 하며, 타깃(program)은 이른바 필요 항목(prerequisites)
또는 종속 항목(depedents)
이라는 하나 이상의 파일들을 기반으로 빌드(build)
된다. 그리고 이들 각 파일들은 또다시 다른 파일들을 필요 항목으로 갖고 있다.
make
의 강점은 '소스 파일과 오브젝트 파일', '오브젝트 파일과 실행 파일'을 잇는 상호 의존적인 계증구조에 민감하게 반응한다는 점이다. 기술 파일
(description file
, 대개 makefile
이나 Makefile
이란 이름이 붙여진다) 내의 의존 관계를 명시해주는 것은 개발자의 몫이지만, make
또한 기술 파일에 설정된 환경을 스스로 이해하여 의존 관계를 직접 결정할 수 있다.
make
는 기존 파일의 이름, 그들의 최종 수정된 시각, 내장된 규칙(또는 규칙군) 등을 이용하여 빌드할 파일을 골라내고 또 어떻게 빌드할지 결정한다.
description file
)아래와 같은 구성 요소로 프로그램을 빌드한다고 가정하자:
main.c
, iodat.c
, dorun.c
)lo.s
)/usr/fred/lib/crtn.a
)프로그램을 직접 컴파일하려면 아래와 같은 명령을 일일이 입력해야 한다:
cc -c main.c
cc -c iodat.c
cc -c dorun.c
as -o lo.o lo.s
cc -o program main.o iodat.o dorun.o lo.o /usr/fred/lib/crtn.a
위 내용을 기술 파일로 옮겨 적게 되면 다음과 같다:
program: main.o iodat.o dorun.o lo.o /usr/fred/lib/crtn.a
cc -o program main.o iodat.o dorun.o /usr/fred/lib/crtn.a
main.o: main.c
cc -c main.c
iodat.o: iodat.c
cc -c iodat.c
dorun.o: dorun.c
cc -c dorun.c
lo.o: lo.s
as -o lo.o lo.s
이 기술 파일에는 모두 다섯 개의 항목이 있다. 각 항목에는 콜론(:
)을 포함하는 행(종속 행
: dependency line
또는 규칙 행
: rule line
)과 탭 문자로 시작하는 하나 이상의 명령 행이 있다. 종속 행에서 콜론 왼쪽은 타깃이며, 콜론 오른쪽은 타깃의 필요 항목이다. 탭 문자로 시작하는 명령 행은 타깃을 필요 항목에서 가져와 빌드하는 방법을 제시한다.
앞 절에서 소개한 기술 파일을 사용해 타깃(program)
을 빌드하도록 요청하더라도 make
는 2행의 명령을 곧바로 실행하지는 않는다. make
는 program
이란 이름을 가진 파일이 존재하는지 유무를 먼저 확인한 다음, program
파일이 존재하 경우 main.o
, iodat.o
, dorun.o
, lo.o
파일과 /usr/fred/lib/crtn.a
라이브러리 등을 검사하여 그 가운데 program
보다 나중에 생성된 것이 있는지 확인한다.
program
이 빌드되는 데 필요한 항목들을 마지막으로 빌드한 이후 다시 수정하지 않았다면 make
는 program
을 다시 빌드(rebuild
)할 필요가 없다고 판단하고 별다른 명령을 내리지 않은 채 종료한다.
하지만 make
는 그와 같은 판단을 내리기 전에 몇 가지 작업을 더 수행한다. 각 .o
파일과 그들의 필요 항목들을 먼저 검사하는 것이다. 예를 들어 3행을 보면 main.o
가 main.c
를 필요로 한다(의존하고 있다)는 사실을 알 수 있다.
따라서 마지막으로 main.o
가 빌드된 이후 main.c
가 변경되었을 경우 make
는 4행에 있는 컴파일 명령을 실행하고 main.o
를 새로 빌드한다. 결국 make
는 program
의 하위 필요 항목들을 모두 확인하고 또 make
의 필요 항목들을 모두 최신 파일로 바꾼 후에야 2행의 명령을 실행한다.
프로그램을 빌드한다는 것을 곧 정확한 순서에 따라 실행되어야 하는 일련의 명령을 구성하는 것이다. 그리고 그 일련의 과정에서 최종 파일을 빌드하는 작업을 바로
make
에 맡길 수 있다.
plot_prompt: basic.o prompt.o
cc -o plot_prompt basic.o prompt.o
plot_win: basic.o window.o
cc -o plot_win basic.o window.o
basic.o: basic.c
cc -c basic.c
prompt.o: prompt.c
cc -c prompt.c
window.o: window.c
cc -c window.c
터미널이나 비트맵 방식의 워크스테이션에서 동작하는 도면 출력 프로그램을 빌드하는 경우 프로그램의 핵심적인 내용은 어떤 환경에서 동작하느냐와는 관계없이 계산과 파일 처리에 연관된 요소들로 이뤄져 있다. 따라서 이 프로그램은 서로 다른 두 버전으로 배포할 수 있는데, 프로그램 내용이 바뀔 때마다 해당 버전을 리빌드해야 한다.
여기서 처음 실행 파일을 만들 때는 반드시 basic.c
를 컴파일해야 한다. 그러나 소스 파일을 변경하지 않는 한, 그리고 basic.o
를 지우지 않는 한 이후에 다시 컴파일할 필요는 없다. 반면에 prompt.c
를 변경하고 plot_prompt
를 다시 만들면, make
는 바뀐 시각을 확인한 다음 prompt.c
를 다시 컴파일하고 링크하게 된다.
make
실행makefile
또는 Makefile
이다.make
명령을 입력할 시점에서 현재 디렉터리이다.위와 같은 조건이라면 아래 명령을 입력하여 기술 파일에 있는 어떤 타깃이든 빌드할 수 있다:
make target
타깃을 빌드하는 데 필요한 각 명령 행은 터미널에 표시된 다음 실행된다. 일부 중간 단계의 파일들이 존재하다가 갱신되는 경우 make
는 그와 관련된 명령들은 건너뛴다. 타깃(program)을 리빌드하는 데 필요한 최소한의 명령만 실행 한다는 얘기이다.
경우에 따라 여러 타깃을 한 번에 make
에 전달하여 실행시킬 수도 있다. 그리고 결과는 make
명령을 여러 번 연속해서 실행할 때와 동일하다.
make main.o dorun.o lo.o
마지막으로 아래처럼 타깃 이름 없이 간단히 입력하기만 하면 기술 파일에 포함된 첫 번째 타깃 이 만들어진다.
make
make
에서 가장 중요한 구문 규칙은 모든 명령들이 탭 문자로 시작한다는 것이다. 프로그래머들이 make
를 사용하면서 가장 많이 저지르는 실수가 바로 명령의 첫 칸에 탭이 아니라 여백 문자(space)를 넣는 것이다.
make
는 첫 문자로 나오는 탭을 통해 명령을 인식하기 때문에, 다른 행들이 탭으로 시작하지 않도록 확인해야 한다. 단지 첫 문자만이 이와 같은 특별한 결과를 만들기 때문에 첫 문자 이후에 행의 어디서나 탭을 자유롭게 쓸 수는 있다.
행이 길어질 경우, 끝에 역슬래시()를 추가하여 계속 이어나갈 수 있다. 여기서도 역슬래시가 행이 바뀌는 문자 바로 앞에 위치해야 한다는 점을 꼭 확인하기 바란다.
make
파일은 비어있는 행과 한 행에 샵 기호(#)가 나오면 그로부터 행의 끝까지 사이에 있는 문자들 역시 무시한다. 따라서 make
에서 #
은 주석의 시작을 표시한다.
기술 파일에서 명령을 제작기 행으로 나눠 사용할 필요는 없다. 종속 항목 행 뒤에 세미콜론(;
)을 사용하여 명령을 계속 이어나가도 된다. 이는 기술 파일에서 모든 명령 행이 탭 문자로 시작해야 한다는 규칙에 대한 유일한 예외이다.
plot_prompt: prompt.o; cc -c plot_prompt prompt.o
단일한 타깃을 여러 종속 항목 행들에 표기할 수도 있다. 이 방법은 서로 다른 종류의 종속 관계를 표시할 때 쓰면 특히 유용하다.
file.o: /usr/src/file.c
cc -c /usr/src/file.c
.
.
.
file.o: global.h defs.h
위 예제에서 실제 file.o
를 만들어내는 명령은 첫 번째 종속 관계 행 아래에 위치한다. 그러나 설사 /usr/src/file.c
가 변경되지 않더라도 .h
파일 가운데 어느 하나라도 바뀌면 make
는 file.o
가 최신 버전이 아니라는 사실을 알아채고 다시 컴파일한다.
마지막으로 필수 항목이 없는 타깃을 상정할 수 있다. 그런데 이런 타깃 중에는 타깃이 파일 이름이 아닌 경우도 꽤 있다. 예를 들어, 대다수 기술 파일들은 아래와 같은 타깃을 갖고 있기 때문에 개발자들은 프로그램을 검사한 후 바쁜 하루를 정리하면서 쉽게 임시 파일들을 제거할 수 있다:
clean:
/usr/bin -f core *.o
그리고 아래 명령을 실행하면 make
는 clean
이란 이름의 실제 파일이 존재하지 않는 한, 명단에 표시된 명령 스크립트를 반드시 실행한다. 이는 make
가 존재하지 않는 모든 타깃을 최신이 아닌 타깃으로 간주하기 때문이다.