[make] # 3. 확장자 규칙 (Suffix Rule)

문연수·2023년 2월 25일
0

make

목록 보기
4/6
post-thumbnail

 해당 장에서 소개하는 확장자 규칙(Suffix Rule) 은 Old-fashioned 이다. (The GNU Make Reference Manual Version 4.2, Richard M. Stallman, Roland McGrath, Paul D. Smith 저, 125pg. 참조 (Oepn Book 이라서 Web 에서도 확인 가능))

 그럼에도 불구하고 필자(책의 저자가 아닌 이 게시글을 작성하는 본인) 가 obsoleted 된 내용을 굳이 정리해서 올리는 이유는 다음과 같다:

Suffix Rules are the old-fashioned way of defining implicit rules for make. Suffix rules are obsolete because pattern rules are more general and clearer. They are supported in GNU make for compatibility with old makefiles.

 구시대 유물일지라도 이미 작성된 makefile 과의 호환성을 위해서 여전히 유지되고 있는 기능이기 때문이다. 따라서 과거의 작성된 makefile 의 분석을 위해 정리해서 남기려 한다. 그러나 이는 어디까지나 하위 호환성을 위함일 뿐이며 더 이상 사용해선 안될 것이다.


 유닉스 시스템에서 C 언어 소스 파일은 항상 확장자 .c 를 사용하는데, 이는 cc 컴파일러의 필수 요구 사항이다. 마찬가지로 포트란 소스 파일은 .f 확장자를, 어셈블리어 소스 파일은 .s 확장자를 사용한다. 나아가 C 와 포트란 컴파일러는 자동으로 오브젝트 모듈을 .o 파일로 만든다.

make 는 이런 제반 규정을 이용하여 확장자 규칙에 따른 여러 동작을 실행할 수 있다. 이런 규칙에 따라 예제 기술 파일을 다음과 같이 단순하게 만들 수 있다:

OBJS = main.o iodat.o dorun.o lo.o
LIB = /usr/proj/lib/crtn.a

program: ${OBJS} ${LIB}
	${CC} -o $@ ${OBJS} ${LIB}

1. 확장자 규칙 (Suffix Rule)

 확장자 규칙은 미리 정의해 놓은 일반화한 기술 파일 항목을 가리킨다. 여기서 다루는 예제에서 사용한 규칙을 정확히 정의하면 아래와 같다:

.SUFFIXES: .o .c .s

.c.o:
	$(CC) $(CLFAGS) -c $<
    
.s.o:
	$(AS) $(ASFLAGS) -o $@ $<

 여기에서 .SUFFIXES 행의 형태는 의존 행이지만 그와 약간 다르다. 이 행의 문자열은 make 가 중요하게 여길 확장자를 나타낸다. make 는 앞선 기술 파일을 통해 program 을 빌드할 때 다음과 같은 단계를 밟게 된다:

  1. ${OBJS}${LIB} 가 지정한 필요 항목의 파일을 각각 살펴보고, 이 파일을 하나씩 차례로 타깃으로 처리한다. 즉, program 이 만들어지기 전에 필요 항목 파일 가운데 어떤 파일이 만들어져야 하는지 확인한다.

  2. program 은 다른 파일 가운데 iodat.o 에 의존한다. makeiodat.o 에 의존하는 파일을 찾을 때 사용자가 타깃으로 지정한 iodat.o 를 포함하는 행을 먼저 확인한다. 해당되는 것이 없을 경우, .o 확장자가 필요하다는 것을 인식하며 현재 디렉토리에서 iodat.o 를 만드는 데 사용할 수 있는 다른 파일을 찾는다. 해당 파일은 다음과 같아야 한다:
    - (확장자를 제외하고) iodat.o 와 같은 이름이 있어야 한다.
    - 중요 확장자를 갖고 있어야 한다.
    - 기존 확장자 규칙에 따라 iodat.o 를 만드는 데 사용할 수 있어야 한다.
    위 예제에선 iodat.c 가 모든 요구사항을 만족한다.

    .c.o:
    	$(CC) $(CFLAGS) -c $<

     여기에서 $< 는 확장자 규칙에서만 사용될 수 있다는 점을 제외하면 $? 와 유사하다. 여기에서 .o 파일은 이 규칙의 타깃이고, .c 파일은 필요항목이라 볼 수 있다.

  3. make 는 계속해서 다음 조건일 경우에만 새로운 iodat.c 를 만드는 확장자 규칙을 실행한다:

    • 먼저 더 이상 확인해야 하는 필요 항목 파일이 없는 경우 (iodat.c 는 명시적으로, 또는 확장자 규칙에 의존하는 다른 파일은 없는가? 답은 "없다" 이다.)
    • iodat.oiodat.c 보다 나중에 만들어진 경우
  4. 마지막으로 ${OBJS}${LIBS} 에서 각 파일에 대해 이와 같은 과정을 거친 뒤에 makeprogram 이 필요 항목 파일의 계층 구조에서 다른 파일보다 시간이 늦은 경우에만 ld 명령을 실행하여 program 을 다시 만든다.

2. 내부 매크로

 방금 전 확장자 규칙 명령에 사용된 $< 는 타깃을 만드는 데 필요 항목의 이름을 생성해낸다. 위 예제에서는 .c.o 규칙으로 .c 파일을 만들어냈다. 확장자 규칙 명령에서만 사용할 수 있는 또 다른 매크로로 $* 가 있다. 이는 필요 항목의 파일 이름 (확장자를 제외한 부분)을 표시한다.

cp $< $*.tmp

 위 확장자 규칙 명령은 main.c 가 규칙이 적용되는 필요 항목인 경우, 다음과 같이 평가된다:

cp main.c main.tmp

3. 기본 규칙 출력하기

make 는 현재의 작성에서 사용한 모든 매크로와 확장자 규칙, 그리고 타깃을 출력하는 유용한 -p 옵션을 제공한다.

 기술 파일의 기본 설정 중 일부를 변경하고 나서, 이들이 잘 설정되었는지 궁금할 때는 다음과 같이 입력하면 된다:

make -pns 2>/dev/null

 오류 메세지(파일 디스크립터 2)는 /dev/null 로 방향을 다시 지정하여, 화면에 보이지 않게 했다. 이런 오류들은 매우 길며 불필요한 내용이 대부분이다.

 타깃을 지정하지 않는다면 make 는 기술 파일의 첫 번째 타깃을 만들려고 할 것이다. -n-s 는 이러한 이유로 ㅜ가된 옵션으로 첫 번째 -nmake 가 명령들을 실행하지 않도록 하며, 두 번째인 -smake 가 명령들을 표준 출력으로 화면에 보이지 않게 한다.

4. .SUFFIXES 와 우선순위

make 는 의존 관계를 따라가 리빌드를 시작할 곳을 정확하게 결정하게 된다. 오브젝트 파일이 없다면, make 는 이를 작성하기 위한 .c, .f 또는 .s 소스파일을 찾는다. 소스 파일이 존재하지 않으면, make 는 계속해서 SCCS 파일을 찾는다.

make 가 최신 버전의 오브젝트 파일을 어떻게 작성하는지 알아보기 위해 아주 단순한 기술 파일을 분석해보려 한다:

exec: zapout.o

zapout.o 는 존재 유무에 상관없이 make 는 소스 파일을 찾아내 zapout.o 가 언제나 소스 파일보다 나중에 생성된 것임을, 다시 말해서 zapout.o 가 생성된 후 소스 파일이 변경되었는지를 확인하고 변경되었을 경우 다시 빌드하여 zapout.o 가 최신의 파일임을 보장한다. 여기에는 많은 규칙들이 사용 가능하다:

  • .c.o:
  • .c~.o:
  • .f.o:
  • .f~.o:
  • .l.o:
  • .l~.o:

 따라서 make.SUFFIXES 리스트의 확장자 순서에 따라 파일을 검색한다. 표준 리스트는 아래와 같다:

`.SUFFIXES: .o .c .c~ .f .f~ .e .e~ .r .r~ .y .y~ .l .l~ .s .s~ .sh .sh~ .h .h~

 이 리스트는 make.c 파일을 먼저 찾고, 뒤를 이어 .c~ 파일 (.c 파일의 SCCS 버전), .f 파일 등의 순서로 파일을 찾도록 한다. 기본 리스트는 각 파일을 처리하는데 필요한 여러 단계들을 반영하여 만들어진다. 이미 .y 파일에 yacc 를 실행하여 .c 파일을 만들었다면, make 는 해당 .c 파일을 사용하여 yacc 가 다시 동작하지 않도록 한다.

 그러나 이런 일을 반복하던 중 .y 파일을 수정하는 경우 이를 절대 무시해선 안된다. 그러므로 make.c 파일을 컴파일하기 전에 모든 필요 항목들을 점검하여 컴파일하는 데 문제가 없는지를 확인한다. .c 파일을 작성하는 규칙은 다음과 같다:

  • .c~.o:
  • .l.o:
  • .y~.c:
  • .y.c:

.SUFFIXES 리스트의 순서에 따라 makeSCCS 버전의 .c 파일, .y 파일, SCCS 버전의 .y 파일, .l 파일을 찾는다. .c 파일이 이들 중 가장 최신이라면, 아무 동작도 진행하지 않는다. 그러나 .c 파일보다 최신 버전의 .y 파일이 발견되면 makeyacc 를 다시 실행시킨다.

 간단히 말하면, 일련의 종속 관계에서 컴파일 과정 중 앞부분에 위치한 파일은 변경된 시간에 따라 참조되므로 불필요하게 다시 빌드하는 일을 최소하하기 위해서라도 .SUFFIXES 리스트의 뒷 부분에 위치해야 한다.


 책에 있는 내용의 거의 절반을 날렸는데 대부분의 내용이 FORTRAN, Pascal, SCCS, RCS, lex, yacc,troff, nroff 에 관련한 내용이었기 때문이다. 독자 중에서 이걸 아는 사람이 얼마나 될지 생각을 해보면... (필자도 이름만 들어본 것들이며 솔직히 직접 다뤄보거나 써 본 적은 없다) 그리고 이런 도구들에 대한 사양을 기술한다고 해서 실용성이 있을 것 같진 않아서 싹 다 쳐냈다.

 아무래도 책 자체의 Edition 자체도 최신이 아닌 듯 하여 다시 최신 버전의 원서 (최신 버전인 3판 조차 2004 에 출판 되었다...) 를 구매하여 정리하려 한다.

Reference

[Book] The GNU Make Reference Manual Version 4.2 (Richard M. Stallman, Roland McGrath, Paul D. Smith), A GNU Manual
[Book] Make: 유닉스, 리눅스 필수 유틸리티 (앤드류 오람, 스티브 탈보트 저; 이석주 역)

profile
2000.11.30

0개의 댓글