Makefile 만들기

이대현·2020년 4월 11일
10

C / CPP

목록 보기
4/28
post-thumbnail

1. C 프로그램의 빌드 절차

  1. 컴파일 : 세 개의 소스파일을 각각 컴파일하여 Object파일(*.o)을 생성하고,
  2. 링크 : 이들을 한 데 묶는 링크 과정을 통해서 실행 파일인 a.out을 생성한다. 여기서 소스파일에 정의된 함수를 main에서 호출하는 의존성이 존재한다.

1.1. 파일 개요

a.c (a.h), b.c (b.h), main.c 파일 생성

https://z-images.s3.amazonaws.com/0/04/Files.png

1.2. 파일 코드 내용

a.c, a.h, b.c, b.h, main.c 파일 내용은 각각 아래와 같다.

a.c

    #include <stdio.h>
    
    void foo()
    {
        printf("foo\n");
    }

a.h

    void foo();

b.c

    #include <stdio.h>
    
    void bar()
    {
        printf("bar\n");
    }

b.h

    void bar();

main.c

    #include "a.h"
    #include "b.h"
    
    int main()
    {
        foo();
        bar();
    
        return 0;
    }

1.3. 컴파일 하기

object 파일 생성 (컴파일)

    $gcc -c -o a.o a.c
    $gcc -c -o b.o b.c
    $gcc -c -o main.o main.c

헤더파일들은 main.c 파일에 포함되어 있으므로 명시해주지 않아도 된다.

여기서 -c 옵션은 링크를 하지 않고 컴파일만 하겠다는 의미이다. 이 옵션을 생략하면 main 함수를 찾을 수 없다는 오류가 출력된다.

실행파일 생성 (링크)

$gcc -o a.out main.o a.o b.o

Object파일들을 한데 묶는 링크 과정을 수행한다. 명령은 gcc지만, gcc 내부적으로 링커(ld)를 실행해서 실행 파일(a.out)을 생성한다.

실행파일 실행

    $ ./a.out
    foo
    bar

2. Makefile 만들기

Makefile을 사용하는 이유는 위와 같은 복잡한 과정을 생략할 수 있어서이기도 하지만, Makefile이 제공하는 강력한 기능 중 하나인 Incremental build 를 사용하기 위해서다.

Incremaental build 란?

반복적인 빌드 과정에서 변경된 소스코드에 의존성(Dependency)이 있는 대상들만 추려서 다시 빌드하는 기능이다.
예를 들어, 위의 빌드 예제에서 main.c의 한 줄만 바꾸고 다시 빌드를 할 때, main.o 컴파일(gcc -c -o main.o main.c)과 app.out링크(gcc -o a.out main.o a.o b.o)만 수행하는 경우가 이에 해당한다.

2.1. Makefile 구조

Makefile 은 대상(Target), 의존 관계(Dependency), 명령(Recipe)의 세 가지로 이뤄진다.

    <target> : <dependency>
    (tab)<Recipe>
  • 대상 : 빌드 대상 이름. 명령에 의해 생성되는 결과 파일, 오브젝트 파일이나 실행 파일

  • 의존 관계 : 대상을 만들 때 의존되는 파일들. 여기에 나열된 대상들을 먼저 만들고 빌드 대상을 생성한다. 의존 파일이 변경됐다면 대상을 만들 도록 명령을 내릴 것이다.

  • 명령 : 빌드 대상을 생성하는 명령. 여러 줄로 작성할 수 있으며, 의존 관계에 있는 파일들이 변경됐거나 대상 파일이 없을 때 명령이 실행된다. 쉘에서 쓸 수 있는 명령을 사용할 수 있다.

    명령을 쓸 때 반드시 tab키로 띄어준 후에 써야한다.

2.2. Makefile 작성

    a.out : a.o b.o main.o
        gcc -o a.out a.o b.o main.o
    
    a.o : a.c
        gcc -c -o a.o a.c
    
    b.o : b.c
        gcc -c -o b.o b.c
    
    main.o : main.c
        gcc -c -o main.o main.c
    
    clean:
        rm *.o a.out

2.3. Makefile 실행

$ ls
a.c  a.h  b.c  b.h  main.c  Makefile

$ make
gcc -c -o a.o a.c
gcc -c -o b.o b.c
gcc -c -o main.o main.c
gcc -o a.out a.o b.o main.o

$ ls
a.c  a.h  a.o a.out b.c  b.h  b.o main.c  main.o  Makefile

실행 파일인 a.out 이 make 명령을 통해 새로 생성된 것을 알 수 있다.

  • a.out 실행

      $ ./a.out
      foo
      bar

2.4. make clean

$ make clean
rm *.o a.out

$ ls
a.c  a.h  b.c  b.h  main.c  Makefile

make clean 명령으로 확장자가 .o인 파일과 a.out 파일이 삭제된 것을 확인할 수 있다.

3. 변수 사용하기

변수를 사용하면 Makefile을 보다 깔끔하고 확장성 있게 작성할 수 있다. 변수들 중에는 Make 내부에서도 함께 사용하는 내장 변수나(CFLAGS 등), 확장성을 용이하게 해 주는 자동 변수($@, $< 등)도 있다.

다음은 위의 예제를 빌드하기 위한 같은 목적의 Makefile이다.

CC=gcc
CFLAGS=-g -Wall
TARGET=a.out
OBJS=a.o b.o main.o
 
$(TARGET): $(OBJS)
    $(CC) -o $@ $(OBJS)

a.o : a.c
    $(CC) -c -o a.o a.c

b.o : b.c
    $(CC) -c -o b.o b.c

main.o : main.c
    $(CC) -c -o main.o main.c

clean:
    rm $(OBJECT) $(TARGET)

이제 좀 제대로 된 Makefile 같다. 여기서 정의한 각 변수의 의미는 다음과 같다.

  • CC: 컴파일러
  • CFLAGS: 컴파일 옵션
  • OBJS: 중간 산물 Object 파일 목록
  • TARGET: 빌드 대상(실행 파일) 이름

그 외에 자주 사용되는 내장 변수는 다음과 같다.

  • LDFLAGS: 링커 옵션
  • LDLIBS: 링크 라이브러리

참고로, Make에서 내부적으로 정의되어 있는 변수들은 다음 명령으로 확인할 수 있다. 주석이 같이 달려서 출력되기 때문에 필요한 기능을 바로 찾기 편리하다.

make -p

3.1. 자동 변수(Automatic variables)

위 예제 Makefile의 7번째 줄을 보면 Recipe 중간에 정의한 적이 없는 변수인 $@이 포함되어 있는 것을 알 수 있다. $@은 현재 빌드 규칙 블록의 Target 이름을 나타내는 자동 변수다.

자동 변수는 위치한 곳의 맥락에 맞도록 치환된다. 즉, 7번재 줄의 $@는 Recipe를 실행할 때 $(TARGET)값으로 치환된다. 이렇게 하면 Target 이름을 수정할 때 Recipe까지 일일이 찾아서 수정하는 수고를 덜 수 있다.

Make에서 지원하는 자동 변수들 중 자주 사용하는 것들은 다음과 같다.

  • $@: 현재 Target 이름

  • $^: 현재 Target이 의존하는 대상들의 전체 목록

  • $?: 현재 Target이 의존하는 대상들 중 변경된 것들의 목록

  • $% : 대상의 이름 (해당 규칙 대상이 아카이브 인 경우)

    For example, if the target is foo.a(bar.o) then ‘%’ is bar.o and ‘@’ is foo.a. ‘$%’ is empty when the target is not an archive member.

    Archive files are files containing named sub-files called members; they are maintained with the program ar and their main use is as subroutine libraries for linking. 즉, 파일을 묶어서 하나로 만든 것.

사용 가능한 자동 변수들의 전체 목록과 설명은 이 사이트에서 확인할 수 있다.

4. Makefile 기본 패턴

CC=<컴파일러>
CFLAGS=<컴파일 옵션>
LDFLAGS=<링크 옵션>
LDLIBS=<링크 라이브러리 목록>
OBJS=<Object 파일 목록>
TARGET=<빌드 대상 이름>
 
all: $(TARGET)
 
clean:
    rm -f *.o
    rm -f $(TARGET)
 
$(TARGET): $(OBJS)
$(CC) -o $@ $(OBJS)

참고

Makefile 만들기

[Make 튜토리얼] Makefile 예제와 작성 방법 및 기본 패턴 - ECE - TUWLAB

GNU Make 강좌: Makefile의 실제 예제

profile
삽질의 기록들 👨‍💻

2개의 댓글

comment-user-thumbnail
2022년 9월 26일

정말 깔끔하게 정리 잘해주셨네요 덕분에 확실하게 개념 다지고 갑니다

답글 달기
comment-user-thumbnail
2023년 2월 15일

감사합니다~

답글 달기