当前位置: 首页 > news >正文

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

相关文章:

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

个人博客:Sekyoro的博客小屋 个人网站:Proanimer的个人网站 最近常常使用cmake构建c项目有感,从创建项目到打包发布总结一下需要注意的事情. 项目组织方式 具体的项目组织方式因人而异,这里推荐一种,在src目录中创建模块目录,再在include目录中常见对应的同名目录包含头文件,…...

Linux自主学习篇

用户及权限管理 sudo 是 "superuser do" 的缩写&#xff0c;是一个在类 Unix 操作系统&#xff08;如 Linux 和 macOS&#xff09;中使用的命令。它允许普通用户以超级用户&#xff08;root 用户&#xff09;的身份执行命令&#xff0c;从而获得更高的权限。 useradd…...

MQ入门(4)

Erlang&#xff1a;面向高并发的 单机的吞吐量就是并发性&#xff1a;Rabbitmq是10w左右&#xff08;现实项目中已经足够用了&#xff09;&#xff0c;RocketMQ是10w到20w&#xff0c;Kafka是100w左右。 公司里的并发&#xff08;QPS&#xff09; 大部分的公司每天的QPS大概…...

linux下共享内存的3种使用方式

进程是资源封装的单位&#xff0c;内存就是进程所封装的资源的一种。一般情况下&#xff0c;进程间的内存是相互隔离的&#xff0c;也就是说一个进程不能访问另一个进程的内存。如果一个进程想要访问另一个进程的内存&#xff0c;那么必须要进过内核这个桥梁&#xff0c;这就是…...

伊丽莎白·赫莉为杂志拍摄一组素颜写真,庆祝自己荣膺全球最性感女人第一名

语录&#xff1a;女性应该做任何她们想做的事&#xff0c;批评她们的人都见鬼去吧。 伊丽莎白赫莉为《Maxim》杂志拍摄一组素颜写真&#xff0c;庆祝自己荣膺全球最性感女人第一名 伊丽莎白赫莉 (Elizabeth Hurley) 实在是太惊艳了&#xff0c;如今&#xff0c;《马克西姆》杂…...

Qt快捷键说明与用法

编辑与查找 CtrlF&#xff1a;在当前编辑窗口中查找关键字。支持大小写相关、全词匹配、正则表达式匹配等选项&#xff0c;并且查找之后还可以进行替换操作。 CtrlShiftF&#xff1a;进行全局查找&#xff0c;不局限于当前文件。注意&#xff0c;在某些情况下&#xff0c;这个…...

技术周刊 | TS 5.6、Chrome DevTools 性能面板上新、Vite 6 Beta、Fastify v5、HTTP 新方法 Query

增长能力&#xff0c;就是持续做出正确决定的能力。 大家好&#xff0c;我是童欧巴&#xff0c;欢迎来到第 128 期技术周刊。 资讯 TypeScript 5.6 TypeScript 5.6 如期发布。 Chrome DevTools 发布全新性能功能 Chrome DevTools 的性能面板上新测试&#xff0c;包括 Core…...

使用Mockito进行单元测试

1、单元测试介绍 Mockito和Junit是用于单元测试的常用框架。单元测试即&#xff1a;从最小的可测试单元&#xff08;如函数、方法或类&#xff09;开始&#xff0c;确保每个单元都能按预期工作。单元测试是白盒测试的核心部分&#xff0c;它有助于发现单元内部的错误。 单元测试…...

CSS 布局三大样式简单学习

目录 1. css 浮动 1.1 效果1 1.2 效果2 1.3 效果3 1.4 效果4 2. css 定位 2.1 absolute 2.2 relative 2.3 fixed 3. css 盒子模型 3.1 效果1 3.2 效果2 3.3 效果3 3.4 效果4 1. css 浮动 1.1 效果1 1.2 效果2 1.3 效果3 1.4 效果4 2. css 定位 2.1 absolute 2.2 …...

集成运放UA741的原理与应用的探索

我们发现TI公司提供了UA741的内部电路&#xff0c;此电路包括22个晶体管&#xff0c;11个电阻&#xff0c;1个二极管&#xff0c;1个电容。 1UA741设计需求 1.1有短路保护 UA741的短路保护功能‌是指当输出端发生短路时&#xff0c;该器件能够自动保护自身&#xff0c;防止因…...

LeetCode337. 打家劫舍III

// 很好的一道题目&#xff0c;既考察递归又考察动归 // 这个版本超时了&#xff0c;原因是暴搜 // 很显然这里使用的是前序&#xff0c;那是不是应该考虑后序&#xff1f;public int rob(TreeNode root) {if (root null) {return 0;}if (root.left null && root.rig…...

python基础(二) 包和import

包的创建 文件创建命令 在 Django 中&#xff0c;python manage.py startapp first_app 这一行命令的作用是创建一个新的应用&#xff08;app&#xff09;&#xff0c;名为 first_app。在 Django 项目中&#xff0c;"app" 是实现某些功能模块的单独部分&#xff0c…...

选址模型 | 基于混沌模拟退火粒子群优化算法的电动汽车充电站选址与定容(Matlab)

目录 效果一览基本介绍程序设计参考资料 效果一览 基本介绍 基于混沌模拟退火粒子群优化算法的电动汽车充电站选址与定容&#xff08;Matlab&#xff09; 问题建模&#xff1a;首先&#xff0c;需要将电动汽车充电站选址与定容问题进行数学建模&#xff0c;确定目标函数和约束…...

WPF入门教学十 资源与字典

在WPF&#xff08;Windows Presentation Foundation&#xff09;中&#xff0c;资源与字典是用于管理和重用UI元素的重要机制。它们不仅有助于保持XAML代码的整洁&#xff0c;还能提升应用程序的性能和可维护性。以下是关于WPF资源与字典的详细说明&#xff1a; 静态资源与动态…...

Ubuntu20.04配置NVIDIA+CUDA12.2+CUDNN【附所有下载资源】【亲测有效】【非常详细】

Ubuntu20.04 安装 cudatookit 12.2 cudnn 安装_ubuntu安装cuda toolkit-CSDN博客【最新】cuDNN在CUDA11.7Ubuntu20.04下的安装及卸载_cuda11.7对应的cudnn-CSDN博客...

Golang | Leetcode Golang题解之第424题替换后的最长重复字符

题目&#xff1a; 题解&#xff1a; func characterReplacement(s string, k int) int {cnt : [26]int{}maxCnt, left : 0, 0for right, ch : range s {cnt[ch-A]maxCnt max(maxCnt, cnt[ch-A])if right-left1-maxCnt > k {cnt[s[left]-A]--left}}return len(s) - left }f…...

软考高级:系统安全 -区块链特点:去中心化、开放性、自治性、安全性、匿名性

讲解 生活化例子 想象一下&#xff0c;你和朋友们玩一个共享账本的游戏。每个人都可以在账本上记账&#xff0c;没人可以单独改动账本&#xff0c;大家都可以随时查看账本内容&#xff0c;也不用再信任某个单独的人来管理账本。这就类似于区块链的工作原理。 概念讲解 去中…...

Pandas 数据分析入门详解

今日内容大纲介绍 DataFrame读写文件 DataFrame加载部分数据 DataFrame分组聚合计算 DataFrame常用排序方式 1.DataFrame-保存数据到文件 格式 df对象.to_数据格式(路径) ​ # 例如: df.to_csv(data/abc.csv) 代码演示 如要保存的对象是计算的中间结果&#xff0c;或者以…...

【网络】高级IO——epoll版本TCP服务器初阶

目录 前言 一&#xff0c;epoll的三个系统调用接口 1.1.epoll_create函数 1.1.1.epoll_create函数干了什么 1.2. epoll_ctl函数 1.2.1.epoll_ctl函数函数干了什么 1.3.epoll_wait函数 1.3.1.epoll_wait到底干了什么 1.4.epoll的工作过程中内核在干什么 二&#xff0c;…...

xml中的转义字符

文章目录 xml中的转义字符 xml中的转义字符 &amp;对应的字符是& <对应的字符是< >对应的字符是> &quot;对应的字符是" &apos;对应的字符是转义的实体引用虽然简单易用&#xff0c;但是需要记忆&#xff0c;而且如果字符串中包含大量的特殊字…...

Webpack:现代前端项目的强大打包工具

在现代前端开发中&#xff0c;随着应用的复杂性不断提高&#xff0c;我们需要一种工具来管理项目的依赖、优化代码结构并打包资源文件。Webpack 就是这样一个强大的打包工具&#xff0c;它为前端开发者提供了灵活、强大且可扩展的功能。本文将介绍 Webpack 的基本概念、安装与使…...

以root用户登陆ubuntu的桌面环境

去我的个人博客观看&#xff0c;观感更佳哦&#xff0c;&#x1f619;&#x1f619; 前言 在学习Linux的时候&#xff0c;经常都需要使用sudo权限来对配置文件进行修改&#xff0c;常用的方法就是用vim编辑器在命令行界面进行修改&#xff0c;比如sudo vim /etc/profile&#…...

《系统架构设计师教程(第2版)》第17章-通信系统架构设计理论与实践-04-其他网络架构(存储网络架构、软件定义网络架构)

文章目录 1. 存储网络架构1.1 网络连接存储 (NAS)1.2 存储区域网络&#xff08;SAN&#xff09; 2. 软件定义网络架构2.1 软件定义网络&#xff08;SDN&#xff09;2.2 SDN架构2.3 相关技术2.3.1 控制平面技术2.3.2 数据平面技术1&#xff09; 硬件处理方式4&#xff09; 软件处…...

大话Python|基础语法(上)

一、单行注释 以下代码输出一个Hello World&#xff01;字符串 在Python代码中&#xff0c;注释会自动被Python解析器忽略 print(Hello World) 二、多行注释 在Python代码中&#xff0c;注释一共有两种形式&#xff1b; 1、单行注释&#xff1a;注释的内容只有一行 2、多行…...

crosscrossover24支持的游戏有那些

CrossOver刚刚更新了24版本&#xff0c;支持《地平线零之曙光》、《以撒的结合&#xff1a;重生》等游戏。一起来看看它有哪些更新吧&#xff01;之前买过23版的用户可以在1年之内免费升级哦&#xff0c;点击这里查看升级教程。 一、功能优化 - 更新 Wine 至最新的稳定版 Wine …...

如何免费调用GPT API进行自然语言处理

在当今这个信息爆炸的时代&#xff0c;自然语言处理&#xff08;NLP&#xff09;技术正逐步渗透到我们生活的各个方面&#xff0c;从智能客服到内容创作&#xff0c;无一不彰显着其强大的应用价值。而GPT&#xff08;Generative Pre-trained Transformer&#xff09;作为NLP领域…...

vue无感刷新Token并重新请求

vue 拦截器拦截401重新请求Token 无感刷新Token 之后重新请求报401的接口 instance.interceptors.response.use(async (response) > {let { data } response;if (data.code 401 || data.code 403) {return await handleExpiredToken(response.config);}if (data.code ! …...

C++和OpenGL实现3D游戏编程【连载10】——纹理的半透明显示

1、本节实现的内容 上一节课我们讲到了图片的镂空显示,它能在显示图片时去除指定颜色的背景,那么这节课我们来说一下图片的半透明显示效果,半透明效果能给画面带来更高质量的提升,使图片显示的更自然,产生更真实的效果。下面是一个气泡向上漂浮的效果。 气泡效果 2、非纹…...

50页PPT麦肯锡精益运营转型五步法

读者朋友大家好&#xff0c;最近有会员朋友咨询晓雯&#xff0c;需要《 50页PPT麦肯锡精益运营转型五步法》资料&#xff0c;欢迎大家下载学习。 知识星球已上传的资料链接&#xff1a; 企业架构 企业架构 (EA) 设计咨询项目-企业架构治理(EAM)现状诊断 105页PPTHW企业架构设…...

Fyne ( go跨平台GUI )中文文档-小部件 (五)

本文档注意参考官网(developer.fyne.io/) 编写, 只保留基本用法 go代码展示为Go 1.16 及更高版本, ide为goland2021.2 这是一个系列文章&#xff1a; Fyne ( go跨平台GUI )中文文档-入门(一)-CSDN博客 Fyne ( go跨平台GUI )中文文档-Fyne总览(二)-CSDN博客 Fyne ( go跨平台GUI…...