├── .github └── workflows │ └── build_cmake.yml ├── .gitignore ├── CMakeLists.txt ├── LICENSE.md ├── README.md ├── exampleProject ├── CMakeLists.txt ├── deps │ └── GitHash └── src │ ├── CMakeLists.txt │ └── main.cpp └── include └── GitHash.hpp /.github/workflows/build_cmake.yml: -------------------------------------------------------------------------------- 1 | name: GitHash 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | workflow_dispatch: 8 | 9 | jobs: 10 | build: 11 | runs-on: ${{ matrix.os }} 12 | strategy: 13 | # Do not stop all jobs if a single job fails. 14 | fail-fast: false 15 | matrix: 16 | os: [ubuntu-24.04, Windows-2025, macos-15] 17 | build-type: ["Debug", "Release"] 18 | 19 | steps: 20 | - name: Checkout repository. 21 | uses: actions/checkout@v4 22 | 23 | - name: Create build folder 24 | run: | 25 | mkdir ${{ runner.workspace }}/GitHash/exampleProject/build 26 | 27 | - name: Run CMake 28 | working-directory: ${{ runner.workspace }}/GitHash/exampleProject/build 29 | run: | 30 | cmake .. -DCMAKE_BUILD_TYPE=${{ matrix.build-type }} 31 | 32 | 33 | - name: Build 34 | working-directory: ${{ runner.workspace }}/GitHash/exampleProject/build 35 | run: | 36 | cmake --build . --config ${{ matrix.build-type }} --verbose 37 | 38 | - name: Run test 39 | working-directory: ${{ runner.workspace }}/GitHash/exampleProject/build 40 | run: | 41 | ctest -C ${{ matrix.build-type }} -V 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.25.3) 2 | 3 | ################################### 4 | ###### CUSTOMIZATION POINTS ####### 5 | ################################### 6 | 7 | # If you change the output file, remember to modify the GitHash.hpp header file 8 | # in sync with it! 9 | 10 | find_package(Git REQUIRED) 11 | 12 | # Commands to read each needed variable 13 | set(variablesToRead "GIT_BRANCH;GIT_SHA1;GIT_SHORTSHA1;GIT_DIRTY") 14 | set(CMD_GIT_BRANCH ${GIT_EXECUTABLE} branch --show-current) 15 | set(CMD_GIT_SHA1 ${GIT_EXECUTABLE} log -1 --format=%H) 16 | set(CMD_GIT_SHORTSHA1 ${GIT_EXECUTABLE} log -1 --format=%h) 17 | set(CMD_GIT_DIRTY ${GIT_EXECUTABLE} describe --always --dirty) # we post-process this one 18 | 19 | # Generator of the .cpp of the githash library 20 | function(genCppContents outputString) 21 | set(${outputString} 22 | "namespace GitHash { 23 | extern const char * const branch; 24 | extern const char * const sha1; 25 | extern const char * const shortSha1; 26 | extern const bool dirty; 27 | const char * const branch = \"${GIT_BRANCH}\"; 28 | const char * const sha1 = \"${GIT_SHA1}\"; 29 | const char * const shortSha1 = \"${GIT_SHORTSHA1}\"; 30 | const bool dirty = ${GIT_DIRTY}; 31 | }" 32 | PARENT_SCOPE 33 | ) 34 | endfunction() 35 | 36 | # Cache format (which, if changed, triggers a regeneration of the .cpp) 37 | function(genCache outputString) 38 | set(${outputString} "${GIT_SHA1}-${GIT_DIRTY}" PARENT_SCOPE) 39 | endfunction() 40 | 41 | ################################### 42 | ###### CONFIGURATION POINTS ####### 43 | ################################### 44 | 45 | set(GitHash_OutputDir "${PROJECT_BINARY_DIR}/GitHash" CACHE STRING "default directory for the output files") 46 | set(GitHash_CppFilename "GitHash.cpp" CACHE STRING "default name of the output cpp file") 47 | set(GitHash_CacheFilename "GitHashCache.txt" CACHE STRING "default name of the output cache file") 48 | 49 | ########################################################## 50 | ### You MUST call SetupGitHash in your CMakeLists.txt! ### 51 | ########################################################## 52 | 53 | # Set utility names for full paths outputs 54 | get_filename_component(GitHash_AbsoluteOutputDir ${GitHash_OutputDir} ABSOLUTE BASE_DIR "${PROJECT_BINARY_DIR}") 55 | set(GitHash_CppFile "${GitHash_AbsoluteOutputDir}/${GitHash_CppFilename}") 56 | set(GitHash_CacheFile "${GitHash_AbsoluteOutputDir}/${GitHash_CacheFilename}") 57 | 58 | # Directory where to actually run the Git Commands. 59 | if (NOT DEFINED GitHash_SourceDir) 60 | set(GitHash_SourceDir "${CMAKE_SOURCE_DIR}") 61 | endif() 62 | 63 | function(SetupGitHash) 64 | # Run this script when building. Note how we pass all variables we need, since we will not get them automatically 65 | # and even the CMake source dir might be wrong (if for example the build folder is outside the original path) 66 | add_custom_target(CheckGitHash COMMAND ${CMAKE_COMMAND} 67 | -DRUN_UPDATE_GIT_HASH=1 68 | -DGitHash_OutputDir="${GitHash_AbsoluteOutputDir}" 69 | -DGitHash_CppFilename="${GitHash_CppFilename}" 70 | -DGitHash_CacheFilename="${GitHash_CacheFilename}" 71 | -DGitHash_SourceDir="${GitHash_SourceDir}" 72 | -P ${_THIS_MODULE_FILE} 73 | BYPRODUCTS ${GitHash_CppFile} 74 | ) 75 | 76 | # Create library for user 77 | add_library(githash ${GitHash_CppFile}) 78 | target_include_directories(githash PUBLIC "${CMAKE_CURRENT_LIST_DIR}/include") 79 | target_sources(githash PUBLIC "${CMAKE_CURRENT_LIST_DIR}/include/GitHash.hpp") 80 | add_dependencies(githash CheckGitHash) 81 | 82 | # Output library name to the other CMakeLists.txt 83 | set(GITHASH_LIBRARIES githash CACHE STRING "Name of githash library" FORCE) 84 | 85 | UpdateGitHash() 86 | endfunction() 87 | 88 | ###################################### 89 | ### Rest of implementation details ### 90 | ###################################### 91 | 92 | # Needed for setup for older CMake versions (reads this file's path). 93 | set(_THIS_MODULE_FILE "${CMAKE_CURRENT_LIST_FILE}") 94 | 95 | # Reads cache file to a variable 96 | function(ReadGitSha1Cache sha1) 97 | if (EXISTS ${GitHash_CacheFile}) 98 | file(STRINGS ${GitHash_CacheFile} CONTENT) 99 | LIST(GET CONTENT 0 tmp) 100 | 101 | set(${sha1} ${tmp} PARENT_SCOPE) 102 | endif() 103 | endfunction() 104 | 105 | # Function called during `make` 106 | function(UpdateGitHash) 107 | # Make sure our working folder exists. 108 | if (NOT EXISTS ${GitHash_AbsoluteOutputDir}) 109 | file(MAKE_DIRECTORY ${GitHash_AbsoluteOutputDir}) 110 | endif() 111 | 112 | # Automatically set all variables. 113 | foreach(c ${variablesToRead}) 114 | execute_process( 115 | COMMAND ${CMD_${c}} 116 | WORKING_DIRECTORY "${GitHash_SourceDir}" 117 | OUTPUT_VARIABLE ${c} 118 | OUTPUT_STRIP_TRAILING_WHITESPACE) 119 | endforeach(c) 120 | 121 | # GIT_DIRTY post-processing 122 | if(${GIT_DIRTY} MATCHES ".*dirty") 123 | set(GIT_DIRTY "true") 124 | else() 125 | set(GIT_DIRTY "false") 126 | endif() 127 | 128 | # Generate new contents for the cache. 129 | genCache(newSha1Cache) 130 | 131 | # Try to read old cache 132 | ReadGitSha1Cache(oldSha1Cache) 133 | if (NOT DEFINED oldSha1Cache) 134 | set(oldSha1Cache "none") 135 | endif() 136 | 137 | # Only update the GitHash.cpp if the hash has changed. This will 138 | # prevent us from rebuilding the project more than we need to. 139 | if (NOT ${newSha1Cache} STREQUAL ${oldSha1Cache} OR NOT EXISTS ${GitHash_CppFile}) 140 | # Set the cache so we can skip rebuilding if nothing changed. 141 | file(WRITE ${GitHash_CacheFile} ${newSha1Cache}) 142 | 143 | # Get the CPP file contents with all variables correctly embedded. 144 | genCppContents(outputString) 145 | 146 | # Finally output our new library cpp file. 147 | file(WRITE ${GitHash_CppFile} "${outputString}") 148 | message(STATUS "Compiling branch ${GIT_BRANCH}, commit ${GIT_SHA1}, dirty is ${GIT_DIRTY}") 149 | endif() 150 | endfunction() 151 | 152 | # This is used to run this function from an external cmake process. 153 | if (RUN_UPDATE_GIT_HASH) 154 | UpdateGitHash() 155 | else() 156 | SetupGitHash() 157 | endif() 158 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2022 Eugenio Bargiacchi 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | GITHASH CMAKE MODULE 2 | ==================== 3 | 4 | [![GitHash](https://github.com/Svalorzen/GitHash/actions/workflows/build_cmake.yml/badge.svg)](https://github.com/Svalorzen/GitHash/actions/workflows/build_cmake.yml) 5 | 6 | This module allows you to obtain the current branch, sha1, short sha1 and dirty 7 | flag directly within C++. It creates a static library called `GitHash` behind 8 | the scenes which contains these values as symbols you can use. 9 | 10 | It additionally creates a cache of the last generated hash (and whether it was 11 | dirty) so it avoids recompilation as long as the current hash is equal to the 12 | last compiled one. 13 | 14 | Setup 15 | ----- 16 | 17 | To use GitHash in your project, you need to: 18 | - Clone the project in some sub-folder of your project, and add to your main 19 | `CMakeLists.txt` file the following line: 20 | ``` 21 | add_subdirectory()` 22 | ``` 23 | - Add the following include where you need it: 24 | ``` 25 | #include "GitHash.hpp" 26 | ``` 27 | - Use these variables in your C++ files: 28 | ``` 29 | GitHash::branch; // C-string 30 | GitHash::sha1; // C-string 31 | GitHash::shortSha1; // C-string 32 | GitHash::dirty; // boolean 33 | ``` 34 | - In CMake, link your project to the GitHash library using the 35 | `${GITHASH_LIBRARIES}` variable: 36 | ``` 37 | target_link_libraries(your_project "${GITHASH_LIBRARIES}") 38 | ``` 39 | 40 | Customization 41 | ------------- 42 | 43 | ### Output Files Names and Path ### 44 | 45 | If for any reason you want the output files to have different names and/or 46 | reside in a different folder than the default, you can configure GitHash via the 47 | following CMake variables: `GitHash_OutputDir`, `GitHash_CppFilename` and 48 | `GitHash_CacheFilename`. For example, you might want to run: 49 | 50 | ``` 51 | cmake -DGitHash_OutputDir=MyCustomFolder 52 | ``` 53 | 54 | ### Additional Fields ### 55 | 56 | It is possible to add additional fields to read (for example, to read tags). For 57 | each new field, you need to: 58 | - Modify the `GitHash.hpp` header file to expose the new field you want. 59 | - Add the new field, and the appropriate `git` command to obtain its value, in 60 | the `CMakeLists.txt` script. All modifications can be done briefly at the top 61 | of the CMake script: 62 | - Add a new CMake variable to `variablesToRead` 63 | - Add a new `CMD_` variable containing the appropriate command to run 64 | - Add a new `extern` field (both declaration and definition) inside the string 65 | in the `genCppContents` function 66 | 67 | ### Other non-Git Commands ### 68 | 69 | The GitHash mechanism can be used more generally than `git`, since you could use 70 | arbitrary commands to generate and expose arbitrary values. If you do so, you 71 | may also want to change what is put in the cache so you can trigger 72 | recompilation when your commands return different values. To do so, you just 73 | have to change the format of the cache file as returned by the `genCache` 74 | function. 75 | 76 | Credits 77 | ------- 78 | 79 | This library was developed with the help of [this blog 80 | post](https://jonathanhamberg.com/post/cmake-embedding-git-hash/). 81 | -------------------------------------------------------------------------------- /exampleProject/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.9) 2 | project(SomeApp) 3 | 4 | add_subdirectory(deps/GitHash) 5 | 6 | include(CTest) 7 | add_subdirectory(${PROJECT_SOURCE_DIR}/src) 8 | -------------------------------------------------------------------------------- /exampleProject/deps/GitHash: -------------------------------------------------------------------------------- 1 | ../../../GitHash -------------------------------------------------------------------------------- /exampleProject/src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(main main.cpp) 2 | 3 | # This automatically adds the GitHash header file dependency to your project 4 | target_link_libraries(main ${GITHASH_LIBRARIES}) 5 | 6 | add_test(NAME main_test WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} COMMAND $) 7 | -------------------------------------------------------------------------------- /exampleProject/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "GitHash.hpp" 4 | 5 | int main() { 6 | std::cout << "Branch: " << GitHash::branch << '\n'; 7 | std::cout << "Sha1: " << GitHash::sha1 << '\n'; 8 | std::cout << "Short sha1: " << GitHash::shortSha1 << '\n'; 9 | std::cout << "Dirty? " << GitHash::dirty << '\n'; 10 | std::cout << "Combined: " << GitHash::shortSha1 << (GitHash::dirty ? "-dirty" : "") << '\n'; 11 | 12 | return 0; 13 | } 14 | -------------------------------------------------------------------------------- /include/GitHash.hpp: -------------------------------------------------------------------------------- 1 | #ifndef GIT_HASH_HEADER_FILE 2 | #define GIT_HASH_HEADER_FILE 3 | 4 | namespace GitHash { 5 | extern const char * const branch; 6 | extern const char * const sha1; 7 | extern const char * const shortSha1; 8 | extern const bool dirty; 9 | } 10 | 11 | #endif 12 | --------------------------------------------------------------------------------