跳到主要内容

CMake杂记

阅读量

0

阅读人次

0

使用FetchContent代码下载和构建

有时候一些比较小众的第三方库,因为其不稳定,还可能有bug,所以我们并不想在使用前就将其编译好生成库文件使用。而是想直接使用其代码构建,这样出现问题,也好直接查看代码,这时就可以使用FetchContent实现目的,例如:

include(FetchContent)
FetchContent_Declare(framelesshelper
GIT_REPOSITORY https://github.com/wangwenx190/framelesshelper
GIT_TAG main
)
set(FRAMELESSHELPER_BUILD_STATIC ON) # 设置 framelesshelper 的各种编译选项
FetchContent_MakeAvailable(framelesshelper)

使用ALIAS将库加入命名空间

有时候我们在编写可复用库时,难免会有将库再细分的想法,例如:

Kylin
├── Core
├── Gui
└── Network

参考Qt、及Boosts应用库的方式,一般都是Qt6::WidgetsQt6::NetworkBoost::log

这时就可以用到别名目标了,别名目标属于伪目标,例如:

add_library(KylinCore
# ......
BoostLog.h BoostLog.inl BoostLog.cpp
BufferUtility.h BufferUtility.cpp
)
add_library(Kylin::Core ALIAS KylinCore)

这时我们在使用时,就可以使用Kylin::Core连接该库了:

target_link_libraries(Younger
PRIVATE Kylin::Core
# ......
)

同样的方式可以实现Kylin::GuiKylin::Network,这样就可以使得Kylin这个库更具有关联性,更具有模块化。

将第三方库引入CMake

像FFmpeg这样的库,是不支持CMake的,一般如果我们想要引用FFmpeg进行开发,一般都会直接使用target_include_directories()target_link_directories()target_link_libraries()这三个函数。其实我们也可以以Find<PackageName>.cmake这样的方式使用,CMake参考文档里面也介绍了使用更现代化的导入目标的方式引入第三方库。

这里简单的编写了一个这样的文件:

FindFFmpeg.cmake
if(NOT FFmpeg_ROOT)
message(FATAL_ERROR "please specify FFmpeg_ROOT to find FFmpeg.")
endif()

add_library(FFmpeg::AVFormat UNKNOWN IMPORTED)
set_target_properties(FFmpeg::AVFormat PROPERTIES
IMPORTED_LOCATION "${FFmpeg_ROOT}/lib/avformat.lib"
INTERFACE_INCLUDE_DIRECTORIES "${FFmpeg_ROOT}/include"
)


add_library(FFmpeg::AVCodec UNKNOWN IMPORTED)
set_target_properties(FFmpeg::AVCodec PROPERTIES
IMPORTED_LOCATION "${FFmpeg_ROOT}/lib/avcodec.lib"
INTERFACE_INCLUDE_DIRECTORIES "${FFmpeg_ROOT}/include"
)

add_library(FFmpeg::AVDevice UNKNOWN IMPORTED)
set_target_properties(FFmpeg::AVDevice PROPERTIES
IMPORTED_LOCATION "${FFmpeg_ROOT}/lib/avdevice.lib"
INTERFACE_INCLUDE_DIRECTORIES "${FFmpeg_ROOT}/include"
)

add_library(FFmpeg::SwScale UNKNOWN IMPORTED)
set_target_properties(FFmpeg::SwScale PROPERTIES
IMPORTED_LOCATION "${FFmpeg_ROOT}/lib/swscale.lib"
INTERFACE_INCLUDE_DIRECTORIES "${FFmpeg_ROOT}/include"
)

add_library(FFmpeg::AVUtil UNKNOWN IMPORTED)
set_target_properties(FFmpeg::AVUtil PROPERTIES
IMPORTED_LOCATION "${FFmpeg_ROOT}/lib/avutil.lib"
INTERFACE_INCLUDE_DIRECTORIES "${FFmpeg_ROOT}/include"
)

set(FFmpeg_FOUND ON)

然后我们在项目中就可以很方便模块化的引用FFmpeg了:

list(APPEND CMAKE_MODULE_PATH path/to/find_ffmpeg_cmake)	# 使得CMake能够找到FindFFmpeg.cmake
set(FFmpeg_ROOT path/to/ffmpeg/root)
find_package(FFmpeg)
target_link_libraries(Younger
# ......
PRIVATE FFmpeg::AVUtil
PRIVATE FFmpeg::AVCodec
PRIVATE FFmpeg::AVFormat
PRIVATE FFmpeg::AVDevice
PRIVATE FFmpeg::SwScale
)

使用CMake生成配置文件

有时候我们希望CMake的变量,能够传入到项目代码以被我们使用,例如编译时间、编译版本、Git提交ID、以及CMake Option来控制代码。这时configure_file()就派上用场了。我们在CMakeLists.txt中加入如下代码:

project(MyApp VERSION 1.2.3)
string(TIMESTAMP BUILD_TIME "%Y-%m-%d %H:%M:%S")

execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD
OUTPUT_VARIABLE COMMIT_ID
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
OUTPUT_STRIP_TRAILING_WHITESPACE
)
configure_file(Version.h.in Version.h @ONLY)
# ......

然后在项目中以创建如下文件:

Version.h.in
#define MyApp_VERSION_MAJOR @ProjectName_VERSION_MAJOR@
#define MyApp_VERSION_MINOR @ProjectName_VERSION_MINOR@
#define MyApp_VERSION_PATCH @ProjectName_VERSION_PATCH@
#define APP_VERSION "@MyApp_VERSION_MAJOR@.@MyApp_VERSION_MINOR@.@MyApp_VERSION_PATCH@"
#define BUILD_TIME "@BUILD_TIME@"
#define COMMIT_ID "@COMMIT_ID@"

因为project()的各个参数都会生成对应的变量,所以可以很方便的实现版本号查询,不用在CMake和代码里面分别手写代码,做到配置信息都在CMakeList.txt中,使其更易查找,也更模块自动化。

在代码构建是,CMake就会在构建目录中生成Version.h文件,我们只需要将构建目录添加到头文件引用目录:

CMakeLists.txt
# ......
target_include_directories(MyApp
PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
)
# ......

然后就可以在项目中引用Version.h文件了:

main.cpp
#include <Version.h>
#include <iostream>

int main(int argc, char *argv[]) {
std::cout << "app version: " << APP_VERSION << std::endl;
std::cout << "git commit id: " << COMMIT_ID << std::endl;
std::cout << "build time: " << BUILD_TIME << std::endl;
return 0;
}

VS Code使用CMake进行开发

首先需要下载CMake ToolsCMake插件,分别提供VS Code项目配置以及语法高亮和智能提示。

在设置中将cmake.generator更改为Ninja

如果需要使用Qt,则可以在cmake.configureSettings下面增加如下变量:

"cmake.configureSettings": {
"Qt6_DIR": "D:/Qt/6.6.0/msvc2019_64/lib/cmake/Qt6",
"Qt6Widgets": "D:/Qt/6.6.0/msvc2019_64/lib/cmake/Qt6Widgets",
"Qt6CoreTools_DIR": "D:/Qt/6.6.0/msvc2019_64/lib/cmake/Qt6CoreTools",
"Qt6GuiTools_DIR": "D:/Qt/6.6.0/msvc2019_64/lib/cmake/Qt6GuiTools",
},

如果需要使用外部动态库,则可以在cmake.environment下面增加PATH变量:

"cmake.environment": {
"PATH": "D:/Qt/6.6.0/msvc2019_64/bin;D:/Qt/Tools/OpenSSLv3/Win_x64/bin;E:/Projects/Libraries/boost_1_83_0_msvc2022_64bit/lib;${env:PATH}",
},

修改构建文件的存放位置:

"cmake.buildDirectory": "${workspaceFolder}/../${workspaceRootFolderName}-${buildKitVendor}-${buildType}"

相关变量可以参考这里

总结

CMake MSVC 链接错误

LNK4098: 默认库“MSVCRT”与其他库的使用冲突;请使用 /NODEFAULTLIB:library

set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}  /NODEFAULTLIB:MSVCRT")