Knowhow/C, C++, CMake

CMakeLists.txt 작성 팁 1

침닦는수건 2024. 1. 31. 12:17
반응형

기본적으로 작성하면서 꼭 이해해야 될 것 같다고 생각한 내용들을 적어두고, 나중에 복붙하기 편하게 정리해두고자 한다.

 

1) C 버전 지정

if (NOT CMAKE_C_STANDARD)
    set(CMAKE_C_STANDARD 99)
endif()

 

웬만하면 최신 99 사용하면 된다.

 

2) C++ 버전 지정

if (NOT CMAKE_CXX_STANDARD)
    set(CMAKE_CXX_STANDARD 20)
endif()

 

C++부터는 버전을 좀 봐야 함. 98, 11, 14, 17, 20, 23, 26 존재함. 대충 20쓰는 것 좋을 듯함.

 

3) compile option 지정 

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  add_compile_options(-Wall -Wextra -Wpedantic)
endif()

 

주로 오류 출력 범위를 지정함.

 

CMAKE_COMPILER_IS_GNUCXX는 deprecate됨. CMAKE_CXX_COMPILER_ID가 무엇인지 확인하는 것이 나음.

 

compiler가 무슨 종류가 있는지는 CMAKE_LANG_COMPILER_ID.html 확인. 

 

all, extra, pedantic flag 각각 출력 범위를 말하는데, 보통 세세할 수록 편하니 다 넣는게 좋음. -Wall을 많이 쓰고 그보다 더하면 -Wextra, 세세한 것까지 전부 다 하려면 -Wpedantic 까지 넣는 느낌. 3개가 완전 다른 오류들을 잡아내는 것 같진 않고 그냥 다 넣어주는 느낌.

 

4) build type 지정 (RELEASE, DEBUG)

if(NOT CMAKE_BUILD_TYPE)
  set(CMAKE_BUILD_TYPE DEBUG)
endif()

 

RELEASE로 하면 DEBUG만을 위해 다르게 compile 된 부분들이 최적화된 형태로 compile하게 되므로 조금 더 빠르고, verbose들도 없어서 더 빠름.

 

5) find_packages

 

자세한 내용은 여기에 나와있음.

 

과정부터 설명하면 find_packages를 사용하면 FindXXX.cmake 파일을 일단 찾게 됨. CMAKE_MODULE_PATH에 가서 구석구석 알아서 찾는데 그래도 없으면 XXXConfig.cmake나 xxx-config.cmake까지 찾는 순서로 이어짐.

 

LIST(APPEND CMAKE_MODULE_PATH ~~~) 이런 구문이 추가되어 있다면 find_package가 검색 편하도록 위치를 알려주는 행위라고 보면 됨. 

 

cmake --help-module-list 치면 기본 FindXXX.cmake들이 있음. 여기 출력되는 module의 경우, 다음과 같이 쓰면 됨.

cmake_minimum_required(VERSION 2.6)
project(Hello C)
add_executable(hello main.c)
find_package(GTK2 REQUIRED)
include_directories(${GTK2_INCLUDE_DIRS})
target_link_libraries(hello ${GTK2_LIBRARIES})

 

OpenCV, Ceres 같이 별도로 설치한 파일들의 경우 --help-module-list에 안 나옴. 그러나 제대로 설치했다면 설치 폴더 하위에 ~/opencv/build/OpenCVConfig.cmake 가 존재하므로 find_package가 찾을 수 있음.  알아서 찾아 연결하므로 목록에 없어도 위와 같은 문법으로 구사 가능한 것.

 

find_package가 못 찾는 경우, cmake version 때문이 대표적인 문제인데 이때는 pkg-config의 도움을 받을 수 있음. 

find_package(PkgConfig REQUIRED)
pkg_search_module(apriltag REQUIRED apriltag)
pkg_check_modules(YAMLCPP REQUIRED yaml-cpp)

 

그래도 못 찾는 경우는 직접 FindXXX.cmake 위치를 알려주면 됨. CMakeLists.txt 상단에 위치시켜야 함.

include(cmake/FindXXX.cmake)

 

6) target_include_directories vs include_directories

프로젝트에게 header 파일 위치들을 알려주는 방법 2가지. 

target_include_directories(
  SOMETHING PUBLIC
  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
  $<INSTALL_INTERFACE:include>
  ${CERES_INCLUDE_DIRS}
  ${OpenCV_INCLUDE_DIRS}
  ${apriltag_INCLUDE_DIRS}
  ${YAMLCPP_INCLUDE_DIRS}
)

 

${OpenCV_INCLUDE_DIRS} 같은 것은 위 find_package와 엮여서 자동으로 처리될 부분이므로 그냥 따라쓰면 됐다만, 여기에 직접 작성한 소스코드에 해당하는 것은 하나씩 나열해주어야 함. (header 파일들)

 

나열하기가 귀찮고, 팩토링 되어있다면 이미 보통 include라는 폴더 안에 모여있을테니 include 폴더 위치를 던져주는 식이 편함. 

 

예시에 적혀있는 BUILD_INTERFACE는 BUILD할 때 찾아갈 경로를 의미하고 INSTALL_INTERFACE는 설치 이후 사용할 때 찾아갈 경로임. 다른 이유는 build가 끝나면 보통 build에 사용한 파일들을 지워 정리해두곤 하는데,  BUILD_INTERFACE만 쓸 경우 해당 파일들을 지웠을 때 동작을 안 함. 따라서 BUILD와 INSTALL후 를 나누어서 include 파일 위치를 알려주는 것이 정석. (중간 CMAKE_CURRENT_SOURCE_DIR는 CMakeLists.txt가 존재하는 경로임. )

 

SOMETHING이라는 대상에게만 include 경로를 주고 싶다면 target_include_directories가 맞음. 하지만 만약 PARENT/SOMETHING과 같은 구조였다면, PARENT는 위 include 경로를 모르게 된다. 만약 대상을 지정하지 않고 프로젝트 전체에 경로를 알려주고 싶을 때는 최상위 CMakeLists.txt에서 include_directories를 사용해야 한다.

 

7) target_link_directories vs link_directories

프로젝트가 header include를 통해 함수들이 존재(declaration)한다는 것을 인식했는데, 실제로 그 구현(definition)이 어디 존재하는지를 알려줘야 정상 동작시킬 수 있음. 그 경로를 또 알려주는 것.

target_link_libraries(
  SOMETHING
  ${CERES_LIBRARIES}
  ${OpenCV_LIBS}
  ${apriltag_LIBRARIES}
  ${YAMLCPP_LIBRARIES}
)

 

6)과 웬만하면 동반됨. 둘 다 적는 것이 정석. 만약 코드 형태가 아닌 library로 build 완료된 형태라면 include 구문없이 link만 구문만 존재하는 것은 가능함. (include만 하는 경우는 없음.)

 

TBB와 같이 FindTBB.cmake 명시적으로 cmake module을 사용하는 경우에는 include는 빠질 수 있음. FindXXX.cmake 파일 안에서 알아서 header 위치를 알려주기 때문. 하지만 link는 무조건 해줘야 함.

 

 

반응형