CMake 是一个开源的 C++构建工具生成器,可以很方便的实现跨平台编译,解决 C++库之间的依赖关系。只需要配置 CMakeList.txt 文件就可以实现对 C++源代码构建的控制,而且还是库平台,简直太好用。下面记录了一些常用的 CMake 命令。

命令模式

平台无关的系统命令,用法示例

# 对 my.txt 文件,生成 md5
cmake -E md5sum my.txt

查看更多命令可以直接输入 cmake -E。

CTest

https://www.bookset.io/read/CMake-Cookbook/content-chapter4-4.1-chinese.md

设置 MSVC 编译器编译 utf8 编码格式的源文件

add_compile_options("$<$<C_COMPILER_ID:MSVC>:/utf-8>")
add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/utf-8>")

禁用特定警告

add_compile_options(“/wd4819”)

安装库

function(INSTALL_PROJECT)
	install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include"
				 DESTINATION "include" OPTIONAL)
	install(TARGETS ${ARGN}
				 CONFIGURATIONS Debug
				 RUNTIME DESTINATION "bin_debug"
				 LIBRARY DESTINATION "lib_debug"
				 ARCHIVE DESTINATION "lib_debug")
	install(TARGETS ${ARGN}
				 CONFIGURATIONS Release
				 RUNTIME DESTINATION "bin"
				 LIBRARY DESTINATION "lib"
				 ARCHIVE DESTINATION "lib")
	# install target pdb
	install(FILES $<TARGET_PDB_FILE:${ARGN}>
				 CONFIGURATIONS Release
				 DESTINATION "bin" OPTIONAL)
endfunction()

导入第三方库

这里针对已经只有头文件的库,已经编译好的非 CMake 工程库,和 CMake 工程库。

方式 1

set(EIGEN3_DIR "path_to_eigen_cmake")
if(NOT EXISTS "${EIGEN3_DIR}")
	set(EIGEN3_DIR "")
endif()
find_package(EIGEN3 REQUIRED)
if(NOT EIGEN3_FOUND)
	message(STATUS "Warning: Eigen was not found.")
else()
	message(STATUS "Info: Eigen was found.)
	include_directories(${EIGEN3_INCLUDE_DIR})
endif()

file(GLOB PLOG_DIR "path/plog*")
find_path(WITH_PLOG_INC "include/plog/Log.h" "${PLOG_DIR}" NO_DEFAULT_PATH)
if(WITH_PLOG_INC)
	set(PLOG_INCLUDE_DIRS "${WITH_PLOG_INC}/include")
	include_directories(${PLOG_INCLUDE_DIRS})
else()
	message(STATUS "Warning: plog was not found.")
endif()

set(Protobuf_SRC_ROOT_FOLDER "path_protobuf_dir")
if(NOT EXISTS "${Protobuf_SRC_ROOT_FOLDER}")
	set(Protobuf_SRC_ROOT_FOLDER "")
endif()
find_package(Protobuf_SRC_ROOT_FOLDER REQUIRED)
if(NOT Protobuf_SRC_ROOT_FOLDER_FOUND)
	message(STATUS "Warning: Protobuf was not found.")
else()
	message(STATUS "Info: Protobuf was found.)
	include_directories(${Protobuf_INCLUDE_DIRS})
endif()

方式 2

使用 CMAKE_PREFIX_PATH

if(WIN32 AND MSVC)
	if(CMAKE_SIZEOF_VOID_P EQUAL 8)
		set(QT_PATH "qt_path" CACHE PATH "Qt path")
	endif()
	set(CMAKE_PREFIX_PATH "${QT_PATH}")
	mark_as_advance(QT_PATH)
endif()

find_package(Protobuf REQUIRED)
if(NOT Protobuf_FOUND)
	message(STATUS "Warning: Protobuf was not found.")
else()
	message(STATUS "Info: Protobuf was found.")
	include_directories(${Protobuf_INCLUDE_DIRS})
endif()

Qt 翻译文件

https://gitlab.kitware.com/cmake/cmake/-/issues/21549

MACRO 和 FUNCTION

https://blog.csdn.net/weixin_34121282/article/details/87972772

https://www.jianshu.com/p/6be3b104ab70

共同点

形式基本相同。

marco 形式如下:

macro(<name> [arg1 [arg2 [arg3 ...]]])
	COMMAND1(ARGS ...)
	COMMAND2(ARGS ...)
endmacro()

function 形式如下:

function(<name> [arg1 [arg2 [arg3 ...]]])
	COMMAND1(ARGS ...)
	COMMAND2(ARGS ...)
endfunction()

变量的引用:

变量 说明
ARGV# 同上
ARGV 所有定义时要求传入的参数
ARGN 定义时要求输入的参数以外的参数,比如定义时要求输入 1 个参数,实际输入了 3 个,那么后面两个参数保存在 ARGN 中
ARGC 传入的实际参数个数

设置构建 toolset

在高版本的 Visual Studio 可以使用低版本的 toolset 进行编译,比如,vs2019,安装了 vs2017 的构建工具和 sdk 后可以使用 vs2017 构建 cmake 项目。

# 设置 generator
-G "Visual Studio 15 2017"
# 设置 toolset
-T v141

设置输出目录

set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/${CMAKE_INSTALL_BINDIR}")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}")
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}")

设置工程调试的工作目录

VS_DEBUGGER_WORKING_DIRECTORY

设置工程属性

输出文件名

set_target_properties(LibraryA PROPERTIES OUTPUT_NAME "my_LibraryA")

预处理器定义

set_target_properties(LibraryA PROPERTIES COMPILE_DEFINITIONS "LibraryA_EXPORTS")

工程分组

根目录设置:

set_property(GLOBAL PROPERTY USE_FOLDERS ON)

每个分组下工程设置:

set_target_properties(LibraryA PROPERTIES FOLDER "Utilities")
set_target_properties(LibraryB PROPERTIES FOLDER "Utilities")

源文件分组

source_group("game\\entitysystem\\components" FILES ${components_SRC})

设置 Windows Qt 工程不弹出命令行窗口

#--------------------------------------------------------------------
# Hide the console window in visual studio projects
#--------------------------------------------------------------------
if(MSVC)
    set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup")
endif()

#--------------------------------------------------------------------
# Hide the console window in visual studio projects - Release
#--------------------------------------------------------------------
if(MSVC)
    set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS} /SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup")
endif()

INCLUDE_DIRECTORIES作用域

The include directories are added to the INCLUDE_DIRECTORIES directory property for the current CMakeLists file. They are also added to the INCLUDE_DIRECTORIES target property for each target in the current CMakeLists file.

set用法

set赋值给一般变量

仅在所在的作用域起作用。除非后面使用 PARENT_SCOPE。举例

// foo 作用域为当前作用域
set(foo "X")

// foo 作用域是当前文件的上一目录的 CMakeList
set(foo "x" PARENT_SCOPE)

set赋值给缓存变量

第一次运行 cmake 时,这些变量缓存到 CMakeCache.txt 中,在整个 cmake 运行过程中都可以起作用。

当使用 CACHE 时,且缓存中没有该变量时,变量被创建并且存入缓存中,如果原缓存中有该变量,也不会改变原缓存中该变量的值,除非后面使用 FORCE。

// 原缓存中没有 foo,则将 foo 赋值为 x,且存入缓存
// 原缓存中有 foo,则不做改变
set(foo "x" CACHE <type> <docstring>)

// 即使原缓存中存在 foo,也会重新设置 foo
set(foo "x" CACHE <type> <docstring> FORCE)

使用 CACHE 时,要设定<type>docstring

type description
FILEPATH File chooser dialog.
PATH Directory chooser dialog.
STRING Arbitrary string.
BOOL Boolean ON/OFF checkbox.
INTERNAL No GUI entry (used for persistent variables).

可以通过 CMake 变量声明另一个变量

set(${macroparam}_LOCATION “”)

在 CMakeList 文件中使用

message(STATUS ${${macroparam}_LOCATION})

设置后缀

set(CMAKE_DEBUG_POSTFIX "_d")