Make build system

Hyungseop Lee·2023년 10월 14일
0

Motivation

  • program이 작다면 하나의 file에 모든 소스코드를 담아서 작성할 수 있다.
    program이 커지게 된다면, 코드의 line이 많아지고 더많은 모듈과 component가 필요.

  • 큰 program을 하나의 file에 담았을 경우,
    어떤 func? component? 호출관계?를 파악하는게 매우 어렵다.
    또한 아주 작은 부분만 고쳐도 파일 전체를 새로 컴파일해야 한다.
    컴파일 시간이 오래 걸린다. 여러 명이 동시에 그 파일에 변경을 가하기가 어렵다.
    따라서 components를 여러 부분으로 나누는 것이 필요함.


Using make in compilation

  • (정리) 대규모 project를 여러 file에 나누어 관리하는 것이 바람직하다.

    1. 각각의 파일들을 compilation해야 하는데 그 명령어를 계속해서 하는 것이 어렵다.
    2. 어떤 파일이 어떻게 변경되었는지 알아야 하고 관리하는 것이 어렵다
    3. 파일들 간의 의존성 관리도 어렵다.
  • 위와 같은 일들을 자동화하여 compile해줄 수 있는 것이 make 명령어이다.
    make는 UNIX에서 사용하는 명령어이다.
    make 명령어는 Makefile을 해석하여 수행시켜준다.

  • [Mm]akefile은 compile에 필요한 rules와 command가 기입된 script file이다.


linux kernel


make command (How make works?)

  • 최종적으로 만들고 싶은 프로그램인 가장 상위의 target을 만든다.

  • 그 target을 만들기 위한 sub file들(object file)을 사용하면 된다.
    하지만 sub file들이 존재하지 않다면 다시 만들어야 한다.

  • 이렇게 target을 만들기 위해 target이 의존하는 file들을 recursive하게 만들어 간다.

  • target이 만들어졌다면, 그 target이 의존하는 file들보다 오래되었을 경우(file 시간 check)만 compile을 새로 한다.

Example

  • 아래와 같이 sum 이라는 programdl 3개의 file로 구성되어 있다고 가정.
    • 만약, sum.c 파일이 수정되었다면
      make command는 sum.o, sum(exec)를 수정하도록 함.

--

makefile

  • 아래는 총 3개의 Rule(target을 만들기 위해서 dependencies 파일들을 의존한다)들이 있음.

Comments

  • '#' : 주석
  • '\' : 해당 라인이 끊어지지 않고 다음 라인으로 연결

Dependency Lines

  • 제일 마지막에 생성되어야 하는 target을 가장 위쪽에 작성해야 한다.
  • main.o와 this.o가 둘 다 funcs.h를 의존한다면, target에 두 file을 작성한다
    main.o this.o : funcs.h

Shell Lines

  • indented lines must have TAB.

Macros(variable)

  • Assignment : MACRONAME = value
  • Usage : ${MACRONAME} or $(MACRONAME)

사용자가 입력하는 macro

  • 반복되는 값들을 macro를 이용하여 쓸 수 있음.
    그래서, 만약에 해당하는 값의 수정이 필요할 때, 모든 반복되는 문자열을 수정해주지 않고
    macro 변수만 바꿔주면 된다.
    • example : /home/seop 이라는 경로가 반복되어 사용됨.
      이때, HOME=/home/seop 라는 macro를 정의하고
      /home/seop을 ${HOME}으로 사용할 수 있음

Predefined Macros

  • $@ : Full target name
    $* : Name of the target, without suffix(확장자)
    $< : First dependency
    $^ : Name of all dependency
    $? : target보다 최근에 생성된 파일들

  • example :

.PHONY target

  • PHONY target :
    target이 file이 아니라 해당되는 action을 호출하기 위해 사용되는 경우가 있다.

    • all : 이 makefile에 있는 모든 규칙들을 적용하라
      • ex:

        .PHONY: all all install
        
        all: ${만들 program}
    • install : compile의 결과를 해당 directory에 설치하라
      • ex:

        .PHONY: all install
        
        install: 
        	cp ${결과} ${경로}
    • clean : compile과정 중에서 생성된 여러가지 file들(object file, binary file)을 지워라
      • ex:
        .PHONY: all install clean
        clean: 
        	rm -rf ${실행파일 만드는 데 생성된 중간 파일들? *.o *.out *.a *.so core core.* ...}
    • distclean : 실행파일 말고 중간에 생성되는 여러 파일들이 있는데, 그것들을 삭제해라
  • 보통 makefile을 부르는 순서 :

    1. make (all) -> 전체 compile
    2. make install -> compile결과가 재위치에 설치됨
    3. make clean -> 필요없는 file들 지움

Example 1

  • 아래와 같이, lib.c와 prog.c를 object file로 생성하고 둘을 link하여 binary라는 실행 파일을 생성할 것임.
gcc -c lib.c -o lib.o
gcc -c prog.c -o prog.o
gcc lib.o prog.o -o biinary

이 build system을 makefile을 이용해서 만들면?

binary: lib.o prog.o
	gcc lib.o prog.o -o binary -g -Wall
    
lib.o: lib.c   
	gcc -c lib.c -o lib.o -g -Wall
    
prog.o: prog.c   
	gcc -c prog.c -o prog.o -g -Wall

위에서 반복적으로 사용되는 string들을, macro를 이용하여 대체할 수 있음.
사용자가 선언하는 macro 이용:

CC = gcc
CFLAGS = -g -Wall
OUTPUT = binary
OBJFILES = lib.o prog.o

${OUTPUT} : ${OBJFILES}
	${CC} ${OBJFILES} -o ${OUTPUT} ${CFLAGS}
    
lib.o : lib.c
	${CC} -c lib.c -o lib.o ${CLFAGS}
    
prog.o : prog.c
	${CC} -c prog.c -o prog.o ${CLFAGS}

predefined macro를 이용해서 대체할 수도 있음.
아래 예제에서는 $<: 첫 번째 dependency, $@: full target name

CC = gcc
CFLAGS = -g -Wall
OUTPUT = binary
OBJFILES = lib.o prog.o

${OUTPUT} : ${OBJFILES}
	${CC} ${OBJFILES} -o ${OUTPUT} ${CFLAGS}
    
lib.o : lib.c
	${CC} -c ${<} -o ${@} ${CLFAGS}
    
prog.o : prog.c
	${CC} -c ${<} -o ${@} ${CLFAGS}

여기서, wild card를 이용하여 더 간편하게 수정할 수도 있음.

CC = gcc
CFLAGS = -g -Wall
OUTPUT = binary
OBJFILES = lib.o prog.o

${OUTPUT} : ${OBJFILES}
	${CC} ${OBJFILES} -o ${OUTPUT} ${CFLAGS}
    
%.o : %.c   # 어떠한.o file은 어떠한.c을 의존함
	${CC} -c ${<} -o ${@} ${CLFAGS}

오래된 makefile에서는
wild card 말고, suffix rule을 이용하기도 함.
wild card 사용이 더 일반적..

CC = gcc
CFLAGS = -g -Wall
OUTPUT = binary
OBJFILES = lib.o prog.o
.SUFFIXES: .o .c # 이렇게 어떤 suffix에 대해서 규칙이 있는지 정해줘야 함

${OUTPUT} : ${OBJFILES}
	${CC} ${OBJFILES} -o ${OUTPUT} ${CFLAGS}
    
.c.o   # 어떠한.o file은 어떠한.c을 의존함
	${CC} -c ${<} -o ${@} ${CLFAGS}

phony target을 선언할 수도 있음

Example 2

  • 1:
  • 2 : Predefined Macros
  • 3-1 : 반복되는 단어들에 대해서 Macro 사용
  • 3-2 : 반복되는 단어들에 대해서 Macro 사용
  • 4 : dependency file 만들기

static library linking

  • static library linking :

shared library linking

  • shared library linking :

Recursive make

  • 규모가 있는 project의 경우, 각각의 directory에 make file이 있다.
    각각의 make file은 하위 디렉터리에 있는 make file을 호출해주는 역할 또는
    하위 디렉터리에서 생성된 결과물을 linking해주는 역할만 한다.
    그 하위에 있는 디렉터리의 make file도 그 하위에 있는 디렉터리의 ...
    (recursive)
    그래서 각각의 directory의 makefile들은 self-contained하게 만들고
    상위 디렉터리의 makefile은 그것들을 recurisve하게 호출하도록 만든다.
    '$$d' :
    makefile이 shell에 넘겨줄 때, d를 해석하지 말고 넘겨주라는 escape의미로 사용.
    따라서 shell line에서 variable을 참조할 때 $를 2개 사용.

order-only prerequisite

  • 만약 foo.c를 수정해서 재컴파일하기 위해 make를 했는데, 수정사항이 없는 goo.c와 main.c까지 컴파일이 되어 불필요한 컴파일이 진행되는 것을 막고자 order-only prerequisite를 사용한다.
  • 만약 foo.c만 수정을 해서 make를 했을 때, foo.o만 만들어지도록 하기 위해서는 다음과 같이 한다.
    (foo.c 내용만 수정)

Examples

simple & basic makefile ex

main()에서 foo(), goo() 호출

zoo() 추가 후 make

  • 이제, zoo() 함수를 추가하여 다시 make
    이때, 변한 것은 zoo() 함수만 추가된 것이기 때문에 기존에 foo(), goo() 함수에 대한 compile을 진행하지 않고,
    코드가 바뀐 zoo.c와 myapp.c만 자동으로 compile해줌.

Predefined macros

  • 위에서처럼 새로운 함수가 추가될 때마다, makefile을 수정하는 것은 복잡한 프로젝트에서 어려운 일이다.
    그래서 make의 predefined macros를 이용해서 간편화할 수 있다.
  • $@ : Full target name
    $* : Name of the target, without suffix(확장자)
    $< : First dependency
    $^ : Name of all dependency
    $? : target보다 최근에 생성된 파일들

User defined macros

  • user가 지정하는 macro를 통해 간편화할 수 있다.

order-only prerequisite

  • foo.c 함수를 아래와 같이 수정하기 위해, 재컴파일(make)했는데
    수정사항이 없는 myapp.c, goo.c, zoo.c까지 컴파일되어 불필요한 컴파일이 진행된다.
  • 이를 막고자 order-only prerequisite를 사용한다.
    order-only prerequisite는 ~~
    이번에는 아래와 같이 goo.c 함수를 수정했고,
    goo.c 함수만 변경되었으므로 goo.c만 컴파일할 수 있도록,
    Make 규칙에서 order-only dependency ( | )로 설정

static library using makefile

shared library using makefile

  • 방법 1: symbolic link에 대한 dependency

  • 방법 2: symbolic link에 대한 dependency

@echo

  • echo 까지 같이 출력되는 것을 없애기 위해,

  • @ 추가

addprefix

profile
Efficient Deep Learning

0개의 댓글