ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
# 6.7 构建时记录Git Hash值 **NOTE**:*此示例代码可以在 https://github.com/dev-cafe/cmake-cookbook/tree/v1.0/chapter-6/recipe-07 中找到,其中包含一个C++例子。该示例在CMake 3.5版(或更高版本)中是有效的,并且已经在GNU/Linux、macOS和Windows上进行过测试。* 前面的示例中,在配置时记录了代码存储库(Git Hash)的状态。然而,前一种方法有一个令人不满意的地方,如果在配置代码之后更改分支或提交更改,则源代码中包含的版本记录可能指向错误的Git Hash值。在这个示例中,我们将演示如何在构建时记录Git Hash(或者,执行其他操作),以确保每次构建代码时都运行这些操作,因为我们可能只配置一次,但是会构建多次。 ## 准备工作 我们将使用与之前示例相同的`version.hpp.in`,只会对`example.cpp`文件进行修改,以确保它打印构建时Git提交Hash值: ```c++ #include "version.hpp" #include <iostream> int main() { std::cout << "This code has been built from version " << GIT_HASH << std::endl; } ``` ## 具体实施 将Git信息保存到`version.hpp`头文件在构建时需要进行以下操作: 1. 把前一个示例的`CMakeLists.txt`中的大部分代码移到一个单独的文件中,并将该文件命名为`git-hash.cmake`: ```cmake # in case Git is not available, we default to "unknown" set(GIT_HASH "unknown") # find Git and if available set GIT_HASH variable find_package(Git QUIET) if(GIT_FOUND) execute_process( COMMAND ${GIT_EXECUTABLE} log -1 --pretty=format:%h OUTPUT_VARIABLE GIT_HASH OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET ) endif() message(STATUS "Git hash is ${GIT_HASH}") # generate file version.hpp based on version.hpp.in configure_file( ${CMAKE_CURRENT_LIST_DIR}/version.hpp.in ${TARGET_DIR}/generated/version.hpp @ONLY ) ``` 2. `CMakeLists.txt`熟悉的部分: ```cmake # set minimum cmake version cmake_minimum_required(VERSION 3.5 FATAL_ERROR) # project name and language project(recipe-07 LANGUAGES CXX) # require C++11 set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_CXX_STANDARD_REQUIRED ON) # example code add_executable(example example.cpp) # needs to find the generated header file target_include_directories(example PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/generated ) ``` 3. `CMakeLists.txt`的剩余部分,记录了每次编译代码时的`Git Hash`: ```cmake add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/generated/version.hpp ALL COMMAND ${CMAKE_COMMAND} -D TARGET_DIR=${CMAKE_CURRENT_BINARY_DIR} -P ${CMAKE_CURRENT_SOURCE_DIR}/git-hash.cmake WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) # rebuild version.hpp every time add_custom_target( get_git_hash ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/generated/version.hpp ) # version.hpp has to be generated # before we start building example add_dependencies(example get_git_hash) ``` ## 工作原理 示例中,在构建时执行CMake代码。为此,定义了一个自定义命令: ```cmake add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/generated/version.hpp ALL COMMAND ${CMAKE_COMMAND} -D TARGET_DIR=${CMAKE_CURRENT_BINARY_DIR} -P ${CMAKE_CURRENT_SOURCE_DIR}/git-hash.cmake WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) ``` 我们还定义了一个目标: ```cmake add_custom_target( get_git_hash ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/generated/version.hpp ) ``` 自定义命令调用CMake来执行`git-hash.cmake`脚本。这里使用CLI的`-P`开关,通过传入脚本的位置实现的。请注意,可以像往常一样使用CLI开关`-D`传递选项。`git-hash.cmake`脚本生成 ` ${TARGET_DIR}/generated/version.hpp `。自定义目标被添加到`ALL`目标中,并且依赖于自定义命令的输出。换句话说,当构建默认目标时,我们确保自定义命令已经运行。此外,自定义命令将`ALL`目标作为输出。这样,我们就能确保每次都会生成` version.hpp`了。 ## 更多信息 我们可以改进配置,以便在记录的`Git Hash`外,包含其他的信息。检测构建环境是否“污染”(即是否包含未提交的更改和未跟踪的文件),或者“干净”。可以使用`git describe --abbrev=7 --long --always --dirty --tags `检测这些信息。根据可重现性,甚至可以将Git的状态,完整输出记录到头文件中,我们将这些功能作为课后习题留给读者自己完成。