CMake Basic
카테고리: CMake
인프런에 있는 삼각형님의 삼각형의 실전! CMake 초급 강의를 듣고 정리한 내용입니다.
CMake
CMake(Cross Platfoem Make)는 엄밀히 말해 Build System이 아니고, Build System Generaotr 이다. 즉, Native Build System(VS, XCode)이 사용하는 파일을 생성해준다. 또한 Test, Package를 통해 간편하게 실행할 수 있도록 지원한다.
CMake는 빌드해야 할 파일이 추가될 때마다 CmakeList 파일을 수정하여 다시 실행해야 한다는 단점이 존재하지만, 변경 사항을 매우 쉽게 인지할 수 있고 version control에 친화적이라는 큰 장점이 존재한다.
폴더 구조
.
├── CMakeLists.txt
├── bin // 동적 라이브러리와 실행 파일
├── data // 데이터와 에셋
├── demo // 데모
├── doc // 문서
├── include // 헤더 파일
├── lib // 라이브러리
├── src // 소스 파일
└── test // 테스트
- 일반적으로 CMake는 위와 같은 폴더 구조를 따른다.
- CMakeList.txt 파일은 루트 폴더에 위치
CMake 단계
빌드가 끝나면 반드시 확인해야 할 출력 메시지 2개가 있음 1) Configuring done 2) Generating done
CMake는 빌드에 필요한 파일들을 생성하기 위해 2 단계를 수행한다.
- 1) Configure
- CmakeList.txt 분석
- 분석한 내용을 바탕으로 CmakeCache.txt 생성
- 2) Generate
- configure에서 생성한 파일들을 가지고 Native Build System 파일 생성
CMakeCache.txt는 문제의 근원
CMakeCache.txt 파일은 위와 같이 <name>:<type>=[value]
의 형식으로 이루어져 있다. 이때 주의할 점은 CMakeCache 파일을 기반으로 Native Build System을 만들기 때문에 CMakeList 파일만 수정하고 CMakeCache 파일을 갱신하지 않는다면, 원치 않는 Build System 파일이 생성될 수 있다. 이런 경우 CMakeCache 파일을 삭제하고 다시 빌드하거나, –fresh 옵션을 통해 Cache 파일을 갱신할 수 있다.
CMake 옵션
- -B : 빌드 파일이 생성될 폴더 정의
- 해당 폴더에 빌드에 필요한 파일들이 생성됨
- -G : CMake가 생성해야 하는 BuildSystem 명시
- ex) Visual Studio, XCode, Make, Ninja, …
- -D : CMakeList.txt에서 사용하는 변수 값 입력
- –build : 빌드를 실행할 폴더 정의 & 실행
- 해당 폴더에 B옵션으로 생성한 빌드에 필요한 파일들이 있어야 함
- –target : build 옵션의 옵션 (build 옵션이 있어야 사용 가능)
- –target [filename] : 특정 타겟만 빌드
- –target clean : 기존의 빌드 결과물 삭제
- –fresh : CMakeCache.txt 파일을 다시 생성
CMake 커맨드
CMake의 커맨드는 위와 같이 커맨드_이름(공백으로 구분된 문자열)
로 이루어져 있다. 기본적으로 다양한 기본 커맨드들이 정의되어 있고, 필요에 따라 직접 정의할 수 있다.
주석
# 한 줄 주석
#[===[
여러줄 라인 주석
#]===]
Basic Command
1) project : 프로젝트 정의
project(<project-name> [Version] <version> [LANGUAGES] <language-name>...)
project(command VERSION 0.0.1 LANGUAGES C CXX)
- C++를 CXX로 표기
2) message : 텍스트 출력
message([<mode>] "text..")
# 메시지 출력
message("Hello CMake!")
# STATUS 모드로 메시지 출력
message(STATUS "Hello CMake in STATUS!")
- STATUS 모드는 자주 사용되는데, 메시지 앞에 “–“이 붙어 해당 메시지를 쉽게 찾을 수 있음
3) set/unset : 변수 정의/파괴
set(<variable> <value>...)
unset(<variable>)
# 변수 생성
set(variable CMake)
# 변수 참조
message("This is a " ${variable} "! ${variable}!")
# 변수 파괴
unset(variable)
- 정의한 변수를 참조할 때는 ${
} 형식으로 가능 - CMake는 문자열 타입만 지원
4) option : 사용자가 선택할 수 있는 옵션 정의
option(<variable> "help" [value])
# 옵션 정의
option(BUILD_EXAMPLE "Enable build examples." ON)
- 초기 값을 지정하지 않으면 기본 값은 OFF
5)list : list와 관련된 편리한 함수 제공
list(<sub-command> <list>...)
# 리스트 생성
list(APPEND FILES foo.cpp bar.cpp baz.cpp qux.cpp)
# 리스트 검색
list(FIND FILES baz.cpp baz_cpp_index)
# 리스트 정렬
list(SORT FILES)
6) if 분기문
if(APPLE)
message(STATUS "My system is macOS.")
elseif(WIN32)
message(STATUS "My system is Windows.")
elseif(UNIX)
message(STATUS "My system is Linux.")
else()
message(STATUS "My system is unknown.")
endif()
- 주의)
endif()
로 분기문의 종료를 명시해야 함
7) foreach 반복문
foreach(FILE IN LISTS FILES)
message(${FILE} " is in the list")
endforeach()
- if 문과 마찬가지로
endforeach()
명시해야 함
8) add_subdirectory : 빌드에 포함할 소스 디렉토리 추가
add_subdirectory(cmake)
- CMakeList.txt 파일은 여러 폴더에 존재할 수 있음
- 루트 폴더가 아닌 다른 폴더의 CMakeList 코드를 빌드에 포함하고 싶을 경우
add_subdirectory
사용 - 해당 디렉토리에 CMakeList.txt 파일 없으면 빌드 에러 남
9) add_compile_options : 컴파일 옵션을 추가
add_compile_options(-Wall -Wextra)
- 추가한 컴파일 옵션은 전역적으로 적용
- 만약 타겟마다 다른 옵션이 적용된다면 에러나니까 당연..
- Wall : Waring All (경고 문구 표시)
- Wextra : Waring All + Extra (좀 더 자세한 경고 문구 표시)
10) add_executable : 명시된 소스 파일을 사용하여 실행 타겟 정의
# src/foo.cpp 파일을 컴파일하여 foo 실행 파일 생성
add_executable(foo src/foo.cpp)
11) add_library : 명시된 소스 파일을 사용하여 라이브러리 타겟 정의
add_library(<name> [STATIC|SHARED] [<source>...])
# 라이브러리 타겟을 정의
add_library(bar STATIC
lib/bar/include/bar.h
lib/bar/src/bar.cpp
)
- STATIC : 정적 라이브러리 (.a or .lib) 생성
- SHARED : 동적 라이브러리 (.so or .dll or .dylib) 생성
12) set_target_properties : 타겟의 속성 설정
set_target_properties(<target>... PROPERTIES <prop value>... )
set_target_properties(foo PROPERTIES
WINDOWS_EXPORT_ALL_SYMBOLS YES # Windows만 적용
MACOSX_BUNDLE YES # macOS, iOS만 적용
)
- 운영체제에서 지원하지 않는 속성은 무시됨
13) target_link_libraries : 타겟을 링킹할 때 필요한 타겟과 라이브러리 지정
target_link_libraries(<target> < [PUBLIC|PRIVATE] <item>...> ...)
# foo 실행 파일에 bar(target or library) 링크
target_link_libraries(foo PRIVATE bar)
- 지정된 타겟에 의존성 정의
- 링크 단계에 영향을 미침
- PRIVATE인 경우 foo가 bar에 의존적이라는 사실을 다른 파일은 알 수 없음
- PUBLIC인 경우 foo에 의존적인 다른 타겟도 bar에 의존적
14) target_sources : 소스 파일을 타겟에 추가
target_sources(<target> < [PUBLIC|PRIVATE] <source>...>...)
arget_sources(bar PRIVATE src/baz.h src/baz.cpp)
15) target_compile_definitions : 컴파일 디파인 지정
target_compile_definitions(<target> < [PUBLIC|PRIVATE] <item>...>...)
target_compile_definitions(foo PRIVATE NOMINMAX)
- foo 파일 내부에
#define NOMINMAX
전처리 추가
16) target_include_directories : 타겟을 컴파일할 때 사용할 인클루드 디렉토리 지정
target_include_directories(<target> < [PUBLIC|PRIVATE] <item>...>...)
target_include_directories(bar PUBLIC bar/include)
17) target_compile_options : 타겟에 컴파일 옵션 추가
target_compile_options(<target> < [PUBLIC|PRIVATE] <item>...>...)
target_compile_options(bar PRIVATE -pedantic)
- pedantic: 표준 준수를 강제하고, 표준에 정의되지 않은 컴파일러 확장을 사용할 때 경고를 생성하는 컴파일러 옵션
CMAKE 변수
CMake에서 사용되는 내장 변수들은 전역 변수이다. 따라서 변수를 통해 해야하는 작업은 가능하면 타겟의 프로퍼티로 대체하는 것이 좋다. 하지만 경우에 따라 내장 변수들을 적절하게 사용하면 아래와 같이 매우 편리하다. 자주 사용되는 내장 변수들을 살펴보자.
❯ cmake -B build -DCMAKE_INSTALL_PREFIX=/install .
- -D : 변수에 값 지정
- DCMAKE_INSTALL_PREFIX 변수에 /install 지정
1) CMAKE_SYSTEM_NAME : 빌드 운영체제의 이름
# CMAKE_SYSTEM_NAME을 사용하여 운영체제마다 다른 명령어 실행
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
message("OS is Windows.")
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
message("OS is Linux.")
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
message("OS is macOS.")
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Android")
message("OS is Android.")
else()
message("OS is unknown.")
endif()
2) BUILD_SHARED_LIBS : 라이브러리 타겟이 동적/정적으로 빌드될지 결정
set(BUILD_SHARED_LIBS FALSE)
add_library(foo src/foo.cpp)
3) CMAKE_INSTALL_PREFIX : 바이너리가 설치될 경로 지정
❯ cmake -B build -DCMAKE_INSTALL_PREFIX=/install .
4) CMAKE_CXX_STANDARD : 모든 타겟의 C++ 버전 설정
set(CMAKE_CXX_STANDARD 20)
댓글 남기기