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. |
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/eversionPoint consumer projects at that prefix with CMAKE_PREFIX_PATH:
cmake -S app -B app/build -DCMAKE_PREFIX_PATH=/path/to/eversionIn 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_infoIf 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.
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 eversionHost applications should call eversion::IsValidComponentInfo() before using a
returned ComponentInfo. The abi_version and struct_size fields leave room
for future ABI evolution.
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, andpatchare 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.
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}"
)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).
cmake -S . -B build -DEVERSION_BUILD_TESTS=ON
cmake --build build
ctest --test-dir build --output-on-failureThe tests cover:
eversion::Versioncomparison, stable-version checks, and string formatting.- Compile-time constants generated by
eversion_declare(). - Conversion from
RuntimeVersiontoeversion::Version. - ABI validation for
ComponentInfo. - Dynamic plugin metadata generated by
eversion_declare_plugin(). - Installed-package consumption through
find_package(EVersion CONFIG REQUIRED).
- Public version information is exposed as
inline constexprC++ 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.2or~1.2.3. - EVersion does not provide a plugin loading framework.
