diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 000000000..be3509e2a --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,65 @@ +cmake_minimum_required(VERSION 3.19) # string(json +cmake_policy(SET CMP0074 NEW) +if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0") + # Avoid warning about DOWNLOAD_EXTRACT_TIMESTAMP in CMake 3.24: + cmake_policy(SET CMP0135 NEW) +endif() +if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.25.0") + # needed by DynamicVersion + cmake_policy(SET CMP0140 NEW) +endif() + +############################# Version and Metadata ############################# + +# can't use PROJECT_SOURCE_DIR etc. before project() call +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules) +include(DynamicVersion) +dynamic_version( + PROJECT_PREFIX Libint2Compiler_ + GIT_ARCHIVAL_FILE ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/.git_archival.txt + OUTPUT_COMMIT LibintRepository_COMMIT + OUTPUT_VERSION LibintRepository_VERSION + OUTPUT_DESCRIBE LibintRepository_DESCRIBE + OUTPUT_DISTANCE LibintRepository_DISTANCE + ) + +project( + Libint2Compiler + VERSION ${LibintRepository_VERSION} + DESCRIPTION + "A library for the evaluation of molecular integrals of many-body operators over Gaussian functions" + HOMEPAGE_URL "http://libint.valeyev.net" + LANGUAGES CXX + ) + # * http://libint.valeyev.net/ redirects to https://github.com/evaleev/libint + +set(${PROJECT_NAME}_AUTHORS "Edward F. Valeev") +set(${PROJECT_NAME}_LICENSE "GPL-3.0 for generator; LGPL-3.0 for generated") + +# along with project(... VERSION) above scanned by dynamic_version() from `git +# describe`, these are the authoritative version source (formerly in configure.ac) +set(LIBINT_BUILDID "post999") +set(LIBINT_SOVERSION "2:3:0") +set(LIBINT_DOI "10.5281/zenodo.10369117") # 2.8.0 + +include(int_versions) + +set(L2 Libint2) # Namespace +set(pnv libint2) # projectnameversion + +################################### Overview ################################### + +# CMake build overview: +# +# >>> ls +# cmake/ COPYING src/ tests/ ... +# >>> cmake -S. -Bbuild -GNinja -DCMAKE_INSTALL_PREFIX=/path/to/install-libint ... +# ... +# -- Generating done +# -- Build files have been written to: /current/dir/build +# >>> cmake --build build --target install + +################################## Main Project ################################# + +# STRICTLY TEMPORARY FOR DEMONSTRATION PURPOSES +configure_file(src/lib/libint/configuration.cc configuration.cc @ONLY) diff --git a/INSTALL.md b/INSTALL.md index 33c868df6..fb9ede0e2 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -130,3 +130,27 @@ Eventually, these will be CMake Components, too. bs - library integrals use ordering bagel + standard = bagel bo - library integrals use ordering + orca ``` + +### Interfacing + +Eventually (approximately 2.9.0 CMake-based), additional functions will be available to retrive Libint version, commit, and literature citation. Below are outputs at the libtool stage. + +``` +auto Mmp = libint2::libint_version(); +printf("Version: Numeric=%s Sortable=%s Commit=%s\n", libint2::libint_version_string(false).c_str(), libint2::libint_version_string(true).c_str(), libint2::libint_commit().c_str()); +printf("Version: Major=%d minor=%d patch=%d\n", std::get<0>(Mmp), std::get<1>(Mmp), std::get<2>(Mmp)); +printf("Citation: DOI=%s Ref=%s\n", libint2::libint_reference_doi().c_str(), libint2::libint_reference().c_str()); +printf("Citation: BibTeX=%s\n", libint2::libint_bibtex().c_str()); +``` +``` +Version: Numeric=2.8.0 Sortable= Commit= +Version: Major=2 minor=8 patch=0 +Citation: DOI= Ref=Libint: , Version Edward F. Valeev, http://libint.valeyev.net/ +Citation: BibTeX=@Misc{Libint2, + author = {E.~F.~Valeev}, + title = {\textsc{Libint}: }, + howpublished = {http://libint.valeyev.net/}, + note = {version }, + year = {} +} +``` diff --git a/cmake/modules/.git_archival.txt b/cmake/modules/.git_archival.txt new file mode 100644 index 000000000..9d376cd1d --- /dev/null +++ b/cmake/modules/.git_archival.txt @@ -0,0 +1,4 @@ +node: $Format:%H$ +node-date: $Format:%cI$ +describe-name: $Format:%(describe:tags=true,match=?[0-9.]*)$ +ref-names: $Format:%D$ diff --git a/cmake/modules/DynamicVersion.cmake b/cmake/modules/DynamicVersion.cmake new file mode 100644 index 000000000..bd4e684ad --- /dev/null +++ b/cmake/modules/DynamicVersion.cmake @@ -0,0 +1,375 @@ +# from https://github.com/LecrisUT/CMakeExtraUtils/commits/main on 11 Dec 2023 at 26450da +# * 12 Dec 2023 added distance variable and field + +## Helper to get dynamic version +# Format is made compatible with python's setuptools_scm (https://github.com/pypa/setuptools_scm#git-archives) + +function(dynamic_version) + # Configure project to use dynamic versioning + # + # Named arguments:: + # PROJECT_PREFIX (string): Prefix to be used for namespacing targets, typically ${PROJECT_NAME} + # OUTPUT_VERSION (string) [PROJECT_VERSION]: Variable where to save the calculated version + # OUTPUT_DESCRIBE (string) [GIT_DESCRIBE]: Variable where to save the pure git_describe + # OUTPUT_COMMIT (string) [GIT_COMMIT]: Variable where to save the git commit + # OUTPUT_DISTANCE (string) [GIT_DISTANCE]: Variable where to save the distance from git tag + # PROJECT_SOURCE (path) [${CMAKE_CURRENT_SOURCE_DIR}]: Location of the project source. + # (either extracted git archive or git clone) + # GIT_ARCHIVAL_FILE (path) [${PROJECT_SOURCE}/.git_archival.txt]: Location of .git_archival.txt + # FALLBACK_VERSION (string): Fallback version + # FALLBACK_HASH (string): Fallback git hash. If not defined target GitHash will not be created if project is not a + # git repo + # TMP_FOLDER (path) [${CMAKE_CURRENT_BINARY_DIR}/tmp]: Temporary path to store temporary files + # OUTPUT_FOLDER (path) [${CMAKE_CURRENT_BINARY_DIR}]: Path where to store generated files + # + # Options:: + # ALLOW_FAILS: Do not return with FATAL_ERROR. Developer is responsible for setting appropriate version if fails + # + # Targets:: + # ${PROJECT_PREFIX}Version: Target that recalculates the dynamic version each time + # ${PROJECT_PREFIX}GitHash: + # + # Generated files:: + # (Note: files are regenerated only when they change) + # ${OUTPUT_FOLDER}/.DynamicVersion.json: All computed data of DynamicVersion + # ${OUTPUT_FOLDER}/.version: Extracted version + # ${OUTPUT_FOLDER}/.git_describe: Computed git describe + # ${OUTPUT_FOLDER}/.git_commit: Current commit + + set(ARGS_Options "") + set(ARGS_OneValue "") + set(ARGS_MultiValue "") + list(APPEND ARGS_Options + ALLOW_FAILS + ) + list(APPEND ARGS_OneValue + PROJECT_PREFIX + OUTPUT_VERSION + OUTPUT_DESCRIBE + OUTPUT_COMMIT + OUTPUT_DISTANCE + PROJECT_SOURCE + GIT_ARCHIVAL_FILE + FALLBACK_VERSION + FALLBACK_HASH + TMP_FOLDER + OUTPUT_FOLDER + ) + + cmake_parse_arguments(ARGS "${ARGS_Options}" "${ARGS_OneValue}" "${ARGS_MultiValue}" ${ARGN}) + + set(DynamicVersion_ARGS "") + + # Set default values + if (NOT DEFINED ARGS_OUTPUT_VERSION) + set(ARGS_OUTPUT_VERSION PROJECT_VERSION) + endif () + if (NOT DEFINED ARGS_OUTPUT_DESCRIBE) + set(ARGS_OUTPUT_DESCRIBE GIT_DESCRIBE) + endif () + if (NOT DEFINED ARGS_OUTPUT_COMMIT) + set(ARGS_OUTPUT_COMMIT GIT_COMMIT) + endif () + if (NOT DEFINED ARGS_OUTPUT_DISTANCE) + set(ARGS_OUTPUT_DISTANCE GIT_DISTANCE) + endif () + if (NOT DEFINED ARGS_PROJECT_SOURCE) + set(ARGS_PROJECT_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}) + endif () + if (NOT DEFINED ARGS_GIT_ARCHIVAL_FILE) + set(ARGS_GIT_ARCHIVAL_FILE ${ARGS_PROJECT_SOURCE}/.git_archival.txt) + endif () + if (DEFINED ARGS_FALLBACK_VERSION OR ARGS_ALLOW_FAILS) + # If we have a fallback version or it is specified it is ok if this fails, don't make messages FATAL_ERROR + set(error_message_type AUTHOR_WARNING) + else () + # Otherwise it should + set(error_message_type FATAL_ERROR) + endif () + if (NOT ARGS_PROJECT_PREFIX) + message(AUTHOR_WARNING + "DynamicVersion: No PROJECT_PREFIX was given. Please provide one to avoid target name clashes") + elseif (NOT ARGS_PROJECT_PREFIX MATCHES ".*_$") + # Append an underscore _ to the prefix if not provided + message(AUTHOR_WARNING + "DynamicVersion: PROJECT_PREFIX did not contain an underscore, please add it for clarity") + set(ARGS_PROJECT_PREFIX ${ARGS_PROJECT_PREFIX}_) + endif () + if (NOT DEFINED ARGS_TMP_FOLDER) + set(ARGS_TMP_FOLDER ${CMAKE_CURRENT_BINARY_DIR}/tmp) + endif () + if (NOT DEFINED ARGS_OUTPUT_FOLDER) + set(ARGS_OUTPUT_FOLDER ${CMAKE_CURRENT_BINARY_DIR}) + endif () + if (ARGS_OUTPUT_FOLDER EQUAL ARGS_TMP_FOLDER) + message(FATAL_ERROR + "DynamicVersion misconfigured: Cannot have both OUTPUT_FOLDER and TMP_FOLDER point to the same path") + endif () + + list(APPEND DynamicVersion_ARGS + PROJECT_SOURCE ${ARGS_PROJECT_SOURCE} + GIT_ARCHIVAL_FILE ${ARGS_GIT_ARCHIVAL_FILE} + TMP_FOLDER ${ARGS_TMP_FOLDER} + ) + if (DEFINED ARGS_FALLBACK_VERSION) + list(APPEND DynamicVersion_ARGS + FALLBACK_VERSION ${ARGS_FALLBACK_VERSION}) + endif () + if (DEFINED ARGS_FALLBACK_HASH) + list(APPEND DynamicVersion_ARGS + FALLBACK_HASH ${ARGS_FALLBACK_HASH}) + endif () + if (ARGS_ALLOW_FAILS) + list(APPEND DynamicVersion_ARGS ALLOW_FAILS) + endif () + # Normalize DynamicVersion_ARGS to be passed as string + list(JOIN DynamicVersion_ARGS "\\;" DynamicVersion_ARGS) + + # Execute get_dynamic_version once to know the current configuration + execute_process(COMMAND ${CMAKE_COMMAND} + -DDynamicVersion_RUN:BOOL=True + # Note: DynamicVersion_ARGS cannot be escaped with "" + -DDynamicVersion_ARGS:STRING=${DynamicVersion_ARGS} + -P ${CMAKE_CURRENT_FUNCTION_LIST_FILE} + COMMAND_ERROR_IS_FATAL ANY) + + # Copy all configured files + foreach (file IN ITEMS .DynamicVersion.json .version .git_describe .git_commit) + if (EXISTS ${file}) + file(COPY_FILE ${ARGS_TMP_FOLDER}/${file} ${ARGS_OUTPUT_FOLDER}/${file}) + endif () + endforeach () + + # Check configuration state + file(READ ${ARGS_TMP_FOLDER}/.DynamicVersion.json data) + string(JSON failed GET ${data} failed) + string(JSON ${ARGS_OUTPUT_VERSION} ERROR_VARIABLE _ GET ${data} version) + string(JSON ${ARGS_OUTPUT_DESCRIBE} ERROR_VARIABLE _ GET ${data} describe) + string(JSON ${ARGS_OUTPUT_COMMIT} ERROR_VARIABLE _ GET ${data} commit) + string(JSON ${ARGS_OUTPUT_DISTANCE} ERROR_VARIABLE _ GET ${data} distance) + + # Configure targets + if (failed) + # If configuration failed, create dummy targets + add_custom_target(${ARGS_PROJECT_PREFIX}Version + COMMAND ${CMAKE_COMMAND} -E true) + add_custom_target(${ARGS_PROJECT_PREFIX}GitHash + COMMAND ${CMAKE_COMMAND} -E true) + else () + # Otherwise create the targets outputting to the appropriate files + add_custom_target(${ARGS_PROJECT_PREFIX}DynamicVersion ALL + BYPRODUCTS ${ARGS_TMP_FOLDER}/.DynamicVersion.json ${ARGS_TMP_FOLDER}/.git_describe ${ARGS_TMP_FOLDER}/.version + COMMAND ${CMAKE_COMMAND} + -DDynamicVersion_RUN:BOOL=True + # Note: For some reason DynamicVersion_ARGS needs "" here, but it doesn't in execute_process + -DDynamicVersion_ARGS:STRING="${DynamicVersion_ARGS}" + -P ${CMAKE_CURRENT_FUNCTION_LIST_FILE} + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${ARGS_TMP_FOLDER}/.DynamicVersion.json ${ARGS_OUTPUT_FOLDER}/.DynamicVersion.json + ) + add_custom_target(${ARGS_PROJECT_PREFIX}Version ALL + DEPENDS ${ARGS_PROJECT_PREFIX}DynamicVersion + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${ARGS_TMP_FOLDER}/.git_describe ${ARGS_OUTPUT_FOLDER}/.git_describe + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${ARGS_TMP_FOLDER}/.version ${ARGS_OUTPUT_FOLDER}/.version + ) + add_custom_target(${ARGS_PROJECT_PREFIX}GitHash + DEPENDS ${ARGS_PROJECT_PREFIX}DynamicVersion + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${ARGS_TMP_FOLDER}/.git_commit ${ARGS_OUTPUT_FOLDER}/.git_commit + ) + endif () + + # This ensures that the project is reconfigured (at least at second run) whenever the version changes + set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} APPEND + PROPERTY CMAKE_CONFIGURE_DEPENDS ${ARGS_OUTPUT_FOLDER}/.version) + + message(VERBOSE + "DynamicVersion: Calculated version = ${${ARGS_OUTPUT_VERSION}}") + + if (CMAKE_VERSION VERSION_LESS 3.25) + # TODO: Remove when cmake 3.25 is commonly distributed + set(${ARGS_OUTPUT_DESCRIBE} ${${ARGS_OUTPUT_DESCRIBE}} PARENT_SCOPE) + set(${ARGS_OUTPUT_VERSION} ${${ARGS_OUTPUT_VERSION}} PARENT_SCOPE) + set(${ARGS_OUTPUT_COMMIT} ${${ARGS_OUTPUT_COMMIT}} PARENT_SCOPE) + set(${ARGS_OUTPUT_DISTANCE} ${${ARGS_OUTPUT_DISTANCE}} PARENT_SCOPE) + endif () + return(PROPAGATE + ${ARGS_OUTPUT_DESCRIBE} + ${ARGS_OUTPUT_VERSION} + ${ARGS_OUTPUT_COMMIT} + ${ARGS_OUTPUT_DISTANCE} + ) +endfunction() + +function(get_dynamic_version) + # Compute the dynamic version + # + # Named arguments:: + # PROJECT_SOURCE (path): Location of the project source. + # (either extracted git archive or git clone) + # GIT_ARCHIVAL_FILE (path): Location of .git_archival.txt + # FALLBACK_VERSION (string): Fallback version + # FALLBACK_HASH (string): Fallback git hash. If not defined target GitHash will not be created if project is not a + # git repo + # TMP_FOLDER (path): Temporary path to store temporary files + + set(ARGS_Options "") + set(ARGS_OneValue "") + set(ARGS_MultiValue "") + list(APPEND ARGS_OneValue + PROJECT_SOURCE + GIT_ARCHIVAL_FILE + FALLBACK_VERSION + FALLBACK_HASH + TMP_FOLDER + ) + list(APPEND ARGS_Options + ALLOW_FAILS + ) + + cmake_parse_arguments(ARGS "${ARGS_Options}" "${ARGS_OneValue}" "${ARGS_MultiValue}" ${ARGN}) + + if (DEFINED ARGS_FALLBACK_VERSION OR ARGS_ALLOW_FAILS) + # If we have a fallback version or it is specified it is ok if this fails, don't make messages FATAL_ERROR + set(error_message_type AUTHOR_WARNING) + else () + # Otherwise it should fail + set(error_message_type FATAL_ERROR) + endif () + + set(data "{}") + # Default set + string(JSON data SET ${data} failed true) + if (ARGS_ALLOW_FAILS) + string(JSON data SET ${data} allow-fails true) + else () + string(JSON data SET ${data} allow-fails false) + endif () + + # Set fallback values + if (DEFINED ARGS_FALLBACK_VERSION) + string(JSON data SET + ${data} version ${ARGS_FALLBACK_VERSION}) + file(WRITE ${ARGS_TMP_FOLDER}/.DynamicVersion.json ${data}) + file(WRITE ${ARGS_TMP_FOLDER}/.version ${ARGS_FALLBACK_VERSION}) + endif () + if (DEFINED ARGS_FALLBACK_HASH) + string(JSON data SET + ${data} commit ${ARGS_FALLBACK_HASH}) + file(WRITE ${ARGS_TMP_FOLDER}/.DynamicVersion.json ${data}) + file(WRITE ${ARGS_TMP_FOLDER}/.git_commit ${ARGS_FALLBACK_HASH}) + endif () + + + if (NOT EXISTS ${ARGS_GIT_ARCHIVAL_FILE}) + # If git_archival.txt is missing, project is ill-formed + message(${error_message_type} + "DynamicVersion: Missing file .git_archival.txt\n" + " .git_archival.txt: ${ARGS_GIT_ARCHIVAL_FILE}") + return() + endif () + + # Get version dynamically from git_archival.txt + file(STRINGS ${ARGS_GIT_ARCHIVAL_FILE} describe-name + REGEX "^describe-name:.*") + if (NOT describe-name) + # If git_archival.txt does not contain the field "describe-name:", it is ill-formed + message(${error_message_type} + "DynamicVersion: Missing string \"describe-name\" in .git_archival.txt\n" + " .git_archival.txt: ${ARGS_GIT_ARCHIVAL_FILE}") + return() + endif () + + # Try to get the version tag of the form `vX.Y.Z` or `X.Y.Z` (with arbitrary suffix) + if (describe-name MATCHES "^describe-name:[ ]?([v]?([0-9\\.]+).*)") + # First matched group is the full `git describe` of the latest tag + # Second matched group is only the version, i.e. `X.Y.Z` + string(JSON data SET + ${data} describe \"${CMAKE_MATCH_1}\") + file(WRITE ${ARGS_TMP_FOLDER}/.git_describe ${CMAKE_MATCH_1}) + string(JSON data SET + ${data} version \"${CMAKE_MATCH_2}\") + file(WRITE ${ARGS_TMP_FOLDER}/.version ${CMAKE_MATCH_2}) + # Get commit hash + file(STRINGS ${ARGS_GIT_ARCHIVAL_FILE} node + REGEX "^node:[ ]?(.*)") + string(JSON data SET + ${data} commit \"${CMAKE_MATCH_1}\") + file(WRITE ${ARGS_TMP_FOLDER}/.git_commit ${CMAKE_MATCH_1}) + message(DEBUG "DynamicVersion: Found appropriate tag in .git_archival.txt file") + else () + # If not it has to be computed from the git archive + find_package(Git REQUIRED) + # Test if project is a git repository + execute_process(COMMAND ${GIT_EXECUTABLE} status + WORKING_DIRECTORY ${ARGS_PROJECT_SOURCE} + RESULT_VARIABLE git_status_result + OUTPUT_QUIET) + if (NOT git_status_result EQUAL 0) + message(${error_message_type} + "DynamicVersion: Project source is neither a git repository nor a git archive:\n" + " Source: ${ARGS_PROJECT_SOURCE}") + return() + endif () + # Get most recent commit hash + execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse HEAD + WORKING_DIRECTORY ${ARGS_PROJECT_SOURCE} + OUTPUT_VARIABLE git-hash + OUTPUT_STRIP_TRAILING_WHITESPACE + COMMAND_ERROR_IS_FATAL ANY) + # Get version and describe-name + execute_process(COMMAND ${GIT_EXECUTABLE} describe --tags --match=?[0-9.]* + WORKING_DIRECTORY ${ARGS_PROJECT_SOURCE} + OUTPUT_VARIABLE describe-name + OUTPUT_STRIP_TRAILING_WHITESPACE + COMMAND_ERROR_IS_FATAL ANY) + # Match any part containing digits and periods (strips out rc and so on) + if (NOT describe-name MATCHES "^([v]?([0-9\\.]+).*)") + message(${error_message_type} + "DynamicVersion: Version tag is ill-formatted\n" + " Describe-name: ${describe-name}") + return() + endif () + string(JSON data SET + ${data} describe \"${CMAKE_MATCH_1}\") + file(WRITE ${ARGS_TMP_FOLDER}/.git_describe ${CMAKE_MATCH_1}) + string(JSON data SET + ${data} version \"${CMAKE_MATCH_2}\") + file(WRITE ${ARGS_TMP_FOLDER}/.version ${CMAKE_MATCH_2}) + # Get full describe with distance + execute_process(COMMAND ${GIT_EXECUTABLE} describe --tags --long --match=?[0-9.]* + WORKING_DIRECTORY ${ARGS_PROJECT_SOURCE} + OUTPUT_VARIABLE describe-name-long + OUTPUT_STRIP_TRAILING_WHITESPACE + COMMAND_ERROR_IS_FATAL ANY) + # Match version (as above) and distance + if (NOT describe-name-long MATCHES "^([v]?([0-9\\.]+)-([0-9]+)-.*)") + message(${error_message_type} + "DynamicVersion: Version tag is ill-formatted\n" + " Describe-name-long: ${describe-name-long}") + return() + endif () + string(JSON data SET + ${data} distance \"${CMAKE_MATCH_3}\") + file(WRITE ${ARGS_TMP_FOLDER}/.git_distance ${CMAKE_MATCH_3}) + string(JSON data SET + ${data} commit \"${git-hash}\") + file(WRITE ${ARGS_TMP_FOLDER}/.git_commit ${git-hash}) + message(DEBUG "DynamicVersion: Found appropriate tag from git") + endif () + + # Mark success and output results + string(JSON data SET ${data} failed false) + message(DEBUG + "DynamicVersion: Computed data:\n" + " data = ${data}") + file(WRITE ${ARGS_TMP_FOLDER}/.DynamicVersion.json ${data}) +endfunction() + +# Logic to run get_dynamic_version() by running this script +if (DynamicVersion_RUN) + if (NOT DEFINED DynamicVersion_ARGS) + message(FATAL_ERROR + "DynamicVersion: DynamicVersion_ARGS not defined") + endif () + get_dynamic_version(${DynamicVersion_ARGS}) +endif () + diff --git a/cmake/modules/int_versions.cmake b/cmake/modules/int_versions.cmake new file mode 100644 index 000000000..973e51b46 --- /dev/null +++ b/cmake/modules/int_versions.cmake @@ -0,0 +1,72 @@ +# top-level CMakeLists.txt has defined: +# * PROJECT_VERSION_{MAJOR|MINOR|PATCH} through `project(... VERSION)` +# * Libint2Compiler_DESCRIPTION through `project(... DESCRIPTION)` +# * LIBINT_BUILDID +# * LIBINT_SOVERSION +# * LIBINT_DOI +# * LibintRepository_{VERSION|DESCRIBE|COMMIT|DISTANCE} through `dynamic_version()` + +# note that 3rd version integer is PATCH in CMake and MICRO in Libint +# see also int_computed.cmake.in for transmitting these values to the library's CMake +# note that with the dynamic/git scheme, it's important for repo to be up-to-date with tags + + +# <<< Sortable Version >>> + +message(DEBUG "LibintRepository_VERSION ${LibintRepository_VERSION}") +message(DEBUG "LibintRepository_COMMIT ${LibintRepository_COMMIT}") +message(DEBUG "LibintRepository_DISTANCE ${LibintRepository_DISTANCE}") +message(DEBUG "LibintRepository_DESCRIBE ${LibintRepository_DESCRIBE}") + +if (LibintRepository_DISTANCE STREQUAL "0") + set(LIBINT_SORTABLE_VERSION "${LibintRepository_VERSION}") +else() + set(LIBINT_SORTABLE_VERSION "${LibintRepository_VERSION}.post${LibintRepository_DISTANCE}") +endif() + +string(SUBSTRING ${LibintRepository_COMMIT} 0 7 LIBINT_GIT_COMMIT) +message(DEBUG "LIBINT_GIT_COMMIT ${LIBINT_GIT_COMMIT}") + +# Below goes into BibTeX citation. Currently year of export. For year of tag, parse: +# `git show -s --no-notes --date=short --pretty='%cd' v2.7.2` responds: 2022-06-20 +string(TIMESTAMP LIBINT_VERSION_YEAR "%Y") +message(DEBUG "LIBINT_VERSION_YEAR ${LIBINT_VERSION_YEAR}") + +set(LIBINT_DESCRIPTION "${Libint2Compiler_DESCRIPTION}") +message(DEBUG "LIBINT_DESCRIPTION ${LIBINT_DESCRIPTION}") + +# <<< Build Version >>> + +set(LIBINT_MAJOR_VERSION ${PROJECT_VERSION_MAJOR}) +set(LIBINT_MINOR_VERSION ${PROJECT_VERSION_MINOR}) +set(LIBINT_MICRO_VERSION ${PROJECT_VERSION_PATCH}) + +set(LIBINT_VERSION ${LIBINT_MAJOR_VERSION}.${LIBINT_MINOR_VERSION}.${LIBINT_MICRO_VERSION}) + + +# <<< Dev Version >>> + +if (LIBINT_BUILDID) + set(LIBINT_EXT_VERSION ${LIBINT_VERSION}-${LIBINT_BUILDID}) +else() + set(LIBINT_EXT_VERSION ${LIBINT_VERSION}) +endif() + +message(STATUS "Version: Full ${LIBINT_EXT_VERSION} Numeric ${LIBINT_VERSION} Sortable ${LIBINT_SORTABLE_VERSION}") + +if (NOT(LibintRepository_VERSION STREQUAL LIBINT_VERSION)) + message(AUTHOR_WARNING + "Version processing has gone wrong: ${LibintRepository_VERSION STREQUAL} != ${LIBINT_VERSION}") +endif() + + +# <<< ABI Version >>> + +string(REPLACE ":" ";" LIBINT_SOVERSION_LIST ${LIBINT_SOVERSION}) + +list(GET LIBINT_SOVERSION_LIST 0 LIBINT_CURRENT_SOVERSION) +list(GET LIBINT_SOVERSION_LIST 1 LIBINT_REVISION_SOVERSION) +list(GET LIBINT_SOVERSION_LIST 2 LIBINT_AGE_SOVERSION) + +math(EXPR LIBINT_MAJOR_SOVERSION "${LIBINT_CURRENT_SOVERSION} - ${LIBINT_AGE_SOVERSION}") +message(STATUS "SO Version: Full ${LIBINT_SOVERSION} Major ${LIBINT_MAJOR_SOVERSION}") diff --git a/include/libint2/util/configuration.h b/include/libint2/util/configuration.h index 28edaabef..be39356d0 100644 --- a/include/libint2/util/configuration.h +++ b/include/libint2/util/configuration.h @@ -21,15 +21,43 @@ #ifndef _libint2_include_libint2_util_configuration_h_ #define _libint2_include_libint2_util_configuration_h_ +#include + /* Runtime accessor for the library configuration: integral derivatives, AM, orderings, etc. @return the semicolon-separated strings from CMake components */ -const char* configuration_accessor(); +const char *configuration_accessor(); + +/* Get the major, minor, and micro version of Libint */ +void libint_version(int *, int *, int *); + +/* Get the version of Libint as a string + @return the version string. At release, strictly "M.m.p" (no alpha/rc/etc.). + Beyond release (arg=true), returns "M.m.p.postD" where D is distance from + release. Beyond release (arg=false), returns most recent release, "M.m.p". */ +const char *libint_version_string(bool); + +/* Get the git commit at which library was generated + @return the commit as a 7-char abbreviated string */ +const char *libint_commit(void); + +/* Literature citation + @return the citation string including description and version */ +const char *libint_reference(void); + +/* Literature citation DOI + @return the string of DOI for latest tag */ +const char *libint_reference_doi(void); + +/* BibTeX for citing Libint + @return the string for literature citation */ +const char *libint_bibtex(void); #ifdef __cplusplus #include #include #include +#include #include namespace libint2 { @@ -57,6 +85,54 @@ inline bool supports(std::string component) { (std::find(seglist.begin(), seglist.end(), component) != seglist.end()); return tf; } + +/// Get the major, minor, and micro version of Libint */ +/// @return the components of the last release +inline std::tuple libint_version() { + int vmajor, vminor, vmicro; + ::libint_version(&vmajor, &vminor, &vmicro); + return std::make_tuple(vmajor, vminor, vmicro); +} + +/// Get the version of Libint as a string +/// @param[in] whether to return the simple-sortable last release or a +/// per-commit version +/// @return the version string. At release, strictly "M.m.p" (no alpha/rc/etc.). +/// Beyond release (ext=true), returns "M.m.p.postD" where D is distance from +/// release. Beyond release (ext=false), returns most recent release, "M.m.p". +inline std::string libint_version_string(bool ext = true) { + std::string version = ::libint_version_string(ext); + return version; +} + +/// Get the git commit at which library was generated +/// @return the commit as a 7-char abbreviated string +inline std::string libint_commit(void) { + std::string commit = ::libint_commit(); + return commit; +} + +/// Literature citation +/// @return the citation string including description and version +inline std::string libint_reference(void) { + std::string ref = ::libint_reference(); + return ref; +} + +/// Literature citation DOI +/// @return the string of DOI for latest tag +inline std::string libint_reference_doi(void) { + std::string ref = ::libint_reference_doi(); + return ref; +} + +/// BibTeX for citing Libint +/// @return the string for literature citation +inline std::string libint_bibtex(void) { + std::string ref = ::libint_bibtex(); + return ref; +} + } // namespace libint2 #endif /* C++ guard */ diff --git a/src/lib/libint/configuration.cc b/src/lib/libint/configuration.cc index b310ea8e5..4945f6c50 100644 --- a/src/lib/libint/configuration.cc +++ b/src/lib/libint/configuration.cc @@ -18,10 +18,49 @@ * */ -/* Runtime accessor for the library configuration: - integral derivatives, AM, orderings, etc. - @return the semicolon-separated strings from CMake components */ -const char* configuration_accessor() { +#include + +#include +#include + +const char *configuration_accessor(void) { // return "@Libint2_CONFIG_COMPONENTS@"; return "(nyi)"; } + +void libint_version(int *major, int *minor, int *micro) { + *major = -1; + *minor = -1; + *micro = -1; + std::sscanf(libint_version_string(false), "%d.%d.%d", major, minor, micro); +} + +const char *libint_version_string(bool ext) { + if (ext) + return "@LIBINT_SORTABLE_VERSION@"; + else + return "@LIBINT_VERSION@"; +} + +const char *libint_commit(void) { return "@LIBINT_GIT_COMMIT@"; } + +const char *libint_reference(void) { + std::string ref = "Libint: @LIBINT_DESCRIPTION@, Version " + + std::string(libint_version_string(true)) + + " Edward F. Valeev, http://libint.valeyev.net/"; + + auto slen = ref.length(); + char *cref = new char[slen + 1]; + std::memcpy(cref, ref.c_str(), slen); + cref[slen] = '\0'; + return cref; +} + +const char *libint_reference_doi(void) { return "@LIBINT_DOI@"; } + +const char *libint_bibtex(void) { + return "@Misc{Libint2,\n author = {E.~F.~Valeev},\n title = " + "{\\textsc{Libint}: @LIBINT_DESCRIPTION@},\n " + "howpublished = {http://libint.valeyev.net/},\n note = {version " + "@Libint2_VERSION@},\n year = {@LIBINT_VERSION_YEAR@}\n}\n"; +} diff --git a/tests/unit/test.cc b/tests/unit/test.cc index a6357c4d5..c84a5898a 100644 --- a/tests/unit/test.cc +++ b/tests/unit/test.cc @@ -66,6 +66,16 @@ int main(int argc, char* argv[]) { libint2::configuration_accessor().c_str()); printf("Supports: dddd=%d mmmm=%d FF=%d\n", libint2::supports("eri_dddd_d0"), libint2::supports("eri_mmmm_d0"), libint2::supports("eri_FF_d0")); + auto Mmp = libint2::libint_version(); + printf("Version: Numeric=%s Sortable=%s Commit=%s\n", + libint2::libint_version_string(false).c_str(), + libint2::libint_version_string(true).c_str(), + libint2::libint_commit().c_str()); + printf("Version: Major=%d minor=%d patch=%d\n", std::get<0>(Mmp), + std::get<1>(Mmp), std::get<2>(Mmp)); + printf("Citation: DOI=%s Ref=%s\n", libint2::libint_reference_doi().c_str(), + libint2::libint_reference().c_str()); + printf("Citation: BibTeX=%s\n", libint2::libint_bibtex().c_str()); #ifdef LIBINT_HAS_MPFR // default to 256 bits of precision for mpf_class