본문 바로가기

운영체제/리눅스(Linux)

Makefile 정리

1. 사용 목적

Makefile은 (C/C++로 이루어진) 큰 프로그램에서 어떤 부분이 다시 컴파일 되어야 하는지 알려준다.

Make의 대안으로 SCons, CMake, Bazel, Ninja 등이 있고, Java로는 Ant, Maven, Gradle이 있다.
각 언어마다 Makefile 역할을 하는 Tool 들이 존재한다.

 

2. Makefile을 사용하지 않은 경우

먼저 Make를 사용하지 않고 빌드 해 보자

g++(gcc) -c main.c -o main.o : 오브젝트 파일을 만들어 주는 명령어
g++(gcc) -c foo.c -o foo.o : 오브젝트 파일을 만들어 주는 명령어
g++(gcc) -c -o bar.o bar.c : 오브젝트 파일을 만들어 주는 명령어
g++(gcc) -o app.out main.o foo.o bar.o : 최종 바이너리를 만드는 명령어

위의 명령어를 수행하는 쉘 스크립트를 만들어 줄 수도 있지만, Makefile에는 쉘 스크립트가 가지고 있지 않은 Incremental build 기능(많은 소스 코드 중 바뀐 부분만을 골라내서 재빌드하는 기능)이 있다.

이는 서버 프로그래밍을 하거나 게임 프로그래밍과 같은 대규모 프로젝트의 프로그래밍에서는 굉장히 중요한 기능이다. 따라서, Makefile을 사용한다.

 

3. 문법

 구조
 <Target>: <Dependencies>
     <Command/Recipe>

목적파일(Target) : 명령어가 수행되어 나온 결과를 저장한다.

의존성(Dependency) : 목적 파일을 만들기 위한 의존성 라이브러리
명령어(Command/Recipe) : 실행 되어야 할 명령어
매크로(Macro)

 

예시)

- 아래의 예시를 보자. make blah를 수행할 경우 Target인 blah를 수행한다. 하지만, Dependency가 blah.o이다.

- Target인 blah.o를 수행한다. Dependency가 blah.c이다.

- Target인 blah.c를 수행한다. Dependency가 없다.

- 이제, cc -c 명령이 실행된다. 이후 cc 명령이 실행된다.

 

#!/bin/sh/
blah: blah.o
    cc blah.o -o blah # Runs third

blah.o: blah.c
    cc -c blah.c -o blah.o # Runs second

blah.c:
    echo "int main() { return 0; }" > blah.c # Runs first

예시2)

- 아래의 예시는 맨 처음 등장하였던 그림의 Makefile 적용 예시이다.

- 코맨트 상의 숫자는 실행 순서이다.

#!/bin/sh
app.out: main.o foo.o bar.o  # (2) app.out은 main.o, foo.o, bar.o Dependency를 가진다.
	gcc -o app.out main.o foo.o bar.o
main.o: foo.h bar.h main.c   # (1) main.o 는 foo.h, bar.h, main.c Dependency를 가진다.
	gcc -c -o main.o main.c
foo.o: foo.h foo.c           # (1) foo.o 는 foo.h, foo.c Dependency를 가진다.
	gcc -c -o foo.o foo.c
bar.o: bar.h bar.c           # (1) bar.o 는 bar.h, bar.c Dependency를 가진다.
	gcc -c -o bar.o bar.c

 

3-1. 문법 - 변수

- 지금부터 Makefile을 강력하게 만들어주는 변수가 등장한다.

- 변수는 상단에 정의하고 $() 혹은 ${}로 불러다 사용한다.

- 아래 예시의 CC, CFLAGS, LDFLAGS, LDLIBS, OBJS, TARGET 모두 사용자가 정의한 변수이다. 내장된 것이 아니다.

CC = g++                           : CC는 Make 내장 변수, 컴파일러
CFLAGS = -g -Wall                  : 컴파일 옵션 
LDFLAGS =                          : 링커 옵션
LDLIBS =                           : 링크 라이브러리
OBJS = main.o foo.o bar.o          : 중간 산물 Object 파일 목록
TARGET = app.out                   : 빌드 대상 이름

변수를 사용하였을 때 구조
$(TARGET): $(OBJS) 
    $(CC) $(CFLAGS) -o $@ $(OBJS) :

예시)

- 아래 예시는 변수를 사용하였을 때 맨 처음 등장하였던 그림의 Makefile 적용 예시이다.

CC=g++                          
CFLAGS=-g -Wall                 
LDFLAGS=                        
LDLIBS =                        
OBJS=main.o foo.o bar.o         
TARGET=app.out                  
$(TARGET): $(OBJS) 
	$(CC) $(CFLAGS) -o $@ $(OBJS) : 
	
main.o: foo.h bar.h main.c 
foo.o : foo.h foo.c
bar.o : bar.h bar.c

all : $(TARGET)
clean:                          
	rm -f *.o                         
	rm -f $(TARGET)
install:

 

 

$@

- 최종 타겟을 의미한다.

- 다중 타겟이 있을 경우, 해당 변수는 각 타겟을 가리킨다.

- 쉘 스크립트와 동일 문법이다(SO). 쉘스크립트 실행 뒤에 이어지는 인자를 뜻한다. 

$^

- 타켓을 생성하기 위해 필요한 모든 의존 오브젝트(.o)를 뜻한다.

$<

- 타겟을 생성하기 위해 가장 왼쪽에 기술된 첫번째 오브젝트

$?

- 타겟을 생성하기 위해 기술된 것 중에 가장 최근에 변경된 오브젝트

 

*, %

- 와일드 카드이다. 

 

$ ./arg.sh "a b c" d e
-->
$0 ./arg.sh

$1 a b c

$2 d

$* a b c d e

$@ a b c d e

$# 3

$? 0

 

 

 


출처

https://stackoverflow.com/questions/9994295/what-does-mean-in-a-shell-script

http://www.itmoa.co.kr/gzboard.php?code=techqna&mode=gz_read&Page=5&no=222

 

 

반응형

'운영체제 > 리눅스(Linux)' 카테고리의 다른 글

tee 명령어  (0) 2022.09.06
쉘 프로그래밍(Shell Programming)  (0) 2022.01.13
리눅스 소소한 질문 모음  (0) 2022.01.11