diff --git a/CMakeLists.txt b/CMakeLists.txt index 57a59656..37a3859b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,6 +12,7 @@ set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS} -O3 -Wall -DNOLOG") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS} -O3 -Wall -ggdb -gdwarf-3 -DNOLOG") set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/Modules") +include(GetGitRevisionDescription) # mutils_FOUND # mutils_INCLUDE_DIRS diff --git a/cmake/Modules/GetGitRevisionDescription.cmake b/cmake/Modules/GetGitRevisionDescription.cmake new file mode 100644 index 00000000..8ab03bc5 --- /dev/null +++ b/cmake/Modules/GetGitRevisionDescription.cmake @@ -0,0 +1,168 @@ +# - Returns a version string from Git +# +# These functions force a re-configure on each git commit so that you can +# trust the values of the variables in your build system. +# +# get_git_head_revision( [ ...]) +# +# Returns the refspec and sha hash of the current head revision +# +# git_describe( [ ...]) +# +# Returns the results of git describe on the source tree, and adjusting +# the output so that it tests false if an error occurs. +# +# git_get_exact_tag( [ ...]) +# +# Returns the results of git describe --exact-match on the source tree, +# and adjusting the output so that it tests false if there was no exact +# matching tag. +# +# git_local_changes() +# +# Returns either "CLEAN" or "DIRTY" with respect to uncommitted changes. +# Uses the return code of "git diff-index --quiet HEAD --". +# Does not regard untracked files. +# +# Requires CMake 2.6 or newer (uses the 'function' command) +# +# Original Author: +# 2009-2010 Ryan Pavlik +# http://academic.cleardefinition.com +# Iowa State University HCI Graduate Program/VRAC +# +# Copyright Iowa State University 2009-2010. +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +if(__get_git_revision_description) + return() +endif() +set(__get_git_revision_description YES) + +# We must run the following at "include" time, not at function call time, +# to find the path to this module rather than the path to a calling list file +get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH) + +function(get_git_head_revision _refspecvar _hashvar) + set(GIT_PARENT_DIR "${CMAKE_CURRENT_SOURCE_DIR}") + set(GIT_DIR "${GIT_PARENT_DIR}/.git") + while(NOT EXISTS "${GIT_DIR}") # .git dir not found, search parent directories + set(GIT_PREVIOUS_PARENT "${GIT_PARENT_DIR}") + get_filename_component(GIT_PARENT_DIR ${GIT_PARENT_DIR} PATH) + if(GIT_PARENT_DIR STREQUAL GIT_PREVIOUS_PARENT) + # We have reached the root directory, we are not in git + set(${_refspecvar} "GITDIR-NOTFOUND" PARENT_SCOPE) + set(${_hashvar} "GITDIR-NOTFOUND" PARENT_SCOPE) + return() + endif() + set(GIT_DIR "${GIT_PARENT_DIR}/.git") + endwhile() + # check if this is a submodule + if(NOT IS_DIRECTORY ${GIT_DIR}) + file(READ ${GIT_DIR} submodule) + string(REGEX REPLACE "gitdir: (.*)\n$" "\\1" GIT_DIR_RELATIVE ${submodule}) + get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH) + get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE} ABSOLUTE) + endif() + set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data") + if(NOT EXISTS "${GIT_DATA}") + file(MAKE_DIRECTORY "${GIT_DATA}") + endif() + + if(NOT EXISTS "${GIT_DIR}/HEAD") + return() + endif() + set(HEAD_FILE "${GIT_DATA}/HEAD") + configure_file("${GIT_DIR}/HEAD" "${HEAD_FILE}" COPYONLY) + + configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in" + "${GIT_DATA}/grabRef.cmake" + @ONLY) + include("${GIT_DATA}/grabRef.cmake") + + set(${_refspecvar} "${HEAD_REF}" PARENT_SCOPE) + set(${_hashvar} "${HEAD_HASH}" PARENT_SCOPE) +endfunction() + +function(git_describe _var) + if(NOT GIT_FOUND) + find_package(Git QUIET) + endif() + get_git_head_revision(refspec hash) + if(NOT GIT_FOUND) + set(${_var} "GIT-NOTFOUND" PARENT_SCOPE) + return() + endif() + if(NOT hash) + set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE) + return() + endif() + + # TODO sanitize + #if((${ARGN}" MATCHES "&&") OR + # (ARGN MATCHES "||") OR + # (ARGN MATCHES "\\;")) + # message("Please report the following error to the project!") + # message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}") + #endif() + + #message(STATUS "Arguments to execute_process: ${ARGN}") + + execute_process(COMMAND + "${GIT_EXECUTABLE}" + describe + ${hash} + ${ARGN} + WORKING_DIRECTORY + "${CMAKE_CURRENT_SOURCE_DIR}" + RESULT_VARIABLE + res + OUTPUT_VARIABLE + out + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + if(NOT res EQUAL 0) + set(out "${out}-${res}-NOTFOUND") + endif() + + set(${_var} "${out}" PARENT_SCOPE) +endfunction() + +function(git_get_exact_tag _var) + git_describe(out --exact-match ${ARGN}) + set(${_var} "${out}" PARENT_SCOPE) +endfunction() + +function(git_local_changes _var) + if(NOT GIT_FOUND) + find_package(Git QUIET) + endif() + get_git_head_revision(refspec hash) + if(NOT GIT_FOUND) + set(${_var} "GIT-NOTFOUND" PARENT_SCOPE) + return() + endif() + if(NOT hash) + set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE) + return() + endif() + + execute_process(COMMAND + "${GIT_EXECUTABLE}" + diff-index --quiet HEAD -- + WORKING_DIRECTORY + "${CMAKE_CURRENT_SOURCE_DIR}" + RESULT_VARIABLE + res + OUTPUT_VARIABLE + out + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + if(res EQUAL 0) + set(${_var} "CLEAN" PARENT_SCOPE) + else() + set(${_var} "DIRTY" PARENT_SCOPE) + endif() +endfunction() diff --git a/cmake/Modules/GetGitRevisionDescription.cmake.in b/cmake/Modules/GetGitRevisionDescription.cmake.in new file mode 100644 index 00000000..6d8b708e --- /dev/null +++ b/cmake/Modules/GetGitRevisionDescription.cmake.in @@ -0,0 +1,41 @@ +# +# Internal file for GetGitRevisionDescription.cmake +# +# Requires CMake 2.6 or newer (uses the 'function' command) +# +# Original Author: +# 2009-2010 Ryan Pavlik +# http://academic.cleardefinition.com +# Iowa State University HCI Graduate Program/VRAC +# +# Copyright Iowa State University 2009-2010. +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +set(HEAD_HASH) + +file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024) + +string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS) +if(HEAD_CONTENTS MATCHES "ref") + # named branch + string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}") + if(EXISTS "@GIT_DIR@/${HEAD_REF}") + configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY) + else() + configure_file("@GIT_DIR@/packed-refs" "@GIT_DATA@/packed-refs" COPYONLY) + file(READ "@GIT_DATA@/packed-refs" PACKED_REFS) + if(${PACKED_REFS} MATCHES "([0-9a-z]*) ${HEAD_REF}") + set(HEAD_HASH "${CMAKE_MATCH_1}") + endif() + endif() +else() + # detached HEAD + configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY) +endif() + +if(NOT HEAD_HASH) + file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024) + string(STRIP "${HEAD_HASH}" HEAD_HASH) +endif() diff --git a/include/derecho/core/derecho_exception.hpp b/include/derecho/core/derecho_exception.hpp index e321c0c6..55eb48ee 100644 --- a/include/derecho/core/derecho_exception.hpp +++ b/include/derecho/core/derecho_exception.hpp @@ -8,6 +8,8 @@ #include #include +#include +#include namespace derecho { @@ -18,7 +20,9 @@ struct derecho_exception : public std::exception { const std::string message; derecho_exception(const std::string& message) : message(message) {} - const char* what() const noexcept { return message.c_str(); } + const char* what() const noexcept { + return (message + " Derecho version: " + VERSION_STRING_PLUS_COMMITS).c_str(); + } }; /** diff --git a/include/derecho/core/git_version.hpp b/include/derecho/core/git_version.hpp new file mode 100644 index 00000000..758a405c --- /dev/null +++ b/include/derecho/core/git_version.hpp @@ -0,0 +1,42 @@ +#pragma once + +namespace derecho { + +/** + * The current major version number of the Derecho library, as defined by Git. + * This is updated when the library is compiled. + */ +extern const int MAJOR_VERSION; +/** + * The current minor version number of the Derecho library, as defined by Git. + * This is updated when the library is compiled. + */ +extern const int MINOR_VERSION; +/** + * The current "patch" (more-minor) version number of the Derecho library, as + * defined by Git. This is updated when the library is compiled. + */ +extern const int PATCH_VERSION; +/** + * If the currently-compiled version of the Derecho library is more recent than + * the last "release" version, this is the number of Git commits by which it is + * ahead. This is updated when the library is compiled. + */ +extern const int COMMITS_AHEAD_OF_VERSION; + +/** + * A constant C-style string containing the current Derecho library version in + * dot-separated format, e.g. "1.0.0" + */ +extern const char* VERSION_STRING; + +/** + * A constant C-style string containing the current Derecho library version in + * dot-separated format, plus the number of Git commits the code is ahead of + * the last "release" version, separated by a plus sign. E.g. "1.0.0+5" + */ +extern const char* VERSION_STRING_PLUS_COMMITS; + +} + + diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index d5e3a26e..77fffe19 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -7,7 +7,16 @@ set(CMAKE_CXX_FLAGS_DEBUG "-O0 -ggdb -gdwarf-3 -pg") set(CMAKE_CXX_FLAGS_RELEASE "-O3") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O3 -ggdb -gdwarf-3 -D_PERFORMANCE_DEBUG") -add_library(core OBJECT derecho_sst.cpp view.cpp view_manager.cpp rpc_manager.cpp p2p_connections.cpp multicast_group.cpp subgroup_functions.cpp connection_manager.cpp restart_state.cpp persistence_manager.cpp version_code.cpp) +git_describe(GIT_VERSION_STRING --tags --long) +string(REGEX MATCH "v([0-9]+)\\.([0-9]+)\\.?([0-9]*)-([0-9]+)-g([0-9|a-z]+)" MATCH_OUTPUT ${GIT_VERSION_STRING}) +set(DERECHO_MAJOR_VERSION ${CMAKE_MATCH_1}) +set(DERECHO_MINOR_VERSION ${CMAKE_MATCH_2}) +set(DERECHO_PATCH_VERSION ${CMAKE_MATCH_3}) +set(DERECHO_COMMITS_AHEAD ${CMAKE_MATCH_4}) +set(DERECHO_COMMIT_HASH ${CMAKE_MATCH_5}) +configure_file(git_version.cpp.in git_version.cpp) + +add_library(core OBJECT derecho_sst.cpp view.cpp view_manager.cpp rpc_manager.cpp p2p_connections.cpp multicast_group.cpp subgroup_functions.cpp connection_manager.cpp restart_state.cpp persistence_manager.cpp version_code.cpp ${CMAKE_CURRENT_BINARY_DIR}/git_version.cpp) target_include_directories(core PRIVATE $ $ diff --git a/src/core/git_version.cpp.in b/src/core/git_version.cpp.in new file mode 100644 index 00000000..3fdc272c --- /dev/null +++ b/src/core/git_version.cpp.in @@ -0,0 +1,49 @@ +/** + * @file git_version.cpp.in + * This is a CMake template file that generates the file git_version.cpp. It is + * used to define the constants in git_version.hpp. + */ + +#include + +#cmakedefine DERECHO_MAJOR_VERSION @DERECHO_MAJOR_VERSION@ +#cmakedefine DERECHO_MINOR_VERSION @DERECHO_MINOR_VERSION@ +#cmakedefine DERECHO_PATCH_VERSION @DERECHO_PATCH_VERSION@ +#cmakedefine DERECHO_COMMITS_AHEAD @DERECHO_COMMITS_AHEAD@ + +#ifndef DERECHO_MAJOR_VERSION +#define DERECHO_MAJOR_VERSION 0 +#endif +#ifndef DERECHO_MINOR_VERSION +#define DERECHO_MINOR_VERSION 0 +#endif +#ifndef DERECHO_PATCH_VERSION +#define DERECHO_PATCH_VERSION 0 +#endif +#ifndef DERECHO_COMMITS_AHEAD +#define DERECHO_COMMITS_AHEAD 0 +#endif + +#define STR(x) #x +#define QUOTE(x) STR(x) + +namespace derecho { + +const int MAJOR_VERSION = DERECHO_MAJOR_VERSION; +const int MINOR_VERSION = DERECHO_MINOR_VERSION; +const int PATCH_VERSION = DERECHO_PATCH_VERSION; +const int COMMITS_AHEAD_OF_VERSION = DERECHO_COMMITS_AHEAD; + +const char* VERSION_STRING = QUOTE(DERECHO_MAJOR_VERSION) + "." + QUOTE(DERECHO_MINOR_VERSION) + "." + QUOTE(DERECHO_PATCH_VERSION); +const char* VERSION_STRING_PLUS_COMMITS = QUOTE(DERECHO_MAJOR_VERSION) + "." + QUOTE(DERECHO_MINOR_VERSION) + "." + QUOTE(DERECHO_PATCH_VERSION) + "+" + QUOTE(DERECHO_COMMITS_AHEAD); +} // namespace derecho \ No newline at end of file diff --git a/src/core/version_code.cpp b/src/core/version_code.cpp index ea12b558..ae393409 100644 --- a/src/core/version_code.cpp +++ b/src/core/version_code.cpp @@ -1,4 +1,5 @@ #include +#include #include /** A simple hash-combine function to "mix" two hashcodes */ @@ -14,13 +15,9 @@ const uint64_t compiler = ISGNU | ((__GNUC__ << 16) + __GNUC_MINOR__); const uint64_t compiler = ISCLANG | (__clang_major__ << 16) + __clang_minor__ ); #endif -// These next assume that this is Derecho release 0.9.0 -// In practice GitHub should be managing the three numbers and we should use -// the feature that substitutes them into the text file here. -#define DERECHO_MAJOR 0L // Replace with a major number managed by GitHub -#define DERECHO_MINOR 9L // Replace with a minor version number managed by GitHub -#define DERECHO_PATCHLEVEL 0L // Replace with a patch version number managed by GitHub -#define DERECHO_VERSION ((((DERECHO_MAJOR << 16) + DERECHO_MINOR) << 32) + DERECHO_PATCHLEVEL) +const uint64_t derecho_version = ((((static_cast(derecho::MAJOR_VERSION) << 16) + + derecho::MINOR_VERSION) << 32) + + derecho::COMMITS_AHEAD_OF_VERSION); /* * The following variables are for detection of Endian order for integers. @@ -98,7 +95,7 @@ namespace derecho { // This function combines all the measurements defined above, using the "mix" function uint64_t version_hashcode() { return mix(mix(mix(mix(mix(mix(mix(std::hash()(compiler), - std::hash()(DERECHO_VERSION)), + std::hash()(derecho_version)), *reinterpret_cast(&int16_array)), *reinterpret_cast(&int32_array)), *reinterpret_cast(&int64_array)), diff --git a/src/core/view_manager.cpp b/src/core/view_manager.cpp index dc375a0d..875d4459 100644 --- a/src/core/view_manager.cpp +++ b/src/core/view_manager.cpp @@ -12,6 +12,7 @@ #include //Needed for the ReplicatedObject interface #include #include +#include #include #include @@ -42,6 +43,7 @@ ViewManager::ViewManager( subgroup_objects(object_reference_map), any_persistent_objects(any_persistent_objects), persistence_manager_callbacks(_persistence_manager_callbacks) { + whenlog(logger->info("Derecho library running version {}.{}.{} + {} commits", derecho::MAJOR_VERSION, derecho::MINOR_VERSION, derecho::PATCH_VERSION, derecho::COMMITS_AHEAD_OF_VERSION)); if(any_persistent_objects) { //Attempt to load a saved View from disk, to see if one is there curr_view = persistent::loadObject(); @@ -99,6 +101,7 @@ ViewManager::ViewManager( subgroup_objects(object_reference_map), any_persistent_objects(any_persistent_objects), persistence_manager_callbacks(_persistence_manager_callbacks) { + whenlog(logger->info("Derecho library running version {}.{}.{} + {} commits", derecho::MAJOR_VERSION, derecho::MINOR_VERSION, derecho::PATCH_VERSION, derecho::COMMITS_AHEAD_OF_VERSION)); const uint32_t my_id = getConfUInt32(CONF_DERECHO_LOCAL_ID); receive_initial_view(my_id, leader_connection); //As soon as we have a tentative initial view, set up the TCP connections