CMake에 대해 알아보자
뭐하는 놈인지는 알고 쓰자 시리즈의 세번째 주제는 CMake
입니다.
해당 포스팅에서는 CMake 공식 홈페이지의 튜토리얼 내용을 번역해보았습니다. CMake 공식 github에서 제공해주는 프로젝트 폴더 구성과 소스 코드를 참조하여 좀 더 상세히 CMake의 사용법에 대해 알아보는 시간을 갖도록 하겠습니다. (튜토리얼 관련 소스 코드는 이곳에서 확인 가능합니다.)
한국어로 된 CMake 관련 설명이 필요하시다면 CMake 할때 쪼오오금 도움이 되는 문서도 굉장히 잘 정리되어있으니 참고하시기 바랍니다.
목차
CMake Tutorial
Introduction
CMake 튜토리얼은 일반적인 빌드 시스템 이슈를 처리하기 위한 단계별 가이드를 제공해줍니다. 예시로 제공되는 프로젝트에서 다양한 주제들이 함께 처리되는 방식을 살펴보는 것은 아주 큰 도움이 될 것입니다.
The CMake tutorial provides a step-by-step guide that covers common build system issues that CMake helps address. Seeing how various topics all work together in an example project can be very helpful.
Step 1: A Basic Starting Point
가장 기본적인 프로젝트는 소스 코드로부터 빌드되는 실행 파일입니다. 이 간단한 프로젝트에서는 CMakeLists.txt
파일에 단 3줄만이 필요합니다. 이것이 우리의 튜토리얼 시작 지점이 될 것입니다. 아래와 같이 작성된 CMakeLists.txt
파일을 Step1
디렉토리안에 생성해봅시다.
The most basic project is an executable built from source code files. For simple projects, a three line
CMakeLists.txt
file is all that is required. This will be the starting point for our tutorial. Create aCMakeLists.txt
file in theStep1
directory that looks like:
1 | cmake_minimum_required(VERSION 3.10) |
이 예제에서는 CMakeLists.txt
파일에 소문자로 이루어진 명령어를 사용한다는 것에 주목하기바랍니다. CMake에서는 대문자, 소문자 그리고 이 둘이 혼합된 명령어를 지원합니다. tutorial.cxx
의 소스 코드는 Step1
디렉토리에서 확인가능하며 이 코드는 제곱근을 계산하기 위해 사용될 수 있습니다.
Note that this example uses lower case commands in the
CMakeLists.txt
file. Upper, lower, and mixed case commands are supported by CMake. The source code fortutorial.cxx
is provided in theStep1
directory and can be used to compute the square root of a number.
Build and Run
이것이 필요한 모든것입니다 - 지금 당장에 우리의 프로젝트를 빌드하고 실행할 수 있습니다! 먼저, 프로젝트를 설정하기 위해 cmake
실행 파일 또는 cmake-gui
실행합니다. 그리고나서 여러분이 선택한 빌드 툴을 가지고 빌드합니다.
That’s all that is needed - we can build and run our project now! First, run the
cmake
executable or thecmake-gui
to configure the project and then build it with your chosen build tool.
예를 들어, 여러분은 커맨드 라인에서 CMake 소스 코드 트리의 Help/guide/tutorial
경로로 이동할 수 있고 아래 명령어를 입력하여 빌드 디렉토리를 생성할 수 있습니다:
For example, from the command line we could navigate to the
Help/guide/tutorial
directory of the CMake source code tree and create a build directory:
1 | mkdir Step1_build |
다음으로, 생성한 빌드 디렉토리로 이동한 뒤 프로젝트를 설정하고 네이티브 빌드 시스템을 생성하기 위해 CMake를 실행합니다:
Next, navigate to the build directory and run CMake to configure the project and generate a native build system:
1 | cd Step1_build |
그리고나서 실질적으로 프로젝트를 컴파일하고 링크하기 위해 빌드 시스템을 호출합니다:
Then call that build system to actually compile/link the project:
1 | cmake --build |
마지막으로, 아래 명령어들을 통해 빌드된 Tutorial
을 사용해봅시다:
Finally, try to use the newly built
Tutorial
with these commands:
1 | Tutorial 4294967296 |
Adding a Version Number and Configured Header File
우리가 추가하게 될 첫번째 기능은 우리의 실행 파일과 프로젝트에 버전 번호를 제공하는 것입니다. 버전 번호를 소스 코드 상에 추가하는 것도 하나의 방법이지만 CMakeLists.txt
의 사용은 이를 좀 더 유연하게 해줍니다.
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.
먼저, 프로젝트의 이름과 버전 번호를 설정하기 위한 명령어인 project()를 사용하기 위해 CMakeLists.txt
파일을 수정합니다.
First, modify the
CMakeLists.txt
file to use the project() command to set the project name and version number.
1 | cmake_minimum_required(VERSION 3.10) |
그리고나서, 버전 번호를 소스 코드로 전달하기 위해 헤더 파일을 설정합니다.
Then, configure a header file to pass the version number to the source code:
1 | configure_file(TutorialConfig.h.in TutorialConfig.h) |
설정된 파일은 바이너리 트리 안에 작성될 것이기때문에 포함 파일을 찾기 위해 경로 리스트에 해당 디렉토리를 추가해주어야합니다. CMakeLists.txt
파일의 끝에 아래 라인을 추가해줍니다.
Since the configured file will be written into the binary tree, we must add that directory to the list of paths to search for include files. Add the following lines to the end of the
CMakeLists.txt
file:
1 | target_include_directories(Tutorial PUBLIC "${PROJECT_BINARY_DIR}") |
여러분이 선호하는 편집기를 사용하여 아래 내용을 포함하고 있는 TutorialConfig.h.in
파일을 소스 디렉토리에 생성해줍시다.
Using your favorite editor, create
TutorialConfig.h.in
in the source directory with the following contents:
1 | // the configured options and settings for Tutorial |
CMake가 이 헤더 파일을 설정하는 동안 @Tutorial_VERSION_MAJOR@
와 @Tutorial_VERSION_MINOR@
값은 대체될 것입니다.
When CMake configures this header file the values for
@Tutorial_VERSION_MAJOR@
and@Tutorial_VERSION_MINOR@
will be replaced.
다음으로 설정된 헤더 파일인 TutorialConfig.h
를 포함하기 위해 tutorial.cxx
파일을 수정합시다.
Next modify
tutorial.cxx
to include the configured header file,TutorialConfig.h
.
마지막으로, 아래와 같이 tutorial.cxx
파일을 수정해서 실행 파일의 이름과 버전 번호를 출력해봅시다.
Finally, let’s print out the executable name and version number by updating
tutorial.cxx
as follows:
1 | if (argc < 2) { |
Specify the C++ Standard
다음으로 tutorial.cxx
소스 내에 atof
를 std::stod
로 변경함으로써 C++11 기능을 우리 프로젝트에 추가해봅시다. 동시에 #include <cstdlib>
선언도 제거해줍니다.
Next let’s add some C++11 features to our project by replacing
atof
withstd::stod
intutorial.cxx
. At the same time, remove#include <cstdlib>
.
우리는 올바른 플래그 값을 사용하도록 CMake 코드에 명시적으로 언급할 필요가 있을것입니다. CMake에서 특정 C++ 버전을 지원하도록 하는 가장 쉬운 방법은 CMAKE_CXX_STANDARD 변수를 사용하는 것입니다. 현재 튜토리얼에서는 CMakeLists.txt
파일의 CMAKE_CXX_STANDARD 변수를 11
로 설정하고, CMAKE_CXX_STANDARD_REQUIRED 변수를 True
로 설정해보겠습니다. CMAKE_CXX_STANDARD
는 add_executable
명령어 호출 이전에 선언되어야한다는 것을 잊지말기 바랍니다.
We will need to explicitly state in the CMake code that it should use the correct flags. The easiest way to enable support for a specific C++ standard in CMake is by using the CMAKE_CXX_STANDARD variable. For this tutorial, set the CMAKE_CXX_STANDARD variable in the
CMakeLists.txt
file to11
and CMAKE_CXX_STANDARD_REQUIRED toTrue
. Make sure to add theCMAKE_CXX_STANDARD
declarations above the call toadd_executable
.
1 | cmake_minimum_required(VERSION 3.10) |
Rebuild
우리의 프로젝트를 다시 빌드해봅시다. 우리는 이미 빌드 디렉토리 생성했고 CMake를 실행했기때문에 빌드 과정으로 건너뛸 수 있습니다:
Let’s build our project again. We already created a build directory and ran CMake, so we can skip to the build step:
1 | cd Step1_build |
이제 이전과 동일한 명령어를 통해 새롭게 빌드된 Tutorial
을 실행해볼 수 있습니다.
Now we can try to use the newly built
Tutorial
with same commands as before:
1 | Tutorial 4294967296 |
인자 없이 실행 파일을 구동 할때 버전 번호가 출력되는지 확인해보시길 바랍니다.
Check that the version number is now reported when running the executable without any arguments.
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.
이번 튜토리얼에서는 해당 라이브러리를 MathFunctions
이라는 하위 디렉토리에 포함시킬 것입니다. 이 디렉토리에는 헤더 파일인 MathFunctions.h
와 소스 파일인 mysqrt.cxx
가 포함되어 있습니다. 소스 파일에는 컴파일러의 sqrt 함수와 유사한 기능을 제공하는 mysqrt
라는 함수가 존재합니다.
For this tutorial we will put the library into a subdirectory called
MathFunctions
. This directory already contains a header file,MathFunctions.h
, and a source filemysqrt.cxx
. The source file has one function calledmysqrt
that provides similar functionality to the compiler’s sqrt function.
MathFunctions
디렉토리의 CMakeLists.txt
파일에 아래와 같이 한 줄을 추가합니다.
Add the following one line
CMakeLists.txt
file to theMathFunctions
directory:
1 | add_library(MathFunctions mysqrt.cxx) |
새롭게 추가한 라이브러리를 사용하기 위해 ①최상위 CMakeLists.txt
파일에 add_subdirectory() 호출을 추가할 것이며, 이 호출을 통해 라이브러리가 빌드될 것 입니다. ②실행 파일에 새로운 라이브러리를 추가하고 mysqrt.h
③헤더 파일을 참조할 수 있도록 MathFunctions
를 포함 디렉토리로 추가합니다. 이제 최상위 CMakeLists.txt
파일의 마지막 몇 개의 라인은 아래와 같아야 합니다:
To make use of the new library we will add an add_subdirectory() call in the top-level
CMakeLists.txt
file so that the library will get built. We add the new library to the executable, and addMathFunctions
as an include directory so that themysqrt.h
header file can be found. The last few lines of the top-levelCMakeLists.txt
file should now look like:
1 | # ① add the MathFunctions library |
이번엔 MathFunctions
라이브러리를 옵션화해봅시다. 이번 튜토리얼을 진행하는데 있어서 이 작업이 반드시 필요한 것은 아니지만, 규모가 더 큰 프로젝트들에서 이러한 옵션화 작업은 일반적입니다. 가장 먼저 최상위 CMakeLists.txt
파일에 option을 추가합니다.
Now let us make the
MathFunctions
library optional. While for the tutorial there really isn’t any need to do so, for larger projects this is a common occurrence. The first step is to add an option to the top-levelCMakeLists.txt
file.
1 | option(USE_MYMATH "Use tutorial provided math implementation" ON) |
이 옵션은 cmake-gui와 ccmake에서 기본 값인 ON
으로 보여지게 될 것이며 이 값은 사용자가 변경할 수 있습니다. 이 설정은 캐시에 저장될 것이기 때문에 사용자는 빌드 디렉토리에서 CMake를 실행할 때마다 이 값을 설정할 필요가 없습니다.
This option will be displayed in the cmake-gui and ccmake with a default value of
ON
that can be changed by the user. This setting will be stored in the cache so that the user does not need to set the value each time they run CMake on a build directory.
다음 변화는 MathFunctions
라이브러리를 조건에 따라 빌드하고 링킹하도록 만드는 것입니다. 이를 위해 옵션 값을 체크하는 if 구문을 생성할 것입니다. if 블록 내부에 라이브러리를 링크하기 위해 필요한 정보를 저장하고 하위 디렉토리를 튜토리얼 타겟의 포함 디렉토리로 추가하기 위한 list 명령어와 함께 add_subdirectory() 명령어를 추가합시다.
The next change is to make building and linking the
MathFunctions
library conditional. To do this, we will create anif
statement which checks the value of the option. Inside the if block, put the add_subdirectory() command from above with some additional list commands to store information needed to link to the library and add the subdirectory as an include directory in theTutorial
target. The end of the top-levelCMakeLists.txt
file will now look like the following:
1 | if(USE_MYMATH) |
나중에 실행파일에 링크되는 옵션화된 라이브러리들을 모으기 위해 EXTRA_LIBS
라는 변수를 사용하는 것을 주목하기 바랍니다. EXTRA_INCLUDES
변수는 옵션화된 헤더 파일을 대상으로 이와 유사하게 사용됩니다. 이것은 많은 옵션 컴포넌트들을 처리하기 위한 고전적인 방식이며, 다음 단계에서 현대적인 접근 방식에 대해 살펴볼 것입니다.
Note the use of the variable
EXTRA_LIBS
to collect up any optional libraries to later be linked into the executable. The variableEXTRA_INCLUDES
is used similarly for optional header files. This is a classic approach when dealing with many optional components, we will cover the modern approach in the next step.
위 작업에 따른 소스 코드 상의 변경점은 꽤 간단합니다. 먼저, 필요 시에 tutorial.cxx
에서 MathFunctions.h
를 포함시켜줍니다.
The corresponding changes to the source code are fairly straightforward. First, in
tutorial.cxx
, include theMathFunctions.h
header if we need it:
1 | #ifdef USE_MYMATH |
그리고 나서, USE_MYMATH
를 제곱근 함수가 사용되는 조건으로 만들어줍니다.
Then, in the same file, make
USE_MYMATH
control which square root function is used:
1 | #ifdef USE_MYMATH |
이제 소스 코드에서 USE_MYMATH
변수를 요구하고 있기때문에 이 값을 TutorialConfig.h.in
파일에 아래와 같이 추가할 수 있습니다:
Since the source code now requires
USE_MYMATH
we can add it toTutorialConfig.h.in
with the following line:
1 | #cmakedefine USE_MYMATH |
프로젝트를 설정(configure)하기 위해 cmake 실행 파일 또는 cmake-gui를 실행시키고 여러분의 빌드 도구에 맞게 빌드합니다. 그리고 빌드된 Tutorial 실행 파일을 실행합니다.
Run the cmake executable or the cmake-gui to configure the project and then build it with your chosen build tool. Then run the built Tutorial executable.
이제 USE_MYMATH
값을 업데이트 해봅시다. 가장 쉬운 방법은 cmake-gui를 사용하거나 터미널 환경이라면 ccmake를 사용하는 것입니다. 또는 command-line의 옵션을 변경하기를 원한다면 아래와 같이 시도해보시기 바랍니다:
Now let’s update the value of
USE_MYMATH
. The easiest way is to use the cmake-gui or ccmake if you’re in the terminal. Or, alternatively, if you want to change the option from the command-line, try:
1 | cmake ../Step2 -DUSE_MYMATH=OFF |
Step 3: Adding Usage Requirements for a Library
사용 요구 사항을 통해 라이브러리 또는 실행 파일의 링크 및 포함 라인을 훨씬 더 잘 제어할 수 있으며 CMake 내 대상의 전이 속성을 더 많이 제어할 수 있습니다. 사용 요구 사항을 활용하는 기본 명령은 다음과 같습니다:
Usage requirements allow for far better control over a library or executable’s link and include line while also giving more control over the transitive property of targets inside CMake. The primary commands that leverage usage requirements are:
- target_compile_definitions()
- target_compile_options()
- target_include_directories()
- target_link_libraries()
사용 요구 사항의 현대적인 CMake 접근 방식을 사용하기 위해 앞선 챕터였던 Adding a Library에서 사용한 소스 코드를 리팩토링 해보겠습니다. 먼저 MathFunctions
라이브러리를 링킹하고자 한다면 현재 소스 디렉토리를 포함해야하지만 MathFunctions
자체는 포함하지 않습니다. 따라서 이것인 INTERFACE
사용 요구사항이 될 수 있습니다.
Let’s refactor our code from Adding a Library to use the modern CMake approach of usage requirements. We first state that anybody linking to
MathFunctions
needs to include the current source directory, whileMathFunctions
itself doesn’t. So this can become anINTERFACE
usage requirement.
INTERFACE
는 소비자가 필요로 하지만 생산자는 그렇지 않다는 것을 기억하기 바랍니다. MathFunctions/CMakeLists.txt
파일의 끝에 아래 라인을 추가합니다.
Remember
INTERFACE
means things that consumers require but the producer doesn’t. Add the following lines to the end ofMathFunctions/CMakeLists.txt
:
1 | target_include_directories(MathFunctions |
이제 MathFunctions
에 대한 사용 요구 사항을 명시했으므로 최상위 CMakeLists.txt
에서 EXTRA_INCLUDES
변수 사용을 안전하게 제거할 수 있습니다:
Now that we’ve specified usage requirements for
MathFunctions
we can safely remove our uses of theEXTRA_INCLUDES
variable from the top-levelCMakeLists.txt
, here:
1 | if(USE_MYMATH) |
And here:
1 | target_include_directories(Tutorial PUBLIC |
위와 같이 수정을 완료하였다면 프로젝트를 설정(configure)하기 위해 cmake 실행 파일 또는 cmake-gui를 실행시키고 여러분의 빌드 도구에 맞게 빌드하거나 빌드 디렉토리에서 cmake --build
를 사용하여 빌드합니다.
Once this is done, run the cmake executable or the cmake-gui to configure the project and then build it with your chosen build tool or by using
cmake --build .
from the build directory.
해당 게시글에서 발생한 오탈자나 잘못된 내용에 대한 정정 댓글 격하게 환영합니다😎
reference