개발/개발 환경

[개발 환경] CMake 프로젝트 구성 및 명령어들 (feat. VS Code)

growing-dev 2023. 1. 27. 22:04

CMake 프로젝트 구성 및 명령어들 (feat. VS Code)

 

  • 만들어 볼 프로젝트 트리
  • CMakeList.txt 분석(각 명령어에 대한 이해)
  • 소스 코드 분석
  • 빌드 및 실행

 

만들어 볼 프로젝트 트리

위와 같이 CMakeLists.txt와 build, inc, src로 구성된 프로젝트를 간단히 만들어 보도록 한다.

build : CMake 설정들과 실제 빌드 아웃풋이 나오는 디렉토리이다.

inc : include header가 포함될 디렉터리이다.

src : 실제 소스가 포함될 디렉터리이다.

 

CMakeList.txt 분석(각 명령어에 대한 이해)

CMake에서 지원하는 명령어(함수)에 대해서 알아보도록 한다. 함수라고 부를지 명령어라고 부를지 애매하긴 한데 그냥 명령어라고 하겠다. 아래는 내가 사용한 CMakeList.txt이다.

cmake_minimum_required(VERSION 3.16)

project(my_test_project LANGUAGES CXX VERSION 0.1.0)

set(CMAKE_BUILD_TYPE Debug)

file(GLOB SRC
    "src/*.cpp" "src/*.c")

add_executable(my_test_project ${SRC})

target_include_directories(my_test_project PUBLIC ${CMAKE_SOURCE_DIR}/inc)

message(${CMAKE_SOURCE_DIR}/inc)

 

cmake_minimum_required(VERSION 3.16)

해당 cmake 프로젝트에서 최소로 필요한 cmake 버전을 명시하는 것이 제일 먼저 나온다.

최신은 3.23 정도까지 릴리즈 되었고 나의 경우는 Ubuntu 20.04의 default 버전인 3.16을 사용했지만 대략 3.10 정도 이상이면 문제없지 않을까 생각한다.

cmake --version

 

project(my_test_project LANGUAGES CXX VERSION 0.1.0)

project name을 정하고 지원하는 언어 및 프로젝트 자체 버전을 명시할 수 있다.

단순히 project 이름만을 설정하는 것이 아니라 해당 project를 실행하면서 여러 가지 초기 세팅을 하는 매우 중요한 명령어이다.



set(CMAKE_BUILD_TYPE Debug)

set은 특정 매크로를 생성할 수 있는 함수로서, CMAKE_BUILD_TYPE처럼 기존 CMake 자체 내장된 변수를 지정하는 것뿐만 아니라 새로운 변수를 자체적으로 선언하고 사용할 수 있는 함수이다. 각 프로젝트에 따라서 자주 사용될 명령어이다.



file(GLOB SRC  "src/*. cpp" "src/*. c")

file은 특정 변수로 파일을 묶을 수 있는 명령어이다. GLOB라는 파라미터 이후 원하는 변수(SRC)와 같은 형태로 지정하고 이후 묶을 파일들을 명시해 주면 된다. *로 표시하면 해당하는 파일들을 모두 묶는 것이라서 편하지만, 파일이 그렇게 많지 않다면 명시적으로 적어주는 것도 나쁘지 않은 것 같다.

또 디렉터리가 트리형태로 줄줄이 있다면 GLOB 대신에 GLOB_RECURSE라는 파라미터로 주면 된다.



add_executable(my_test_project ${SRC})

실제 프로그램을 만드는 명령어다. 위 file로 묶은 SRC 변수를. exe로 만들겠다는 명령어이다.

라이브러리 형태로 만들 거면 add_library를 사용하면 된다.



target_include_directories(my_test_project PUBLIC ${CMAKE_SOURCE_DIR}/inc)

include path를 프로젝트에 포함한다. 이것이 없다면 소스파일에서 #include를 할 때 일일이 경로를 입력해줘야 한다.

당장은 가능할지라도 프로젝트 구조가 변하거나 추가될 때 깨질 수 있어서 필수적이라고 할 수 있는 명령어다.

PUBLIC과 PRIVATE 옵션을 줄 수 있는데, 상위 모듈에서 해당 디렉터리를 보이게 할 건지 안 보이게 할 건지 지정할 수 있다. 가능한 PRIVATE로 하는 게 당연히 좋겠지만 지금 예제에서는 별로 중요하지 않아서 PUBLIC으로 지정했다.



message(${CMAKE_SOURCE_DIR}/inc)

print 기능이다. CMake 설정 중 문제가 발생하거나 값을 확인하고 싶을 때 사용할 수 있고, 특정 정보나 메시지를 항상 출력하고 싶을 때 사용하면 되는 명령어다.

 

 

소스 코드 분석

코드는 main, moduleA, moduleB로 나누어서 작성했다.

moduleA는 functionA를 포함하고, moduleB는 functionB를 포함한다.

main에서 functionA를 호출하고 functionA가 functionB를 호출하는 구조로 잡았다.

따라서 main에서는 moduleA.h만 필요하고 moduleA.cpp에서는 moduleB.h만 필요하고 moduleB.cpp는 헤더가 필요 없다.

이렇게 잡은 이유는 나중에 의존성을 분석하고 CMake가 의존성 자체를 관리할 수 있는 수단이 될 수도 있다는 점을 보여주기 위해서이다.

 

main.cpp

#include <iostream>
#include "moduleA.h"

int main(int, char**) {
   functionA();
}

 

moduleA.cpp

#include <iostream>
#include "moduleB.h"

void functionA(void) {
    std::cout << "functionA called\n";
    functionB();
}

 

moduleB.cpp

#include <iostream>

void functionB(void){
    std::cout << "functionB called\n";
}

 

 

 

빌드 및 실행

빌드를 수행하니 잘 빌드가 되었다.

 

build내에 생성된 my_test_project를 실행하면 functionA called와 functionB called가 순차적으로 호출됨을 확인할 수 있다.

 

 

결론

CMake는 CMakeList.txt를 기반으로 각종 명령어들을 구성해서 빌드하는 시스템이다.

단순히 빌드만을 위한 것이 아니라 다양한 명령어들을 지원함으로써 빌드 시스템을 효율적으로 관리하고 의존성까지 설정할 수 있는 것이 장점이다. 이제 이 프로젝트를 기반으로 차근차근 더 공부해 나가겠다.

반응형