[C++] CMake의 사용법


Study/C , C++ , MFC  2020. 3. 10. 00:43

안녕하세요. 명월입니다.


이 글은 CMake의 사용법에 대한 글입니다.


제가 이전에 Make와 Makefile의 작성법에 대한 설명을 한 적이 있습니다.

링크 - [C++] Make와 Makefile 작성법


Linux환경에서 C++ 언어를 사용한다고 하면 이 Make와 Makefile를 이용해서 빌드을 해야합니다. 하지만, 프로젝트가 커지면 커질 수록 Makefile에 의존성 관계를 관리하는게 쉽지만은 않습니다.

그래서 이 Makefile을 만들어 주는 툴이 있는데 이게 CMake입니다.


CMake는 각각 소스의 내용을 파악하고 의존성 정보를 알아서 작성하고 Makefile를 추상화 작업을 하여 Meta데이터로서 Makefile를 관리한다는 프로그램입니다.

사실 Makefile를 직접 작성해서 관리하는 건 거의 없습니다. 보통 이 CMake를 통해 Makefile를 관리하고 make를 통해 빌드를 하는게 일반적입니다. 이 일반적도 한 10년전 이야기입니다. Visual studio가 있는데, 굳이..


먼저 CMake를 사용하기 위해서는 cmake를 설치해야 합니다.

# ubuntu의 경우
sudo apt install cmake
# Centos의 경우
sudo yum install cmake

설치가 되었으면 CMakeLists.txt파일을 만듭니다. (참고로 파일명을 주의해야합니다. 특히 대소문자 구분해서 CMakeLists.txt파일입니다.)

위의 소스의 리스트를 빌드하겠습니다.([C++] Make와 Makefile 작성법에서 사용하던 소스들...)

ADD_EXECUTABLE(main.exe main.cpp function.cpp)

Makefile로 작성할 때는 여러 의존성 관계까지 작성을 했는데 cmake의 빌드 파일인 CMakeLists.txt은 엄청 심플하네요.


이걸로 Makefile 파일을 작성하겠습니다.

cmake CMakeLists.txt

cmake를 실행하면 3개의 파일과 1개의 디렉토리가 생긴 것을 확인할 수 있습니다.

그럼 저 Makefile를 make하기 전에 Makefile의 내용을 살펴보겠습니다.

무언가 엄청난 빌드 파일입니다... 자세히 분석해 보면 이해를 하겠는데... 굳이 이해할 필요는 없습니다.


그대로 make를 합니다.

main.exe파일이 생성되었습니다. 실행해 보겠습니다.

작성된 프로그램대로 출력이 되었습니다.


사실 cmake파일을 사용하는 이유는 좀 더 복잡한 구조의 프로젝트를 빌드하기 위한 프로그램입니다. 좀 더 복잡하게 설정하겠습니다.

디렉토리 구조는 위와 같이 작성하고 메인 프로그램은 main 폴더입니다.

common은 공통 해더 파일을 두는 폴더입니다.

lib은 lib은 라이브러리 소스입니다.

// 해더 파일
#include <stdio.h>
#include <iostream>

using namespace std;

void function();
// 소스 파일
#include "function.h"

void function()
{
  cout << "function" << endl;
}
// 메인 파일
#include <stdio.h>
#include <iostream>
#include "function.h"
#include "../common/libtest.h"

using namespace std;

int main()
{
  cout << "hello world" << endl;
  // 다른 cpp의 함수를 호출
  function();
  // 라이브러리의 함수 호출
  libtest();
}
// 공통 해더 파일
#include <stdio.h>
#include <iostream>

using namespace std;

void libtest();
// 라이브러리 파일
#include "../common/libtest.h"

void libtest()
{
  cout << "libtest" << endl;
}

위 예제는 이전의 예제보다 라이브러리를 추가하여 참조하는 방식의 빌드 구조입니다. 즉, 프로젝트를 분리해서 참조하는 것과 같은 것이다.


cmake 파일을 작성하겠습니다.

# 프로젝트 이름 설정
project(example)

# 변수 ROOT 설정 = ..
set(ROOT ..)
# 변수 LIB_DIR 설정 = ../lib
set(LIB_DIR ../lib)
# 변수 MAIN_DIR 설정 = ../main
set(MAIN_DIR ../main)
# 변수 COMMON_DIR 설정 = ../common
set(COMMON_DIR ../common)

# 변수 LIB_LIST 설정 = ../libtest.cpp
set(LIB_LIST ${LIB_DIR}/libtest.cpp)
# 변수 LIB_NAME 설정 = libtest
set(LIB_NAME libtest)
# 변수 SOURCE 설정 = ../main/main.cpp ../main/function.cpp
set(SOURCE ${MAIN_DIR}/main.cpp ${MAIN_DIR}/function.cpp)
# 변수 OUTPUT 설정 = ../program.out
set(OUTPUT ${ROOT}/program.out)
# 변수 EXE_FILE 설정 = program
set(EXE_FILE program)

# 최소 cmake 요구 버젼
cmake_minimum_required(VERSION 2.8)
# 메시지 설정
message(${CMAKE_PROJECT_NAME})
# 컴파일 옵션 = g++ -W -Wall -o -c source
add_compile_options(-W -Wall)

# 공통 해더 파일 설정
include_directories(${COMMON_DIR})
# 빌드 설정
add_executable(${OUTPUT} ${SOURCE})

# 라이브러리 설정, 라이브러리 이름 = libtest.so
add_library(${LIB_NAME} SHARED ${LIB_LIST})
# 빌드시에 참조 라이브러리
target_link_libraries(${OUTPUT} PUBLIC ${LIB_NAME})

# make install 설정, TARGETS 1, 2 ,3 = RUNTIME DESTINATION, LIBRARY DESTINATION, ARCHIVE DESTINATION
install(TARGETS ${OUTPUT} ${LIB_NAME}
        RUNTIME DESTINATION /home/bin
        LIBRARY DESTINATION /home/lib)

MakeFile이 만들어 졌습니다.


이제 make로 빌드를 하겠습니다.

실행 파일을 만들겠습니다.

생성된 실행 파일을 실행하니 hello world, function함수 호출한 것, library 참조한 함수를 호출한 내용까지 제대로 출력이 되었습니다.


마지막으로 make install로 파일이 복사가 되는지 확인해 보겠습니다.

제대로 파일이 복사가 되었습니다. cmake 기능이 더 있기는 하는데, 제 경험으로는 거의 여기까지 사용했던 걸로 기억합니다. 뭐, visual studio가 있는데....


여기까지 CMake의 사용법에 대한 글이었습니다.


궁금한 점이나 잘못된 점이 있으면 댓글 부탁드립니다.