mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-02 02:43:44 +00:00
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:
71
cmake/BuildInfo.cmake
Normal file
71
cmake/BuildInfo.cmake
Normal file
@@ -0,0 +1,71 @@
|
||||
# Writes BuildInfo.cpp (the data behind dCommon/BuildInfo.h) at build time.
|
||||
# Run as a script:
|
||||
# cmake -DSOURCE_DIR=... -DTEMPLATE=... -DOUTPUT=... -DVERSION_MAJOR=... -DVERSION_MINOR=... -DVERSION_PATCH=... -P BuildInfo.cmake
|
||||
# The output is only rewritten when its contents change, so an unchanged tree recompiles nothing.
|
||||
#
|
||||
# Build kind comes from the environment of the build: DLU_BUILD_KIND=release|ci|local wins, otherwise CI=true
|
||||
# (set by GitHub Actions and most CI systems) means ci, otherwise local.
|
||||
|
||||
find_package(Git QUIET)
|
||||
|
||||
set(BUILD_COMMIT "")
|
||||
set(BUILD_BRANCH "")
|
||||
set(BUILD_DIRTY "false")
|
||||
|
||||
if(GIT_FOUND)
|
||||
execute_process(COMMAND "${GIT_EXECUTABLE}" -C "${SOURCE_DIR}" rev-parse HEAD
|
||||
OUTPUT_VARIABLE BUILD_COMMIT OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET RESULT_VARIABLE result)
|
||||
if(NOT result EQUAL 0)
|
||||
set(BUILD_COMMIT "")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(BUILD_COMMIT)
|
||||
execute_process(COMMAND "${GIT_EXECUTABLE}" -C "${SOURCE_DIR}" rev-parse --abbrev-ref HEAD
|
||||
OUTPUT_VARIABLE BUILD_BRANCH OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET)
|
||||
if(BUILD_BRANCH STREQUAL "HEAD")
|
||||
# Detached HEAD (CI checkouts): use the branch the CI names, if any.
|
||||
if(NOT "$ENV{GITHUB_HEAD_REF}" STREQUAL "")
|
||||
set(BUILD_BRANCH "$ENV{GITHUB_HEAD_REF}")
|
||||
else()
|
||||
set(BUILD_BRANCH "$ENV{GITHUB_REF_NAME}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
execute_process(COMMAND "${GIT_EXECUTABLE}" -C "${SOURCE_DIR}" update-index -q --refresh OUTPUT_QUIET ERROR_QUIET)
|
||||
execute_process(COMMAND "${GIT_EXECUTABLE}" -C "${SOURCE_DIR}" diff-index --quiet HEAD --
|
||||
RESULT_VARIABLE result OUTPUT_QUIET ERROR_QUIET)
|
||||
if(NOT result EQUAL 0)
|
||||
set(BUILD_DIRTY "true")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
string(TOLOWER "$ENV{DLU_BUILD_KIND}" kind)
|
||||
string(TOLOWER "$ENV{CI}" ci)
|
||||
if(kind STREQUAL "release")
|
||||
set(BUILD_KIND "RELEASE")
|
||||
elseif(kind STREQUAL "ci" OR (kind STREQUAL "" AND (ci STREQUAL "true" OR ci STREQUAL "1")))
|
||||
set(BUILD_KIND "CI")
|
||||
else()
|
||||
set(BUILD_KIND "LOCAL")
|
||||
endif()
|
||||
|
||||
# Readable identifier, e.g. 3.0.0-experimental+g1a2b3c4d-dirty. Release builds leave out the branch.
|
||||
set(BUILD_STRING "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}")
|
||||
if(BUILD_BRANCH AND NOT BUILD_KIND STREQUAL "RELEASE")
|
||||
string(REGEX REPLACE ".*/" "" branchName "${BUILD_BRANCH}")
|
||||
string(REGEX REPLACE "[^0-9A-Za-z.-]" "-" branchName "${branchName}")
|
||||
string(APPEND BUILD_STRING "-${branchName}")
|
||||
endif()
|
||||
if(BUILD_COMMIT)
|
||||
string(SUBSTRING "${BUILD_COMMIT}" 0 8 shortCommit)
|
||||
string(APPEND BUILD_STRING "+g${shortCommit}")
|
||||
if(BUILD_DIRTY)
|
||||
string(APPEND BUILD_STRING "-dirty")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Git allows " in branch names; keep the C++ string literal valid.
|
||||
string(REPLACE "\"" "\\\"" BUILD_BRANCH "${BUILD_BRANCH}")
|
||||
|
||||
configure_file("${TEMPLATE}" "${OUTPUT}" @ONLY)
|
||||
13
dCommon/BuildInfo.cpp.in
Normal file
13
dCommon/BuildInfo.cpp.in
Normal 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
51
dCommon/BuildInfo.h
Normal 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
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user