Skip to content

Commit

Permalink
Merge pull request #18 from bigladder/versioning
Browse files Browse the repository at this point in the history
Add automatic versioning based on Git tag
  • Loading branch information
nealkruis authored Oct 24, 2024
2 parents fc9fe19 + 6048358 commit f9cd80c
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 6 deletions.
8 changes: 6 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
"MinSizeRel" "RelWithDebInfo")
endif ()

find_package(Git QUIET)

set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")

include(CMakeDependentOption)
Expand All @@ -29,6 +27,12 @@ endif ()

include(build-options-interface)

# Automatic generation of version info
include(git-versioning)
process_git_version()
make_version_header()
message(STATUS "Building ${PROJECT_NAME} ${${PROJECT_NAME}_version}")

# Set up testing/coverage
if (${PROJECT_NAME}_BUILD_TESTING)
enable_testing()
Expand Down
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Manages the overall build with specific scripts for handling:
- Defaulting build type (i.e., Release)
- [Uniform compiler flags](cmake/build-options-interface.cmake)
- Identifying build target architecture (i.e., operating system, compiler, and CPU instruction architecture) (TODO)
- Automatic versioning based on Git repository tags (TODO)
- [Automatic versioning based on Git repository tags](cmake/git-versioning.cmake)
- Optional static or dynamic libraries
- Handling of MSVC and Windows specific requirements (e.g., MT/MD flags, export headers)

Expand All @@ -60,7 +60,9 @@ IDE, which can be configured to automatically format code upon file-save. Else,
* On Mac, `brew install clang-format` and run at the command line with `clang-format -i <file>`
* On Ubuntu, `sudo apt install clang-format` and run at the command line with `clang-format -i <file>`

**NOTE**: ensure the version of clang-format your development team is using locally matches the version used in `.github/workflows/clang-format-check.yml` or you may get false errors. The .clang-format file can be set to a version earlier than your current.
**NOTE**: ensure the version of clang-format your development team is using locally matches the version used
in `.github/workflows/clang-format-check.yml` or you may get false errors. The .clang-format file can be set to a
version earlier than your current.

## Code Coverage: Codecov

Expand Down
124 changes: 124 additions & 0 deletions cmake/git-versioning.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
macro(git_command args output_variable)
execute_process(
COMMAND ${GIT_EXECUTABLE} ${args}
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
RESULT_VARIABLE exit_status
ERROR_VARIABLE error_output
OUTPUT_VARIABLE temp_variable
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if (NOT ${exit_status} MATCHES "0")
message(SEND_ERROR "Unable to retrieve ${output_variable}.")
message(FATAL_ERROR ${error_output})
endif ()
set(${output_variable} "${temp_variable}")
endmacro()

macro(process_git_version)

find_package(Git)

if (GIT_FOUND)

# Branch
git_command("rev-parse;--abbrev-ref;HEAD" ${PROJECT_NAME}_git_branch)

# SHA
git_command("rev-parse;--verify;--short;HEAD" ${PROJECT_NAME}_git_sha)

# Tag
git_command("tag" tag_list)

if (tag_list MATCHES "^$")
# Use v0.0.0 if there aren't tags yet
set(${PROJECT_NAME}_git_tag "v0.0.0")
git_command("rev-list;--count;HEAD" ${PROJECT_NAME}_git_build_number)
else ()
git_command("describe;--tags;--abbrev=0" ${PROJECT_NAME}_git_tag)

# Build number
git_command("rev-list;--count;HEAD;^${${PROJECT_NAME}_git_tag}" ${PROJECT_NAME}_git_build_number)

endif ()

# Status (dirty?)
git_command("diff;--shortstat" ${PROJECT_NAME}_git_status)

if (${PROJECT_NAME}_git_status MATCHES "changed")
set(${PROJECT_NAME}_git_status "dirty")
else ()
unset(${PROJECT_NAME}_git_status)
endif ()

# Get version components
set(semver_normal_regex "v(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)")
set(semver_prerelease_regex "(-((0|[1-9][0-9]*|[0-9]*[a-zA-Z-][0-9a-zA-Z-]*)(\\.(0|[1-9][0-9]*|[0-9]*[a-zA-Z-][0-9a-zA-Z-]*))*))")
set(semver_regex "^${semver_normal_regex}${semver_prerelease_regex}?$")
if (NOT ${PROJECT_NAME}_git_tag MATCHES ${semver_regex})
message(FATAL_ERROR "Tag, \"${${PROJECT_NAME}_git_tag}\", does not conform to SemVer \"${${PROJECT_NAME}_semver_regex}\".")
endif ()

string(REGEX REPLACE ${semver_regex} "\\1" ${PROJECT_NAME}_version_major "${${PROJECT_NAME}_git_tag}")
string(REGEX REPLACE ${semver_regex} "\\2" ${PROJECT_NAME}_version_minor "${${PROJECT_NAME}_git_tag}")
string(REGEX REPLACE ${semver_regex} "\\3" ${PROJECT_NAME}_version_patch "${${PROJECT_NAME}_git_tag}")
if (${PROJECT_NAME}_git_tag MATCHES "^${semver_normal_regex}${semver_prerelease_regex}$")
string(REGEX REPLACE ${semver_regex} "\\4" ${PROJECT_NAME}_version_prerelease "${${PROJECT_NAME}_git_tag}")
else ()
set(${PROJECT_NAME}_version_prerelease "")
endif ()

# Build meta info
if (NOT ${PROJECT_NAME}_git_build_number MATCHES "^0$")
set(${PROJECT_NAME}_version_meta "+${${PROJECT_NAME}_git_branch}.${${PROJECT_NAME}_git_sha}.${${PROJECT_NAME}_git_build_number}")
if (DEFINED ${PROJECT_NAME}_git_status)
set(${PROJECT_NAME}_version_meta "${${PROJECT_NAME}_version_meta}.${${PROJECT_NAME}_git_status}")
endif ()
else ()
if (DEFINED ${PROJECT_NAME}_git_status)
set(${PROJECT_NAME}_version_meta "+${${PROJECT_NAME}_git_status}")
else ()
set(${PROJECT_NAME}_version_meta "")
endif ()
endif ()


else ()

message(WARNING "Failed to find git executable. Unable to establish version information for ${PROJECT_NAME}.")
set(${PROJECT_NAME}_version_major "0")
set(${PROJECT_NAME}_version_minor "0")
set(${PROJECT_NAME}_version_patch "0")
set(${PROJECT_NAME}_version_meta "+git.not.found")

endif ()

set(${PROJECT_NAME}_version "v${${PROJECT_NAME}_version_major}.${${PROJECT_NAME}_version_minor}.${${PROJECT_NAME}_version_patch}${${PROJECT_NAME}_version_prerelease}${${PROJECT_NAME}_version_meta}")

endmacro()

macro(add_version_variable version_variable)
string(TOUPPER ${version_variable} temp_upper)
string(APPEND content "#define ${temp_upper} ${${version_variable}}\n")
endmacro()

macro(make_version_header)

set(content "#pragma once\n\n")

add_version_variable(${PROJECT_NAME}_git_branch)
add_version_variable(${PROJECT_NAME}_git_sha)
add_version_variable(${PROJECT_NAME}_git_tag)
add_version_variable(${PROJECT_NAME}_git_build_number)
add_version_variable(${PROJECT_NAME}_git_status)
add_version_variable(${PROJECT_NAME}_version_major)
add_version_variable(${PROJECT_NAME}_version_minor)
add_version_variable(${PROJECT_NAME}_version_patch)
add_version_variable(${PROJECT_NAME}_version_prerelease)
add_version_variable(${PROJECT_NAME}_version_meta)
add_version_variable(${PROJECT_NAME}_version)

file(CONFIGURE
OUTPUT "${PROJECT_BINARY_DIR}/include/${PROJECT_NAME}/version.h"
CONTENT ${content}
)
endmacro()
2 changes: 2 additions & 0 deletions include/atheneum/atheneum.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
#include <functional>
#include <string_view>

#include <atheneum/version.h>

namespace Atheneum
{

Expand Down
4 changes: 2 additions & 2 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ set(library_sources
${sources}
${public_headers}
${private_headers}
)
)

if (${PROJECT_NAME}_STATIC_LIB)
add_library(${PROJECT_NAME} STATIC ${library_sources})
Expand All @@ -17,7 +17,7 @@ else ()
add_library(${PROJECT_NAME} SHARED ${library_sources})
endif ()

target_include_directories(${PROJECT_NAME} PUBLIC "${PROJECT_SOURCE_DIR}/include" PRIVATE "${PROJECT_SOURCE_DIR}/src")
target_include_directories(${PROJECT_NAME} PUBLIC "${PROJECT_SOURCE_DIR}/include" "${PROJECT_BINARY_DIR}/include" PRIVATE "${PROJECT_SOURCE_DIR}/src")

target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_17)

Expand Down

0 comments on commit f9cd80c

Please sign in to comment.