-
Build_System_CMake1Tools/CMake 2024. 1. 9. 16:09
이전에 미리 작성된 CMake를 사용하여 빌드를 하였지만, CMake를 어떻게 작성하는지 정리하려고 한다.
https://mokchanic.tistory.com/100
Build System
1. 개요
- 소스 코드를 실행 가능한 프로그램 또는 라이브러리로 변환하는 과정을 자동화하는 도구와 방법들의 집합.
- 프로젝트의 규모가 커질수록 잘 관리하는것이 매우 중요하다.
2. 목적
- 프로젝트의 일관성: 빌드 시스템을 통해 동일한 소스 코드로부터 일관된 결과물을 생성한다.
- 자동화 & 효율성: 반복적, 수동적인 작업을 최소화하여 개발자의 생산성을 향상시킬수 있다.
- 오류 감소: 자동화된 빌드 및 테스트 과정을 통해 오류를 조기에 발견하고 수정이 가능하다.
3. 주요기능
- 컴파일: 소스 코드를 기계어나 중간 코드로 변환을 해준다.
- 각 소스 코드 파일을 오브젝트 파일로 컴파일 해주며 컴파일 오류가 발생하면 빌드 프로세스는 중지된다.
- 링킹: 다양한 오브젝트 파일 및 라이브러리를 하나의 실행 파일 또는 라이브로 결합해준다.
- 오브젝트 파일들이 링커를 통해 하나의 실행 파일 또는 라이브러리로 합쳐진다.
- 의존성 관리: 외부 라이브러리, 모듈의 의존성을 관리하고, 필요한 버전을 가져오거나 빌드에 포함시킨다.
- 리소스 관리: 이미지, 텍스트, 데이터베이스 스키마 등의 리소스를 알맞게 포함하거나 변환을 해준다.
- 패키징: 완성된 소프트웨어를 사용자나 클라이언트에 배포할 수 있는 형태로 패키징을 해준다.
- 생성된 실행 파일, 라이브러리, 설정, 리소스 파일 등을 패키징 해준다. (ex. deb, rpm, msi등)
- 테스트 자동화: 코드의 정확성, 안전성을 확인하기 위해 테스트를 자동으로 실행한다.
- 빌드된 아티팩트에 대한 자동화된 테스트를 실행. 오류나 회귀를 확인해준다.
- 문서 생성: 소스 코드에서 주석을 추출하여 개발자 문서를 자동으로 생성한다. (ex. Doxygen)
- 빌드 및 테스트가 성공적으로 완료된 후, 서버나 저장소에 문서를 배포.
4. Build System 종류
- Make(GNU Make)
- 가장 오래되고 널리 사용되는 빌드 도구.
- Makefile이라는 script를 작성하여 빌드 과정을 정의.
- 다양한 플랫폼에서 사용이 가능, 많은 프로젝트에서 기본 빌드 도구로 선택이 된다.
- CMake
- 플랫폼 및 컴파일러 독립적인 빌드 시스템.
- CMakeLists.txt 파일을 통해 빌드 규칙을 정의한다.
- CMake는 다양한 빌드 시스템의 프로젝트 파일 생성이 가능하다. (ex. Makefile, Visual Studio 프로젝트 파일등)
- Ninja
- 속도에 중점을 둔 소형 빌드 시스템.
- 쉬운 문법과 간단한 사용법 Make와 같이 암묵적인 규칙이 없다. (이는 치명적인 단점이 되기도 한다.)
CMake 실습
순서
- set()
- message()
- cmake_minmum_required()
- project()
- CMAKE_BUILD_TYPE
- add_excutable()
- add_library()
- target_link_libraries()
- target_include_directories()
- target_compile_options()
- target_compile_definitions()
- file()
- install()1. 명령어
- set()
- 변수를 설정하는데 사용
- 변수는 여러 설정, 옵션, 경로 등을 지정하고 참조하는데 사용가능.
- SCREAMING_SNAKE_CASE 로 기술
- 사용법
- set(VARIABLE_NAME VALUE)
- set(LIST_NAME VALUE1, VALUE2, VALUE3, VALUE4 ...)
- set(BOOL_NAME FALSE)
- 참조
- ${VARIABLE_NAME}
- PARENT_SCOPE
- 현재 디렉토리의 부모 디렉토리에 변수를 설정하도록 하는데 사용한다.
- 주로 함수나 매크로 내에 부모 스코프의 변수를 변경할 필요가 있을때 사용한다.
- 사용법
- set(VARIABLE_NAME VALUE PARENT_SCOPE)
- CACHE
- 전역 캐시에 변수를 설정하는데 사용.
- 이를 활용하면 CMakeLists.txt 파일 또는 후속 빌드에서 변수 값을 재사용할 수 있다.
- 사용법
- set(GLOBAL_VARIABLE_NAME "value" CACHE ${TYPE} "a simple description for GLOBAL_VARIABLE_NAME")
- ${TYPE}은 만들어질 변수의 타입. STRING, BOOL, FILEPATH, PATH, INTERNAL등이 있음.
- message()
- 디버깅, 정보 제공, 경고, 오류 메시지 등을 출력하기 위해 사용한다.
- 다양한 로그 수준을 가짐. 이를 통해 메시지의 중요도나 형식 구분이 가능하다.
- 사용법
- message(${TYPE} "message to display...")
- ${TYPE}은 출력 메시지의 형태.
- ${TYPE}의 종류
- STATUS
- 일반 정보 메시지.
- CMake 출력에서 보통의 정보와 함께 표시된다.
- 사용법
- message(STATUS "Configuring the project...")
- WARNING
- 경고 메시지.
- 잘못될 가능성이 있거나 주의가 필요한 경우에 사용한다.
- 사용법
- message(WARNING "Deprecated function used!")
- AUTHOR_WARNING
- 프로젝트의 작성자나 유지 관리자에게만 관련된 경고 메시지.
- CMAKE_SUPPRESS_DEVELOPER_WARNINGS 변수가 TRUE로 설정되지 않은 경우에만 출력이 된다.
- True인 경우 AUTHOR_WARNING이 출력되지 않는다.
- 사용법
- message(AUTHOR_WARNING "This is a warning for the developer.")
- FATAL_ERROR
- 치명적 오류 메시지.
- 이 메시지가 출력되면 CMake는 즉시 종료된다.
- 사용법
- message(FATAL_ERROR "Critical error!")
- SEND_ERROR
- 오류 메시지.
- CMake는 오류를 출력하지만 구성을 계속 진행한다.
- 사용법
- message(SEND_ERROR "An error occurred, but configuration continues.")
- DEPRECATION
- Deprecated된 기능이 사용될 때의 경고 메시지.
- 어떤 기능이 기존에 존재하다가 특정 버전에서 그 기능이 삭제될 경우에 사용된다.
- CMAKE_ERROR_DEPRECATED 또는 CMAKE_WARN_DEPRECATED 변수의 값에 따라 동작한다.
- 사용법
- message(DEPRECATION "This feature will be removed in future versions.")
- NOTICE
- 중요한 정보 메시지.
- 사용자에게 알려야 할 중요한 정보에 사용된다.
- 사용법
- message(NOTICE "Consider reading the updated documentation")
- STATUS
cmake_minimum_required(VERSION 3.22) set(PACKAGE_NAME EXAMPLE_CMAKE_PROJECT) set(PACKAGE_VERSION 1.1.0) project(${PACKAGE_NAME} VERSION ${PACKAGE_VERSION} LANGUAGES CXX) # SET VARIABLE set(STRINT_VARIABLE "Hello World") set(BOOL_VARIABLE TRUE) set(CMAKE_SUPPRESS_DEVELOPER_WARNINGS TRUE) # OUTPUT message(STATUS "[STATUS] STRINT_VARIABLE: ${STRINT_VARIABLE}") message(WARNING "[WARNING] STRINT_VARIABLE: ${STRINT_VARIABLE}") message(AUTHOR_WARNING "[AUTHOR_WARNING] STRINT_VARIABLE: ${STRINT_VARIABLE}") # message(FATAL_ERROR "[FATAL_ERROR] STRINT_VARIABLE: ${STRINT_VARIABLE}") message(SEND_ERROR "[SEND_ERROR] STRINT_VARIABLE: ${STRINT_VARIABLE}") message(DEPRECATION "[DEPRECATION] STRINT_VARIABLE: ${STRINT_VARIABLE}") message(NOTICE "[NOTICE] STRINT_VARIABLE: ${STRINT_VARIABLE}") message(STATUS "BOOL_VARIABLE: ${BOOL_VARIABLE}")
결과
- cmake_minmum_required()
- 최소 요구 버전을 명시한다.
- 반드시 프로젝트 최상단의 CMakeLists.txt 에 기술이 되어야한다.
- 사용법
- cmake_minmum_required(VERSION Major.Minor.Patch)
- Patch는 생략이 가능하다.
cmake_minimum_required(VERSION 3.22)
- project()
- 프로젝트의 이름을 설정하는데 사용한다.
- 사용할 언어, 버전 번호와 여러 프로젝트 관련 변수도 설정이 가능하다.
- 최소한 사용할 언어, 버전, 프로젝트 이름까지는 기술하도록 하자.
- 사용법
- project(PROJECT_NAME)
- project(PROJECT_NAME LANGUAGES LANGUAGE_NAME)
- project(PROJECT_NAME VERSION Major.Minor.Patch)
- project(PROJECT_NAME DESCRIPTION "Description")
- project(PROJECT_NAME HOMEPAGE_URL "address")
- 생성 변수
CMAKE_PROJECT_NAME 최상위 CMakeLists.txt에서 project() 함수로 설정된 프로젝트의 이름. PROJECT_NAME 최근에 project() 함수를 통해 설정된 프로젝트의 이름. PROJECT_VERSION project()함수의 version 옵션. 설정된 프로젝트의 전체 버전 번호. PROJECT_VERSION_MAJOR 설정된 프로젝트의 주 버전 번호. PROJECT_VERSION_MINOR 설정된 프로젝트의 부 버전 번호. PROJECT_VERSION_PATCH 설정된 프로젝트의 패치 버전 번호. PROJECT_VERSION_TWEAK 설정된 프로젝트의 수정 버전 번호. 이 값이 제공되지 않은 경우, 기본적으로 설정되지 않는다. PROJECT_DESCRIPTION DESCRIPTION 옵션. 제공된 프로젝트 설명 PROJECT_HOMEPAGE_URL HOMEPAGE_URL 옵션. 제공된 프로젝트의 홈페이지 URL PROJECT_SOURCE_DIR 현재 프로젝트의 소스 디렉토리 경로. 이 변수는 project() 함수가 호출된 위치와 관련. PROJECT_BINARY_DIR 현재 프로젝트의 빌드 디렉토리 경로 예시
cmake_minimum_required(VERSION 3.22) set(PACKAGE_NAME EXAMPLE_CMAKE_PROJECT) set(PACKAGE_VERSION 1.1.0) project(${PACKAGE_NAME} VERSION ${PACKAGE_VERSION} LANGUAGES CXX DESCRIPTION "CMake Eaxmple" HOMEPAGE_URL "https://github.com/Mokchanic" ) message(STATUS "[Status] PROJECT_VERSION_MAJOR: ${PROJECT_VERSION_MAJOR}") # 1 message(STATUS "[Status] PROJECT_VERSION_MINOR: ${PROJECT_VERSION_MINOR}") # 4 message(STATUS "[Status] PROJECT_VERSION_PATCH: ${PROJECT_VERSION_PATCH}") # 5 message(STATUS "[Status] PROJECT_DESCRIPTION: ${PROJECT_DESCRIPTION}") message(STATUS "[Status] PROJECT_HOMEPAGE_URL: ${PROJECT_HOMEPAGE_URL}") message(STATUS "[Status] PROJECT_SOURCE_DIR: ${PROJECT_SOURCE_DIR}") message(STATUS "[Status] PROJECT_BINARY_DIR: ${PROJECT_BINARY_DIR}")
이와 같이 project에 정보를 부여할 수 있다.
- CMAKE_BUILD_TYPE()
- 단일 설정 빌드 시스템을 사용할 때 프로젝트의 빌드 형식을 결정하는 변수
- 다양한 최적화 수준과 디버그 정보의 포함 여부를 결정. 여러가지 사전 정의된 값을 가질 수 있다.
- 이를 가지고 빌드의 분기를 만들 수 있다.
- 사용법
- CMake 명령 줄에서 -DCMAKE_BUILD_TYPE=${TYPE} 을 사용.
- CMakeLists.txt 파일 내에서 set()함수를 사용한다.
- ${TYPE}
- None
- 특정 최적화 플래그나 디버그 플래그를 적용하지 않는다. 사용자가 직접 필요한 컴파일러 플래그를 설정할 의도로 남겨둠.
- Debug
- Debug 정보를 포함하여 빌드. 최적화 적용하지 않음.
- Release
- Debug 정보를 포함하지 않음. 최적화 적용.
- 최종 배포를 위한 코드 빌드에 사용.
- RelWithDebInfo (Release with Debug Info)
- Debug 정보를 포함하여 빌드. 최적화 적용.
- 최적화된 코드를 디버그할 필요가 있을때 유용하다.
- MinSizeRel (Minimum Size Release)
- 가능한 작은 크기의 실행 파일을 생성함. 코드를 최적화 해줌.
- None
- ${TYPE}
- add_executable()
- 실행 파일을 생성하기 위해 사용되는 명령어
- 이 명령어는 주어진 소스 파일들로 부터 실행 파일을 빌드하기 위한 규칙을 정의한다.
- 주로 main을 실행.
- 사용법
- add_executable(TARGET_NAME ${OPTION} source1 source2 source3 ...)
- ${OPTION}: (Build Option 정의)
- WIN32, MACOSX_BUNDLE, EXCLUDE_FROM_ALL 등의 추가적인 옵션을 사용할 수 있다.
- 옵션은 주로 플랫폼별 또는 빌드 설정에 따라 특정 행동을 지정하는데 사용한다.
add_executable(${PROJECT_NAME}_MAIN main.cpp)
- add_library()
- 라이브러리를 생성하기 위해 사용되는 명령어이다. (ex. Eigen, OpenCV등을 불러올 수 있다.)
- 라이브러리는 동적(공유) 라이브러리, 정적 라이브러리 또는 모듈 라이브러리로 생성이 가능하다.
- 이 명령어를 사용하면 주어진 소스 파일들로부터 라이브러리를 빌드하기 위한 규칙을 정의한다.
- add_executable()과 같지만, 실행하는 파일은 없다.
- 사용법
- add_library(TARGET_NAME ${TYPE} source1 source2 source3 ...)
- ${TYPE}
- SHARED: 동적 라이브러리 생성.
- STATIC: 정적 라이브러리 생성.
- MODULE
- 플러그인 또는 모듈로 로드되는 라이브러리 생성.
- 대부분 플러그인 아키텍처를 구현할 때 사용된다.
- OBJECT
- 오브젝트 라이브러리를 생성.
- 오브젝트 라이브러리는 실제로 빌드된 바이너리로 연결되지 않은 중간 컴파일 결과물이다.
- INTERFACE
- 소스 코드가 없는 인터페이스 라이브러리를 생성.
- 주로 헤더 전용 라이브러리나 컴파일 플래그, 정의 등을 전파하기 위해 사용한다.
add_library(${LIBRARY_NAME} STATIC ${${LIBRARY_NAME}_SOURCE_FILES} )
- target_link_libraries()
- 특정 타겟에 라이브러리를 연결하기 위해 사용됨.
- 주로 add_executable() 또는 add_library() 명령어로 생성된 타겟에 라이브러리를 링크할 때 사용된다. (add_executable(), add_library()와 함께 사용이 된다.)
- 사용법
- target_link_libraries(TARGET_NAME ${OPTION} LIBRARY_1 LIBRARY_2 ...)
- ${OPTION}
- PRIVATE: 타겟에만 의존성을 부여.
- PUBLIC: 타겟과 그 타겟의 소비자 모두에게 의존성을 부여.
- INTERFACE: 타겟의 소비자에게만 의존성을 부여.
ex) A라는 라이브러리가 있을때, B 라이브러리를 사용하기 위해서 사용하는 경우.
- target_include_directories()
- 특정 타겟의 헤더파일의 포함 경로(include path)를 설정하기 위해 사용됨.
- 해당 명령어를 사용시 소스 코드에서 #include를 사용하여 헤더 파일을 참조할 때 해당 파일들이 위치한 디렉토리를 컴파일러에게 알려줄 수 있다.
- target_link_libraries()와 동일하게 사용이 된다.
- 사용법
- target_include_directories(TARGET_NAME ${OPTION} PATH_1 PATH_2 ...)
- ${OPTION}
- PRIVATE: 타겟에만 의존성을 부여.
- PUBLIC: 타겟과 그 타겟의 소비자 모두에게 의존성을 부여.
- INTERFACE: 타겟의 소비자에게만 의존성을 부여.
target_include_directories(${LIBRARY_NAME} PUBLIC ${${LIBRARY_NAME}_INCLUDE_PATH} )
- target_compile_options()
- 특정 타겟에 컴파일 옵션을 추가하기 위해 사용됨.
- 이 명령어를 사용하면 컴파일러 플래그나 옵션을 타겟별로 지정이 가능하다.
- 사용법
- target_compile_options(TARGET_NAME ${OPTION} COMPILE_OPTION_1 COMPILE_OPTION_2 ...)
- ${OPTION}
- PRIVATE: 타겟에만 의존성을 부여.
- PUBLIC: 타겟과 그 타겟의 소비자 모두에게 의존성을 부여.
- INTERFACE: 타겟의 소비자에게만 의존성을 부여.
- 주의
- 다양한 컴파일러는 서로 다른 컴파일 옵션을 가질 수 있다.
- 크로스 플랫폼 프로젝트에서는 CMAKE_<LANG>_COMPILER_ID 변수를 사용하여 컴파일러 타입을 체크할 수 있다.
- 해당 컴파일러에 적합한 옵션을 설정하는 것이 좋다.
target_compile_options(${LIBRARY_NAME} PRIVATE -Wall -Wextra -Wpedantic -Werror # 가장 기본적으로 추천하는 컴파일 옵션 )
- target_compile_definitions()
- 타겟에 컴파일 시간의 전처리기 정의(Preprocessor definitions)를 추가하는데 사용되는 명령어.
- 이 정의들은 코드 내의 #ifdef, #ifndef, #if defined 등의 전처리기 지시문과 함께 사용이 되며 코드의 조건부 컴파일을 가능하게 도와준다.
- 사용법
- target_compile_definitions(TARGET_NAME ${OPTION} ${COMPILE_OPTION_1} ${COMPILE_OPTION_2} ...)
- ${OPTION}
- PRIVATE: 타겟에만 의존성을 부여.
- PUBLIC: 타겟과 그 타겟의 소비자 모두에게 의존성을 부여.
- INTERFACE: 타겟의 소비자에게만 의존성을 부여.
- ${COMPILE_OPTION}
- 많은 컴파일러에서 정의는 커맨드 라인에서 -D<definition>으로 지정된다.
- target_compile_definitions()에서는 -D 접두사 없이 정의를 제공해야 한다.
- file()
- ${OPTION}
- READ: 파일 내용을 변수에 읽는다.
- WRITE: 파일 내용을 작성한다.
- APPEND: 기존 파일에 내용을 추가한다.
- GLOB: 주어진 패턴과 일치하는 파일 및 디렉토리를 변수에 추가한다.
- GLOB_RECURSE: 주어진 패턴과 일치하는 파일 및 디렉토리를 재귀적으로 추가한다.
- RENAME: 파일 또는 디렉토리의 이름을 변경한다.
- COPY: 파일 및 디렉토리를 다른 위치로 복사한다.
- REMOVE: 하나 이상의 파일 또는 디렉토리를 삭제한다.
- MAKE_DIRECTORY: 디렉토리를 생성한다.
- TO_CMAKE_PATH: 경로를 CMake 스타일로 변환.
- TO_NATIVE_PATH: 경로를 Native 스타일로 변환.
- DOWNLOAD: 주어진 URL에서 파일을 다운로드 한다.
- install()
- 설치 규칙을 지정하는 명령어.
- 이 명령어는 프로젝트의 타겟, 파일, 디렉토리를 지정된 위치에 설치하도록 설정하는데 사용한다.
- CMake를 사용하여 프로젝트를 빌드한 후 install 명령을 실행하면, 지정된 설치 규칙에 따라 타겟과 파일들이 설치된다.
- 사용법
- install(TARGETS TARGET_1, TARGET_2 ... ${OPTION})
- ${OPTION}
- DESTINATION ${DIRECTORY_NAME}: 타겟, 파일 또는 디렉토리가 설치될 목적지를 지정.
- PERMISSIONS: 설치된 파일의 권한을 지정한다.
- CONFIGURATIONS: 설치를 제한할 구성을 지정한다. (ex. Debug, Release ...)
- COMPONENT: 설치 구성 요소의 이름을 지정한다. 이를 통해 여러 설치 구성 요소를 그룹화 하거나 별도로 처리할 수 있다.
2. 제어문
- if(조건문)
- 다양한 조건문을 제공하여 구성 및 빌드 환경에 따라 다른 동작을 수행하게 하거나 다른 설정을 적용할 수 있다.(ex. OpenCV version에 따른 동작수행, OS에 따른 동작수행)
- 주로 if(). elseif(), else(), endif()가 있다.
- 사용법
if(<condition>) else if(<another condition>) else() endif()${OUT_DIRECTORY_NAME}(Optional)
if()의 마지막에 endif()는 같이 넣어줘야한다.
- 사용되는 조건 연산자
- NOT: 부정.
- AND: 논리곱.
- OR: 논리합.
- EQUAL, STREQUAL: 비교.
- LESS, GREATER, LESS_EQUAL, GREATER_EQUAL: 수치 비교.
- MATCHES: 정규 표현식을 사용한 문자열 매치.
- EXISTS, IS_DIRECTORY, IS_ABSOLUTE, 등: 파일, 디렉토리 관련 조건 연산자.
- DEFINED: 변수가 정의 되었는지 확인해줌.
set(LOCAL_BOOL_VAL TRUE) if(LOCAL_BOOL_VAL) message(STATUS "[Status] Check bool variable") endif()
- foreach(반목문)
- CMake에서 반목문을 수행하기 위한 기본 구조. foreach()를 제공한다.
- foreach()를 사용하여 각 리스트의 각 항목에 대해 일련의 명령어를 반복적으로 실행할 수 있다.
- 사용법
foreach(VARIABLE_NAME IN LISTS ${LIST_NAME}) endforeach() foreach(value RANGE ${VALUE}) endforeach() foreach(value RANGE ${START_VALUE} ${END_VALUE} ${INTERVAL_VALUE}) endforeach()
끝 부분에 endforeach()를 붙여야한다.
## example for set(LIST "0" "1" "2" "3" "4") foreach(VAR IN LISTS LIST) message(STATUS "VAR: ${VAR}") endforeach()
결과
실습
이번에는 Local과 Global 변수를 구별해보자.
프로젝트 구조는 다음과 같이 example 폴더에 CMakeLists.txt를 생성했다.
다른 CMake를 불러올때는 add_subdirectory()를 사용하면 된다.
add_subdirectory(example)
하지만, example 폴더 내부의 CMakeLists.txt에 변수를 지정하고 상위 폴더의 CMakeLists.txt에 변수를 출력하려고 하면 아무것도 출력이 되지 않을 것이다.
그렇기에 Local variable로 지정된 변수를 Global variable로 바꿔야한다.
이를 위해 CACHE를 사용하면 된다.
# LOCAL VARIABLE set(LOCAL_STRINT_VARIABLE "Hello CMake!") set(LOCAL_BOOL_VARIABLE TRUE) # GLOBAL VARIABLE set(GLOBAL_STRINT_VARIABLE "Hello CMake!" CACHE STRING "Global string variable") set(GLOBAL_BOOL_VARIABLE TRUE CACHE BOOL "Global bool variable")
CACHE에 등록을 했기 때문에 어디서든 사용할 수 있는 변수가 되었다.
아래는 추가적으로 공부를 진행하며 업데이트한 프로젝트의 주소다.
https://github.com/Mokchanic/Example_CMake_Project.git
지금까지 배운 것은 가장 기본적인것이므로, CMake의 Documentation을 참고하도록 하자.
https://cmake.org/documentation/
'Tools > CMake' 카테고리의 다른 글
Basic_CMake (0) 2023.12.31 CMake Build System (0) 2023.10.09