c++与cmake:完整的C++项目构建注意事项

个人博客:Sekyoro的博客小屋
个人网站:Proanimer的个人网站
最近常常使用cmake构建c++项目有感,从创建项目到打包发布总结一下需要注意的事情.

项目组织方式

具体的项目组织方式因人而异,这里推荐一种,在src目录中创建模块目录,再在include目录中常见对应的同名目录包含头文件,可执行程序的源代码或者最终生成库的源代码可以放在app目录中.

比如我看的一个项目组织如图
在这里插入图片描述

  1. 在src目录中包括demo,view,assignment三个项目,对应include目录相同,或者在生成程序的目录中包含头文件而不另外放include中.

  2. 此外也有src目录中放所有的源代码文件,include目录分别放每个模块对应的头文件,相对来说更方便.

针对第一种组织方式,cmake会在src目录添加模块

add_subdirectory(view)
add_subdirectory(demo)
add_subdirectory(assignments)

每个模块再单独写cmake,甚至可以单独写project,这样方便模块化,可以看到下面利用不同的${PROJECT_NAME}设置库生成位置

project(demo_hello_world)
file(GLOB source"${CMAKE_CURRENT_SOURCE_DIR}/demo_hello_world.cpp"
)
add_executable(${PROJECT_NAME} ${source})
set_target_properties(${PROJECT_NAME} PROPERTIES DEBUG_POSTFIX "_d"RUNTIME_OUTPUT_DIRECTORY "${BINARY_DIR}"LIBRARY_OUTPUT_DIRECTORY "${LIBRARY_DIR}"ARCHIVE_OUTPUT_DIRECTORY "${LIBRARY_DIR}") 
target_link_libraries(${PROJECT_NAME} PUBLIC view) project(demo)
file(GLOB source"${CMAKE_CURRENT_SOURCE_DIR}/demo.cpp""${CMAKE_CURRENT_SOURCE_DIR}/window_demo.cpp""${CMAKE_CURRENT_SOURCE_DIR}/window_demo.h"
)
add_executable(${PROJECT_NAME} ${source})
set_target_properties(${PROJECT_NAME} PROPERTIES DEBUG_POSTFIX "_d"RUNTIME_OUTPUT_DIRECTORY "${BINARY_DIR}"LIBRARY_OUTPUT_DIRECTORY "${LIBRARY_DIR}"ARCHIVE_OUTPUT_DIRECTORY "${LIBRARY_DIR}") 
target_link_libraries(${PROJECT_NAME} PUBLIC view) 

比较来看,如果给src目录中放所有的cpp源文件,那不好给模块分离,因为一个项目中一般包括一个生成可执行程序或最终库的源代码,一起一堆供这个目标依赖的模块,这些模块如果能单独提出来更好,也就是说这些模板的cpp代码如果放在分别的模块目录下虽然更麻烦但更好. 此外将main程序放在app目录中也更加清晰.

依赖图与文档生成

查看目标的依赖

下载graphviz

cd build && cmake .. --graphviz=graph.dot && dot -Tpng graph.dot -o graphImage.png

使用Doxygen生成文档

需要按照规定格式撰写注释,根据注释生成文档Doxygen: Documenting the code.

doxygen支持许多格式注释,下面列举三种

/*** ... text ...*//** Brief description which ends at this dot. Details follow*  here.*//*!* ... text ...*//*! \brief Brief description.*         Brief description continued.**  Detailed description starts here.*//*!... text ...
*/
注释介绍
@file文件说明
@author作者的信息
@brief用于class 或function的批注中,后面为class 或function的简易说明
@param参数介绍
@return函数传回值的说明

Doxygen 还需要一个 Doxyfile,其包含文档生成的所有参数,比如输出格式、排除的文件模式、 项目名称等。因为配置参数太多,开始配置 Doxygen 可能会让人望而生畏,但 CMake 可以自动生 成 Doxyfile。

doxygen -g # 生成doxyfile

配置doxygenfile然后运行doxygen生成.

当然更好的方式是结合cmake,首先找到doxygen程序,然后设置需要的选项Doxygen: Configuration,最后生成文旦. 可以使用add_custom_target或者doxygen_add_docs(推荐)[FindDoxygen — CMake 3.30.3 Documentation](htt在这里插入图片描述
ps://cmake.org/cmake/help/latest/module/FindDoxygen.html)

find_package(Doxygen)
if (DOXYGEN_FOUND)
set(DOXYGEN_OUTPUT_DIRECTORY"${CMAKE_CURRENT_BINARY_DIR}/docs")
set(DOXYGEN_GENERATE_HTML YES)
set(DOXYGEN_GENERATE_MAN YES)
set(DOXYGEN_MARKDOWN_SUPPORT YES)
set(DOXYGEN_AUTOLINK_SUPPORT YES)
set(DOXYGEN_HAVE_DOT YES)
set(DOXYGEN_COLLABORATION_GRAPH YES)
set(DOXYGEN_CLASS_GRAPH YES)
set(DOXYGEN_UML_LOOK YES)
set(DOXYGEN_DOT_UML_DETAILS YES)
set(DOXYGEN_DOT_WRAP_THRESHOLD 100)
set(DOXYGEN_CALL_GRAPH YES)
set(DOXYGEN_QUIET YES)
#add_custom_target(docs  ${DOXYGEN_EXECUTABLE} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/docs))
doxygen_add_docs(
docs
"${CMAKE_CURRENT_LIST_DIR}"
ALL
COMMENT "Generating documentation for myproject"
)
endif()```![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/435137d20d034a0fbb17522f6e38371d.png)

doxygen_add_docs(targetName
[filesOrDirs…]
[ALL]
[USE_STAMP_FILE]
[WORKING_DIRECTORY dir]
[COMMENT comment]
[CONFIG_FILE filename])


第一个参数 targetName 是文档目标的名称,该函数将生成一个名为 targetName 的自定义目 标。这个目标将触发 Doxygen,并在构建时使用代码创建文档。filesOrDirs包含想要从文档生成的代码的文件或目录的列表。ALL 参数用于使 CMake 的 ALL 元目标依赖于 doxygen_add_docs(…) 创建的文档目标,因此在构建 ALL 元目标时自动生成文档。WORKING_DIRECTORY默认是 `CMAKE_CURRENT_SOURCE_DIR`## 代码检查和格式化工具这部分工作其实完全可以交由IDE提供,不需要在cmake build时使用的,但为了保持兼容,这里简略写一点可以考虑使用`clang-tidy`和`clang-format`工具,在cmake文件中```cmake
cmake_minimum_required(VERSION 3.28)
project(my-project)add_executable(my-app main.c)file(GLOB_RECURSE ALL_SOURCE_FILES*.c *.h *.cpp *.hpp *.cxx *.hxx *.cc *.hh *.cppm *.ipp *.ixx)
add_custom_target(formatCOMMAND clang-format-i${ALL_SOURCE_FILES}
)

对于clang-tidy完全可以在.clang-tidy文件中设置并通过clangd进行检查Enabling clang-tidy checks in clangd - Clang Frontend / clangd - LLVM Discussion Forums

Configuration (llvm.org)

# .clangd
Diagnostics:ClangTidy:CheckOptions:readability-identifier-naming.VariableCase: CamelCase

此外还可以设置编译器编译链接选项检查内存和初始化等错误

function (add_sanitier target)
message(STATUS "Adding sanitizer to target ${target}")if (CMAKE_CXX_COMPILER_ID MATCHES "CLANG" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU")add_compile_options("-fno-omit-frame-pointer")add_link_options("-fno-omit-frame-pointer")target_compile_options(${target} PRIVATE -fsanitize=address)target_link_libraries(${target} PRIVATE -fsanitize=address)target_compile_options(${target} PRIVATE -fsanitize=undefined)target_link_libraries(${target} PRIVATE -fsanitize=undefined)elseif (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")target_compile_definitions(${target} PRIVATE /fsanitize=address)
else()message(WARNING "Sanitier is not supported for ${CMAKE_CXX_COMPILER_ID}")endif()
endfunction()

除此之外,可以使用valgrind等工具动态debug查找内存问题.

进行测试

CTest

[Testing With CMake and CTest — Mastering CMake](https://cmake.org/cmake/help/book/mastering-cmake/chapter/Testing With CMake and CTest.html)

include(CTest)
add_executable(TestInstantiator TestInstantiator.cxx)
target_link_libraries(TestInstantiator vtkCommon)
add_test(NAME TestInstantiatorCOMMAND TestInstantiator)
make test

CTest 模块通常应该只包含在项目的顶层 CMakeLists.txt 中。自从 CMake 版本 3.21 以 来,PROJECT_IS_TOP_LEVEL 可以用来测试当前的 CMakeLists.txt 是否为顶层文件。

对于项目的顶层目录和使用 ExternalProject 添加的项目顶层目录,此变量为 True。对于使用 add_subdirectory 或 FetchContent 添加的目录,该值为 False

project(CMakeBestPractice)
...
if(PROJECT_IS_TOP_LEVEL)
include(CTest)
endif()
add_test(NAME <name> COMMAND <command> [<arg>...]
[CONFIGURATIONS <config>...]
[WORKING_DIRECTORY <dir>]
[COMMAND_EXPAND_LISTS])
ctest --test-dir <build_dir>
cmake --build <build_dir> --target test # 注意这里目标就是test,而不是add_test中添加的NAME
ctest --build-and-test <source_dir> <build_dir>

可以设置ctest多个lable,然后通过过滤查看对应结果

add_test(NAME labeled_test_1 COMMAND someTest)
set_tests_properties(labeled_test PROPERTIES LABELS "example")
add_test(NAME labeled_test_2 COMMAND anotherTest)
set_tests_properties(labeled_test_2 PROPERTIES LABELS "will_fail" )
add_test(NAME labeled_test_3 COMMAND YetAnotherText)
set_tests_properties(labeled_test_3 PROPERTIES LABELS "example;will_fail")
ctest -L "example|will_fail"

-L进行过滤

ctest -I [Start,End,Stride,test#,test#,...|Test file] 

通过 Start、End 和 Stride,可以指定要执行的测试的范围。这三个数字是与显式测试数字 test# 相结合的范围,或传递包含参数的文件

处理大量测试
create_test_sourcelist (SourceListNameDriverNametest1 test2 test3EXTRA_INCLUDE include.hFUNCTION function)

注意ctest并不提供方便测试的方法,可以使用第三方库提供的REQUIRE等方法

使用include(Ctest)add_test可使得可以方便使用ctest命令进行测试

比如

cmake --build build
cd build && ctest

使用Catch2

catchorg/Catch2: A modern, C+±native, test framework for unit-tests, TDD and BDD - using C++14, C++17 and later (C++11 support is in v2.x branch, and C++03 on the Catch1.x branch) (github.com)

创建tests目录,编写cmake文件

if(ENABLE_TESTING)set(TEST_MAIN "unit_tests")set(TEST_SOURCES main.cpp)set(TEST_INCLUDES "./")add_executable(${TEST_MAIN} ${TEST_SOURCES})target_include_directories(${TEST_MAIN} PUBLIC ${TEST_INCLUDES})target_link_libraries(${TEST_MAIN} PUBLIC ${LIBRARY_NAME} Catch2::Catch2WithMain)
endif()

自动发现测试

cmake_minimum_required(VERSION 3.5)
project(baz LANGUAGES CXX VERSION 0.0.1)
find_package(Catch2 REQUIRED)
add_executable(tests test.cpp)
target_link_libraries(tests PRIVATE Catch2::Catch2)
# list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras) # use FetchContent
include(CTest)
include(Catch)
catch_discover_tests(tests)

使用GoogleTest

cmake_minimum_required(VERSION 3.14)
project(my_project)# GoogleTest requires at least C++14
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)include(FetchContent)
FetchContent_Declare(googletestURL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip
)
# For Windows: Prevent overriding the parent project's compiler/linker settings
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)enable_testing()add_executable(hello_test hello_test.cpp)
target_link_libraries(hello_test GTest::gtest_main)include(GoogleTest)
gtest_discover_tests(hello_test) # 自动发现测试

代码覆盖检查

检查测试了哪些代码并生成覆盖率报告,使用Gcov生成覆盖率信息,使用覆盖分析程序,如 Gcovr 或 LCOVn分析覆盖文件并生成报告

小结

CTest+Catch2即可

第三方库管理

使用FetchContent下载库

include(FetchContent)
FetchContent_Declare(nlohmann_json 
GIT_REPOSITORY https://github.com/nlohmann/json
GIT_TAG v3.11.2
GIT_SHALLOW TRUE)
FetchContent_Makeavailable(nlohmann_json)

需要项目是cmake项目

使用vcpkg等包管理工具下载库

包管理器与xmake介绍

{"version": 6,"configurePresets": [{"name": "my-preset","binaryDir": "${sourceDir}/build","toolchainFile": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake"}]
}
cmake_minimum_required(VERSION 3.28)
project(my-project)
find_package(ftxui REQUIRED)
add_executable(my-app main.cpp)
target_compile_features(my-app PRIVATE cxx_std_20)
target_link_libraries(my-app PRIVATE ftxui::dom ftxui::screen ftxui::component)
vcpkg install
cmake --preset my-preset
cmake --build build
./build/my-app

使用Conan

简单介绍一下使用流程

首先定义conanfile.txt和conan profile

[requires]
zlib/1.2.11[generators]
CMakeDeps
CMakeToolchain
conan profile detect --force

然后执行conan insatll会生成conan_toolchain.cmake

conan install . --output-folder=build --build=missing

再在cmake中使用

cd build
cmake .. -G "Visual Studio 15 2017" -DCMAKE_TOOLCHAIN_FILE="conan_toolchain.cmake"

借助git submodule下载库

Git - git-submodule Documentation

Git submodule | Atlassian

git submodule add https://bitbucket.org/jaredw/awesomelibrary # 添加子模块(当前新版本git也会下载对应模块)
git submodule init //初始化子模块 (根据.gitmodules更新信息)
git submodule update //更新子模块
git submodule update --init --recursive # 更新映射关系并递归下载模块

使用git submodule add之后会创建**.gitmodules**文件并写入相关信息,包括子模块path和url,其中path是安装路径,因此我们可以借助修改path,使得git update --init安装子模块时安装到3rd_party或vendor目录便于管理,比如

[submodule "glfw"]
path = third_party/glfw
url = https://github.com/glfw/glfw.git

修改.gitmodules 文件中对应模块的名字或者path,然后使用git submodule sync进行更新.

git submodule sync --recursive
git submodule update --init --recursive

此外还会在.git/config.git/modules中添加子模块信息

update的作用是根据项目的配置信息,拉取更新子模块中的代码,也可以使用git clone --recurse-submodules直接下载子模块

卸载子模块

git submodule deinit project-sub # 在.gitmodules中对应的模块名 
git rm project-sub # 删除模块目录与.git/config,.git/modules信息

总结来说,可以使用第三方管理工具,下载链接非常方便. 对于自己写的一些库或者没有cmake的项目可以使用vendor/3rd_party方式,放在一个单独目录,如果是源代码,添加源文件和头文件,生成库,cmake如下

# glad
set(glad_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/glad" CACHE STRING "")
file(GLOB source"${glad_SOURCE_DIR}/src/*.c"
)
add_library(glad ${source})
target_include_directories(gladPUBLIC "${glad_SOURCE_DIR}/include"
)
set_target_properties(glad PROPERTIES FOLDER "third_party")

如果是已经编译好的库,使用add_library(xxx SHARED IMPORTED)并设置库文件位置

add_library(glad SHARED IMPORTED)
set_target_properties(glad PROPERTIES IMPORTED_LOCATION "/path/to/glad/library")
set_target_properties(glad PROPERTIES IMPORTED_IMPLIB "/path/to/glad/library") # 针对windowsset_target_properties(glad PROPERTIES FOLDER "third_party")

IMPORTED_IMPLIB:用于指定导入库的导入库文件(import library file)。在 Windows 上,通常用于 .lib 文件。这个属性通常用于静态链接的导入库

小结

vcpkg,conan的逻辑是使用一个文件声明项目信息和依赖,然后在cmake中添加toolchainfile用于下载对应的包,而CPM和FetchContent直接在cmake中声明需要添加的包.

优先使用第三方包管理工具,因为相比FetchContent提供更多功能,如果第三方库不是cmake项目,使用git submodule方式,下载到某个文件夹编译源代码、链接库

项目打包、安装与分发

install

安装target

具体来说install(TARGETS …)会安装生成的的东西,不会安装头文件或者项目中的json、txt等读取文件.

install(TARGETS <target>... [...])

在这里插入图片描述

这里最需要注意的就是动态库不包括windows上的dll.

默认安装路径如下,安装目录在Unix上usr/local,Windows是C:/program files,前缀通过cmake --prefix CMAKE_INSTALL_PREFIX指定

在这里插入图片描述

install(TARGETS…) 如果 包 含 EXPORT 参 数, 用 于 从 给 定 的 install(…) 目 标 创 建 一 个 导 出 名 称,可以使用此导出名称导出这些目标

安装文件

安装的东西并不总是目标输出构件的一部分。它们可能是目标的运行时依赖项,例如图片、源文件、脚本和配置文件

install (
DIRECTORY include/
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
)

install(FILES…) 指令接受一个或多个文件作为参数,TYPE 和DESTINATION 用于确定指定文件的目标目录。TYPE 用于指示哪些文件将使用该文件类型的默认 路径作为安装目录
在这里插入图片描述

install(FILES "${CMAKE_CURRENT_LIST_DIR}/greeter_content"
DESTINATION "${CMAKE_INSTALL_BINDIR}")
install(PROGRAMS "${CMAKE_CURRENT_LIST_DIR}/greeter.py"
DESTINATION "${CMAKE_INSTALL_BINDIR}" RENAME chapter4_greeter)
安装目录
install(DIRECTORY dir1 dir2 dir3 TYPE LOCALSTATE)install(DIRECTORY dir2 DESTINATION ${CMAKE_INSTALL_
LOCALSTATEDIR} FILES_MATCHING PATTERN "*.hpp"
EXCLUDE PATTERN "*")

可以指定匹配文件和排除文件模式.

config-file

当别人安装了你的库,也要方便使用.为了让其他用户使用find_package找到我们的包,需要config-file.

包配置文件Config.cmake设置如下

include(GNUInstallDirs) # 便于获取安装路径变量
set(FOO_INCLUDE_DIRS ${PREFIX}/include/foo-1.2)
set(FOO_LIBRARIES ${PREFIX}/lib/foo-1.2/libfoo.a)

搜索包时,find_package(…) 会查找 /cmake 目录,所以包配置文件放在/cmake中.

# top level cmake
include(GNUInstallDirs)
set(project_INSTALL_CMAKEDIR cmake CACHE PATH
"Installation directory for config-file package cmake files") # 于设置 config-file 打包配置文件的安装目录
target_include_directories(ch4_ex05_lib PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
)
target_compile_features(ch4_ex05_lib PUBLIC cxx_std_11)

使用$<BUILD_INTERFACE>设置头文件目录,因为在安装时

install(TARGETS ex05_lib
EXPORT cex05_lib_export
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)
install (
DIRECTORY ${PROJECT_SOURCE_DIR}/include/
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)

使用install(EXPORT)得到xxConfig.cmake文件

install(EXPORT ex05_lib_export
FILE ex05_lib-config.cmake
NAMESPACE ex05_lib::
DESTINATION ${project_INSTALL_CMAKEDIR}
)

要实现对 find_package(…) 的完全支持,还需要获取xxxConfig-version.cmake 文件

include(CMakePackageConfigHelpers)
write_basic_package_version_file(
"ex05_lib-config-version.cmake"
# Package compatibility strategy. SameMajorVersion is
essentially 'semantic versioning'.
COMPATIBILITY SameMajorVersion # 与主版本号相同即可
)
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/ex05_lib-config-version.
cmake"
DESTINATION "${project_INSTALL_CMAKEDIR}"
)
cmake –S . -B ./build
cmake --build ./build
cmake --install ./build --prefix /3rdparty

安装包并使用find_package使用包

if(NOT PROJECT_IS_TOP_LEVEL)
message(FATAL_ERROR "The chapter-4, ex05_consumer project is
intended to be a standalone, top-level project. Do not
include this directory.")
endif()
find_package(ex05_lib 1 CONFIG REQUIRED)
add_executable(ex05_consumer src/main.cpp)
target_compile_features(ex05_consumer PRIVATE cxx_std_11)
target_link_libraries(ex05_consumer ex05_lib::ch4_ex05_
lib)

CPack

[Packaging With CPack — Mastering CMake](https://cmake.org/cmake/help/book/mastering-cmake/chapter/Packaging With CPack.html)

cpack包含多种生成器生成包

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

常用cpack变量

在这里插入图片描述

cmake_minimum_required(VERSION 3.21)
project(
ch4_ex06_pack
VERSION 1.0
DESCRIPTION "Chapter 4 Example 06, Packaging with CPack"
LANGUAGES CXX)
if(NOT PROJECT_IS_TOP_LEVEL)
message(FATAL_ERROR "The chapter-4, ex06_pack project is
intended to be a standalone, top-level project.
Do not include this directory.")
endif()
add_subdirectory(executable)
add_subdirectory(library)
set(CPACK_PACKAGE_VENDOR "CTT Authors") # 作者
set(CPACK_GENERATOR "DEB;RPM;TBZ2") # 包管理器
set(CPACK_THREADS 0)
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "CTT Authors")
include(CPack)

CPACK_PACKAGE_NAME 和 CPACK_PACKAGE_VERSION_* 默认从顶层项目名称和版本中获取

cmake –S . -B build/

项目配置后,生成CpackConfig.cmake 和 CpackConfigSource.cmake文件到build/CPack*中,使用cpack得到最终包

cmake --build build/
cpack --config build/CPackConfig.cmake -B build/

在这里插入图片描述

注意设置generator时,其中每个都需要符合条件. 比如nsis需要安装对应的软件,否则设置generator包括它时会直接报错

经常遗忘的指令

add_custom_target(Name [ALL] [command1 [args1...]]
[COMMAND command2 [args2...] ...]
[DEPENDS depend depend depend ... ]
[BYPRODUCTS [files...]]
[WORKING_DIRECTORY dir]
[COMMENT comment]
[JOB_POOL job_pool]
[VERBATIM] [USES_TERMINAL]
[COMMAND_EXPAND_LISTS]
[SOURCES src1 [src2...]])

add_custom_target 的核心是通过 COMMAND 选项传递的命令列表。虽然第一个命令可 以不带这个选项,但最好在 add_custom_target 中添加 COMMAND 选项。

默认情况下,定制目标只在显式请求时执行,除非指定了 ALL 选项

自定义目标总认为是过时的,因此总是运行指 定的命令,而不管是否会反复产生相同的结果。

使用 DEPENDS 关键字,可以使定制目标依赖于使用 add_custom_command 或其他目标定义的定制命令的文件和输出。

要使自定义目标依赖于另 一个目标,可以使用 add_dependencies。若使用自定义目标创建文件,可以在 BYPRODUCTS 选项下列出这些文件。

列出的文件都将使用 GENERATED 属性标记,CMake 使用该属性来确定构建是否过期,并找出需要清理的文件,但使用 add_custom_command 创建文件的任务可能更适 合。

通常,命令在当前二进制目录中执行,该目录在 CMAKE_CURRENT_BINARY_DIRECTORY 缓存变量中。若需要修改,这可以通过 WORKING_DIRECTORY 选项来更改。该选项可以是绝对 路径,也可以是相对路径 (当前二进制目录的相对路径)。

add_custom_command(TARGET <target>
PRE_BUILD | PRE_LINK | POST_BUILD
COMMAND command1 [ARGS] [args1...]
[COMMAND command2 [ARGS] [args2...] ...]
[BYPRODUCTS [files...]]
[WORKING_DIRECTORY dir]
[COMMENT comment]
[VERBATIM] [USES_TERMINAL]
[COMMAND_EXPAND_LISTS])

可以在以下时段将命令连接到构建中:

• PRE_BUILD: 在 Visual Studio 中,此命令在执行其他构建步骤之前执行。当使用其他生成器 时,会在 PRE_LINK 命令之前运行。

• PRE_LINK: 此命令将在编译源代码之后运行,在可执行文件或存档工具链接到静态库之前运行。

POS_BUILD: 这将在执行所有其他构建规则后运行该命令。 执行自定义步骤最常见的方法是使用 POST_BUILD; 其他两个选项很少使用,要么是因为支持 有限,要么是因为它们既不能影响链接,也不能影响构建。

cmake_parse_arguments(<prefix> <options> <one_value_keywords><multi_value_keywords> <args>...)cmake_parse_arguments(PARSE_ARGV <N> <prefix> <options><one_value_keywords> <multi_value_keywords>)

cmake_parse_arguments — CMake 3.30.3 Documentation

Great resoureces for learning

  1. Effective Modern CMake (github.com)
  2. franneck94/CppProjectTemplate: C++ project template with unit-tests, documentation, ci-testing and workflows. (github.com)

书籍推荐CMake Best Practices (豆瓣) (douban.com)和Professional CMake (豆瓣) (douban.com)

如有疑问,欢迎各位交流!

服务器配置
宝塔:宝塔服务器面板,一键全能部署及管理
云服务器:阿里云服务器
Vultr服务器: Vultr服务器
GPU服务器:Vast.ai
代码练习平台
CodeCrafters CodeCrafters

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1538752.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

阿里巴巴API助力电商:商品详情获取与数据驱动的完美结合

阿里巴巴API在电商领域的应用&#xff0c;特别是在商品详情获取与数据驱动的决策过程中&#xff0c;发挥着至关重要的作用。以下是对这一主题的详细阐述&#xff1a; 一、阿里巴巴API在商品详情获取中的应用 丰富的数据支持&#xff1a; 阿里巴巴提供的商品详情API&#xff0…

html详细知识

1-标题标签、水平线、字体标签 <!--1.标题标签1&#xff09;格式&#xff1a;<hn></hn> n的范围是1-6&#xff0c;依次递减2&#xff09;标题标签特点&#xff1a;a:单独占一行b:自动加粗2.水平线1&#xff09;格式&#xff1a;<hr/>2)属性&#xff1a;…

深度学习对抗海洋赤潮危机!浙大GIS实验室提出ChloroFormer模型,可提前预警海洋藻类爆发

2014 年 8 月&#xff0c;美国俄亥俄州托莱多市超 50 万名居民突然收到市政府的一则紧急通知——不得擅自饮用自来水&#xff01; 水是人类生存的基本供给&#xff0c;此通告关系重大&#xff0c;发出后也引起了不小的恐慌。究其原因&#xff0c;其实是美国伊利湖爆发了大规模…

如何使用ssm实现在线视频网站开发

TOC ssm631在线视频网站开发jsp 绪论 1.1 选题背景 当人们发现随着生产规模的不断扩大&#xff0c;人为计算方面才是一个巨大的短板&#xff0c;所以发明了各种计算设备&#xff0c;从结绳记事&#xff0c;到算筹&#xff0c;以及算盘&#xff0c;到如今的计算机&#xff0…

关于嵌入式硬件需要了解的基础知识

成长路上不孤单&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a; 【14后&#x1f60a;///C爱好者&#x1f60a;///持续分享所学&#x1f60a;///如有需要欢迎收藏转发///&#x1f60a;】 今日分享关于嵌入式硬件基础知识的相关内容&#xff…

html,css基础知识点笔记(二)

9.18&#xff08;二&#xff09; 本文主要教列表的样式设计 1&#xff09;文本溢出 效果图 文字限制一行显示几个字&#xff0c;多余打点 line-height: 1.8em; white-space: nowrap; width: 40em; overflow: hidden; text-overflow: ellipsis;em表示一个文字的大小单位&…

828华为云征文|云服务器Flexus X实例|Ubunt部署Vue项目

概要 本章将深入阐述Vue项目在Ubuntu环境下&#xff0c;实现在华为云Flexus X云服务器上的部署过程&#xff0c;此次演示以Vue.js项目为核心华为云在已经到来的828 B2B企业节上&#xff0c;为Vue等前端项目的部署与运维提供强有力的支持。 Ubuntu部署Vue项目的影响&#xff1…

VS Code远程连接虚拟机

VS Code远程连接虚拟机 1.下载vscode2.打开VS Code下载Remote-SSH插件1.修改相关信息 3.虚拟机检查或安装ssh4.检查虚拟机服务是否安装成功5.开启ssh&#xff0c;并检查是否开启成功 1.下载vscode 2.打开VS Code下载Remote-SSH插件 1.修改相关信息 2. 3.虚拟机检查或安装ssh…

封装svg图片

前言 项目中有大量svg图片&#xff0c;为了方便引入&#xff0c;所以对svg进行了处理 一、svg是什么&#xff1f; svg是可缩放矢量图形&#xff0c;是一种图片格式 二、使用步骤 1.创建icons文件夹 将icons文件夹放进src中&#xff0c;并创建一个svg文件夹和index.js&…

PMP--一模--解题--161-170

文章目录 13.干系人管理161、 [单选] 项目经理正在领导一个公司内部项目&#xff0c;该项目正处于早期阶段。该项目与一年前结束的另一个项目很相似&#xff0c;项目经理该做什么来分析涉及的干系人&#xff1f; 10.沟通管理162、 [单选] 在项目执行过程中&#xff0c;一位关键…

Docker安装 ▎Docker详细讲解 ▎数据卷挂载 ▎Nginx安装理解

前言 Docker是一种容器化技术&#xff0c;简化软件的部署和管理。文章详细解释了Docker的架构、安装步骤和常用命令&#xff0c;帮助用户快速启动和管理容器。还介绍了Docker镜像命令和数据卷挂载的实例&#xff0c;增强对持久化存储的理解&#xff0c;并涵盖了Nginx的安装方法…

『功能项目』QFrameWork框架重构OnGUI【63】

我们打开上一篇62QFrameWork背包框架的项目&#xff0c; 上文将功能实现在一个脚本中 本章要做的事情让脚本实现背包框架思想 首先按照图示创建脚本&#xff1a; 创建脚本&#xff1a;Item.cs namespace QFramework {public class Item{//道具public string Key;public string …

【网络】传输层协议TCP

TCP协议 TCP&#xff08;Transmission Control Protocol&#xff0c;传输控制协议&#xff09;是一种面向连接的、可靠的、基于字节流的传输层通信协议&#xff0c;由IETF的RFC 793定义。TCP在IP&#xff08;Internet Protocol&#xff0c;互联网协议&#xff09;网络层上提供…

最长连续子序列 - 华为OD统一考试(E卷)

OD统一考试&#xff08;E卷&#xff09; 分值&#xff1a; 100分 题解&#xff1a; Java / Python / C 2024华为OD机试&#xff08;E卷D卷C卷&#xff09;最新题库【超值优惠】Java/Python/C合集 题目描述 有N个正整数组成的一个序列。给定整数sum&#xff0c;求长度最长的连续…

海报制作哪个软件好?探索5个免费海报生成器

Hey&#xff0c;最近发现了几个超酷的海报生成器&#xff0c;它们简直是设计新手的救星&#xff01;无论是想要快速制作一张吸引眼球的促销海报&#xff0c;还是为即将到来的活动设计一张有创意的邀请函&#xff0c;这些工具都能让整个过程变得既简单又有趣。 设想一下&#x…

React框架搭建,看这一篇就够了,看完你会感谢我

传统搭建框架的方式 在2024年以前&#xff0c;我们构建框架基本上采用官方脚手架&#xff0c;但是官方脚手架其实大概率都不符合我们的项目要求&#xff0c;搭建完了以后往往需要再继续集成一些第三方的包。这时候又会碰到一些版本冲突&#xff0c;配置教程等&#xff0c;往往…

PMP--二模--解题--1-10

文章目录 4.整合管理--商业文件--商业论证&#xff08;是否值得所需投资、高管们决策的依据&#xff09;反映了&#xff1a;1、 [单选] 收到新项目的客户请求之后&#xff0c;项目经理首先应该做什么&#xff1f; 14.敏捷--角色--产品负责人PO–职责–1.创建待办列表并排序;2.确…

大数据-137 - ClickHouse 集群 表引擎详解2 - MergeTree 存储结构 一级索引 跳数索引

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

k8s的一些命令

kubectl get nodes &#xff1a;查看节点的状态 查看Pod的状态&#xff1a; kubectl get pod --all -namespacesPending,ContainerCreating,ImagePullBackOff都表明Pod没有就绪&#xff0c;Running才是就绪状态 查看Pod的具体情况&#xff1a; kubectl describe pod podnamek…

关于 Qt运行加载内存较大崩溃添加扩大运行内存 的解决方法

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/142341544 长沙红胖子Qt&#xff08;长沙创微智科&#xff09;博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV…