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

news2024/9/20 9:24:01

个人博客: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(format
    COMMAND 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 TestInstantiator
         COMMAND 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 (SourceListName
                        DriverName
                        test1 test2 test3
                        EXTRA_INCLUDE include.h
                        FUNCTION 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(
  googletest
  URL 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(glad
  PUBLIC "${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") # 针对windows

set_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.coloradmin.cn/o/2145725.html

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

相关文章

【Binlog实战】:基于Spring监听Binlog日志

【Binlog实战】&#xff1a;基于Spring监听Binlog日志 binlog的三种模式 MySQL 的二进制日志&#xff08;binlog&#xff09;有三种不同的格式&#xff0c;通常被称为 binlog 模式。这三种模式分别是 Statement 模式、Row 模式和Mixed 模式。 Statement 模式&#xff1a; 在 …

Redis存储原理

前言 我们从redis服务谈起&#xff0c;redis是单reactor&#xff0c;命令在redis-server线程处理。还有若干读写IO线程负责IO操作&#xff08;redis6.0之后&#xff0c;Redis之pipeline与事务&#xff09;。此外还有一个内存池线程负责内存管理、一个后台文件线程负责大文件的关…

信息安全数学基础(17)Wilson定理

前言 Wilson定理&#xff08;Wilsons Theorem&#xff09;是数论中的一个基本定理&#xff0c;它揭示了素数与其阶乘之间的一个重要关系。 一、表述 对于任意素数p&#xff0c;有(p−1)!≡−1(modp)&#xff0c;其中(p−1)!表示p−1的阶乘&#xff0c;即123⋯(p−1)。 这个定理…

C++STL~~priority_queue

文章目录 容器适配器一、priority_queue的概念二、priority_queue的使用三、priority_queue的练习四、仿函数五、总结 容器适配器 什么是适配器 适配器是一种设计模式(设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结)&#xff0c;该种模式是将…

python画图|3D bar进阶探索

前述学习过程只能怪&#xff0c;已经探究了3D直方图的基础教程&#xff0c;详见下述链接&#xff1a; python画图|3D直方图基础教程-CSDN博客 实际上&#xff0c;基础文章直接进入了堆叠教程&#xff0c;相对来说基础的程度不够&#xff0c;因此有必要再次探索。 【1】官网教…

spug项目实现代码本地启动步骤

一、spug代码仓库地址: spug: 开源运维平台&#xff1a;面向中小型企业设计的无 Agent的自动化运维平台&#xff0c;整合了主机管理、主机批量执行、主机在线终端、文件在线上传下载、应用发布、任务计划、配置中心、监控、报警等一系列功能。 - Gitee.com 注意&#xff1a;如…

【制作100个unity游戏之32】unity开发属于自己的一个2d/3d桌面宠物,可以实时计算已经获取的工资

最终效果 文章目录 最终效果一、实现Windows消息弹窗二、将窗口扩展到工作区三、穿透能点击到其他区域四、模型交互1、我们可以新增ObjectDrag 代码控制人物拖拖动2、实现模型交互五、最终代码六、其他七、游玩地址使用Live2D实现桌宠七、源码参考完结一、实现Windows消息弹窗 …

jetson nano开发板安装todesk远程乌班图aarch版本

打开todesk官网&#xff0c;并打开Linux系统的一栏&#xff1a;ToDesk远程桌面软件-免费安全流畅的远程连接电脑手机 我这里选择deb安装包&#xff0c;然后安装依赖 sudo apt-get install libappindicator3-1 由于我下载上传到了根目录&#xff0c;所以直接在终端运行安装命令…

Golang | Leetcode Golang题解之第415题字符串相加

题目&#xff1a; 题解&#xff1a; func addStrings(num1 string, num2 string) string {add : 0ans : ""for i, j : len(num1) - 1, len(num2) - 1; i > 0 || j > 0 || add ! 0; i, j i - 1, j - 1 {var x, y intif i > 0 {x int(num1[i] - 0)}if j &g…

nonlocal本质讲解(前篇)——从滤波到Nonlocal均值滤波

线性滤波 → \rightarrow →高斯滤波 → \rightarrow →高斯滤波 → \rightarrow →双边滤波 → \rightarrow →Nonlocal均值滤波 平均 高斯 双边 Nonlocal 目录 线性滤波高斯滤波双边滤波Nonlocal均值滤波 滤波最初是频域的概念&#xff0c;由于频域乘积对应空域卷积&am…

Qwen2-VL环境搭建推理测试

引子 2024年8月30号&#xff0c;阿里推出Qwen2-VL&#xff0c;开源了2B/7B模型&#xff0c;处理任意分辨率图像无需分割成块。之前写了一篇Qwen-VL的博客&#xff0c;感兴趣的童鞋请移步&#xff08;Qwen-VL环境搭建&推理测试-CSDN博客&#xff09;&#xff0c;这么小的模…

Spring Cloud Alibaba-(1)搭建项目环境

1.Spring Cloud Alibaba&#xff08;官网&#xff1a;https://sca.aliyun.com/&#xff09; Spring Cloud Alibaba 是阿里巴巴结合自身丰富的微服务实践而推出的微服务开发的一站式解决方案&#xff0c;是 Spring Cloud 第二代实现的主要组成部分。吸收了 Spring Cloud Netflix…

一键文本提示实现图像对象高质量剪切与透明背景生成

按照提示词裁剪 按照边框裁剪 要实现您描述的功能,即通过一个文本提示就能自动从图片中切割出指定的对象并生成一个带有透明背景的新图像,这需要一个结合了先进的计算机视觉技术和自然语言处理能力的系统。这样的系统可以理解输入的文本指令,并将其转化为对图像内容的精确分…

深入探究HTTP网络协议栈:互联网通信的基石

在我们日常使用互联网的过程中&#xff0c;HTTP&#xff08;HyperText Transfer Protocol&#xff0c;超文本传输协议&#xff09;扮演着至关重要的角色。无论是浏览网页、下载文件&#xff0c;还是进行在线购物&#xff0c;HTTP协议都在背后默默地支持着这些操作。今天&#x…

【隐私计算篇】中国剩余定理解释以及Paillier解密加速应用

1. 背景介绍 本篇主要关注中国剩余定理的原理以及在paillier同态加密系统中的应用。在很多工作中&#xff0c;都可以看到中国剩余定理的影子&#xff0c;特别是同态加密提升计算效率的优化工作中&#xff0c;将paillier与中国剩余定理进行结合&#xff0c;能够实现在加密状态下…

[产品管理-25]:NPDP新产品开发 - 23 - 产品创新中的市场调研 - 定量市场调研的常见工具

目录 前言&#xff1a; 一、问卷调查 二、消费者测评组 三、概念测试与概念分类 概念测试 概念分类 四、感官检验 1、定义与特点 2、基本方法 3、应用领域 4、优势与局限性 五、眼动追踪 1、技术原理 2、应用领域 3、技术优势 4、市场现状与发展趋势 5、结论 …

彩蛋岛 销冠大模型案例

彩蛋岛 销冠大模型案例 任务&#xff1a; https://kkgithub.com/InternLM/Tutorial/tree/camp3/docs/EasterEgg/StreamerSales 视频 https://www.bilibili.com/video/BV1f1421b7Du/?vd_source4ffecd6d839338c9390829e56a43ca8d 项目git地址&#xff1a; https://kkgithu…

设计模式-结构型-11-代理模式

文章目录 1. 基本介绍2. 静态代理2.1 基本介绍UML 类图 2.2 应用实例定义接口目标对象代理对象调用代理 2.3 静态代理优缺点 3. 动态代理3.1 基本介绍3.2 JDK 中生成代理对象的 API参数说明UML类图 3.3 应用实例定义接口目标对象代理工厂调用代理 4. Cglib 代理4.1 基本介绍4.2…

2011-2022年数字金融与企业ESG表现:效应、机制与“漂绿”检验(内含原始数据+处理代码)

2011-2022年数字金融与企业ESG表现&#xff1a;效应、机制与“漂绿”检验&#xff08;内含原始数据处理代码&#xff09; 1、时间&#xff1a;2011-2022年 2、来源&#xff1a;上市公司年报、华证ESG、北大数字普惠金融 3、指标&#xff1a;年份、股票代码、股票简称、行业名…

使用Maven创建一个Java项目并在repository中使用

JDK环境&#xff1a;1.8.0_371 Maven环境 &#xff1a;Apache Maven 3.6.3 配置完成jdk和mvn后&#xff0c;进入到指定文件夹下执行如下语句&#xff1a; mvn archetype:generate -DgroupIdtop.chengrongyu -DartifactIdCyberSpace -DarchetypeArtifactIdmaven-archetype-quic…