[Build]cmake常用配置项
keywords: cmake常用配置项, 配置与构建, cmd, command
常用设置
Specify build directory
3.13版本(2018年11月)之前,用于build的文件夹需要手动创建:
mkdir build
cd build
cmake ..
cmake --build .
3.13版本开始,cmake可以自动创建build文件夹,并cd进去:
cmake -S . -B build
cmake --build build
-S .
表示source directory,也就是CMakeLists.txt所在目录,3.13之前叫-H
。
也可以省去,例如:cmake .
。
参考:
https://stackoverflow.com/a/24435795/1645289
Config & build command
有四种build命令:
- 不指定-G参数(默认方式):
cmake path/to/source/dir cmake --build . --config release
- msbuild:
cmake若不跟参数(比如:
cmake -G "Visual Studio 17 2022" -A x64 path/to/source/dir msbuild Project.sln /p:Configuration=Release
cmake ..
),则生成Visual Studio工程结构文件,且x64模式,与第一种方式等价。 - nmake
nmake编译速度比msbuild快,且能以命令行显示编译进度(百分比),合适CI中构建,而msbuild适合IDE中debug。
cmake -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Release path/to/source/dir nmake
- 第三方工具ninja (推荐方式)
Windows平台上,ninja调用的也是nmake。
cmake -G Ninja -DCMAKE_BUILD_TYPE=Release path/to/source/dir cmake --build .
-G "NMake Makefiles"
无法跨平台,而-G Ninja
不受平台限制。
3.23版本(2022年4月)貌似有bug:Windows下若不指定-G
参数,build时不会输出编译日志。
设置编译输出路径
在CMakeLists.txt中如下设置后,无论是编译可执行文件还是编译静态库,都会输出到指定目录下,而不再是默认的当前目录。
#静态库输出目录(lib, a)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "lib/")
#编译时动态库输出目录(dll + lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "lib/")
#动态库输出目录(dll, so)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "lib/")
#可执行文件输出目录
set(EXECUTABLE_OUTPUT_PATH "bin/")
参考:
is CMake ignoring CMAKE_LIBRARY_OUTPUT_DIRECTORY?
https://stackoverflow.com/a/38450844/1645289
How do I make CMake output into a ‘bin’ dir? https://stackoverflow.com/a/6595001/1645289
cmake 常用设定及函数
https://blog.csdn.net/LaineGates/article/details/79190398
获取指定目录下的所有源文件
获取./src
目录下的所有hpp和cpp文件(只获取./src
当前目录文件,不递归查询./src
下的子目录):
file(GLOB CPP_FILES ./src/*.hpp ./src/*.cpp)
add_library(mylib STATIC ${CPP_FILES})
获取./src/player/
目录和./src/monster/
目录下的所有cpp文件(同时递归这两个目录下的所有子目录):
file(GLOB_RECURSE CPP_FILES
./src/player/*.cpp
./src/monster/*.cpp)
参考:How to use cmake GLOB_RECURSE for only some subdirectories
https://stackoverflow.com/a/27994855/1645289
复制文件
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/media DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
打印log
if (NOT CMAKE_VERSION VERSION_GREATER "3.0")
message(FATAL_ERROR "cmake version 3.x is required!")
endif()
打印变量
message(STATUS "foo include dir: ${foo_INCLUDE}")
修改默认的CMAKE_MODULE_PATH目录
CMAKE_MODULE_PATH是供find_package
搜索第三方库用的。cmake的默认Modules目录在安装目录中:cmake-3.11.3-win64-x64\share\cmake-3.11\Modules
。
如果要追加Modules目录,有3种方式:
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_PATH}:${CMAKE_MODULE_PATH}")
LIST(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_PREFIX}")
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
find_package用法
通常情况下,包含第三方库需要写以下内容:
include_directories("${project_root_path}/include/")
link_directories(./lib)
add_executable(myapp myapp.cpp)
target_link_libraries(myapp mylib)
如果引用的很多个第三方库,那么类似上面的内容会写很多(实际项目的第三方库结构比上述更复杂),且当多个项目都引用了同一个第三方库,那么我每个项目的CMakeLists.txt
都得写一遍,重复劳动很多。
那么有没办法为每个第三方库只定义一次它的头文件和库文件信息,然后在自己的工程中只指定名称即可?(类似编译Java的Maven仓库)答案是可以,find_package
帮你解决。
find_package
定义在自己工程的CMakeLists.txt中:
两种方式,一种叫Module模式:
find_package( XXX REQUIRED )
另一种叫Config模式:
find_package( XXX CONFIG REQUIRED )
- Module模式
搜索CMAKE_MODULE_PATH指定路径下的FindXXX.cmake文件,通过该文件从而找到XXX库的头文件和lib文件位置。FindXXX.cmake中需要定义XXX_INCLUDE_DIRS
和XXX_LIBRARIES
。 - Config模式
搜索XXX_DIR指定路径下的XXXConfig.cmake文件(XXX_DIR定义在自己工程的CMakeLists.txt或者通过-DXXX_DIR
命令行参数传入),通过该文件从而找到XXX库的头文件和lib文件位置。XXXConfig.cmake中需要定义XXX_INCLUDE_DIRS
和XXX_LIBRARIES
Module模式示例:
- 先新建FindXXX.cmake,参考:FindGLFW3.cmake;
- 然后将FindXXX.cmake所在路径加入
CMAKE_MODULE_PATH
,参考:CMAKE_MODULE_PATH; - 然后通过
find_package()
查找include和lib,参考:find_package(); - 最后将lib文件路径引入到
target_link_libraries()
,参考:target_link_libraries()如果不使用
find_package()
,target_link_libraries中只需写lib的名称即可,例如target_link_libraries(main glfw3)
。但find_package()
方式下,需要完整的引用名,例如target_link_libraries(main ${GLFW3_LIBRARY})
,其中GLFW3_LIBRARY
定义在FindGLFW3.cmake。
${GLFW3_LIBRARY}
有助于阅读CMakeLists.txt,因为当glfw3
定义在FindGLFW3.cmake文件中时,其他用户很难理解glfw3
是在哪里定义的,尤其是一些小众的第三方lib。
Config模式示例:
有点复杂,具体看这个demo:find_package_example。
Installation: cmake –install
3.15版本(2019年6月)之前,install
必须跟着--build
和--target
两个参数同时执行:
cmake . -B build
cmake --build build --target install --config release
也就是说,install必须和build同时执行,而这种情况不允许:提前build好了,现在只想install。
3.15版本开始,install和build可以分开执行:
cmake . -B build
cmake --build build --config release
cmake --install build --config release
参考:How to use CMake to install
https://stackoverflow.com/a/48428846/1645289
要执行install命令,前提是CMakeLists.txt中配置了install()
命令,示例(引用自glew-cmake):
install(TARGETS ${GLEW_TARGETS} EXPORT glew-cmake
ARCHIVE DESTINATION ${INSTALL_LIBDIR}
LIBRARY DESTINATION ${INSTALL_LIBDIR})
install(EXPORT glew-cmake DESTINATION ${INSTALL_LIBDIR}/cmake/glew FILE glewConfig.cmake)
file(GLOB PUBLIC_HEADERS "include/GL/*.h")
install(FILES ${PUBLIC_HEADERS} DESTINATION include/GL/)
编写install配置的好处是:别人需要引用你写的lib时,仅用find_package
即可,让CMakeLists.txt更简洁,也省去了手动拷贝lib和header的工作,但缺点是install脚本编写比较复杂。
完整的install配置案例:find_package_example
Installation: CMAKE_INSTALL_PREFIX
编译完成后执行安装时:cmake --install build
,此时安装的目录默认是/usr/local
(Mac、Linux)或C:/Program Files (x86)
(Windows),有没办法手动指定安装目录?
解决办法:CMAKE_INSTALL_PREFIX
或者--prefix
。
示例:
cmake . -B build -DCMAKE_INSTALL_PREFIX=D:/MyLibs/FOO
或者
cmake --install build --prefix D:/MyLibs/FOO
两者等价。
使用通配符批量指定头文件或源文件 (include source files batch using wildcard)
GLOB:
FILE(GLOB cpp_files ../src/*.cc)
add_executable(helloworld ${cpp_files})
There’s also GLOB_RECURSE
if you want to find the files recursively.
How to use all *.c files in a directory with the Cmake build system?
https://stackoverflow.com/a/3366701
Automatically add all files in a folder to a target using CMake?
https://stackoverflow.com/a/3201211
How to enable Google Sanitizers in CMake
Configuration for GCC in CMakeLists.txt
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fsanitize=leak -fsanitize=thread -g")
Configuration for xcode schema
in CMake:
cmake_minimum_required(VERSION 3.13)
set(CMAKE_XCODE_GENERATE_SCHEME ON)
set(CMAKE_XCODE_SCHEME_ADDRESS_SANITIZER ON)
set(CMAKE_XCODE_SCHEME_ADDRESS_SANITIZER_USE_AFTER_RETURN ON)
When Build:
xcodebuild -enableAddressSanitizer YES
Reference:
What’s the proper way to enable AddressSanitizer in CMake that works in Xcode
https://stackoverflow.com/a/45940322
How to change the name of CMakeLists.txt
There’s no reasonable excuse that CMake doesn’t use a specified file extension like every other tool in the toolkit. You can do this by putting a dummy CMakeLists.txt, which contains:
cmake_minimum_required(VERSION 3.8)
INCLUDE("meaningfulFilename.cmake")
Then put your actual cmake code in the .cmake file.
Origin:
https://stackoverflow.com/a/53743295/1645289
Link other library by CMakeLists.txt
A CMakeLists.txt dependents other CMakeLists.txt:
- add sub-project A and sub-project B in main project using
add_subdirectory
, example code;add_subdirectory( ProjA ) add_subdirectory( ProjB )
- then you can link project A in project B, example code;
target_link_libraries(ProjB ProjA)
add_subdirectory: relative patch of subdirectory
Add subdirectory in current directory:
add_subdirectory(Lua)
Add subdirectory in relative path:
add_subdirectory(./../Lua build_lua)
build_lua
is a directory under ./../Lua/
How to check specified platform
if(WIN32)
set(LIBS glfw3 opengl32 assimp freetype irrKlang)
elseif(UNIX AND NOT APPLE)
set(LIBS ${GLFW3_LIBRARY} X11 Xrandr Xinerama Xi Xxf86vm Xcursor GL dl pthread freetype ${ASSIMP_LIBRARY})
elseif(APPLE)
SET(APPLE_LIBS ${COCOA_LIBRARY} ${IOKit_LIBRARY} ${OpenGL_LIBRARY} ${CoreVideo_LIBRARY})
SET(APPLE_LIBS ${APPLE_LIBS} ${GLFW3_LIBRARY} ${ASSIMP_LIBRARY} ${FREETYPE_LIBRARIES})
set(LIBS ${LIBS} ${APPLE_LIBS})
else()
set(LIBS )
endif(WIN32)
Example: LearnOpenGL
How to link win32 library which likes /subsystem:windows
For example, if want to link subsystem:windows: #pragma comment(linker, "/subsystem:windows")
,
you can do this an cmake:
add_executable(${PROJECT_NAME} WIN32 main.cpp)
Origin: Correctly set Visual Studio linker flag /SUBSYSTEM in CMAKE
https://stackoverflow.com/a/57645234/1645289
Visual Studio
Set startup project
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT MyExeTarget)
Origin:
https://stackoverflow.com/a/59789571/1645289
Compile with /MT
or /MD
How to set Runtime Library
for Visual Studio:
MT
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd")
MD
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MD")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MDd")
Origin:
Compile with /MT instead of /MD using CMake
https://stackoverflow.com/a/14172871/1645289
Warning: LNK4098, defaultlib ‘MSVCRT’ conflicts with use of other libs
if(MSVC)
# suppress warning on debug config - LNK4098: defaultlib 'MSVCRT' conflicts with use of other libs
set_property(TARGET ${MyProj} APPEND PROPERTY LINK_FLAGS "/NODEFAULTLIB:MSVCRT")
endif()
Origin:
https://blog.csdn.net/Ellan_BM/article/details/105688991
Compiler
How to enable C++17 features for compiler
Global:
set (CMAKE_CXX_STANDARD 17)
Target only:
# enable C++17 features
target_compile_features(${MY_PROJECT_NAME} PRIVATE cxx_std_17)
Origin:
https://stackoverflow.com/a/53830867/1645289
add_compile_definitions
add_compile_definitions
is a new feature added in version 3.12, example:
add_compile_definitions(GLEW_STATIC)
It will be translated as preprocessor macros: #define GLEW_STATIC
.
example 2:
add_compile_definitions(WORK_DIR="${CMAKE_CURRENT_SOURCE_DIR}")
Then you can use the define: std::string dir = std::string(WORK_DIR) + "/src/glsl/";
target_compile_definitions
Similar with add_compile_definitions
, but for specified target only:
target_compile_definitions(MyApp PRIVATE
ADD_DEBUG_PRINTS=1
ABCDE
)
target_compile_options
Choose the appropriate compiler flags based on the compiler used. Furthermore you save yourself the -D
:
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
target_compile_options(MyTarget PRIVATE -Wall)
endif()
Origin:
https://stackoverflow.com/a/71861489/1645289
How to disable deprecated warnings
set(CMAKE_WARN_DEPRECATED YES)
Or
target_compile_options(<target> [PRIVATE|PUBLIC] "-Wno-deprecated")
Origin:
https://stackoverflow.com/a/77008909/1645289
CMake Examples
Comprehensive
Command line C++ barcode reader for Windows, Linux, and macOS.
https://github.com/dynamsoft-dbr/cmake
learning cmake
https://github.com/Akagi201/learning-cmake
Useful CMake Examples
https://github.com/ttroy50/cmake-examples
A curated list of awesome CMake resources, scripts, modules, examples and others.
https://github.com/onqtam/awesome-cmake
A template for modern C++ projects using CMake, clang-format and unit testing, with support for downstream inclusion
https://github.com/filipdutescu/modern-cpp-template
iOS, MacOS
A CMake toolchain file for iOS, watchOS and tvOS C/C++/Obj-C++ development
https://github.com/leetal/ios-cmake
Example. Compile android, iOS, mac static lib on MacOS.
https://github.com/9b9387/CMakeCrossCompiling
伦常乖舛,立见消亡;德不配位,必有灾殃。----《朱子家训》