[make] # 1. 컴파일, 실행, 디버그

문연수·2023년 2월 6일
0

make

목록 보기
2/6
post-thumbnail

책에서는 build 를 작성이라 번역했는데 아무리 봐도 문맥상 너무 그 내용이 맞질 않아서 전부 다 빌드로 번역했다. 작성이라는 단어가 코드를 write 한건지 프로그램을 build 한건지 도통 감이 안 잡힌다. 아마 1990년대 책이라 그런 것일 수도 있겠다. 아직 IT 용어 자체가 정립되지 않은 시기여서 작성이라 번역한 것이라 생각된다

단말기라는 용어도 요즈음엔 잘 이해하기 어려울 것 같다는 생각이 들어서 그냥 터미널이라고 번역을 했다.


 다음은 program 을 빌드하는 경우에 따라 서로 다른 버전을 생성할 수 있는 명령이다.

$ make program

 위 명령어를 입력하게 되면 해당 파일(program)을 생성하는 데 필요한 모든 컴파일과 링크 작업을 수행할 수 있다. make 유틸리티를 사용하면 수많은 cc 컴파일러 명령들을 일일이 손으로 입력하지 않고도 자동으로 수행할 수 있다.

 위 예제에 나오는 program 같은 파일을 작업할 타깃(target) 이라 하며, 타깃(program)은 이른바 필요 항목(prerequisites) 또는 종속 항목(depedents) 이라는 하나 이상의 파일들을 기반으로 빌드(build) 된다. 그리고 이들 각 파일들은 또다시 다른 파일들을 필요 항목으로 갖고 있다.

make 의 강점은 '소스 파일과 오브젝트 파일', '오브젝트 파일과 실행 파일'을 잇는 상호 의존적인 계증구조에 민감하게 반응한다는 점이다. 기술 파일(description file, 대개 makefile 이나 Makefile 이란 이름이 붙여진다) 내의 의존 관계를 명시해주는 것은 개발자의 몫이지만, make 또한 기술 파일에 설정된 환경을 스스로 이해하여 의존 관계를 직접 결정할 수 있다.

make 는 기존 파일의 이름, 그들의 최종 수정된 시각, 내장된 규칙(또는 규칙군) 등을 이용하여 빌드할 파일을 골라내고 또 어떻게 빌드할지 결정한다.

1. 기술 파일 (description file)

아래와 같은 구성 요소로 프로그램을 빌드한다고 가정하자:

  • C 소스 파일 세 개 (main.c, iodat.c, dorun.c)
  • 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)과 탭 문자로 시작하는 하나 이상의 명령 행이 있다. 종속 행에서 콜론 왼쪽은 타깃이며, 콜론 오른쪽은 타깃의 필요 항목이다. 탭 문자로 시작하는 명령 행은 타깃을 필요 항목에서 가져와 빌드하는 방법을 제시한다.

2. 의존 관계 확인

 앞 절에서 소개한 기술 파일을 사용해 타깃(program)을 빌드하도록 요청하더라도 make 는 2행의 명령을 곧바로 실행하지는 않는다. makeprogram 이란 이름을 가진 파일이 존재하는지 유무를 먼저 확인한 다음, program 파일이 존재하 경우 main.o, iodat.o, dorun.o, lo.o 파일과 /usr/fred/lib/crtn.a 라이브러리 등을 검사하여 그 가운데 program보다 나중에 생성된 것이 있는지 확인한다.

program이 빌드되는 데 필요한 항목들을 마지막으로 빌드한 이후 다시 수정하지 않았다면 makeprogram 을 다시 빌드(rebuild)할 필요가 없다고 판단하고 별다른 명령을 내리지 않은 채 종료한다.

 하지만 make는 그와 같은 판단을 내리기 전에 몇 가지 작업을 더 수행한다. 각 .o 파일과 그들의 필요 항목들을 먼저 검사하는 것이다. 예를 들어 3행을 보면 main.omain.c를 필요로 한다(의존하고 있다)는 사실을 알 수 있다.

 따라서 마지막으로 main.o가 빌드된 이후 main.c가 변경되었을 경우 make는 4행에 있는 컴파일 명령을 실행하고 main.o를 새로 빌드한다. 결국 makeprogram 의 하위 필요 항목들을 모두 확인하고 또 make의 필요 항목들을 모두 최신 파일로 바꾼 후에야 2행의 명령을 실행한다.

 프로그램을 빌드한다는 것을 곧 정확한 순서에 따라 실행되어야 하는 일련의 명령을 구성하는 것이다. 그리고 그 일련의 과정에서 최종 파일을 빌드하는 작업을 바로 make에 맡길 수 있다.

3. 리빌드의 최소화

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 를 다시 컴파일하고 링크하게 된다.

4. make 실행

  • 기술 파일과 프로젝트 파일은 동일한 디렉터리 안에 있다.
  • 기술 파일명은 makefile 또는 Makefile 이다.
  • 위 디렉터리는 make 명령을 입력할 시점에서 현재 디렉터리이다.

위와 같은 조건이라면 아래 명령을 입력하여 기술 파일에 있는 어떤 타깃이든 빌드할 수 있다:

make target

 타깃을 빌드하는 데 필요한 각 명령 행은 터미널에 표시된 다음 실행된다. 일부 중간 단계의 파일들이 존재하다가 갱신되는 경우 make 는 그와 관련된 명령들은 건너뛴다. 타깃(program)을 리빌드하는 데 필요한 최소한의 명령만 실행 한다는 얘기이다.

 경우에 따라 여러 타깃을 한 번에 make 에 전달하여 실행시킬 수도 있다. 그리고 결과는 make 명령을 여러 번 연속해서 실행할 때와 동일하다.

make main.o dorun.o lo.o

 마지막으로 아래처럼 타깃 이름 없이 간단히 입력하기만 하면 기술 파일에 포함된 첫 번째 타깃 이 만들어진다.

make

5. 구문의 기본 규칙

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 파일 가운데 어느 하나라도 바뀌면 makefile.o 가 최신 버전이 아니라는 사실을 알아채고 다시 컴파일한다.

 마지막으로 필수 항목이 없는 타깃을 상정할 수 있다. 그런데 이런 타깃 중에는 타깃이 파일 이름이 아닌 경우도 꽤 있다. 예를 들어, 대다수 기술 파일들은 아래와 같은 타깃을 갖고 있기 때문에 개발자들은 프로그램을 검사한 후 바쁜 하루를 정리하면서 쉽게 임시 파일들을 제거할 수 있다:

clean:
	/usr/bin -f core *.o

 그리고 아래 명령을 실행하면 makeclean 이란 이름의 실제 파일이 존재하지 않는 한, 명단에 표시된 명령 스크립트를 반드시 실행한다. 이는 make 가 존재하지 않는 모든 타깃을 최신이 아닌 타깃으로 간주하기 때문이다.

profile
2000.11.30

0개의 댓글