Programming Tools/CMake

[CMake] Makefile과 CMake

LiDARian 2021. 8. 31. 13:00
반응형

 

 

Makefile에 대한 소개

 

makefile은 컴파일 작업에 있어서 여러번 터미널창에 쳐야하는 명령어들을 하나의 파일에 모아서 작성한 것입니다.

 

default:
	gcc main.cpp -o out

 

라고 작성하면, 터미널 창에 make라고 입력했을 때 터미널 창에 자동으로 `gcc main -o out`이 입력됩니다.

 

 

이를 이용하면 간단하게 out이라는 이름의 실행파일이 생성됩니다.

 

 

 

 

여러개의 파일을 동시에 컴파일 해보자

main.c, foo1.c, foo2.c를 각각 컴파일하여 Object파일을 생성하고, 링크를 통해서 a.out을 생성합니다.

여기서 foo1과 foo2는 main에 대해 의존성이 발생합니다.

 

원래는 이렇게 컴파일하려면 다음과 같이 CLI에서 명령해줘야합니다.

gcc -c -o main.o main.c
gcc -c -o foo1.o foo1.c
gcc -c -o foo2.o foo2.c
gcc -o a.out main.o foo1.o foo2.o

 

이 정도면 해줄만하지 않나 싶지만, 파일이 10개, 20개가 넘어가기 시작하면 상당히 힘들어집니다.

그러므로 우리는 makefile을 따로 만들어서 이런 명령어를 자동으로 처리해줄 겁니다.

 

또한 Makefile을 사용하면, Incremental build를 사용할 수 있습니다. Incremental build란 변경된 소스코드에서 의존성(Dependency)이 있는 대상들만 추려서 다시 빌드하는 기능입니다. 예를 들어, 위의 빌드 예제에서 main.c의 한 줄만 바꾸고 다시 빌드를 할 때, main 컴파일과(gcc -c -o main.o main.c)과 a.out링크(gcc -o a.out main.o foo1.o foo2.o)만 수행한다는 겁니다.

 

Makefile로 다시 명령어를 처리해보자

 

위의 명령어를 Makefile로 만들면 아래와 같습니다.

a.out: main.o foo1.o foo2.o
    gcc -o a.out main.o foo1.o foo2.o
 
main.o: foo1.h foo2.h main.c
    gcc -c -o main.o main.c
 
foo1.o: foo1.h foo1.c
    gcc -c -o foo1.o foo1.c
 
foo2.o: foo2.h foo2.c
    gcc -c -o foo2.o foo2.c

이렇게 하고 난 후 터미널 창에서,

make

make 명령을 치면 한 번에 a.out 실행파일을 얻을 수 있습니다.

make foo1.o

make foo1.o를 하면 foo1.o만 선별적으로 컴파일을 통해 만들어 냅니다.

 

Makefile 기본 패턴

 

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

 

Rule Block

 

위의 기본패턴을 해석하는 방법에 대해서 설명해보겠습니다. 

 

기본적으로 아래와 같은 패턴을 Rule Block이라고 부르고, 다음과 같이 작성합니다.

 

<Target>: <Dependencies>
    <Recipe>


Target: 빌드 대상 이름. 최종적으로 생성해내는 파일명을 써 줍니다.
Dependencies: 빌드 대상이 의존하는 Target/파일 목록.
Recipe: 빌드 대상을 생성하는 명령. 줄 시작에 반드시 Tab문자로 된 Indent가 있어야 합니다.

 

 

자동 처리 Rule Block


자주 사용되는 빌드 규칙들은 굳이 기술하지 않아도 자동으로 처리해 줍니다. 소스 파일(*.c)을 컴파일해서 Object 파일(*.o)로 만들어 주는 규칙이 여기에 해당합니다.

 

즉 실행파일은 아래만 적어도 완성됩니다.

app.out: main.o foo.o bar.o
    gcc -o app.out main.o foo.o bar.o

 

다만 의존성 검사에서 헤더파일의 변경을 감지하려면, 각각의 Target에 대한 Dependencies에 대해 명시해야합니다.

 

그리고 헤더파일을 추가할 때마다 이 부분을 업데이트 해야합니다.

app.out: main.o foo.o bar.o
    gcc -o app.out main.o foo.o bar.o
 
main.o: foo.h bar.h main.c
    foo.o: foo.h foo.c
    bar.o: bar.h bar.c

 

내장 변수와 자동 변수


Make에는 변수도 있습니다. 변수들 중에는 Make 내부에서도 함께 사용하는 내장 변수(CFLAGS), 확장성을 용이하게 해 주는 자동 변수($@, $<)가 있습니다.

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


CC: 컴파일러. Make 내장 변수
CFLAGS: 컴파일 옵션. Make 내장 변수
OBJS: 중간 산물 Object 파일 목록
TARGET: 빌드 대상(실행 파일) 이름
LDFLAGS: 링커 옵션
LDLIBS: 링크 라이브러리

make -p

를 사용하면 Make 내부에 정의된 변수를 확인할 수 있습니다.

 

자동변수 $@는 현재 빌드 규칙 블록의 Target 이름을 나타냅니다.

자동 변수는 위치한 곳의 맥락에 맞도록 치환됩니다. 위의 경우 $@는 Recipe를 실행할 때 $(TARGET)값으로 치환됩니다.

$@: 현재 Target 이름
$^: 현재 Target이 의존하는 대상들의 전체 목록
$?: 현재 Target이 의존하는 대상들 중 변경된 것들의 목록

 

 

Clean Rule : make clean 명령을 구현

 

Clean Rule을 추가해보겠습니다.


Makefile의 Rule에는 매크로를 지정해서 사용할 수도 있습니다. 그 중 하나가 clean입니다.

Clean 매크로는 빌드 결과물(e.g. app.out)과 중간 부산물들(*.o )을 모두 삭제하여 '깨끗한' 상태에서 다시 빌드할 수 있는 환경을 만들어 줍니다. 그 후 수행하는 빌드를 Clean Build라고 합니다.


Makefile에 다음 빌드 규칙을 추가하면 Clean 매크로를 사용할 수 있습니다. (최소 Clean 매크로까지는 넣어 줘야 Makefile의 완성~!^^)

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

 

make clean

 

이 명령은 변경사항이 반영되지 않아 오류가 발생하는 것으로 추정될 때 수행합니다.


헤더파일을 추가하는 등 소스 코드를 수정하면서 의존성이 변동할 때 Makefile을 제때 업데이트해 주지 않으면 이렇게 됩니다.

 


 

 


CMake

 

CMake는 Makefile을 자동으로 생성해주는 프로그램입니다.

명령어는 이렇게 치면 됩니다.

 

추가로

cmake -S [소스코드 경로] -B[빌드 후 경로]

 

를 이용해도 좋다.

 

실습을 위해서 다음과 같이 따로 폴더를 만들어줍시다.

 

mkdir practice_cmake
code .
cd practice_cmake

 

그리고 다음과 같이 쳐준다.

 

#include <iostream>

using namespace std;

int main(){
   cout << "Hello" << '\n';
   return 0;
}

 

Visual Studio Code에서 비어있는 CMakeLists.txt를 만들어주는 명령 및 다양한 명령을 쳐줍니다.

 

touch CMakeLists.txt
mkdir -p out/build
cd out/build
cmake -S ../../ -B .

 

 

해주면 다음과 같이 Makefile을 포함한 여러 파일이 자동으로 생깁니다.

위에서 배운 것과 같은 내용이지만, 궁금하시면 Makefile을 들쳐보셔도 될 것 같네요.

 

 

 

가장 외부의 CMakeLists.txt에 다음과 같이 적습니다.

 

cmake_minimum_required(VERSION 3.10.2)
project(OLAS) # 프로젝트명 : 실행파일 이름이 된다.
add_executable(${PROJECT_NAME} main.cpp) # 프로젝트명을 실행파일 이름으로 한다. 빌드 대상은 main.cpp

 

그리고 다음 명령들을 쳐줍니다.

 

cd ../..
cmake -S . -B out/build
cd out/build
make
./OLAS

 

Hello라고 결과가 나올 겁니다.

 


 

 


공대생지식창고 오픈카톡방

https://open.kakao.com/o/swnAyLyc

 

공대생지식창고님의 오픈프로필

공대생에게 도움이 될만한 글을 씁니다. www.knowledgeforenginners.tistory.com

open.kakao.com

 

공대생지식창고 Github

engineerJPark (github.com)

 

engineerJPark - Overview

engineerJPark has 2 repositories available. Follow their code on GitHub.

github.com

 

 

반응형