CMake 명령어
CMake를 사용할 때는 CMakeLists.txt에 다양한 명령어를 함수처럼 호출해서 사용한다. 이번 글에서 각 명령어들에 대해 소개하도록 한다.
cmake_minimum_required
cmake_minimum_required (VERSION [minimum required version])
최소로 요구되는 CMake
version을 명시한다.
project
project(<PROJECT-NAME> [VERSION <major>[.<minor>[.<patch>[.<tweak>]]]] [DESCRIPTION <project-description-string>] [HOMEPAGE_URL <url-string>] [LANGUAGES <language-name>...])
해당 project의 필수 정보들을 입력한다. 이 명령은 CMake 수행에 반드시 존재해야 한다.
add_executable
add_executable ([target] [source code 1] [source code 2] ...)
예를 들면 아래와 같이 main.cpp
, foo.cpp
파일이 있어서 이를 executable file을 만든다고 가정하자.
// foo.cpp #include "foo.hpp" int foo() { return 100; }
// foo.hpp int foo();
// main.cpp #include <iostream> #include "foo.hpp" int main() { std::cout << "Hello CMake" << std::endl; std::cout << "foo=" << foo() << std::endl; return 0; }
그리고 CMakeLists.txt를 아래와 같이 작성 후 실행해보자.
# CMakeLists.txt # Minimum required CMake version cmake_minimum_required (VERSION 3.20) # Project information project ( ShuminProject VERSION 0.1 DESCRIPTION "Toy Exmaple" LANGUAGES CXX ) add_executable (a.out main.cpp foo.cpp)
$ make [ 33%] Building CXX object CMakeFiles/a.out.dir/main.cpp.o [ 66%] Building CXX object CMakeFiles/a.out.dir/foo.cpp.o [100%] Linking CXX executable a.out [100%] Built target a.out $ ./a.out Hello CMake foo=100
위와 같이 예상하는 결과를 출력한 것을 볼 수 있다.
target_compile_options
target_compile_options([target] PUBLIC [compile opt1] [compile opt2] ...)
Build option을 추가 할 때 사용하는 명령어로, executable file 생성 시 필요한 옵션을 추가해준다. 참고로 add_executable
에 명시한 executable file
과 다른 경우 cmake 수행 시 error를 발생시킨다.
# CMakeLists.txt # Minimum required CMake version cmake_minimum_required (VERSION 3.20) # Project information project ( ShuminProject VERSION 0.1 DESCRIPTION "Toy Exmaple" LANGUAGES CXX ) add_executable (a.out main.cpp foo.cpp) target_compile_options (a.out PUBLIC -Wall -Werror -save-temps)
make Consolidate compiler generated dependencies of target a.out [ 33%] Building CXX object CMakeFiles/a.out.dir/main.cpp.o x86_64-conda_cos6-linux-gnu-g++: warning: -pipe ignored because -save-temps specified [ 66%] Building CXX object CMakeFiles/a.out.dir/foo.cpp.o x86_64-conda_cos6-linux-gnu-g++: warning: -pipe ignored because -save-temps specified [100%] Linking CXX executable a.out [100%] Built target a.out $ ls a.out CMakeCache.txt CMakeFiles cmake_install.cmake foo.ii foo.s main.ii main.s Makefile
위 결과를 보면 -save-temps
옵션으로 인해 temporary file들이 생성된 것을 볼 수 있다.
참고로 target이란 꼭 executable만을 말하지 않고 library file이 될 수도 있다.
target_include_directories
target_include_directories([target] PUBLIC [path 1] [path 2] ...)
프로젝트 규모가 커지면 include 디렉토리를 따로 관리하는 경우가 존재한다. 이를 위한 명령어 target_include_directories
를 통해서 include path를 지정 가능하다.
$ tree . |-- CMakeLists.txt |-- foo.cpp |-- include | `-- foo.hpp `-- main.cpp
include
디렉토리를 생성해 header file을 내부에 저장하도록 변경해서 명령어를 통해 해당 경로를 지정하는 방식으로 CMakeLists.txt
를 수정했다.
# CMakeLists.txt # Minimum required CMake version cmake_minimum_required (VERSION 3.20) # Project information project ( ShuminProject VERSION 0.1 DESCRIPTION "Toy Exmaple" LANGUAGES CXX ) add_executable (a.out main.cpp foo.cpp) target_compile_options (a.out PUBLIC -Wall -Werror -save-temps) target_include_directories(a.out PUBLIC ${CMAKE_SOURCE_DIR}/includes)
참고로 CMAKE_SOURCE_DIR
는 CMake에서 기본으로 제공하는 변수로 최상위 디렉토리 위치, 즉 CMakeLists.txt
파일이 있는 곳을 가리킨다.
add_library
add_library (<library> [STATIC | SHARED | MODULE ] <source 1> <source 2> ...)
만약에 중간에 static library 또는 shared library를 생성하고 싶을 때는 add_library
명령을 통해서 실행이 가능하다.
Static library는 STATIC
, shared library는 SHARED
, MODULE
은 동적으로 링크되진 않지만 dlopen
함수와 같이 runtime에 불러올 수 있는 library를 말한다. add_library
를 사용하는 예제를 다음과 같이 나타낼 수 있다.
$ tree . |-- CMakeLists.txt |-- foo.cpp |-- includes | |-- bar.hpp | `-- foo.hpp |-- lib | |-- bar.cpp | `-- CMakeLists.txt `-- main.cpp
# lib/CMakeLists.txt # Make static library bar add_library(bar STATIC bar.cpp) # Header file path to compile library target_include_directories(bar PUBLIC ${CMAKE_SOURCE_DIR}/includes) # Option to compile the library target_compile_options(bar PRIVATE -Wall -Werror)
// bar.cpp #include "bar.hpp" #include <iostream> void bar() { std::cout << "Bar() Function" << std::endl; }
// bar.hpp void bar();
우선 lib 안에 bar.cpp
를 넣고 내부에 CMakeLists.txt
를 새로 작성해준다. CMakeLists.txt
를 새로 작성 할 때는 우선 add_library(bar STATIC bar.cpp)
를 통해 static library를 생성 할 것을 명시해주며 include path를 명시해준다. (bar.cpp
에서 bar.hpp
를 참조하기 때문에)
그리고 컴파일 옵션을 지정해줄 때는 PRIVATE
을 명시해주는데 이는 bar
를 build 할 때는 위 옵션을 사용하고 싶지만, bar
를 사용하는 곳에서까지 위 옵션을 사용하고 싶지 않기 때문이다.
최종적으로 최상위 CMakeLists.txt
파일은 다음과 같다.
# CMakeLists.txt # Minimum required CMake version cmake_minimum_required (VERSION 3.20) # Project information project ( ShuminProject VERSION 0.1 DESCRIPTION "Toy Exmaple" LANGUAGES CXX ) # Add sub directory add_subdirectory (lib) add_executable (a.out main.cpp foo.cpp) target_compile_options (a.out PUBLIC -Wall -Werror -save-temps) target_link_libraries (a.out bar)
$ ./a.out Hello CMake foo=100 Bar() Function
target_link_libraries
target_link_libraries
는 linking 할 때 특정 library를 추가 할 때 사용하는 command다. 이 때 add_library
와의 차이점이 무엇인지 궁금할 수 있는데, add_library
는 target을 library file로 지정할 수 있는 반면에, target_link_libraries
는 불가능하다.
만약 app
이라는 binary를 target으로 생성하고 싶을 때, app.cpp
에서 libA.a
library를 사용한다고 했을 때 다음과 같이 명령어를 통해 linking이 가능하다.
add_executable (app app.cpp) add_library (a_lib STATIC a.cpp) target_link_libraries (app a_lib)
추가로 add_library
명령어를 통해서는 dependencies, compile definitions, compile flags 등을 지정할 수 있다고 한다. (참고)
file (Include All Source Files)
CMake에서 target을 build 하는데 만약 다수의 source file이 필요하다고 하면 각각을 명시하지 않고도 추가 할 수 있도록 명령을 제공한다.
file (GLOB_RECURSE [variable name] [options] [source files])
# CMakeLists.txt file(GLOB_RECURSE SRC_FILES CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp )
위 CMakeLists.txt
파일을 분석해보면 현 디렉토리에서 재귀적으로 *.cpp
확장자 파일들을 모두 포함해 SRC_FILES
변수에 추가하는 명령이다.
GLOB_RECURSE
옵션은 인자로 주어진 해당 디렉토리 및 하위폴더를 포함해서 전체를 재귀적으로 보도록 하는 옵션이다. 만약 재귀적으로 포함하고 싶지 않는 경우는 GLOB
을 주면 된다.
CONFIGURE_DEPENDS
옵션은 만약 파일 목록이 전과 다를 경우 CMake를 다시 실행해서 build file들을 재생성 하라는 옵션이다. 따라서 만약 파일이 추가가 되더라도 cmake ..
을 수행할 필요 없이 그냥 make
만 실행해도 cmake
가 호출된다.
CMAKE_CURRENT_SOURCE_DIR
변수는 현재의 CMakeLists.txt
의 위치를 나타낸다.
참고로 CMake는 변수를
{}
로 감싸며, Make에서는()
로 감싸는 규칙이 존재한다.
# lib/CMakeLists.txt # Include all source files (*.cpp) file(GLOB_RECURSE SRC_FILES CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ) # Make static library bar add_library(bar STATIC ${SRC_FILES}) # Header file path to compile library target_include_directories(bar PUBLIC ${CMAKE_SOURCE_DIR}/includes) # Option to compile the library target_compile_options(bar PRIVATE -Wall -Werror)
add_dependencies
add_dependencies(<target> [<target-dependency>]...)
Target간 의존성을 정의하고 싶을 때 사용한다. Target은 add_executable
, add_library
, add_custom_target
으로 정의한 것들을 말한다.
Reference
- https://cmake.org/cmake/help/latest/manual/cmake-commands.7.html
- https://modoocode.com/332
- https://gist.github.com/luncliff/6e2d4eb7ca29a0afd5b592f72b80cb5c
- https://cgold.readthedocs.io/en/latest/index.html
- https://www.tuwlab.com/ece/27260