본문 바로가기
Linux/CMake

간단한 CMake 예제

by khd0801 2024. 1. 10.
반응형

간단한 예제를 이용하여 C++에 대한 CMake를 사용하는 방법과 CMakeLists.txt를 작성 하는 방법을 소개하도록 하겠다.

해당 예제를 이용하면 C언어에 대한 CMakeLists.txt도 쉽게 구현 할 수 있다.

지금 블로그에서는 CMake에서 사용되어지는 문법은 간단하게만 설명하며, 자세한 문법 및 함수들의 입력 파라미터, 기능들에 대해서는 차후에 소개 하도록 하겠다.

 

 1.  폴더트리

$ ls -l

total 20
drwxrwxr-x 2 khd0801 khd0801 4096 1월 10 22:47 app
-rw-rw-r-- 1 khd0801 khd0801 556 1월 10 22:47 CMakeLists.txt
drwxrwxr-x 2 khd0801 khd0801 4096 1월 10 22:47 include
drwxrwxr-x 2 khd0801 khd0801 4096 1월 10 22:47 lib
-rw-rw-r-- 1 khd0801 khd0801 63 1월 10 22:47 README.md

$ tree

.
├── app
│ ├── CMakeLists.txt
│ └── main.cpp
├── CMakeLists.txt
├── include
│ └── TestLib.h
├── lib
│ ├── CMakeLists.txt
│ └── TestLib.cpp
└── README.md
3 directories, 7 files

기본적으로 폴더는 app폴더와 include 폴더, lib 폴더가 존재한다.

최상위 폴더에는 Main이 되는 CMakeLists.txt있고 헤더파일이 존재하는 include 폴더를 제외한 다른 *.cpp 파일이 있는 app과 lib 폴더에는 *.cpp를 컴파일 하기 위한 CMakeLists.txt 파일이 존재한다.

 

 

 2. 각 폴더의 CMakeLists.txt 내용

최상위 폴더의 CmakeLists.txt

# CMake 프로그램의 최소 버전
cmake_minimum_required(VERSION 3.16)

# 프로젝트 정보
project(
  khd0801
  VERSION 0.1
  DESCRIPTION "예제 프로젝트"
  LANGUAGES CXX)

SET(CMAKE_BUILD_TYPE Debug)
# 공통 컴파일러
SET(CMAKE_CXX_COMPILER "/usr/bin/g++")
# 공통 컴파일 옵션, 링크 옵션
ADD_COMPILE_OPTIONS ( -g -Wall )
SET(CMAKE_EXE_LINKER_FLAGS "-static -Wl,--gc-sections")

message(PROJECT_NAME : ${PROJECT_NAME})

# 라이브러리 파일은 빌드 디렉토리 안에 lib 폴더에 출력.
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)

# 실행 파일은 빌드 디렉토리 안에 bin 폴더에 출력.
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)

add_subdirectory(app)
add_subdirectory(lib)

 

Line Number Name 설명
2번 줄 cmake_minimum_required() Cmake를 사용 할 때, 최소 버전을 설정 한다. 해당 버전 보다 낮을 경우 Error 로그를 출력 한다.
5~9번 줄 project() 프로젝트의 최상위 CMakeLists.txt 파일에 꼭 포함되어야 한다.
Cmake를 사용한 프로젝트의 정보를 기입하며 첫 파라미터는 PROJECT_NAME 변수에 값이 저장되며, 필수적으로 기입이 되어야 한다. 
그외에는 옵션으로 기입을 하지 않으면 빈 문자열로 설정된다.
그외 옵션으로는 프로젝트 버전, 설명, 프로젝트의 Language 설정등이 있다. 해당 Language 설정을 C로 수정하고, 각 폴더 C파일과 CMakeLists.txt를 일부 수정하면 C 언어에 대한 Makefile을 생성 할 수 있다.
11번 줄 SET(CMAKE_BUILD_TYPE Debug) 빌드되는 바이너리를 debug용으로 빌드한다.
13번 줄 SET(CMAKE_CXX_COMPILER
"/usr/bin/g++")
Compiler는 g++ 쓴다고 설정한다. 해당 경로에 arm compiler의 경로를 설정하면 arm용으로 바이너리가 생성된다.
해당 변수를 설정하지 않거나 잘 못 설정 하더라도 cmake는 자동으로 프로젝트에 맞는 컴파일러를 검색하고 체크하여 해당 변수를 설정한다. 
프로젝트에 맞는 컴파일러가 없을 경우에는 에러를 출력한다.
15번 줄 ADD_COMPILE_OPTIONS ( -g -Wall ) 컴파일 옵션에 -g -Wall 옵션을 추가 설정한다.
16번 줄 SET(CMAKE_EXE_LINKER_FLAGS 
"-static -Wl,--gc-sections")
실행파일 생성에 사용할 링커 플래그를 설정한다.
18번 줄 message(PROJECT_NAME : ${PROJECT_NAME}) 디버깅 용 함수로 cmake 명령어 사용시 () 내용을 출력한다.
${ PROJECT_NAME }은 해당 PROJECT_NAME 변수의 내용을 출력한다.
21번 줄 set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) 정적 라이브러리 생성 위치를 설정한다.
CMAKE_BINARY_DIR은 cmake를 실행한 절대 경로가 저장되며, 해당 프로젝트에서는 build/lib 폴더에 정적 라이브러리가 install 되어진다.
22번 줄 set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) 동적 라이브러리 생성 위치를 설정한다.
CMAKE_BINARY_DIR은 cmake를 실행한 절대 경로가 저장되며, 해당 프로젝트에서는 build/lib 폴더에 정적 라이브러리가 install 되어진다.
25번 줄 set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) 실핼 파일의 생성 위치를 설정한다.
해당 구문을 실행함으로 RUNTIME_OUTPUT_DIRECTORY property 값이 설정되며, 현재 프로젝트에서는 build/bin 폴더에 실행 파일이 install 되어진다.
27번 줄 add_subdirectory(app) 하위 디렉토리 추가로 app 폴더를 설정한다. 해당 함수가 호출되는 시점에는 app 폴더에 CMakeLists.txt 파일이 존재해야한다.  
28번 줄 add_subdirectory(lib) 하위 디렉토리 추가로 lib 폴더를 설정한다. 해당 함수가 호출되는 시점에는 lib 폴더에 CMakeLists.txt 파일이 존재해야한다.  

 

 

app 폴더의 CMakeLists.txt

# 현재 디렉토리에 있는 모든 파일을 SRC_FILES 변수에 추가한다.
file(GLOB_RECURSE SRC_FILES CONFIGURE_DEPENDS
  ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp
)

SET(OUTPUT_NAME
	"program-${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.out"
)
add_executable(${OUTPUT_NAME} ${SRC_FILES})
target_link_libraries(${OUTPUT_NAME} PUBLIC TestLib)
Line Number Name 설명
2~4번 줄 file(GLOB_RECURSE SRC_FILES CONFIGURE_DEPENDS
  ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp
)
CMakeLists.txt가 존재하는 폴더의 cpp 파일들의 이름들을 SRC_FILES 변수에 저장한다. 
CMAKE_CURRENT_SOURCE_DIR 변수는 CMakeLists.txt가 존재하는 절대 경로가 저장되어 있다.
GLOB_RECURSE 옵션은 인자로 주어진 현재 폴더 및 하위폴더를 포함해서 전체를 재귀적으로 보도록 하는 옵션이다. 만약 재귀적을 포함하고 싶지 않는 경우 GLOB을 주면 된다.
CONFIGURE_DEPENDS 옵션은 파일 목록이 전과 다를 경우 CMake를 재실행해 build file들을 재생성 하는 옵션이다. 만약 파일이 추가가 되더라도 cmake 명령어를 수행할 필요 없이 그냥 make만 실행해도 cmake가 호출된다.
6~8번 줄 SET(OUTPUT_NAME
"program-${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.out"
)
실행 바이너리 이름을 설정한다.
PROJECT_VERSION_MAJOR, PROJECT_VERSION_MINOR는 최상위 CMakeLists.txt의 project 함수의 VERSION를 참고한다. 
9번 줄 add_executable(${OUTPUT_NAME} 
${SRC_FILES})
SRC_FILES 변수 내용에 저장된 파일들을 빌드하여 실행 바이너리를 생성하게 만드는 함수이다. 
실행 바이너리 이름은 OUTPUT_NAME에 저장되어 있는 이름으로 생성한다.
10번 줄 target_link_libraries(${OUTPUT_NAME} 
PUBLIC TestLib)
실행 바이너리에 필요한 라이브러리를 설정한다.
해당 라이브러리의 생성은 lib 폴더의 CMakeLists.txt를 참조하라.

 

 

Lib 폴더의 CMakeLists.txt

# 현재 디렉토리에 있는 모든 파일을 SRC_FILES 변수에 추가한다.
file(GLOB_RECURSE SRC_FILES CONFIGURE_DEPENDS
  ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp
)

add_library(TestLib STATIC ${SRC_FILES})

# TestLib 의 include 경로 지정
target_include_directories(TestLib PUBLIC ${CMAKE_SOURCE_DIR}/include)

# TestLib 의 컴파일 옵션 지정
target_compile_options(TestLib PRIVATE -Wall -Werror)

# TestLib 를 C++ 17 로 컴파일
target_compile_features(TestLib PRIVATE cxx_std_17)
Line Number Name 설명
2~4번 줄 file(GLOB_RECURSE SRC_FILES CONFIGURE_DEPENDS
  ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp
)
CMakeLists.txt가 존재하는 폴더의 cpp 파일들의 이름들을 SRC_FILES 변수에 저장한다. 
CMAKE_CURRENT_SOURCE_DIR 변수는 CMakeLists.txt가 존재하는 절대 경로가 저장되어 있다.
GLOB_RECURSE 옵션은 인자로 주어진 현재 폴더 및 하위폴더를 포함해서 전체를 재귀적으로 보도록 하는 옵션이다. 만약 재귀적을 포함하고 싶지 않는 경우 GLOB을 주면 된다.
CONFIGURE_DEPENDS 옵션은 파일 목록이 전과 다를 경우 CMake를 재실행해 build file들을 재생성 하는 옵션이다. 만약 파일이 추가가 되더라도 cmake 명령어를 수행할 필요 없이 그냥 make만 실행해도 cmake가 호출된다.
6번 줄 add_library(TestLib STATIC ${SRC_FILES}) SRC_FILES의 파일들을 이용하여 정적 TestLib 파일을 생성한다.
9번 줄 target_include_directories(TestLib PUBLIC ${CMAKE_SOURCE_DIR}/include) TestLib 타겟을 빌드하기 위해서 필요한 include 파일을 경로를 알려준다.
CMAKE_SOURCE_DIR 변수에는 최상위 CMakeLists.txt 파일이 존재하는 절대경로가 입력되어 있다.
12번 줄 target_compile_options(TestLib PRIVATE -Wall -Werror) TestLib를 컴파일 할 때 사용할 옵션들을 추가 설정한다.

15번 줄 target_compile_features(TestLib PRIVATE cxx_std_17) TestLib를 컴파일 할 때 사용할 feature들을 추가한다.
 
 
 

 3.  각 폴더의 cpp 내용

app 폴더의 main.cpp

#include "TestLib.h"

int main() {

    TestLib Test = TestLib();

    Test.TestPrint(10);

  return 0;
}

 

 

lib 폴더의 TestLib.cpp

#include <stdio.h>
#include "TestLib.h"

TestLib::TestLib() {
    printf("Create TestLib Class\n");
}

TestLib::~TestLib() {
    printf("Destroy TestLib Class\n");
}

int TestLib::TestPrint(int a) {
    
    printf("test : %d\n",a);

    return 0;
}
    
int TestLib::TestCal(int x, int y) {
    return x+y;
}

 

 

include 폴더의 TestLib.h

#ifndef TESTLILB_H
#define STESTLIB_H

class TestLib {
  public:
    TestLib();
    ~TestLib();

    int TestPrint(int a);

    int TestCal(int x, int y);

  private:
    int x, y;
};

#endif

 

 

 4. Cmake 빌드 방법

Cmake는 cmake라는 명령어를 이용하여 빌드를 위한 파일들을 생성하게 된다. 생성되는 빌드 파일들은 프로젝트 소스 파일들이 존재하는 디렉토리가 아닌 다른 디렉토리에 생성하도록 권장하는데, 권장이라기 보다는 필수로 분리해서 사용하는 것이 좋다. 

분리해야 되는 이유로는 cmake로 생성되는 여러 파일로 인해 소스 파일 관리가 지저분해 질 수 있고, 심볼들이 내 프로젝트 코드 외 cmake로 생성된 파일들을 잘 못 참조 할 수 있기 때문이다.

따라서 아래와 같이 프로젝트 소스와 분리하여 build 폴더를 만든 후 cmake를 이용하여 Makefile을 만들어서 사용한다.

$ cd ..

$ mkdir build

$ cd build/
build $ cmake ../Default-CMake/
-- The CXX compiler identification is GNU 9.4.0
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/khd0801/study/cmake/build
build$ make
Scanning dependencies of target TestLib
[ 25%] Building CXX object lib/CMakeFiles/TestLib.dir/TestLib.cpp.o
[ 50%] Linking CXX static library libTestLib.a
[ 50%] Built target TestLib
Scanning dependencies of target program
[ 75%] Building CXX object app/CMakeFiles/program.dir/main.cpp.o
[100%] Linking CXX executable ../bin/program
[100%] Built target program
build$ ls
app bin CMakeCache.txt CMakeFiles cmake_install.cmake lib Makefile
 

 

 

 5. 빌드 결과 실행파일 실행 결과

CMakeLists.txt에서 실행 파일의 경로를 build/bin 폴더로 들어가도록 설정을 해놨었다.

따라서 build/bin/ 폴더의 program 바이너리를 실행시키면 아래와 같이 출력이 된다.

build$ ./bin/program

Create TestLib Class
test : 10
Destroy TestLib Class

 

 

 

반응형

'Linux > CMake' 카테고리의 다른 글

Linux(Ubuntu) 최신 CMake 설치하기  (0) 2022.03.04

댓글