From bd41ade5f0eee5afe8bc7f6c7c3ca76f1fa296b4 Mon Sep 17 00:00:00 2001 From: Gereon Kremer Date: Thu, 7 Oct 2021 07:51:09 -0700 Subject: [PATCH] Add new versioning scheme (#7253) This introduces a new versioning mechanism that allows for better automation. --- CMakeLists.txt | 24 +--- cmake/version-base.cmake | 14 ++ cmake/version-base.cmake.template | 14 ++ cmake/version.cmake | 116 +++++++++++++++++ contrib/make-release.py | 120 ++++++++++++++++++ src/CMakeLists.txt | 2 +- src/api/java/CMakeLists.txt | 4 +- src/api/python/setup.py.in | 2 +- src/base/CMakeLists.txt | 15 +-- src/base/GitInfo.cmake.in | 48 ------- src/base/configuration.cpp | 45 +------ src/base/configuration.h | 22 +--- src/base/cvc5config.h.in | 15 --- ..._versioninfo.cpp.in => versioninfo.cpp.in} | 10 +- src/main/driver_unified.cpp | 2 +- src/options/options_handler.cpp | 17 +-- test/unit/util/configuration_black.cpp | 3 - 17 files changed, 296 insertions(+), 177 deletions(-) create mode 100644 cmake/version-base.cmake create mode 100644 cmake/version-base.cmake.template create mode 100644 cmake/version.cmake create mode 100755 contrib/make-release.py delete mode 100644 src/base/GitInfo.cmake.in rename src/base/{git_versioninfo.cpp.in => versioninfo.cpp.in} (62%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 07ff3c76f..8f646e50a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,26 +22,10 @@ project(cvc5) include(CheckIPOSupported) include(GNUInstallDirs) +set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) -set(CVC5_MAJOR 1) # Major component of the version of cvc5. -set(CVC5_MINOR 0) # Minor component of the version of cvc5. -set(CVC5_RELEASE 0) # Release component of the version of cvc5. - -# Extraversion component of the version of cvc5. -set(CVC5_EXTRAVERSION "-prerelease") - -# Shared library versioning. Increment SOVERSION for every new cvc5 release. -set(CVC5_SOVERSION 1) +include(version) -# Full release string for cvc5. -if(CVC5_RELEASE) - set(CVC5_RELEASE_STRING - "${CVC5_MAJOR}.${CVC5_MINOR}.${CVC5_RELEASE}${CVC5_EXTRAVERSION}") -else() - set(CVC5_RELEASE_STRING "${CVC5_MAJOR}.${CVC5_MINOR}${CVC5_EXTRAVERSION}") -endif() - -set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) set(CMAKE_C_STANDARD 99) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_EXTENSIONS OFF) @@ -574,7 +558,7 @@ configure_package_config_file( write_basic_package_version_file( ${CMAKE_CURRENT_BINARY_DIR}/cvc5ConfigVersion.cmake - VERSION ${CVC5_RELEASE_STRING} + VERSION ${CVC5_VERSION} COMPATIBILITY ExactVersion ) @@ -604,7 +588,7 @@ get_directory_property(CVC5_DEFINITIONS COMPILE_DEFINITIONS) string(REPLACE ";" " " CVC5_DEFINITIONS "${CVC5_DEFINITIONS}") message("") -print_info("cvc5 ${CVC5_RELEASE_STRING}") +print_info("cvc5 ${CVC5_VERSION}") message("") if(ENABLE_COMP_INC_TRACK) print_config("Build profile " "${CVC5_BUILD_PROFILE_STRING} (incremental)") diff --git a/cmake/version-base.cmake b/cmake/version-base.cmake new file mode 100644 index 000000000..4bf7d28f5 --- /dev/null +++ b/cmake/version-base.cmake @@ -0,0 +1,14 @@ +# These are updated when making a release +set(CVC5_LAST_RELEASE "0.0.0") +set(CVC5_IS_RELEASE "false") + +# These are used in other places in cmake +# If possible, they are updated by version.cmake +set(GIT_BUILD "false") +set(CVC5_IS_RELEASE "false") +set(CVC5_VERSION "${CVC5_LAST_RELEASE}") +set(CVC5_FULL_VERSION "${CVC5_LAST_RELEASE}") +set(CVC5_GIT_INFO "") + +# Shared library versioning. Increment SOVERSION for every new cvc5 release. +set(CVC5_SOVERSION 1) diff --git a/cmake/version-base.cmake.template b/cmake/version-base.cmake.template new file mode 100644 index 000000000..e149ea380 --- /dev/null +++ b/cmake/version-base.cmake.template @@ -0,0 +1,14 @@ +# These are updated when making a release +set(CVC5_LAST_RELEASE "{{VERSION}}") +set(CVC5_IS_RELEASE "{{IS_RELEASE}}") + +# These are used in other places in cmake +# If possible, they are updated by version.cmake +set(GIT_BUILD "false") +set(CVC5_IS_RELEASE "false") +set(CVC5_VERSION "${CVC5_LAST_RELEASE}") +set(CVC5_FULL_VERSION "${CVC5_LAST_RELEASE}") +set(CVC5_GIT_INFO "") + +# Shared library versioning. Increment SOVERSION for every new cvc5 release. +set(CVC5_SOVERSION 1) diff --git a/cmake/version.cmake b/cmake/version.cmake new file mode 100644 index 000000000..fcc6c085a --- /dev/null +++ b/cmake/version.cmake @@ -0,0 +1,116 @@ +############################################################################### +# Top contributors (to current version): +# Aina Niemetz, Mathias Preiner +# +# This file is part of the cvc5 project. +# +# Copyright (c) 2009-2021 by the authors listed in the file AUTHORS +# in the top-level source directory and their institutional affiliations. +# All rights reserved. See the file COPYING in the top-level source +# directory for licensing information. +# ############################################################################# +# +# Responsible to identify the version of this source tree, expose this version +# information to the rest of cmake and properly update the versioninfo.cpp. +# +# Note that the above responsibilities are split among configure and build +# time. To achieve this, this file is both executed as a part of the regular +# cmake setup during configure time, but also adds a special target to call +# itself during build time to always keep versioninfo.cpp updated. +## + +if(CMAKE_SCRIPT_MODE_FILE) + # was run as standalone script + set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) +else() + # was run within the overall cmake project + # add target to update versioninfo.cpp at build time + add_custom_target(gen-versioninfo + BYPRODUCTS + ${CMAKE_BINARY_DIR}/src/base/versioninfo.cpp + COMMAND ${CMAKE_COMMAND} + -DPROJECT_SOURCE_DIR=${PROJECT_SOURCE_DIR} + -DCMAKE_BINARY_DIR=${CMAKE_BINARY_DIR} + -P ${PROJECT_SOURCE_DIR}/cmake/version.cmake) +endif() + +# include basic version information +include(version-base) + +# now use git to retrieve additional version information +find_package(Git) +if(GIT_FOUND) + # git is available + + # call git describe. If result is not 0 this is not a git repository + execute_process( + COMMAND ${GIT_EXECUTABLE} describe --long --tags --match cvc5-* + RESULT_VARIABLE GIT_RESULT + OUTPUT_VARIABLE GIT_DESCRIBE + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + if(GIT_RESULT EQUAL 0) + # it is a git working copy + + set(GIT_BUILD "true") + # get current git branch + execute_process( + COMMAND ${GIT_EXECUTABLE} rev-parse --abbrev-ref HEAD + RESULT_VARIABLE GIT_RESULT + OUTPUT_VARIABLE GIT_BRANCH + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + # result is != 0 if worktree is dirty + # note: git diff HEAD shows both staged and unstaged changes. + execute_process( + COMMAND ${GIT_EXECUTABLE} diff HEAD --quiet + RESULT_VARIABLE GIT_RESULT + ) + if(GIT_RESULT EQUAL 0) + set(GIT_DIRTY_MSG "") + else() + set(GIT_DIRTY_MSG " with local modifications") + endif() + + string(REGEX MATCH "^cvc5-([0-9.]+)-([0-9]+)-g([0-9a-f]+)$" MATCH "${GIT_DESCRIBE}") + if(NOT MATCH) + message(SEND_ERROR "Unexpected format from 'git describe': '${GIT_DESCRIBE}'") + endif() + set(GIT_LAST_TAG "${CMAKE_MATCH_1}") + set(GIT_COMMITS_SINCE_TAG "${CMAKE_MATCH_2}") + set(GIT_COMMIT "${CMAKE_MATCH_3}") + + if(GIT_COMMITS_SINCE_TAG EQUAL "0") + # this version *is* a tag + set(CVC5_IS_RELEASE "true") + set(CVC5_VERSION "${GIT_LAST_TAG}") + set(CVC5_FULL_VERSION "${GIT_LAST_TAG}") + set(CVC5_GIT_INFO "git tag ${GIT_LAST_TAG} branch ${GIT_BRANCH}${GIT_DIRTY_MSG}") + else() + # this version is not a tag + + # increment patch part of version + string(REGEX MATCHALL "[0-9]+" VERSION_LIST "${GIT_LAST_TAG}") + list(LENGTH VERSION_LIST VERSION_LIST_LENGTH) + # append .0 until we have a patch part + while(VERSION_LIST_LENGTH LESS "3") + list(APPEND VERSION_LIST "0") + list(LENGTH VERSION_LIST VERSION_LIST_LENGTH) + endwhile() + # increment patch part + list(POP_BACK VERSION_LIST VERSION_LAST_NUMBER) + math(EXPR VERSION_LAST_NUMBER "${VERSION_LAST_NUMBER} + 1") + list(APPEND VERSION_LIST ${VERSION_LAST_NUMBER}) + string(JOIN "." GIT_LAST_TAG ${VERSION_LIST}) + + set(CVC5_VERSION "${GIT_LAST_TAG}-dev") + set(CVC5_FULL_VERSION "${GIT_LAST_TAG}-dev.${GIT_COMMITS_SINCE_TAG}.${GIT_COMMIT}") + set(CVC5_GIT_INFO "git ${GIT_COMMIT} on branch ${GIT_BRANCH}${GIT_DIRTY_MSG}") + endif() + endif() +endif() + +# actually configure versioninfo.cpp +configure_file( + ${PROJECT_SOURCE_DIR}/src/base/versioninfo.cpp.in ${CMAKE_BINARY_DIR}/src/base/versioninfo.cpp) diff --git a/contrib/make-release.py b/contrib/make-release.py new file mode 100755 index 000000000..1d04d3952 --- /dev/null +++ b/contrib/make-release.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python3 + +import argparse +import collections +import logging +import os +import re +import subprocess +import sys + +args = None + + +def exec(cmd): + """Execute given command""" + return subprocess.check_output(cmd).decode().strip() + + +def parse_options(): + """Handle command line options""" + ap = argparse.ArgumentParser(description='Make a new release') + ap.add_argument('bump', + choices=['major', 'minor', 'patch'], + help='which version part to bump') + ap.add_argument('-v', + '--verbose', + action='store_true', + help='be more verbose') + global args + args = ap.parse_args() + + logging.basicConfig(format='[%(levelname)s] %(message)s') + if args.verbose: + logging.getLogger().setLevel(level=logging.DEBUG) + else: + logging.getLogger().setLevel(level=logging.INFO) + + +def identify_next_version(): + """Figure out the new version number""" + try: + curversion = exec(['git', 'describe', '--tags', '--match', 'cvc5-*']) + except: + logging.error('git describe was unable to produce a proper version') + sys.exit(1) + logging.debug('git version info: {}'.format(curversion)) + + re_release = re.compile('^cvc5-(\d+)\.(\d+)\.(\d+)') + m = re_release.match(curversion) + if m: + major, minor, patch = map(int, m.groups()) + if args.bump == 'major': + major += 1 + minor = 0 + patch = 0 + elif args.bump == 'minor': + minor += 1 + patch = 0 + elif args.bump == 'patch': + patch += 1 + version = "{}.{}.{}".format(major, minor, patch) + logging.debug('target version: {}'.format(version)) + return version + + logging.error( + "Did not understand current git version: '{}'".format(curversion)) + sys.exit(1) + + +def generate_cmake_version_file(version, is_release): + """Update the cmake version file""" + filename = os.path.join(os.path.dirname(os.path.dirname(__file__)), + 'cmake/version-base.cmake') + tpl = open(filename + '.template').read() + tpl = tpl.replace('{{VERSION}}', version) + tpl = tpl.replace('{{IS_RELEASE}}', 'true' if is_release else 'false') + open(filename, 'w').write(tpl) + + +def make_release_commit(version): + """Make the release commit""" + tagname = 'cvc5-{}'.format(version) + exec(['git', 'add', 'cmake/version-base.cmake']) + exec(['git', 'commit', '-m', 'Bump version to {}'.format(version)]) + exec(['git', 'tag', tagname]) + return tagname + + +def make_post_release_commit(version): + """Make the post-release commit""" + exec(['git', 'add', 'cmake/version-base.cmake']) + exec(['git', 'commit', '-m', 'Start post-release for {}'.format(version)]) + return exec(['git', 'rev-parse', 'HEAD']) + + +if __name__ == '__main__': + parse_options() + + # Compute next version + version = identify_next_version() + + # release commit + logging.info('Performing release commit') + generate_cmake_version_file(version, True) + tagname = make_release_commit(version) + + # post-release commit + logging.info('Performing post-release commit') + generate_cmake_version_file(version, False) + postcommit = make_post_release_commit(version) + + # Show commits and ask user to push + print('Please check the following commits carefully:') + subprocess.call(['git', 'show', tagname]) + subprocess.call(['git', 'show', postcommit]) + + print( + 'If you are sure you want to push this release, use the following command:' + ) + print('\tgit push --tags origin master') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1dd005dd7..67347bbf4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1308,7 +1308,7 @@ target_link_libraries(cvc5-obj PUBLIC $ $& solver) << Configuration::getVersionString(); if (Configuration::isGitBuild()) { - CVC5Message() << " [" << Configuration::getGitId() << "]"; + CVC5Message() << " [" << Configuration::getGitInfo() << "]"; } CVC5Message() << (Configuration::isDebugBuild() ? " DEBUG" : "") << " assertions:" diff --git a/src/options/options_handler.cpp b/src/options/options_handler.cpp index 0dfeb3b2c..93b08a858 100644 --- a/src/options/options_handler.cpp +++ b/src/options/options_handler.cpp @@ -451,20 +451,9 @@ void OptionsHandler::showConfiguration(const std::string& option, std::cout << Configuration::about() << std::endl; print_config("version", Configuration::getVersionString()); - if (Configuration::isGitBuild()) { - const char* branchName = Configuration::getGitBranchName(); - if (*branchName == '\0') - { - branchName = "-"; - } - std::stringstream ss; - ss << "git [" << branchName << " " - << std::string(Configuration::getGitCommit()).substr(0, 8) - << (Configuration::hasGitModifications() ? " (with modifications)" : "") - << "]"; - print_config("scm", ss.str()); + print_config("scm", Configuration::getGitInfo()); } else { @@ -474,9 +463,7 @@ void OptionsHandler::showConfiguration(const std::string& option, std::cout << std::endl; std::stringstream ss; - ss << Configuration::getVersionMajor() << "." - << Configuration::getVersionMinor() << "." - << Configuration::getVersionRelease(); + ss << Configuration::getVersionString(); print_config("library", ss.str()); std::cout << std::endl; diff --git a/test/unit/util/configuration_black.cpp b/test/unit/util/configuration_black.cpp index 0b54af43a..5e659ed28 100644 --- a/test/unit/util/configuration_black.cpp +++ b/test/unit/util/configuration_black.cpp @@ -84,9 +84,6 @@ TEST_F(TestUtilBlackConfiguration, versions) { // just test that the functions exist Configuration::getVersionString(); - Configuration::getVersionMajor(); - Configuration::getVersionMinor(); - Configuration::getVersionRelease(); } TEST_F(TestUtilBlackConfiguration, about) -- 2.30.2