diff --git a/arbor/include/CMakeLists.txt b/arbor/include/CMakeLists.txt index b61d4a6d45ed4be0176ce18213511a1b00bef474..fde67911fb44b94a2ebddca43fb420a66c8814e4 100644 --- a/arbor/include/CMakeLists.txt +++ b/arbor/include/CMakeLists.txt @@ -50,10 +50,12 @@ if(ARB_VECTORIZE) list(APPEND arb_features VECTORIZE) endif() +string(TOUPPER "${CMAKE_BUILD_TYPE}" arb_config_str) + add_custom_command( OUTPUT version.hpp-test DEPENDS _always_rebuild - COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/git-source-id ${FULL_VERSION_STRING} ${ARB_ARCH} ${arb_features} > version.hpp-test + COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/git-source-id ${FULL_VERSION_STRING} ${ARB_ARCH} ${arb_config_str} ${arb_features} > version.hpp-test ) set(version_hpp_path arbor/version.hpp) diff --git a/arbor/include/git-source-id b/arbor/include/git-source-id index 7946b82330d40b419c9c91bdaac0a7cf75c92699..c3fc3ec9e4e793f3b42775f89be2da8c17f948c1 100755 --- a/arbor/include/git-source-id +++ b/arbor/include/git-source-id @@ -1,31 +1,73 @@ #!/usr/bin/env bash -# arguments: version-string [feature...] +function usage () { + helpstr="Usage: ${0##*/} version arch config [feature ...]" + if [ -n "$1" ]; then + echo "${0##*/}: $1" >&2 + echo "$helpstr" >&2 + exit 1 + else + echo "$helpstr" + exit 0 + fi +} + +if [ -z "$1" -o "$1" = "--help" -o "$1" = "-h" ]; then usage; fi +if [ -z "$2" ]; then usage "missing arch"; fi +if [ -z "$3" ]; then usage "missing config"; fi version="$1" arch="$2" -shift 2 +config="$3" +shift 3 + +if [[ "$version" =~ ^([0-9]+)\.([0-9]+)(\.([0-9]+))?(-(.*))?$ ]]; then + version_major="${BASH_REMATCH[1]}" + version_minor="${BASH_REMATCH[2]}" + version_patch="${BASH_REMATCH[4]:-0}" + version_dev="${BASH_REMATCH[6]}" +else + usage "bad version format" +fi -if gitlog=$(git log -1 --pretty=format:'%ci %H' 2>/dev/null); then +if gitlog=$(git log -1 --pretty=format:'%cI %H' 2>/dev/null); then git diff --quiet HEAD 2>/dev/null || gitlog="${gitlog} modified" else gitlog='unknown commit' fi +full_build_id="source_id=${gitlog};version=${version};arch=${arch};config=${config};" +for feature in "$@"; do + full_build_id="${full_build_id}${feature}_ENABLED;" +done + cat << __end__ #pragma once -#define ARB_VERSION "${version}" -#define ARB_SOURCE_ID "${gitlog}" -#define ARB_ARCH "${arch}" - namespace arb { -extern const char version[]; -extern const char source_id[]; -extern const char arch[]; +extern const char* source_id; +extern const char* arch; +extern const char* build_config; +extern const char* version; +extern const char* full_build_id; +constexpr int version_major = ${version_major}; +constexpr int version_minor = ${version_minor}; +constexpr int version_patch = ${version_patch}; +extern const char* version_dev; } + +#define ARB_SOURCE_ID "${gitlog}" +#define ARB_ARCH "${arch}" +#define ARB_BUILD_CONFIG "${config}" +#define ARB_FULL_BUILD_ID "${full_build_id}" +#define ARB_VERSION "${version}" +#define ARB_VERSION_MAJOR ${version_major} +#define ARB_VERSION_MINOR ${version_minor} +#define ARB_VERSION_PATCH ${version_patch} __end__ +if [ -n "$version_dev" ]; then echo "#define ARB_VERSION_DEV \"${version_dev}\""; fi + for feature in "$@"; do echo "#define ARB_${feature}_ENABLED" done diff --git a/arbor/version.cpp b/arbor/version.cpp index e3b689cf01268cef4ca6657919cace9b5b057b73..0b2233a29c6a17df73b48920cd1f55f673d6831b 100644 --- a/arbor/version.cpp +++ b/arbor/version.cpp @@ -1,7 +1,14 @@ #include <arbor/version.hpp> namespace arb { -const char version[] = ARB_VERSION; -const char source_id[] = ARB_SOURCE_ID; -const char arch[] = ARB_ARCH; +const char* source_id = ARB_SOURCE_ID; +const char* arch = ARB_ARCH; +const char* build_config = ARB_BUILD_CONFIG; +const char* version = ARB_VERSION; +#ifdef ARB_VERSION_DEV +const char* version_dev = ARB_VERSION_DEV; +#else +const char* version_dev = ""; +#endif +const char* full_build_id = ARB_FULL_BUILD_ID; } diff --git a/doc/cpp/index.rst b/doc/cpp/index.rst index bd97b673ee7f18e6068665aeaef770d6fdf3ffbe..f4f437ec0115050a9bf139730f93f96af5532948 100644 --- a/doc/cpp/index.rst +++ b/doc/cpp/index.rst @@ -23,3 +23,4 @@ A :cpp:type:`arb::recipe` describes a model, and a :cpp:type:`arb::simulation` i simulation profiler cable_cell + version diff --git a/doc/cpp/version.rst b/doc/cpp/version.rst new file mode 100644 index 0000000000000000000000000000000000000000..05d56e09c79b10acf8761467ff899eac6ca54357 --- /dev/null +++ b/doc/cpp/version.rst @@ -0,0 +1,77 @@ +.. _cppversion: + +Version and build information +============================= + +The Arbor library records version and configuration information in +two ways: + +* The ``version.hpp`` header has preprocessor defines with the prefix ``ARB_``. + +* The library presents this information in variables within the ``arb::`` namespace. + +Version information +------------------- + +The Arbor version string is in the format MAJOR.MINOR.PATCH, +or for development versions MAJOR.MINOR.PATCH-DEV, where DEV +is a string, usually literally "dev". + +:c:macro:`ARB_VERSION` + Full Arbor version string. Available as :cpp:var:`arb::version`. + +:c:macro:`ARB_VERSION_MAJOR` + Major version number. Available as :cpp:var:`arb::version_major`. + +:c:macro:`ARB_VERSION_MINOR` + Major version number. Available as :cpp:var:`arb::version_minor`. + +:c:macro:`ARB_VERSION_PATCH` + Major version number. Available as :cpp:var:`arb::version_patch`. + +:c:macro:`ARB_VERSION_DEV` + Development version suffix string. Only defined if Arbor is a development version. + Available as :cpp:var:`arb::version_dev`, which will be an empty string + if :c:macro:`ARB_VERSION_DEV` is not defined. + +Source information +------------------ + +:c:macro:`ARB_SOURCE_ID` + The source id contains the git commit time stamp, the commit hash, + and if there are uncommitted changes in the source tree, a suffix "modified", + e.g. ``"2020-01-02T03:04:05+06:00 b1946ac92492d2347c6235b4d2611184 modified"``. + Available as :cpp:var:`arb::source_id`. + +Build information +----------------- + +Arbor can be built in the default 'Release' configuration, or in an unoptimized +'Debug' configuration that is useful for development. Additionally, it can be +built for a particular CPU architecture given by the ``ARB_ARCH`` CMake configuration +variable. + +:c:macro:`ARB_BUILD_CONFIG` + Configuration string, all uppercase. Will be ``"DEBUG"`` or ``"RELEASE"``. + Available as :cpp:var:`arb::build_config`. + +:c:macro:`ARB_ARCH` + Value of the ``ARB_ARCH`` configuration variable, e.g. ``"native"``. + Available as :cpp:var:`arb::arch`. + +Features +-------- + +Configuration-time features are enabled in Arbor via CMake configuration variables +such as ``ARB_WITH_MPI`` and ``ARB_WITH_PYTHON``. Each enabled feature has +a corresponding preprocessor symbol in ``version.hpp`` of the form ``ARB_FEATURENAME_ENABLED``. +Examples include :c:macro:`ARB_MPI_ENABLED`, :c:macro:`ARB_ASSERT_ENABLED`. + +Full build information +---------------------- + +A single string containing all the identification information for an Arbor build +is available in the macro :c:macro:`ARB_FULL_BUILD_ID` and in the variable +:cpp:var:`arb::full_build_id`. This string contains the source id, the full version, +the build configuration, the target architecture, and a list of enabled features. + diff --git a/test/unit/test_version.cpp b/test/unit/test_version.cpp index cdf4230451befa58ed49f1e869b85cdf142429e3..05f5ebe08b230c6f0754304b8f5b9651870d3128 100644 --- a/test/unit/test_version.cpp +++ b/test/unit/test_version.cpp @@ -1,18 +1,78 @@ +#include <regex> #include <string> #include "../gtest.h" #include <arbor/version.hpp> -TEST(version, libmatch) { - using std::string; +using namespace std::string_literals; +using std::regex; +using std::regex_search; +using std::string; +using std::to_string; +TEST(version, libmatch) { string header_version = ARB_VERSION; string header_source_id = ARB_SOURCE_ID; + string header_arch = ARB_ARCH; + string header_build_config = ARB_BUILD_CONFIG; + string header_full_build_id = ARB_FULL_BUILD_ID; +#ifdef ARB_VERSION_DEV + string header_version_dev = ARB_VERSION_DEV; + EXPECT_FALSE(header_version_dev.empty()); +#else + string header_version_dev; +#endif + int header_version_major = ARB_VERSION_MAJOR; + int header_version_minor = ARB_VERSION_MINOR; + int header_version_patch = ARB_VERSION_PATCH; string lib_version = arb::version; string lib_source_id = arb::source_id; + string lib_arch = arb::arch; + string lib_build_config = arb::build_config; + string lib_full_build_id = arb::full_build_id; + constexpr int lib_version_major = arb::version_major; + constexpr int lib_version_minor = arb::version_minor; + constexpr int lib_version_patch = arb::version_patch; + string lib_version_dev = arb::version_dev; EXPECT_EQ(header_version, lib_version); EXPECT_EQ(header_source_id, lib_source_id); + EXPECT_EQ(header_arch, lib_arch); + EXPECT_EQ(header_build_config, lib_build_config); + EXPECT_EQ(header_full_build_id, lib_full_build_id); + EXPECT_EQ(header_version_major, lib_version_major); + EXPECT_EQ(header_version_minor, lib_version_minor); + EXPECT_EQ(header_version_patch, lib_version_patch); + EXPECT_EQ(header_version_dev, lib_version_dev); +} + +TEST(version, sane_config) { + EXPECT_TRUE(arb::build_config=="DEBUG"s || arb::build_config=="RELEASE"s); +} + +TEST(version, version_components) { + string dev = arb::version_dev; + + if (arb::version_patch>0) { + auto expected = to_string(arb::version_major)+"."+to_string(arb::version_minor)+"."+to_string(arb::version_patch); + expected += dev.empty()? "": "-"+dev; + + EXPECT_EQ(expected, ARB_VERSION); + } + else { + auto expected_majmin = to_string(arb::version_major)+"."+to_string(arb::version_minor); + auto expected_suffix = dev.empty()? "": "-"+dev; + + EXPECT_TRUE(expected_majmin+expected_suffix==ARB_VERSION || expected_majmin+".0"+expected_suffix==ARB_VERSION); + } } + +TEST(version, full_build_id) { + EXPECT_TRUE(regex_search(arb::full_build_id, regex("(;|^)config=."))); + EXPECT_TRUE(regex_search(arb::full_build_id, regex("(;|^)version=."))); + EXPECT_TRUE(regex_search(arb::full_build_id, regex("(;|^)source_id=."))); + EXPECT_TRUE(regex_search(arb::full_build_id, regex("(;|^)arch=."))); +} +