build: generate a BuildInfo translation unit with version, commit, branch, dirty flag and build kind

cmake/BuildInfo.cmake runs on every build and rewrites the generated
BuildInfo.cpp only when something changed, so a new commit recompiles one
file and relinks. The build kind comes from DLU_BUILD_KIND
(release/ci/local) or CI=true, defaulting to local.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Aaron Kimbrell
2026-09-29 15:43:08 -05:00
parent e74a7ea701
commit 97a6e27d56
4 changed files with 156 additions and 1 deletions

13
dCommon/BuildInfo.cpp.in Normal file
View File

@@ -0,0 +1,13 @@
// Generated by cmake/BuildInfo.cmake on every build. Do not edit.
#include "BuildInfo.h"
namespace BuildInfo {
const uint8_t versionMajor = @VERSION_MAJOR@;
const uint8_t versionMinor = @VERSION_MINOR@;
const uint8_t versionPatch = @VERSION_PATCH@;
const eBuildKind buildKind = eBuildKind::@BUILD_KIND@;
const bool dirty = @BUILD_DIRTY@;
const std::string_view commit = "@BUILD_COMMIT@";
const std::string_view branch = "@BUILD_BRANCH@";
const std::string_view buildString = "@BUILD_STRING@";
}

51
dCommon/BuildInfo.h Normal file
View File

@@ -0,0 +1,51 @@
#ifndef BUILDINFO_H
#define BUILDINFO_H
#include <cstdint>
#include <string_view>
// What this binary was built from. The values live in BuildInfo.cpp, which cmake/BuildInfo.cmake regenerates on
// every build (only rewriting it when something changed), so a new commit recompiles that one file and relinks.
namespace BuildInfo {
// Stored in 2 bits of Flags().
enum class eBuildKind : uint8_t {
UNKNOWN = 0,
LOCAL = 1,
CI = 2,
RELEASE = 3,
};
constexpr uint8_t BUILD_KIND_MASK = 0b011;
constexpr uint8_t DIRTY_FLAG = 0b100;
extern const uint8_t versionMajor;
extern const uint8_t versionMinor;
extern const uint8_t versionPatch;
extern const eBuildKind buildKind;
extern const bool dirty; // The build had uncommitted changes to tracked files
extern const std::string_view commit; // Full hex commit hash, empty without git
extern const std::string_view branch; // Empty when detached or without git
extern const std::string_view buildString; // e.g. 3.0.0-experimental+g1a2b3c4d-dirty
// The build kind in bits 0-1 and the dirty flag in bit 2.
inline uint8_t Flags() {
return (static_cast<uint8_t>(buildKind) & BUILD_KIND_MASK) | (dirty ? DIRTY_FLAG : 0);
}
// The first 32 bits (8 hex digits) of the commit hash, 0 without git.
inline uint32_t CommitPrefix() {
uint32_t value = 0;
for (size_t i = 0; i < 8 && i < commit.size(); i++) {
const char c = commit[i];
uint32_t digit = 0;
if (c >= '0' && c <= '9') digit = c - '0';
else if (c >= 'a' && c <= 'f') digit = c - 'a' + 10;
else if (c >= 'A' && c <= 'F') digit = c - 'A' + 10;
else return 0;
value = (value << 4) | digit;
}
return commit.size() >= 8 ? value : 0;
}
}
#endif // BUILDINFO_H

View File

@@ -88,7 +88,27 @@ else ()
)
endif ()
# Build identifier (BuildInfo.h). The generator runs on every build but only rewrites BuildInfo.cpp when the
# commit, branch, dirty state or build kind changed, so only that one file recompiles.
set(BUILDINFO_SOURCE "${CMAKE_CURRENT_BINARY_DIR}/generated/BuildInfo.cpp")
add_custom_target(BuildInfoGenerate
COMMAND "${CMAKE_COMMAND}"
"-DSOURCE_DIR=${PROJECT_SOURCE_DIR}"
"-DTEMPLATE=${CMAKE_CURRENT_SOURCE_DIR}/BuildInfo.cpp.in"
"-DOUTPUT=${BUILDINFO_SOURCE}"
"-DVERSION_MAJOR=${PROJECT_VERSION_MAJOR}"
"-DVERSION_MINOR=${PROJECT_VERSION_MINOR}"
"-DVERSION_PATCH=${PROJECT_VERSION_PATCH}"
-P "${PROJECT_SOURCE_DIR}/cmake/BuildInfo.cmake"
BYPRODUCTS "${BUILDINFO_SOURCE}"
COMMENT "Checking build identifier"
VERBATIM
)
add_library(dBuildInfo STATIC "${BUILDINFO_SOURCE}")
target_include_directories(dBuildInfo PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}")
add_dependencies(dBuildInfo BuildInfoGenerate)
target_link_libraries(dCommon
PUBLIC glm::glm
PUBLIC glm::glm dBuildInfo
PRIVATE ZLIB::ZLIB bcrypt tinyxml2
INTERFACE dDatabase)