본문 바로가기
CMake

[CMake] Tutorial (1) - Start CMake

by 별준 2021. 10. 28.

References

Contents

  • CMake ?
  • cmake_minimum_required()
  • project()
  • add_excutable()
  • configure_file()
  • target_include_directories()

회사에서 개발을 하거나 다른 github repository를 살펴보면 대부분 CMake로 빌드 환경을 구성하도록 되어 있습니다. 저 역시도 CMake를 자주 접하지만, 주먹구구식으로 알고 있는 것들이 많아서 한 번 정리할 필요가 있다고 생각이 들었고, 이번 기회를 통해서 CMake의 기본적인 사용법, 문법부터 프로젝트 구성을 위해서 어떻게 사용해야하는지에 대해서 알아보도록 하겠습니다.


CMake를 왜 사용하는가 ?

리눅스 환경에서 프로그래밍을 한다면 익숙하겠지만, 보통 프로그램을 컴파일 할 때 make라는 프로그램을 사용하게 됩니다. 윈도우에서는 예를 들어 비주얼 스튜디오를 사용한다면 컴파일 버튼을 누르면 알아서 컴파일이 되는 것과는 달리, 리눅스 상에서 컴파일을 하려면 어떤 파일을 컴파일할 지, 어떠한 방법으로 할 지 직접 컴파일러에게 지시를 내려야 합니다. 매번 명령어를 치면 문제가 없지만, 프로젝트의 크기가 커지면서 파일이 많아진다면 매번 입력하는 것은 거의 불가능에 가깝습니다.

이 문제를 해결하기 위해서 리눅스에서는 make라는 프로그램을 제공하는데, 이 make는 Makefile이라는 파일을 읽어서 주어진 방식대로 명령어를 처리합니다.

하지만, Makefile은 간단한 프로젝트를 관리하기에는 좋지만, 프로젝트의 크기가 커진거나 여러 플랫폼에서 배포하기 위해서는 불편한 점이 많습니다. 이를 해결하기 위해서 CMake를 사용합니다.

특히 다양한 환경(여러 플랫폼)을 위한 빌드 프로세스 관리를 보다 쉽게 하기 위해서 CMake를 사용합니다.


CMake ?

CMake빌드 설정부터 배포를 위한 패키지 생성에 이르기까지 모든 것을 다루는 프로그램입니다.

또한, 위 프로세스를 관리해줄 뿐만 아니라 다양한 플랫폼을 위한 도구 및 언어도 지원합니다.

CMake 프로그램의 첫 번째 단계는 개발자가 사용하는 플랫폼의 빌드 도구(make, visual studio, etc...)에 적합한 빌드 파일(프로젝트 파일)을 생성합니다.

예를 들어, 윈도우 환경이라면 CMake를 통해서 비주얼 스튜디오로 작업할 수 있는 빌드 파일들을 생성해줄 것이고, 리눅스 환경이라면 Makefile을 생성해줄 것입니다.

(즉, 빌드 프로그램이 아닌, 빌드 파일들을 생성해주는 프로그램입니다)

그리고 빌드는 각 플랫폼에 맞추어서 진행할 수 있으며(빌드 도구를 CMake에서 호출할 수도 있습니다.), 이 단계 이후에는 테스트 및 패키징 단계를 위한 CTest나 CPack이 포함되는데 이것도 별도로 추가하여 간단하게 사용할 수 있습니다.

이번 글에서 하나의 소스코드로 이루어진 프로젝트가 CMake를 통해 어떻게 빌드 환경이 구성되는 지 살펴보겠습니다.

(윈도우와 리눅스 환경 모두 살펴보겠습니다.)


간단한 프로젝트 빌드 및 실행

CMake를 사용하는 모든 프로젝트에는 반드시 프로젝트 최상위 디렉토리에 CMakeLists.txt라는 파일이 존재해야 합니다. 이 파일에서 빌드해야할 항목과 빌드 방법, 테스트/패키지를 정의하고, 빌드 파일을 생성하는데 필요한 정보들이 들어 있습니다.

즉, 이 파일은 CMake가 빌드를 위한 프로젝트 파일을 생성하기 위해 읽어야하는 설명서라고 보시면 됩니다.

일단 CMakeLists.txt와 tutorial.cxx 두 개의 파일로 구성된 아주 간단한 프로젝트를 구성했습니다.

소스코드는 CMake에서 제공되는 튜토리얼 코드이며, 아래 링크에서 확인할 수 있습니다.

https://github.com/Kitware/CMake/blob/master/Help/guide/tutorial/Step1/tutorial.cxx

그리고 CMakeLists.txt의 내용은 다음과 같습니다.

cmake_minimum_required(VERSION 3.10)

# set the project name
project(Tutorial)

# add the executable
add_executable(Tutorial tutorial.cxx)

위 내용이 무엇을 뜻하는 지는 아래에서 설명하고, 우선 빌드 파일들을 생성하고 빌드 및 실행을 해보도록 하겠습니다.

먼저 위 프로젝트 경로에서 cmake를 실행해야 합니다.

cmake를 실행하면, 빌드하기 위한 파일들이 생성되는데, 생성되는 빌드 파일들은 프로젝트 소스 파일들이 존재하는 디렉토리가 아닌 다른 디렉토리에 생성하도록 권장합니다.

(권장이라기 보단 필수입니다.. ! CMake 실행 시에는 여러 가지 파일들이 생성되는데 프로젝트 폴더에서 생성하게 되면 디렉토리가 아주 엉망이 될 수 있습니다.)

따라서, 위 프로젝트 경로에서 build라는 폴더를 새로 만듭니다. 이는 윈도우나 리눅스 모두 동일합니다.

그 다음 build 폴더로 이동해서 아래의 명령어를 입력합니다.

윈도우의 경우에는 cmd나 bash를 사용하면 됩니다. (저는 bash를 주로 사용합니다.)

cmake ..

'..'은 최상위 CMakeLists.txt가 존재하는 경로의 상대 경로입니다.

  • Windows에서 'cmake ..' 실행 결과

생성된 빌드 파일

여기서 Visual Studio 솔루션인 Tutorial.sln 을 실행하면, 익숙한 Visual Studio가 실행됩니다.

  • Ubuntu 20.04에서 'cmake ..' 실행 결과

이렇게 생성된 후에 make 명령어를 입력해주면, 빌드가 진행됩니다.

위와 같이 CMake에서 여러 가지 설정들을 체크하고 빌드 파일을 생성한 것을 볼 수 있습니다.

윈도우의 경우에는 visual studio에서 실행할 수 있도록 빌드 파일들이 생성된 것을 볼 수 있고, 리눅스의 경우에는 make로 빌드할 수 있도록 Makefile이 생성된 것을 볼 수 있습니다.

간단히 다음과 같이 실행할 수 있습니다.

윈도우의 경우에는 argument를 입력해주어야 하기 때문에 [Tutorial - Properties - Debugging - Command Arguments]에 argument를 입력해주어야 합니다.

그리고 Ctrl + F5로 실행해보면, 아래의 결과를 얻을 수 있습니다.

 


그럼 이제, CMaksLists.txt의 내용을 살펴보겠습니다.

cmake_minimum_required(VERSION 3.10)

# set the project name
project(Tutorial)

# add the executable
add_executable(Tutorial tutorial.cxx)
  • cmake_minimum_required()

파일의 처음에 나오는 cmake_minimum_required는 해당 프로젝트에서 사용될 cmake의 최소 버전을 명시해줍니다. 사용자마다 설치된 CMake의 버전이 다른데, 저의 경우에는 3.20 버전(리눅스의 경우 3.16)이 설치되어 있습니다. CMake는 버전에 따라서 차이가 많이 나기 때문에(특히 2.X 버전), 옛날 버전의 CMake를 사용하는 경우 지원하지 않는 기능이 있을 수도 있습니다.

  • proejct()

그 다음으로 project를 통해 프로젝트의 정보를 명시할 수 있습니다.

여기서는 프로젝트의 이름만 추가했지만, Version, Description, Homepage URL, Languages도 추가할 수 있습니다.

proejct(Tutorial
    Version 1.0
    Description "Tutorial 01"
    LANGUAGES CXX C)

LANGUAGES(3.0 이후 버전부터 지원)는 C프로젝트라면 'C', C++프로젝트면 'CXX'를 명시하면 되고, 만약 명시하지 않는다면 디폴트로 'C'와 'CXX'가 설정됩니다. 그 이외에 CUDA, OBJC, OBJCXX 등 여러 가지 옵션들이 있습니다.

project는 최상위 CMakeLists.txt에 필수로 입력되어야 하며, 만약 입력되지 않는다면 project(Project)가 있는 것처럼 동작하게 됩니다.(프로젝트의 이름을 Project로 설정해버립니다. 빌드 파일 생성은 정상적으로 진행됩니다.)

그리고, project는 CMakeList.txt의 최상단에 위치해야하는데, 단, cmake_minimum_required()의 뒤쪽에 위치해야합니다. (CMP0000 참조)

  • add_excutable()

add_excutable은 생성할 실행 파일을 추가하는 명령입니다. argument로 맨 처음 생성하고자하는 실행 파일의 이름을 적은 뒤에, 그 뒤로 해당 실행 파일을 만드는데 필요한 소스들을 나열하면 됩니다. 위 예제는 tutorial.cxx 하나만 사용하므로 하나만 추가되어 있습니다.

우리는 이 명령어로 Tutorial이라는 실행 파일을 생성하도록 설정했습니다. 따라서 위에서 빌드를 수행했을 때, 결과물에 Tutorial 또는 Tutorial.exe 라는 실행파일이 생성된 것을 확인할 수 있습니다.

(리눅스 : build/Tutorial / 윈도우 : build/Debug(or Release)/Tutorial.exe)

참고로 CMake 명령어들은 대소문자를 구분하지 않습니다.
따라서, 아래의 명령어는 모두 동일합니다.

add_excutable(Tutorial tutorial.cxx)
ADD_EXCUTABLE(Tutorial tutorial.cxx)
Add_Excutable(Tutorial tutorial.cxx)

 

 


Configured Header File 추가

위 내용에서 추가로 project의 버전 정보를 추가하고, 이 버전 정보를 configured header file로 전달하여 헤더파일을 생성하는 법을 알아보겠습니다. 이 방법을 응용하면, CMake 단계에서 필요한 헤더파일들을 빌드 전에 생성할 수 있습니다.

 

먼저 위에서 사용한 소스코드가 아닌, 다음 소스코드를 준비해주세요.

https://github.com/Kitware/CMake/blob/master/Help/guide/tutorial/Step2/tutorial.cxx

변경된 소스코드에서는 cmake 단계에서 생성된 TutorialConfig.h를 include하여 사용하는 코드가 추가되어 있습니다.

 

그리고 CMakeLists.txt 파일을 다음과 같이 수정합니다.

cmake_minimum_required(VERSION 3.10)

# set the project name and version
project(Tutorial
	VERSION 1.0)

# configure a header file to pass some of the CMake settings
# to the source code
configure_file(TutorialConfig.h.in TutorialConfig.h)

# add the executable
add_executable(Tutorial tutorial.cxx)

# add the binary tree to the search path for include files
# so that we will find TutorialConfig.h
target_include_directories(Tutorial PUBLIC
                           "${PROJECT_BINARY_DIR}"
                           )

이전과 다르게 project에 VERSION 1.0 이 추가되었습니다.

버전이 추가되면, 아래의 CMake 변수들의 값이 set됩니다. 

이렇게 정의되는 버전 정보에 대한 CMake 변수들을 사용하여 해당 버전 정보를 소스 코드에서 사용할 수 있도록 헤더파일을 생성해보도록 하겠습니다.

 

  • configure_file()

configure_file 명령어는 어떠한 파일을 다른 위치에 복사하거나 CMake 변수값들을 파일에 쓸 수 있도록 할 수 있습니다. 따라서, 이 명령어를 통해 project의 버전(1.0) 정보를 담고 있는 CMake 변수들 값을 헤더파일에 써보도록 하겠습니다. configure_file의 첫 번째 argument는 소스가 되는 파일이며, 두 번째 argument는 결과물의 파일명입니다. (절대경로가 아닌 파일명만 입력하면, 현재 CMakeLists.txt가 존재하는 경로를 기준으로 상대경로로 인식하게 됩니다.)

# configure a header file to pass some of the CMake settings
# to the source code
configure_file(TutorialConfig.h.in TutorialConfig.h)

그 전에 생성할 헤더파일의 포맷이 되는 파일을 정의해주어야 합니다. 여기에선 TutorialConfig.h.in 파일인데, 이 파일은 아래와 같이 작성되어 있습니다.

<TutorialConfig.h.in 파일>

// the configured options and settings for Tutorial
#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@

define을 통해서 Tutorial_VERSION_MAJOR와 Tutorial_VERSION_MINOR를 정의해주는데 이 값은 CMake에서 정의된 버전 변수명으로 접근하고 있습니다. CMake 변수에 접근하기 위해서 변수명의 양 끝을 '@'로 감싸주면 되는데, '@'로 감싸주게 되면 configure_file을 통해서 파일이 copy될 때, 해당 변수의 값으로 대체되게 됩니다.

 

그럼 다시 'cmake ..' 명령어로 빌드 파일을 생성하면, build 폴더 내에 'TutorialConfig.h' 라는 파일이 생성된 것을 확인할 수 있습니다.

내용을 확인해보면, 해당 파일을 복사할 때 정상적으로 CMake 변수(Tutorial_VERSION_MAJOR, Tutorial_VERSION_MINOR)값으로 대체된 것을 확인할 수 있습니다.

생성된 TutorialConfig.h 내용

  • target_include_directories()

이렇게 생성된 헤더파일을 우리가 생성할 실행파일(즉, 소스코드)에서 사용하려면, target_include_directories 명령어를 통해서 include 시켜주어야 합니다.

Target과 Property
타겟(target)
이라는 용어가 처음 나왔는데, CMake에서 말하는 타겟은 프로그램을 구성하는 요소라고 생각하면 됩니다. 예를 들어, 위에서 생성한 것처럼 실행 파일이 될 수도 있고, 다음에 살펴볼 라이브러리가 될 수도 있습니다.
CMake의 모든 명령어들은 이 타겟을 기준으로 돌아가고, 각 타겟에는 속성(Property)을 정의할 수 있습니다. 지금처럼 타겟에 include directory를 추가하는 것도 include 속성을 추가하는 것과 같다고 보면 됩니다.
속성(Property)은 추후에 더 알아보도록 하겠습니다.
# add the binary tree to the search path for include files
# so that we will find TutorialConfig.h
target_include_directories(Tutorial PUBLIC
                           "${PROJECT_BINARY_DIR}"
                           )

여기서 실행파일인 Tutorial의 include directories에 ${PROJECT_BINARY_DIR}를 추가합니다. (PUBLIC은 우선 무시해주세요). PROJECT_BINARY_DIR는 CMake에서 미리 정의되는 변수로써, 프로젝트의 build directory를 값으로 가지고 있습니다. 그리고 ${...} 은 CMake 변수값을 취하기 위해서 사용되는 CMake의 문법입니다.

(변수나 다른 기타 문법은 추후에 다른 글에서 자세하게 알아보도록 하겠습니다.)

 

이는 윈도우를 예로 들면, 비주얼 스튜디오에서 [Properties - C/C++ - Additional Include Directories]에 추가하는 것을 CMake에서 해주는 것입니다. 따라서, 윈도우에서 CMakeLists.txt 파일로 다시 빌드 파일을 생성하면, Additional Include Directories에 build 폴더가 추가되어 있을 것입니다.

 

cmake 후, 빌드를 진행하고 실행하면 다음의 결과를 확인할 수 있습니다.

 


 

이렇게 소드코드가 하나인 매우 간단한 프로젝트에서 CMake가 어떻게 사용되는지 알아봤습니다.

프로젝트가 커질수록 CMakeLists.txt의 내용도 많아지는데, 차근차근 하나씩 알아보도록 하겠습니다 !

 

글 중간에 소스 코드 주소를 적어뒀지만, 나머지 파일들을 다운받아서 사용하고 싶으시다면, 아래 github에서 확인하실 수 있습니다. (tutorial의 Step1, Step2의 코드를 사용했습니다.)

https://github.com/Kitware/CMake/tree/master/Help/guide/tutorial/Step1

https://github.com/Kitware/CMake/tree/master/Help/guide/tutorial/Step2

 

 

'CMake' 카테고리의 다른 글

[CMake] Flow Control - if  (0) 2021.10.30
[CMake] Lists  (0) 2021.10.29
[CMake] String Handling (string 명령어)  (0) 2021.10.29
[CMake] Variable (변수)  (0) 2021.10.29
[CMake] Tutorial (2) - Library 추가  (0) 2021.10.28

댓글