From 8d4bb6207ce84bcb2c2be5f1619a67f377b3c2f8 Mon Sep 17 00:00:00 2001 From: Tobias Wolf Date: Fri, 24 Jul 2026 19:07:08 -0500 Subject: [PATCH 1/2] fix: stop version.h dropping zero-valued version components version.h.in declared ALPS_VERSION_MAJOR/MINOR/PATCH with cmakedefine, which emits "/* #undef NAME */" when the substituted value is false-y. CMake counts 0 as false-y, so any x.y.0 release generated a header with that component silently missing. 2.4.0 would have tripped it. Use a plain #define for every macro the build unconditionally sets. Keep cmakedefine only for ALPS_XML_ALTERNATE_DIR, which the build never sets and parser/xslt_path.C guards with #ifdef. Also add ALPS_VERSION_NUMBER/ALPS_VERSION_NUM() for preprocessor version comparisons (BOOST_VERSION packing), and drop two macros: ALPS_SVN_REVISION, which expanded a variable unset since the SVN migration and was always #undef, and ALPS_SRCDIR, which baked the build machine's source path into an installed header for one line of pconfig output. Refs #95 Co-Authored-By: Claude Opus 5 --- src/alps/version.h.in | 40 ++++++++++++++++++++++++++-------------- tool/pconfig.C | 1 - 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/src/alps/version.h.in b/src/alps/version.h.in index 6543db8b2..2035029e9 100644 --- a/src/alps/version.h.in +++ b/src/alps/version.h.in @@ -30,34 +30,46 @@ #ifndef ALPS_VERSION_H #define ALPS_VERSION_H +// Plain #define for everything the build always sets: the cmakedefine directive +// emits an "undef" when its value is false-y, and CMake counts 0 as false-y, so +// a zero component (2.4.0) would vanish. It is used below only where the macro +// is genuinely optional. (configure_file matches that directive even inside a +// comment, hence no leading '#' on it here.) + // ALPS version -#cmakedefine ALPS_VERSION "@ALPS_VERSION@" +#define ALPS_VERSION "@ALPS_VERSION@" + +#define ALPS_VERSION_MAJOR @ALPS_VERSION_MAJOR@ +#define ALPS_VERSION_MINOR @ALPS_VERSION_MINOR@ +#define ALPS_VERSION_PATCH @ALPS_VERSION_PATCH@ -#cmakedefine ALPS_VERSION_MAJOR @ALPS_VERSION_MAJOR@ -#cmakedefine ALPS_VERSION_MINOR @ALPS_VERSION_MINOR@ -#cmakedefine ALPS_VERSION_PATCH @ALPS_VERSION_PATCH@ -#cmakedefine ALPS_SVN_REVISION @ALPS_WC_REVISION@ +// ALPS version as a single integer, for preprocessor comparisons: +// #if ALPS_VERSION_NUMBER >= ALPS_VERSION_NUM(2, 3, 4) +#define ALPS_VERSION_NUM(major, minor, patch) \ + ((major) * 100000 + (minor) * 100 + (patch)) +#define ALPS_VERSION_NUMBER \ + ALPS_VERSION_NUM(ALPS_VERSION_MAJOR, ALPS_VERSION_MINOR, ALPS_VERSION_PATCH) // ALPS version (full string) -#cmakedefine ALPS_VERSION_STRING "@ALPS_VERSION_STRING@" +#define ALPS_VERSION_STRING "@ALPS_VERSION_STRING@" // latest publish year of ALPS -#cmakedefine ALPS_YEAR "@ALPS_YEAR@" +#define ALPS_YEAR "@ALPS_YEAR@" // install path of ALPS -#cmakedefine ALPS_PREFIX "@ALPS_PREFIX@" - -// source directory of ALPS -#cmakedefine ALPS_SRCDIR "@ALPS_SRCDIR@" +#define ALPS_PREFIX "@ALPS_PREFIX@" // XSLT PATH -#cmakedefine ALPS_XML_DIR "@ALPS_XML_DIR@" +#define ALPS_XML_DIR "@ALPS_XML_DIR@" + +// Optional Windows fallback for the 32/64-bit "Program Files" split. Never set +// by the build, and guarded by #ifdef in parser/xslt_path.C. #cmakedefine ALPS_XML_ALTERNATE_DIR "@ALPS_XML_ALTERNATE_DIR@" // hostname where configure script was executed -#cmakedefine ALPS_CONFIG_HOST "@ALPS_CONFIG_HOST@" +#define ALPS_CONFIG_HOST "@ALPS_CONFIG_HOST@" // username who executed configure script -#cmakedefine ALPS_CONFIG_USER "@ALPS_CONFIG_USER@" +#define ALPS_CONFIG_USER "@ALPS_CONFIG_USER@" #endif // ALPS_VERSION_H diff --git a/tool/pconfig.C b/tool/pconfig.C index 78eeefa3f..5f1787cdc 100644 --- a/tool/pconfig.C +++ b/tool/pconfig.C @@ -35,7 +35,6 @@ int main() { std::cout << "ALPS version: " << alps::version() << std::endl << "Boost version: " << BOOST_LIB_VERSION << std::endl - << "source directory: " << ALPS_SRCDIR << std::endl << "installed at: " << ALPS_PREFIX << std::endl << "configured on: " << alps::config_host() << std::endl << "configured by: " << alps::config_user() << std::endl From 9bbc5a617f284c6a619c43d2497cab0a35af168a Mon Sep 17 00:00:00 2001 From: Tobias Wolf Date: Fri, 24 Jul 2026 19:29:49 -0500 Subject: [PATCH 2/2] build: single-source the ALPS version, add ALPSConfigVersion.cmake The version was hardcoded in CMakeLists.txt and had already drifted: CMake said 2.3.3, the newest tag is v2.3.4, and pyalps' pyproject.toml says 2.3.4b1. Put the numeric release in ALPS_VERSION.txt, read it in cmake/ALPSVersion.cmake before project(), and derive ALPS_VERSION_MAJOR/MINOR/PATCH from PROJECT_VERSION_*. The file holds MAJOR.MINOR.PATCH and nothing else, because project(VERSION) rejects non-numeric input and neither SOVERSION nor find_package() matching has any notion of prerelease ordering. A malformed file is rejected with a message naming the file, rather than CMake's bare "VERSION format invalid". Corrects the version to 2.3.4 in passing. ALPS_VERSION_BUILD, which was always empty, becomes the ALPS_VERSION_PRERELEASE cache variable: it carries "beta.2" into display strings while the numeric version stays clean. "Prerelease" because a later change adds real build metadata (a git hash), and two similarly-named slots would confuse. Generate and install ALPSConfigVersion.cmake. Without it find_package(ALPS ) accepted any version it found and silently discarded the constraint. SameMinorVersion: within 2.3.x a patch release is drop-in, a minor bump is not guaranteed to be. Note this is stricter than the SOVERSION of MAJOR alone advertises; reconciling the soname is a packaging-visible change and is left alone here. Derive ALPS_YEAR with string(TIMESTAMP), which honours SOURCE_DATE_EPOCH, so distro and conda reproducible builds still get a stable year. ALPS_SRCDIR is dropped from the installed header by the preceding commit, but two tests use it to locate reference .h5 inputs. Give those two targets a private compile definition instead: a build-tree path belongs there, not in an installed public header. Refs #95 Co-Authored-By: Claude Opus 5 --- ALPS_VERSION.txt | 1 + CMakeLists.txt | 53 ++++++++++++++++++++++++++----------- cmake/ALPSConfig.cmake.in | 7 +++-- cmake/ALPSVersion.cmake | 41 ++++++++++++++++++++++++++++ test/hdf5/CMakeLists.txt | 5 ++++ test/numeric/CMakeLists.txt | 7 +++++ 6 files changed, 97 insertions(+), 17 deletions(-) create mode 100644 ALPS_VERSION.txt create mode 100644 cmake/ALPSVersion.cmake diff --git a/ALPS_VERSION.txt b/ALPS_VERSION.txt new file mode 100644 index 000000000..3f684d2d9 --- /dev/null +++ b/ALPS_VERSION.txt @@ -0,0 +1 @@ +2.3.4 diff --git a/CMakeLists.txt b/CMakeLists.txt index 588b71511..b295ed5d3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -70,13 +70,17 @@ if(ALPS_BUILD_LIBS_ONLY) set(ALPS_BUILD_APPLICATIONS OFF) endif() +# Sets ALPS_VERSION_CORE from ALPS_VERSION.txt. Included by full path because +# CMAKE_MODULE_PATH is not set up until after project(). +include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/ALPSVersion.cmake) + if(ALPS_BUILD_FORTRAN) - project(alps C CXX Fortran) + project(alps VERSION ${ALPS_VERSION_CORE} LANGUAGES C CXX Fortran) if (APPLE) set (CMAKE_Fortran_RUNTIME_LIBRARIES "-lgcc_s.1") endif(APPLE) else(ALPS_BUILD_FORTRAN) - project(alps C CXX) + project(alps VERSION ${ALPS_VERSION_CORE} LANGUAGES C CXX) endif(ALPS_BUILD_FORTRAN) set(CMAKE_CXX_STANDARD 14) @@ -118,25 +122,35 @@ list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) ###################################################################### # Version information ###################################################################### -set(ALPS_YEAR 2026) -set(ALPS_VERSION_MAJOR 2) -set(ALPS_VERSION_MINOR 3) -set(ALPS_VERSION_PATCH 3) -set(ALPS_VERSION_BUILD "") - -if(ALPS_VERSION_BUILD) - set(ALPS_VERSION "${ALPS_VERSION_MAJOR}.${ALPS_VERSION_MINOR}.${ALPS_VERSION_PATCH}-${ALPS_VERSION_BUILD}") -else(ALPS_VERSION_BUILD) - set(ALPS_VERSION "${ALPS_VERSION_MAJOR}.${ALPS_VERSION_MINOR}.${ALPS_VERSION_PATCH}") -endif(ALPS_VERSION_BUILD) +# The numeric components come from project() above, which took its version from +# ALPS_VERSION.txt. Do not hardcode them here. +set(ALPS_VERSION_MAJOR ${PROJECT_VERSION_MAJOR}) +set(ALPS_VERSION_MINOR ${PROJECT_VERSION_MINOR}) +set(ALPS_VERSION_PATCH ${PROJECT_VERSION_PATCH}) + +# Prerelease label, e.g. "beta.2" for the v2.3.4-beta.2 tag. Display only: it is +# deliberately kept out of the numeric version that drives SOVERSION and +# find_package() matching. Set by the release process, not checked in. +set(ALPS_VERSION_PRERELEASE "" CACHE STRING + "Prerelease label for this build, e.g. beta.2 (affects version strings only)") +mark_as_advanced(ALPS_VERSION_PRERELEASE) + +if(ALPS_VERSION_PRERELEASE) + set(ALPS_VERSION "${ALPS_VERSION_CORE}-${ALPS_VERSION_PRERELEASE}") +else() + set(ALPS_VERSION "${ALPS_VERSION_CORE}") +endif() set(ALPS_VERSION_STRING "ALPS Libraries version ${ALPS_VERSION}") -MESSAGE(STATUS "ALPS version: ${ALPS_VERSION}") +message(STATUS "ALPS version: ${ALPS_VERSION}") + +# Derived, never hardcoded. string(TIMESTAMP) honours SOURCE_DATE_EPOCH, so +# distro and conda reproducible builds still get a stable year. +string(TIMESTAMP ALPS_YEAR "%Y" UTC) set(ALPS_CONFIG_HOST unknown) set(ALPS_CONFIG_USER unknown) set(ALPS_PREFIX "${CMAKE_INSTALL_PREFIX}") -set(ALPS_SRCDIR "${CMAKE_SOURCE_DIR}") set(libdir "${CMAKE_INSTALL_PREFIX}/lib") set(bindir "${CMAKE_INSTALL_PREFIX}/bin") @@ -396,6 +410,14 @@ endif(HDF5_LIBRARIES) configure_file(cmake/ALPSConfig.cmake.in ${PROJECT_BINARY_DIR}/cmake/ALPSConfig.cmake @ONLY) configure_file(cmake/include.mk.in ${PROJECT_BINARY_DIR}/cmake/include.mk) +# Without this file find_package(ALPS ) accepts any version it finds +# and silently discards the constraint. SameMinorVersion: patch releases within +# a minor series are drop-in, a minor bump is not guaranteed to be. +include(CMakePackageConfigHelpers) +write_basic_package_version_file( + ${PROJECT_BINARY_DIR}/cmake/ALPSConfigVersion.cmake + COMPATIBILITY SameMinorVersion) + # installation ###################################################################### @@ -429,6 +451,7 @@ add_subdirectory(cmake) install(FILES cmake/UseALPS.cmake ${PROJECT_BINARY_DIR}/cmake/ALPSConfig.cmake + ${PROJECT_BINARY_DIR}/cmake/ALPSConfigVersion.cmake ${PROJECT_BINARY_DIR}/cmake/include.mk cmake/run_test.cmake cmake/run_test_mpi.cmake diff --git a/cmake/ALPSConfig.cmake.in b/cmake/ALPSConfig.cmake.in index c7469387f..cecdccdc0 100644 --- a/cmake/ALPSConfig.cmake.in +++ b/cmake/ALPSConfig.cmake.in @@ -20,11 +20,14 @@ set(ALPS_LIBRARY_DIRS "@ALPS_LIBRARY_DIRS_CONFIG@") # of runtime binaries for each configuration type. set(ALPS_RUNTIME_LIBRARY_DIRS "@ALPS_RUNTIME_LIBRARY_DIRS_CONFIG@") -# The ALPS version number. +# The ALPS version number. ALPS_VERSION carries the prerelease label when there +# is one; ALPS_VERSION_CORE is always plain MAJOR.MINOR.PATCH and is what +# ALPSConfigVersion.cmake compares against. SET(ALPS_VERSION_MAJOR "@ALPS_VERSION_MAJOR@") SET(ALPS_VERSION_MINOR "@ALPS_VERSION_MINOR@") SET(ALPS_VERSION_PATCH "@ALPS_VERSION_PATCH@") -SET(ALPS_VERSION_BUILD "@ALPS_VERSION_BUILD@") +SET(ALPS_VERSION_PRERELEASE "@ALPS_VERSION_PRERELEASE@") +SET(ALPS_VERSION_CORE "@ALPS_VERSION_CORE@") SET(ALPS_VERSION "@ALPS_VERSION@") # The location of the UseALPS.cmake file. diff --git a/cmake/ALPSVersion.cmake b/cmake/ALPSVersion.cmake new file mode 100644 index 000000000..d14bb0fe9 --- /dev/null +++ b/cmake/ALPSVersion.cmake @@ -0,0 +1,41 @@ +# Copyright ALPS collaboration 2026. +# Distributed under the MIT licence; see LICENSE.txt. +# +# Single source of truth for the ALPS release version. +# +# ALPS_VERSION.txt holds the numeric release core (MAJOR.MINOR.PATCH) and +# nothing else. Two constraints force that: +# +# * project(VERSION ...) rejects anything non-numeric, so "2.4.0-beta.1" +# fails to configure. +# * find_package() version matching and the library SOVERSION have no notion +# of prerelease ordering. +# +# A prerelease label such as "beta.2" therefore lives in ALPS_VERSION_PRERELEASE +# (see the version block in the top-level CMakeLists.txt), where it affects the +# display string only -- never the numeric version used for ABI and +# find_package() decisions. +# +# This file is included by full path before project(), so it cannot rely on +# CMAKE_MODULE_PATH or PROJECT_SOURCE_DIR. + +set(_alps_version_file "${CMAKE_CURRENT_LIST_DIR}/../ALPS_VERSION.txt") + +if(NOT EXISTS "${_alps_version_file}") + message(FATAL_ERROR "Cannot read the ALPS version file: ${_alps_version_file}") +endif() + +file(STRINGS "${_alps_version_file}" ALPS_VERSION_CORE LIMIT_COUNT 1) +string(STRIP "${ALPS_VERSION_CORE}" ALPS_VERSION_CORE) + +# Fail loudly here rather than letting project() emit "VERSION format invalid", +# which gives no hint about which file is at fault. +if(NOT ALPS_VERSION_CORE MATCHES "^[0-9]+\\.[0-9]+\\.[0-9]+$") + message(FATAL_ERROR + "${_alps_version_file} must contain exactly MAJOR.MINOR.PATCH, but reads " + "'${ALPS_VERSION_CORE}'. Prerelease labels belong in " + "ALPS_VERSION_PRERELEASE, and the leading 'v' of a release tag is not " + "part of the version.") +endif() + +unset(_alps_version_file) diff --git a/test/hdf5/CMakeLists.txt b/test/hdf5/CMakeLists.txt index 54cd3f7dc..d6b24daae 100644 --- a/test/hdf5/CMakeLists.txt +++ b/test/hdf5/CMakeLists.txt @@ -38,6 +38,11 @@ FOREACH (name hdf5_complex hdf5_copy hdf5_real_complex_vec hdf5_real_complex_mat set_property(TEST ${name} PROPERTY LABELS hdf5) ENDFOREACH(name) +# Reads a reference .h5 file from the source tree; see the note in +# test/numeric/CMakeLists.txt. +target_compile_definitions(hdf5_fortran_string + PRIVATE ALPS_SRCDIR="${PROJECT_SOURCE_DIR}") + IF (ALPS_ENABLE_OPENMP AND OPENMP_FOUND) add_executable(hdf5_omp hdf5_omp.cpp) add_dependencies(hdf5_omp alps) diff --git a/test/numeric/CMakeLists.txt b/test/numeric/CMakeLists.txt index 32d13d304..41d9c5df8 100644 --- a/test/numeric/CMakeLists.txt +++ b/test/numeric/CMakeLists.txt @@ -30,5 +30,12 @@ if(LAPACK_FOUND) add_alps_test(${name}) set_property(TEST ${name} PROPERTY LABELS numeric) ENDFOREACH(name) + + # This test reads a reference .h5 file from the source tree. That path is a + # property of this build, not of the installed library, so it is a private + # definition on the one target that needs it rather than a macro in the + # installed alps/version.h. + target_compile_definitions(matrix_deprecated_hdf5_format_test + PRIVATE ALPS_SRCDIR="${PROJECT_SOURCE_DIR}") endif(LAPACK_FOUND)