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 -G "Visual Studio 17 2022" -A x64 path/to/source/dir
    msbuild Project.sln /p:Configuration=Release
    
    cmake若不跟参数(比如:cmake ..),则生成Visual Studio工程结构文件,且x64模式,与第一种方式等价。
  • nmake
    cmake -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Release path/to/source/dir
    nmake
    
    nmake编译速度比msbuild快,且能以命令行显示编译进度(百分比),合适CI中构建,而msbuild适合IDE中debug。
  • 第三方工具ninja (推荐方式)
    cmake -G Ninja -DCMAKE_BUILD_TYPE=Release path/to/source/dir
    cmake --build .
    
    Windows平台上,ninja调用的也是nmake。-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_DIRSXXX_LIBRARIES
  • Config模式
    搜索XXX_DIR指定路径下的XXXConfig.cmake文件(XXX_DIR定义在自己工程的CMakeLists.txt或者通过-DXXX_DIR命令行参数传入),通过该文件从而找到XXX库的头文件和lib文件位置。XXXConfig.cmake中需要定义XXX_INCLUDE_DIRSXXX_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

A CMakeLists.txt dependents other CMakeLists.txt:

  1. add sub-project A and sub-project B in main project using add_subdirectory, example code;
    add_subdirectory( ProjA )
    add_subdirectory( ProjB )
    
  2. 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

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


伦常乖舛,立见消亡;德不配位,必有灾殃。----《朱子家训》