Skip to content

KodeXMachina/EVersion

Repository files navigation

EVersion logo

EVersion

Easy Version is a small C++20 library for exposing project, library, and dynamic-plugin version metadata without public VERSION_* macros.

EVersion provides three pieces:

Surface Purpose
eversion::Version A constexpr-friendly version value type with comparison and string formatting.
eversion_declare() A CMake function that generates an inline constexpr version header for a target.
eversion_declare_plugin() A CMake function that generates a stable C ABI metadata entry point for a dynamic plugin.

Install

The recommended usage is to install EVersion once, then consume it with find_package() from other projects:

cmake -S EVersion -B EVersion/build -DEVERSION_BUILD_TESTS=ON
cmake --build EVersion/build
cmake --install EVersion/build --prefix /path/to/eversion

Point consumer projects at that prefix with CMAKE_PREFIX_PATH:

cmake -S app -B app/build -DCMAKE_PREFIX_PATH=/path/to/eversion

Generate Version Information for a Target

In a consumer CMake project:

find_package(EVersion CONFIG REQUIRED)

project(sample_core
    VERSION 1.0.0
    LANGUAGES CXX
)

add_library(sample_core ...)
target_link_libraries(sample_core PUBLIC eversion::eversion)

eversion_declare(sample_core
    NAMESPACE sample_core::version_info
    MAJOR ${PROJECT_VERSION_MAJOR}
    MINOR ${PROJECT_VERSION_MINOR}
    PATCH ${PROJECT_VERSION_PATCH}
    OUTPUT sample_core/version_info.h
)

Use the generated header from C++:

#include <sample_core/version_info.h>

static_assert(sample_core::version_info::kVersionMajor == 1);
static_assert(sample_core::version_info::kVersionString == "1.0.0");

constexpr eversion::Version current = sample_core::version_info::kVersion;

The generated header exposes these constants:

namespace sample_core::version_info {

inline constexpr std::uint32_t kVersionMajor;
inline constexpr std::uint32_t kVersionMinor;
inline constexpr std::uint32_t kVersionPatch;
inline constexpr std::string_view kVersionPrerelease;
inline constexpr std::string_view kVersionBuildMetadata;
inline constexpr std::string_view kVersionString;
inline constexpr ::eversion::Version kVersion;

}  // namespace sample_core::version_info

Generate Version Metadata for a Dynamic Plugin

If a host application loads dynamic plugins at runtime, each plugin can export a small metadata entry point generated by EVersion. EVersion does not load plugins; it only defines the metadata structure and generates the exported function.

Plugin CMake example:

find_package(EVersion CONFIG REQUIRED)

add_library(sample_plugin MODULE ...)
target_link_libraries(sample_plugin PRIVATE eversion::eversion)

eversion_declare_plugin(sample_plugin
    ID sample.plugin
    NAME "Sample Plugin"
    NAMESPACE sample_plugin::version_info
    MAJOR 1
    MINOR 2
    PATCH 3
    BUILD_METADATA git.deadbeef
    OUTPUT sample_plugin/version_info.h
)

The plugin can still use its generated C++ version header internally:

#include <sample_plugin/version_info.h>

static_assert(sample_plugin::version_info::kVersionString ==
              "1.2.3+git.deadbeef");

The generated plugin source exports eversion_plugin_info. A host application can load the plugin, resolve that symbol, and validate the returned metadata:

#include <eversion/runtime.h>

void* symbol = LoadSymbol(plugin_handle, eversion::kPluginInfoSymbol.data());
auto info_function =
    reinterpret_cast<eversion::ComponentInfoFunction>(symbol);

const eversion::ComponentInfo* info = info_function();
if (eversion::IsValidComponentInfo(info)) {
  const eversion::Version plugin_version = eversion::ToVersion(info->version);
}

LoadSymbol() is intentionally left to the host application. For example, Linux/macOS hosts can use dlsym(), and Windows hosts can use GetProcAddress(). Plugin discovery, dynamic-library lifetime, and error handling are also host responsibilities.

Runtime Metadata ABI

The plugin boundary avoids std::string, std::string_view, virtual interfaces, exceptions, and RTTI. The exported data contains only integers and const char* fields:

namespace eversion {

inline constexpr std::uint32_t kComponentInfoAbiVersion = 1U;
inline constexpr std::string_view kPluginInfoSymbol = "eversion_plugin_info";

struct RuntimeVersion {
  std::uint32_t major;
  std::uint32_t minor;
  std::uint32_t patch;
  const char* prerelease;
  const char* build_metadata;
};

struct ComponentInfo {
  std::uint32_t abi_version;
  std::size_t struct_size;
  const char* id;
  const char* name;
  RuntimeVersion version;
};

using ComponentInfoFunction = const ComponentInfo* (*)() noexcept;

}  // namespace eversion

Host applications should call eversion::IsValidComponentInfo() before using a returned ComponentInfo. The abi_version and struct_size fields leave room for future ABI evolution.

Use Only the Value Type

If a project does not need generated headers, it can include the value type directly:

#include <eversion/version.h>

constexpr eversion::Version current{1, 4, 0};
constexpr eversion::Version minimum{1, 2, 0};
static_assert(current >= minimum);

Version comparison rules:

  • major, minor, and patch are compared numerically.
  • A version with a prerelease tag ranks below the same final version.
  • Numeric prerelease identifiers are compared numerically.
  • Numeric prerelease identifiers rank below non-numeric identifiers.
  • Build metadata is informational and does not affect precedence.

Therefore, 1.2.3+git.a and 1.2.3+git.b compare as equal.

Add Build Metadata

Pass build metadata such as a git commit or CI build id through BUILD_METADATA:

eversion_declare(sample_core
    NAMESPACE sample_core::version_info
    MAJOR 1
    MINOR 2
    PATCH 3
    BUILD_METADATA "${GIT_COMMIT_SHA}"
)

Plugins can use the same argument:

eversion_declare_plugin(sample_plugin
    ID sample.plugin
    NAME "Sample Plugin"
    NAMESPACE sample_plugin::version_info
    MAJOR 2
    MINOR 0
    PATCH 1
    BUILD_METADATA "${GIT_COMMIT_SHA}"
)

Vendored Source Builds

add_subdirectory(external/EVersion) is still supported for local development or vendored source builds:

add_subdirectory(external/EVersion)

add_library(sample_core ...)
target_link_libraries(sample_core PUBLIC eversion::eversion)
eversion_declare(sample_core ...)

The preferred reusable-library path remains installation plus find_package(EVersion CONFIG REQUIRED).

Build and Test EVersion

cmake -S . -B build -DEVERSION_BUILD_TESTS=ON
cmake --build build
ctest --test-dir build --output-on-failure

The tests cover:

  • eversion::Version comparison, stable-version checks, and string formatting.
  • Compile-time constants generated by eversion_declare().
  • Conversion from RuntimeVersion to eversion::Version.
  • ABI validation for ComponentInfo.
  • Dynamic plugin metadata generated by eversion_declare_plugin().
  • Installed-package consumption through find_package(EVersion CONFIG REQUIRED).

Design Choices

  • Public version information is exposed as inline constexpr C++ symbols.
  • No public version macros are generated.
  • CMake writes generated headers with configure_file(... @ONLY).
  • The plugin ABI uses C-style structs to avoid exposing C++ STL types, exceptions, or vtables across dynamic-library boundaries.
  • EVersion does not parse version strings at runtime.
  • EVersion does not implement version range expressions such as ^1.2 or ~1.2.3.
  • EVersion does not provide a plugin loading framework.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Generated from winterYANGWT/Cpp-Dev