[CMake] CMakelist.txt 작성
2021-07-07 # CMake

CMakeList 작성법




CMake란?


학교 과제를 하다보면 Makefile을 이용하여 빌드 하곤합니다. CMake란 이러한 Makefile을 만들어주는 툴입니다.

CMake와 Makefile 모두 Incremental build방식을 사용하여 처음에 빌드한 파일들을 제외하고 수정된 파일들만 새롭게 빌드하여 시간을 줄여주게 됩니다.

Cmake와 Makefile의 차이점은 다음과 같습니다.

Makefile에서는 Object 파일들의 이름과 의존성 정보를 모두 기술해 줘야 하지만, CMake에서는 그럴 필요없이 소스파일 내부를 자동으로 분석해 의존성 정보를 스스로 파악합니다.

컴파일 의존성정보 등을 파일에 기록하여 자동으로 빌드해주는 Makefile의 단점을 보완한 것이 CMake입니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
OBJS=main.o foo.o bar.o
TARGET=app.out

all: $(TARGET)

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

$(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

CMakeLists.txt로 작성하면 다음과 같습니다.

1
ADD_EXECUTABLE( app.out main.c foo.c bar.c )

cmake CMakeLists.txt명령어를 입력하면 Makefile이 생성되게 됩니다.


Makefile의 다소 지저분한 루틴들을 추상화하여 직관적으로 빌드과정을 기술해주는 것입니다.

그 외에 CLion이나 Eclipse와 같은 IDE에서 프로젝트 설정 파일로 사용할 수도 있다고 합니다.



CMake function

Upper, lower, and mixed case commands are supported by CMake.

CMake Documentation의 Tutorial을 보며 살펴보겠습니다.


Step 1: A Basic Starting Point

1
2
3
4
5
6
7
cmake_minimum_required(VERSION 3.10)

# set the project name
project(Tutorial)

# add the executable
add_executable(Tutorial tutorial.cxx)

가장 간단한 CMakeLists.txt파일입니다.


  1. cmake_minimum_required()

CMake 빌드 스크립트를 실행하기 위한 최소버전을 명시합니다.

보통 파일의 최상단에 위치합니다.

명시한 버전보다 낮은 CMake가 해당 빌드 스크립트를 해석시 오류를 출력하고 종료합니다.

  1. project()

project(프로젝트명)으로 쓰이게 되고 프로젝트의 이름을 정해주는 명령어로 CMAKE_PROJECT_NAME이라는 변수에 저장이됩니다.

  1. add_executable()

빌드 최종 결과물로 생성할 실행 파일을 추가합니다. 이 명령을 반복하여 계속해서 실행파일을 생성할 수 있습니다.

1
2
## add_executable ( <실행파일명> <소스파일> <소스파일> )
add_executable ( a.out main.c foo.c bar.c )
  1. configure_file()

The first feature we will add is to provide our executable and project with a version number. While we could do this exclusively in the source code, using CMakeLists.txt provides more flexibility.

1
configure_file(TutorialConfig.h.in TutorialConfig.h)

자리잇슈를 빌드하면서 다음과 같이 version.h파일을 찾지 못하는 이슈가 발생했습니다.

1
2
3
4
5
[ 94%] Building CXX object src/CMakeFiles/jariitsue.dir/jariitsue.cpp.o
/home/choisj/git/jariItsue/src/jariitsue.cpp:9:10: fatal error: version.h: No such file or directory
#include "version.h"
^~~~~~~~~~~
compilation terminated.

확인해보니 src 디렉터리에 version.h.in파일이 존재하는 것을 확인했습니다.

따라서, 다음과 같은 코드를 jariItsue/src/CMakefiles.txt에 추가하였습니다.

1
2
3
4
configure_file(
version.h.in
"${CMAKE_BINARY_DIR}/include/version.h"
)

현재 디렉터리에 있는 version.h.in이라는 파일을 ${CMAKE_BINARY_DIR}/include에 version.h라는 파일로 변환하여 저장하게 하였습니다.

이 때, ${CMAKE_BINARY_DIR}를 빌드과정에 포함시키기 위해 상위 CMakefiles인 jariItsue/CMakefiles.txt에 다음과 같은 코드를 추가하였습니다.

1
2
3
include_directories(
"${CMAKE_BINARY_DIR}/include"
)


Step 2: Adding a Library

Now we will add a library to our project. This library will contain our own implementation for computing the square root of a number. The executable can then use this library instead of the standard square root function provided by the compiler.

  1. add_library()

빌드 최종 결과물로 생성할 라이브러리를 추가합니다.

1
2
3
4
5
## add_library ( <라이브러리_이름> [STATIC|SHARED|MODULE] <소스_파일> <소스_파일> ... )
## <라이브러리_이름> : 생성할 라이브러리 이름 (lib~~~~.a / lib~~~~.so에서 ~~~~에 들어갈 값)
## [STATIC|SHARED|MODULE] : 라이브러리 종류 (default = STATIC)
## MathFunctions라는 라이브러리를 mysqrt.cxx라는 파일을 통해 STATIC으로 생성한다.
add_library(MathFunctions mysqrt.cxx)


set


  1. 변수 정의

    1
    set ( <변수명> <값> )

    <값>에 공백이 포함되어 있는 경우에는 ""를 사용하여 감싸줍니다.

  1. 목록 변수 정의

    1
    set ( <목록_변수명> <항목> <항목> ... )

    <항목>들은 spacebar(공백문자)로 구분합니다. 만약 항목내에 공백이 있다면 ""를 이용하여 감싸줍니다.

  1. 변수 참조

    1
    ${<변수명>}

    빌드 대상 소스파일 목록을 SRC_FILES로 지정하고 app.out 실행파일을 생성할 때의 변수참조를 이용한 빌드는 다음과 같습니다.

    1
    2
    3
    4
    set ( SRC_FILES main.c foo.c bar.c)
    add_executable ( app.out ${SRC_FILES})
    ## 혹은 다음과 같이 씁니다.
    add_executable ( app.out main.c foo.c bar.c )

add_definitions


전처리기에 전달할 매크로를 정의하는 명령어입니다.

컴파일러 옵션 중 -D에 해당합니다.


1
2
3
## ADD_DEFINITIONS ( -D<매크로> -D<매크로> -D<매크로>=값 ... )
## ICACHE_FLASH 변수를 정의 및 MY_DEBUG라는 매크로변수를 1로 정의ㄹ
ADD_DEFINITIONS( -DICACHE_FLASH -DMY_DEBUG=1 )

find_package


패키지 빌드를 위해 사전에 필요한 다른 의존 패키지를 지정합니다.

그 안에 정의된 함수, 매크로 그리고 변수를 사용할 수 있도록 해줍니다. include()와 달리 외부 라이브러리를 사용하기 위해 만들어진 모듈들을 부르는데 사용됩니다.

1
2
## find_package(${패키지_이름} <옵션> )
find_package( OpenCV REQUIRED)

이 때, REQUIRED 옵션은 라이브러리를 찾지 못했을 경우 에러를 발생시키는 옵션입니다. 단, 찾기만 할뿐 실질적으로 lib을 추가하고 include하는 작업은 하지 않습니다.


include_directories


소스 파일에서 #include 구문으로 포함시킨 헤더 파일을 찾을 디렉터리 목록을 지정합니다.

컴파일러 옵션 중 -l에 해당합니다.

1
2
3
## INCLUDE_DIRECTORIES ( <디렉토리> <디렉토리> ... )
## include디렉터리와 driver/include 디렉터리에서 헤더파일을 찾는다.
include_directories ( include driver/include )


링크과정에서 필요한 라이브러리 파일을 찾을 디렉터리 목록을 지정합니다.

컴파일러 옵션 중 -L에 해당합니다.

1
2
3
## LINK_DIRECTORIES ( <디렉토리> <디렉토리> ... )
## lib디렉터리와 /var/lib 디렉터리에서 라이브러리 파일을 찾는다.
link_directories ( lib /var/lib )

target_include_directories


타겟에 포함된 소스파일에서 #include으로 포함한 헤더 파일을 찾을 디렉터리 목록을 추가합니다.

컴파일러 옵션 중 -l에 해당합니다.

대상 타겟들은 반드시 미리 선언되어 있어야합니다.

1
2
3
## TARGET_INCLUDE_DIRECTORIES ( <Target_이름> PUBLIC <디렉토리> <디렉토리> ... )
## include 디렉터리와 driver/include 디렉터리에서 헤더파일을 찾는다.
TARGET_INCLUDE_DIRECTORIES ( include driver/include )


타겟 링크시 포함할 라이브러리 목록을 지정합니다.

중요한 점은 라이브러리 파일명의 prefixpostfix는 제외하고 라이브러리 이름만 입력합니다.

EX. libxxx.a에서 lib을 제외하고 입력

대상 타겟들은 반드시 미리 선언되어 있어야합니다.

1
2
3
## TARGET_LINK_LIBRARIES ( <Target_이름> <라이브러리> <라이브러리> ... )
## app.out을 빌드할 때 libuart.a(혹은 libuart.so)와 libwifi.a를 포함하고 Shared 라이브러리를 제외하는 옵션(-static)을 지정합니다.
TARGET_LINK_LIBRARIES ( app.out uart wifi -static )


3.13버젼에서 추가된 명령어로

대상 타겟들은 반드시 미리 선언되어 있어야합니다.

1
2
3
target_link_directories(<target> [BEFORE]
<INTERFACE|PUBLIC|PRIVATE> [items1...]
[<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])

Reference

  1. https://cmake.org/cmake/help/v3.21/guide/tutorial/index.html
  2. https://iostream1029.tistory.com/7
  3. https://www.tuwlab.com/ece/27260