diff --git a/.github/workflows/ci_cmake.yml b/.github/workflows/ci_cmake.yml index 961012401..41aa64c46 100644 --- a/.github/workflows/ci_cmake.yml +++ b/.github/workflows/ci_cmake.yml @@ -13,19 +13,35 @@ jobs: matrix: build_type : [ Release, Debug ] os : [ macos-latest, ubuntu-20.04 ] + subproject : [ ON, OFF ] + exclude: + - build_type: Release + subproject: ON include: - os: ubuntu-20.04 cxx: g++-10 cc: gcc-10 + compressor: gzip - os: macos-latest cxx: clang++ cc: clang - - build_type: Release - build_shared_libs: ON + compressor: bzip2 - build_type: Debug build_shared_libs: OFF + do_fortran: ON + - build_type: Release + build_shared_libs: ON + os: ubuntu-20.04 + do_fortran: ON + - build_type: Release + build_shared_libs: ON + os: macos-latest + do_fortran: OFF + # someday when mac+fortran+shared is fixed (plus universal do_fortran: ON) + #- build_type: Release + # build_shared_libs: ON - name: "Repo • ${{ matrix.os }}: ${{ matrix.cxx }} ${{ matrix.build_type }}" + name: "Repo • ${{ matrix.os }}: ${{ matrix.cxx }} ${{ matrix.build_type }} shared=${{ matrix.build_shared_libs }} fetch=${{ matrix.subproject }}" runs-on: ${{ matrix.os }} env: CC: ${{ matrix.cc }} @@ -35,19 +51,23 @@ jobs: CCACHE_COMPRESSLEVEL : 6 BUILD_CONFIG : > -G Ninja - -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} - -DBUILD_SHARED_LIBS=${{ matrix.build_shared_libs }} - -DMPIEXEC_PREFLAGS='--bind-to;none;--allow-run-as-root' - -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/build/library - -DWITH_MAX_AM=2;2 - -DWITH_ERI_MAX_AM=2;2 - -DWITH_ERI3_MAX_AM=3;2 - -DENABLE_ERI=1 - -DENABLE_ERI3=1 - -DENABLE_ONEBODY=1 - -DDISABLE_ONEBODY_PROPERTY_DERIVS=ON - -DMULTIPOLE_MAX_ORDER=2 - -DLIBINT2_ENABLE_PYTHON=ON + -D CMAKE_BUILD_TYPE=${{ matrix.build_type }} + -D BUILD_SHARED_LIBS=${{ matrix.build_shared_libs }} + -D LIBINT_BUILD_LIBRARY_AS_SUBPROJECT=${{ matrix.subproject }} + -D CMAKE_INSTALL_PREFIX=${{github.workspace}}/build/library + -D WITH_MAX_AM=2;2 + -D WITH_ERI_MAX_AM=2;2 + -D WITH_ERI3_MAX_AM=3;2 + -D ENABLE_ERI=1 + -D ENABLE_ERI3=1 + -D ENABLE_ONEBODY=1 + -D DISABLE_ONEBODY_PROPERTY_DERIVS=ON + -D MULTIPOLE_MAX_ORDER=2 + -D LIBINT2_REQUIRE_CXX_API_COMPILED=ON + -D LIBINT2_ENABLE_FORTRAN=${{ matrix.do_fortran }} + -D LIBINT2_ENABLE_PYTHON=ON + -D Python_EXECUTABLE=${{ matrix.os == 'macos-latest' && '/usr/local/Frameworks/Python.framework/Versions/3.11/bin/python3.11' || '/usr/bin/python3.8' }} + -D EXPORT_COMPRESSOR=${{ matrix.compressor }} outputs: should_skip: ${{ steps.skip_check.outputs.should_skip }} @@ -66,8 +86,6 @@ jobs: - name: Create Build Environment if: ${{ steps.skip_check.outputs.should_skip != 'true' }} - # Some projects don't allow in-source building, so create a separate build directory - # We'll use this as our working directory for all subsequent commands run: | cmake -E make_directory ${{github.workspace}}/build/compiler cmake -E make_directory ${{github.workspace}}/build/library @@ -105,7 +123,7 @@ jobs: restore-keys: | ${{ matrix.config.name }}-ccache- - - name: Generate Libint generator + - name: Build Libint generator if: ${{ steps.skip_check.outputs.should_skip != 'true' }} # Use a bash shell so we can use the same syntax for environment variable # access regardless of the host operating system @@ -116,16 +134,16 @@ jobs: cmake -S ../.. -B build $BUILD_CONFIG --log-level=DEBUG cmake --build build --target check-libint2compiler - - name: Generate Libint export + - name: Generate Libint library tarball if: ${{ steps.skip_check.outputs.should_skip != 'true' }} shell: bash working-directory: ${{github.workspace}}/build/compiler run: | cmake --build build --target export - cd build && echo "ARTIFACT=`ls -1 libint*tgz`" >> $GITHUB_ENV + cd build && echo "ARTIFACT=`ls -1 libint*z*`" >> $GITHUB_ENV - - name: Archive Library Tarball - if: ${{ steps.skip_check.outputs.should_skip != 'true' && matrix.build_type == 'Release'}} + - name: Archive Libint library tarball + if: ${{ steps.skip_check.outputs.should_skip != 'true' && matrix.build_type == 'Release' }} uses: actions/upload-artifact@v3 with: if-no-files-found: error @@ -133,17 +151,28 @@ jobs: path: ${{github.workspace}}/build/compiler/build/${{ env.ARTIFACT }} retention-days: 1 - - name: Generate Libint library + - name: Build Libint library (FetchContent=${{ matrix.subproject }}) + if: ${{ steps.skip_check.outputs.should_skip }} + shell: bash + working-directory: ${{github.workspace}}/build/compiler + run: cmake --build build + + - name: Test Libint library - unit tests if: ${{ steps.skip_check.outputs.should_skip != 'true' }} shell: bash working-directory: ${{github.workspace}}/build/compiler - run: | - cmake --build build --target library check-libint2 - cd build && echo "ARTIFACT=`ls -1 libint*tgz`" >> $GITHUB_ENV + run: CTEST_PARALLEL_LEVEL=2 cmake --build build --target check-libint2 -#rm -rf buildcm/ && cmake -S. -Bbuildcm -GNinja -DEXPORT_COMPRESSOR=bzip2 -DWITH_MAX_AM="2;2" -DWITH_ERI_MAX_AM="2;2" -DWITH_ERI3_MAX_AM="3;2" -DENABLE_ERI=1 -DENABLE_ERI3=1 -DMULTIPOLE_MAX_ORDER=2 -DBUILD_SHARED_LIBS=ON && time cmake --build buildcm --target check-libint2 + - name: Build & Test Python bindings + if: ${{ steps.skip_check.outputs.should_skip != 'true' }} + shell: bash + working-directory: ${{github.workspace}}/build/compiler + run: cmake --build build --target check-python build_export: + # to debug the second stage, it is handy to short-circuit the first stage: + # * suppress steps beyond "Archive Library Tarball" with "if: false" + # * for further reduction, run mostly Linux lanes with "os : [ ubuntu-20.04 ]" needs: build_repo if: always() && (needs.build_repo.outputs.should_skip != 'true') strategy: @@ -153,37 +182,40 @@ jobs: - runs-on: ubuntu-latest lane: ubuntu-gnu libargs: > - -DBUILD_SHARED_LIBS=ON + -D BUILD_SHARED_LIBS=ON + -D LIBINT2_ENABLE_FORTRAN=ON testargs: "" - runs-on: windows-latest lane: windows-clang-cl libargs: > - -GNinja - -DCMAKE_BUILD_TYPE=Release - -DBUILD_SHARED_LIBS=OFF - -DCMAKE_CXX_COMPILER=clang-cl - -DCMAKE_C_COMPILER=clang-cl + -G Ninja + -D CMAKE_BUILD_TYPE=Release + -D BUILD_SHARED_LIBS=OFF + -D CMAKE_CXX_COMPILER=clang-cl + -D CMAKE_C_COMPILER=clang-cl testargs: > - -GNinja - -DCMAKE_BUILD_TYPE=Release - -DCMAKE_CXX_COMPILER=clang-cl - -DCMAKE_C_COMPILER=clang-cl + -G Ninja + -D CMAKE_BUILD_TYPE=Release + -D CMAKE_CXX_COMPILER=clang-cl + -D CMAKE_C_COMPILER=clang-cl - runs-on: macos-latest lane: macos-clang libargs: > - -DBUILD_SHARED_LIBS=ON + -D BUILD_SHARED_LIBS=OFF + -D LIBINT2_ENABLE_FORTRAN=ON + # return to shared when mac+fortran+shared is fixed testargs: "" - runs-on: ubuntu-latest lane: ubuntu-intel libargs: > - -DCMAKE_CXX_COMPILER=icpx - -DCMAKE_CXX_FLAGS="--gcc-toolchain=${CONDA_PREFIX} --sysroot=${CONDA_PREFIX}/${HOST}/sysroot -target ${HOST}" + -D CMAKE_CXX_COMPILER=icpx + -D CMAKE_CXX_FLAGS="--gcc-toolchain=${CONDA_PREFIX} --sysroot=${CONDA_PREFIX}/${HOST}/sysroot -target ${HOST}" testargs: > - -DCMAKE_CXX_COMPILER=icpx - -DCMAKE_CXX_FLAGS="--gcc-toolchain=${CONDA_PREFIX} --sysroot=${CONDA_PREFIX}/${HOST}/sysroot -target ${HOST}" + -D CMAKE_CXX_COMPILER=icpx + -D CMAKE_CXX_FLAGS="--gcc-toolchain=${CONDA_PREFIX} --sysroot=${CONDA_PREFIX}/${HOST}/sysroot -target ${HOST}" name: "Export • ${{ matrix.cfg.lane }}" runs-on: ${{ matrix.cfg.runs-on }} @@ -191,6 +223,10 @@ jobs: steps: # Note we're not checking out the repo. All src from Linux tarball generated above. + - uses: actions/download-artifact@v3 + with: + name: Linux-g++-10 + - name: Write a Conda Env File shell: bash -l {0} run: | @@ -202,6 +238,7 @@ jobs: - cmake - ninja - cxx-compiler + - fortran-compiler - python - boost - eigen @@ -212,6 +249,7 @@ jobs: EOF if [[ "${{ runner.os }}" == "Windows" ]]; then sed -i "s/- cxx/#- cxx/g" export.yaml + sed -i "s/- fortran/#- fortran/g" export.yaml fi if [[ "${{ matrix.cfg.lane }}" == "ubuntu-intel" ]]; then sed -i "s/#- dpcpp_linux-64/- dpcpp_linux-64/g" export.yaml @@ -241,25 +279,22 @@ jobs: with: arch: x64 - - uses: actions/download-artifact@v3 - with: - name: Linux-g++-10 - - name: Extract, Build, Install Libint Library shell: bash -l {0} run: | tar -zxf libint*tgz mkdir libint && mv libint-2*/* libint/ && cd libint/ cmake \ - -S. \ - -Bbuild \ - -GNinja \ - -DCMAKE_INSTALL_PREFIX="${{github.workspace}}/installed" \ - -DCMAKE_CXX_COMPILER=${CXX} \ - -DLIBINT2_PYTHON=OFF \ - -DCMAKE_PREFIX_PATH="${CONDA_PREFIX}" \ + -S . \ + -B build \ + -G Ninja \ + -D CMAKE_INSTALL_PREFIX="${{github.workspace}}/installed" \ + -D CMAKE_CXX_COMPILER=${CXX} \ + -D LIBINT2_REQUIRE_CXX_API_COMPILED=ON \ + -D LIBINT2_ENABLE_PYTHON=ON \ + -D CMAKE_PREFIX_PATH="${CONDA_PREFIX}" \ ${{ matrix.cfg.libargs }} - cmake --build build --target install + cmake --build build --target install libint2-python-test - name: Test Libint library - unit tests shell: bash -l {0} diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index d313421ea..ce187abc9 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -4,7 +4,7 @@ on: push: branches: - master - #pull_request: + pull_request: jobs: build_repo: diff --git a/CMakeLists.txt b/CMakeLists.txt index 80a00f777..57bccfc58 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,10 +13,13 @@ include(DynamicVersion) dynamic_version( PROJECT_PREFIX Libint2Compiler_ GIT_ARCHIVAL_FILE ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/.git_archival.txt + VERSION_FULL_MODE POST OUTPUT_COMMIT LibintRepository_COMMIT OUTPUT_VERSION LibintRepository_VERSION OUTPUT_DESCRIBE LibintRepository_DESCRIBE OUTPUT_DISTANCE LibintRepository_DISTANCE + OUTPUT_SHORT_HASH LibintRepository_SHORT_HASH + OUTPUT_VERSION_FULL LibintRepository_VERSION_FULL ) set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build.") # foil Ninja Debug on Windows @@ -100,13 +103,20 @@ message(STATUS "Building using CMake ${CMAKE_VERSION} Generator ${CMAKE_GENERATO option_with_default(CMAKE_BUILD_TYPE "Build type (Release or Debug)" Release) ### compiler-only +option_with_print(LIBINT_BUILD_LIBRARY_AS_SUBPROJECT + "[EXPERT] Build generated library as a subproject: if FALSE will configure and build separately" OFF) ### library-only -option_with_default(LIBINT2_ENABLE_PYTHON +option_with_print(LIBINT2_REQUIRE_CXX_API + "C++11 Libint API: define library targets + test (requires Eigen3, Boost is optional but strongly recommended)" ON) +option_with_print(LIBINT2_REQUIRE_CXX_API_COMPILED + "Build C++11 Compiled (not just header-only) targets (requires Eigen3, Boost strongly recommended)" ON) +option_with_print(LIBINT2_ENABLE_FORTRAN + "Build Fortran03+ Libint interface (requires Fortran)" OFF) +option_with_print(LIBINT2_ENABLE_PYTHON "Build Python bindings (requires Python and Pybind11 and Eigen3)" OFF) -option_with_default(LIBINT2_PREFIX_PYTHON_INSTALL +option_with_print(LIBINT2_PREFIX_PYTHON_INSTALL "For LIBINT2_ENABLE_PYTHON=ON, whether to install the Python module in the Linux manner to CMAKE_INSTALL_PREFIX or to not install it. See target libint2-python-wheel for alternate installation in the Python manner to Python_EXECUTABLE's site-packages." OFF) - option_with_print(BUILD_SHARED_LIBS "Build Libint library as shared, not static" OFF) option_with_print(LIBINT2_BUILD_SHARED_AND_STATIC_LIBS diff --git a/INSTALL.md b/INSTALL.md index 529ae3e9c..28389d2b1 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -73,13 +73,21 @@ cmake/ COPYING src/ tests/ ... ### Build Targets -| `--target ...` | incl. | steps | ( | see | above | ) | | (TARBALL) `--target ...` [^25] | -| -------------- | ----- | ----- | ----- | ----- | ----- | ----- | ----- | ------------------------------ | +| `--target ...` [^27] | incl. | steps | ( | see | above | ) | | (TARBALL) `--target ...` [^25] | +| -------------------- | ----- | ----- | ----- | ----- | ----- | ----- | ----- | ------------------------------ | | `build_libint` | 1 | - | - | - | - | - | - | n/a | | `check-libint2compiler` | 1 | 2 | - | - | - | - | - | n/a | +| `export` | 1 | - | 3 | - | - | - | - | n/a | +| `library` (default) [^26] | 1 | - | 3 | 4 | - | - | - | (default) [^26] | +| `check` | 1 | 2 | 3 | 4 | 5 | - | - | `check` | + +Use combined targets like `cmake --target check install` to avoid some unnecessary rebuilding (esp. of build_libint) that occurs with successive targets. The CMake dependency structure is imperfect. [^25]: (TARBALL) targets can include steps 4 onwards; the starting tarball itself is the product of step 3. +[^26]: See [see "Internal Targets" column in table](#consumption-targets) for individual library targets. + +[^27]: For FetchContent/`LIBINT_BUILD_LIBRARY_AS_SUBPROJECT=ON`, build target `export` aka `libint-library-export`. Then, as a separate command, build further targets like `check` or `install`; target plain `library` is not available. ----------------------------------------------------------------------------- @@ -259,6 +267,9 @@ Note that options, docs, and CMake components are focused on the C++ interface, ### Build Library What (L) (TARBALL) +* `LIBINT2_REQUIRE_CXX_API` — L — Build C++11 Libint API. Define header-only library target and check target (requires Eigen3; Boost recommended; [see prereq line](#prerequisites)). [Default=ON] +* `LIBINT2_REQUIRE_CXX_API_COMPILED` — L — Build C++11 Libint API. Define compiled (not just header-only) targets (requires Eigen3; Boost recommended). [Default=ON] +* `LIBINT2_ENABLE_FORTRAN` — L — Build Fortran03+ module/bindings (requires C and Fortran compilers and Python). [Default=OFF] * `LIBINT2_ENABLE_MPFR` — L — Use MPFR library to test Libint integrals in high precision (requires MPFR; experts only). [Default=OFF] * `LIBINT2_LOCAL_Eigen3_INSTALL` — L — Install an exported target with hard-coded Eigen3 dependency paths. This is potentially useful and important when consuming the compiled C++11 interface library so that the Libint library build and Libint consumer build use the same Eigen3 installation & ABI. This is at most a convenience when consuming the header-only C++11 interface library. See `LIBINT2_LOCAL_Eigen3_FIND`. [Default=OFF] * `LIBINT2_ENABLE_PYTHON` — L — Build Python bindings (requires Python and Eigen3; Boost and pybind11 recommended; [see prereq line](#prerequisites)). Can instead be enabled and built through separate CMake configuration after library build. [Default=OFF] @@ -269,6 +280,7 @@ Note that options, docs, and CMake components are focused on the C++ interface, * `CMAKE_BUILD_TYPE` — G L — [Standard CMake variable](https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html) [Default=Release] * `BUILD_SHARED_LIBS` — L — Build Libint library as shared, not static. [Standard CMake variable](https://cmake.org/cmake/help/latest/variable/BUILD_SHARED_LIBS.html) [Default=OFF] * `LIBINT2_BUILD_SHARED_AND_STATIC_LIBS` — L — Build both shared and static Libint libraries in one shot. Uses `-fPIC`. [Default=OFF] +* `LIBINT_BUILD_LIBRARY_AS_SUBPROJECT` — G — If building compiler and library in continuous command, build generated library as a subproject (FetchContent); if OFF will configure and build separately (ExternalProject) (expert only). [Default=OFF] ### Detecting Dependencies (G L C) (TARBALL) @@ -352,6 +364,35 @@ Note that options, docs, and CMake components are focused on the C++ interface, * `LIBINT_FLOP_COUNT` — G — Support (approximate) FLOP counting by the library. (Generated code will require C++11!). [Default=OFF] * `LIBINT_PROFILE` — G — Turn on profiling instrumentation of the library. (Generated code will require C++11!). [Default=OFF] +----------------------------------------------------------------------------- + +# Consuming Libint + + +### Programming to Access Integrals + +* if you use C++11 or later (strongly recommended): read the [Libint Wiki](https://github.com/evaleev/libint/wiki/using-modern-CPlusPlus-API) +* if you use pre-2011 C++, C, Fortran, or any other language, refer to the [Libint Programmer's Manual](https://sourceforge.net/projects/libint/files/libint-for-beginners/progman-2.0.3-stable.pdf/download) for brief information on how to use the library in your code. + + +### Consumption Targets + +| Namespaced Target[^15] | CMake[^16] Component | Built by Default | Ensure Built | Ensure Excluded | Internal Target(s)[^17] | Alias[^18] | +| ---------------------- | -------------------- | ---------------- | ------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | ----------------------------------- | ------------ | +| | | yes | always | impossible | `int-obj` | | +| `Libint2::int2` | `C` | yes | always | impossible | `int-{static,shared}` | `libint2` | +| `Libint2::cxx` | `CXX_ho` | yes | `LIBINT2_REQUIRE_CXX_API=ON` | `LIBINT2_REQUIRE_CXX_API=OFF` & withhold Eigen3 & `LIBINT2_REQUIRE_CXX_API_COMPILED=OFF` & `LIBINT2_ENABLE_PYTHON=OFF` | `int-cxx-headeronly-{static,shared}` | `libint2_cxx` | +| `Libint2::int2-cxx` | `CXX` | yes | `LIBINT2_REQUIRE_CXX_API_COMPILED=ON` | `LIBINT2_REQUIRE_CXX_API_COMPILED=OFF` | `int-cxx-{static,shared}` | | +| Fortran local[^19] | (NYI) | no | `LIBINT2_ENABLE_FORTRAN=ON` | `LIBINT2_ENABLE_FORTRAN=OFF` | `libint_f` | | + +[^15]: Targets for library consumer use. These are available after `find_package(Libint2)` or `add_subdirectory()`. +[^16]: Ensure target found in installation after `find_package(Libint2 COMPONENTS ...)`. +[^17]: Targets in src/lib/libint/CMakeLists.txt.export . Names subject to change. Use namespaced target names in any consuming code. +[^18]: Deprecated legacy aliases. Update any uses to namespaced target. +[^19]: The `libint_f` internal target defines the Fortran interface to Libint2. One must also link to `Libint2::int2` or `Libint2::cxx`. At present, it is not exported, and a namespaced target is not defined. + + +----------------------------------------------------------------------------- # GNU Autotools Update Guide @@ -407,14 +448,21 @@ Note that options, docs, and CMake components are focused on the C++ interface, * `--disable-1body-property-derivs` --> `-D DISABLE_ONEBODY_PROPERTY_DERIVS=ON` - +* `--enable-shared` --> `-D BUILD_SHARED=ON` --> `-D BUILD_SHARED_LIBS=ON` (standard CMake variable) +* `--enable-static` --> `-D BUILD_STATIC=ON` --> `-D BUILD_SHARED_LIBS=OFF` (standard CMake variable) +* `--enable-shared --enable-static` --> `-D BUILD_SHARED=ON -D BUILD_STATIC=ON` --> `-D LIBINT2_BUILD_SHARED_AND_STATIC_LIBS=ON` +* `-D REQUIRE_CXX_API=ON` --> `-D ENABLE_CXX11API=ON` --> `-D LIBINT2_REQUIRE_CXX_API=ON` * `--enable-mpfr` --> assumed present --> `-D ENABLE_MPFR=ON` --> `-D LIBINT2_ENABLE_MPFR=ON` +* (target) `libint2` --> `Libint2::int2` +* (target) `libint2_cxx` --> `Libint2::cxx` + * `ENV(CPPFLAGS)=-I/path/to/boost/includes` --> `-D BOOST_ROOT=/path/to/boost/prefix` * `-D LIBINT2_PYTHON=ON` --> `-D LIBINT2_ENABLE_PYTHON=ON` * `-D LIBINT_USE_BUNDLED_BOOST=ON` --> `-D CMAKE_DISABLE_FIND_PACKAGE_Boost=ON` (standard CMake variable) * `--with-boost` & `--with-boost-libdir` --> see `BOOST_ROOT` & `Boost_DIR` +* `-D ENABLE_FORTRAN=ON` --> `-D LIBINT2_ENABLE_FORTRAN=ON` * `-D LIBINT_LOCAL_Eigen3_INSTALL` --> `-D LIBINT2_LOCAL_Eigen3_INSTALL` * `-D LIBINT_LOCAL_Eigen3_FIND` --> `-D LIBINT2_LOCAL_Eigen3_FIND` diff --git a/cmake/modules/DynamicVersion.cmake b/cmake/modules/DynamicVersion.cmake index c50be9de4..4c62477fa 100644 --- a/cmake/modules/DynamicVersion.cmake +++ b/cmake/modules/DynamicVersion.cmake @@ -1,5 +1,4 @@ -# copied from https://github.com/LecrisUT/CMakeExtraUtils/blob/main/cmake/DynamicVersion.md ~20 Dec 2023 -# * modified to add distance field and return +# copied from https://github.com/LecrisUT/CMakeExtraUtils/blob/main/cmake/DynamicVersion.md 15 Jan 2023 #[===[.md: # DynamicVersion @@ -16,9 +15,9 @@ Helper module to get the project's version dynamically. Format is compatible wit include_guard() list(APPEND CMAKE_MESSAGE_CONTEXT DynamicVersion) if (POLICY CMP0140) - # Enable using return(PROPAGATE) - # TODO: Remove when cmake 3.25 is commonly distributed - cmake_policy(SET CMP0140 NEW) + # Enable using return(PROPAGATE) + # TODO: Remove when cmake 3.25 is commonly distributed + cmake_policy(SET CMP0140 NEW) endif () #[==============================================================================================[ @@ -42,8 +41,12 @@ function(dynamic_version) Main interface dynamic_version(PROJECT_PREFIX ) dynamic_version(PROJECT_PREFIX - [OUTPUT_VERSION ] [OUTPUT_DESCRIBE ] [OUTPUT_COMMIT ] - [OUTPUT_DISTANCE ] [PROJECT_SOURCE ] [GIT_ARCHIVAL_FILE ]) + [OUTPUT_VERSION ] [OUTPUT_VERSION_FULL ] + [OUTPUT_DESCRIBE ] [OUTPUT_COMMIT ] + [OUTPUT_DISTANCE ] [OUTPUT_SHORT_HASH ] + [VERSION_FULL_MODE ] + [PROJECT_SOURCE ] [GIT_ARCHIVAL_FILE ] + ) Fallbacks dynamic_version(... @@ -61,6 +64,12 @@ function(dynamic_version) `OUTPUT_VERSION` [Default: PROJECT_VERSION] Variable where to save the calculated version + `OUTPUT_VERSION_FULL` [Default: PROJECT_VERSION_FULL] + Variable where to save the full version in the format + + `VERSION_FULL_MODE` [Default: DEV] + Format of the `OUTPUT_VERSION_FULL`. Must be one of [`DEV`, `POST`] + `OUTPUT_DESCRIBE` [Default: GIT_DESCRIBE] Variable where to save the pure `git describe` output @@ -70,6 +79,9 @@ function(dynamic_version) `OUTPUT_DISTANCE` [Default: GIT_DISTANCE] Variable where to save the distance from git tag + `OUTPUT_SHORT_HASH` [Default: GIT_SHORT_HASH] + Variable where to save the shortened git commit hash + `PROJECT_SOURCE` [Default: `${CMAKE_CURRENT_SOURCE_DIR}`] Location of the project source. Has to be either an extracted git archive or a git clone @@ -135,9 +147,12 @@ function(dynamic_version) set(ARGS_OneValue PROJECT_PREFIX OUTPUT_VERSION + OUTPUT_VERSION_FULL + VERSION_FULL_MODE OUTPUT_DESCRIBE OUTPUT_COMMIT OUTPUT_DISTANCE + OUTPUT_SHORT_HASH PROJECT_SOURCE GIT_ARCHIVAL_FILE FALLBACK_VERSION @@ -156,6 +171,14 @@ function(dynamic_version) if (NOT DEFINED ARGS_OUTPUT_VERSION) set(ARGS_OUTPUT_VERSION PROJECT_VERSION) endif () + if (NOT DEFINED ARGS_OUTPUT_VERSION_FULL) + set(ARGS_OUTPUT_VERSION_FULL PROJECT_VERSION_FULL) + endif () + if (NOT DEFINED ARGS_VERSION_FULL_MODE) + set(ARGS_VERSION_FULL_MODE DEV) + elseif (NOT ARGS_VERSION_FULL_MODE MATCHES "(DEV|POST)") + message(FATAL_ERROR "Unsupported VERSION_FULL_MODE = ${ARGS_VERSION_FULL_MODE}") + endif () if (NOT DEFINED ARGS_OUTPUT_DESCRIBE) set(ARGS_OUTPUT_DESCRIBE GIT_DESCRIBE) endif () @@ -165,6 +188,9 @@ function(dynamic_version) if (NOT DEFINED ARGS_OUTPUT_DISTANCE) set(ARGS_OUTPUT_DISTANCE GIT_DISTANCE) endif () + if (NOT DEFINED ARGS_OUTPUT_SHORT_HASH) + set(ARGS_OUTPUT_SHORT_HASH GIT_SHORT_HASH) + endif () if (NOT DEFINED ARGS_PROJECT_SOURCE) set(ARGS_PROJECT_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}) endif () @@ -205,6 +231,7 @@ function(dynamic_version) PROJECT_SOURCE ${ARGS_PROJECT_SOURCE} GIT_ARCHIVAL_FILE ${ARGS_GIT_ARCHIVAL_FILE} TMP_FOLDER ${ARGS_TMP_FOLDER} + VERSION_FULL_MODE ${ARGS_VERSION_FULL_MODE} ) if (DEFINED ARGS_FALLBACK_VERSION) list(APPEND DynamicVersion_ARGS @@ -236,16 +263,22 @@ function(dynamic_version) else () file(COPY ${ARGS_TMP_FOLDER}/${file} DESTINATION ${ARGS_OUTPUT_FOLDER}/) endif () - endif () + endif () endforeach () # Check configuration state file(READ ${ARGS_TMP_FOLDER}/.DynamicVersion.json data) + # failed, mode, and version are always set if get_dynamic_version did not exit with failure 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_VERSION} GET ${data} version) + string(JSON ${ARGS_OUTPUT_VERSION_FULL} GET ${data} version-full) + # Other outputs are optional, populate the variables if found + # These are populated if failed = false + string(JSON ${ARGS_OUTPUT_SHORT_HASH} ERROR_VARIABLE _ GET ${data} short-hash) string(JSON ${ARGS_OUTPUT_DISTANCE} ERROR_VARIABLE _ GET ${data} distance) + # These may not be populated depending on mode + string(JSON ${ARGS_OUTPUT_COMMIT} ERROR_VARIABLE _ GET ${data} commit) + string(JSON ${ARGS_OUTPUT_DESCRIBE} ERROR_VARIABLE _ GET ${data} describe) # Configure targets if (failed) @@ -265,15 +298,28 @@ function(dynamic_version) -P ${CMAKE_CURRENT_FUNCTION_LIST_FILE} COMMAND ${CMAKE_COMMAND} -E copy_if_different ${ARGS_TMP_FOLDER}/.DynamicVersion.json ${ARGS_OUTPUT_FOLDER}/.DynamicVersion.json ) + set(extra_version_args) + # .git_describe might not be generated, e.g. if it's an sdist. Make it optional + if (EXISTS ${ARGS_OUTPUT_FOLDER}/.git_describe) + list(APPEND extra_version_args + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${ARGS_TMP_FOLDER}/.git_describe ${ARGS_OUTPUT_FOLDER}/.git_describe + ) + endif () 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 + ${extra_version_args} ) - 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 - ) + # .git_commit might not exist, make the target a no-op in that case + if (NOT EXISTS ${ARGS_OUTPUT_FOLDER}/.git_commit) + add_custom_target(${ARGS_PROJECT_PREFIX}GitHash + COMMAND ${CMAKE_COMMAND} -E true) + else () + 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 () endif () # This ensures that the project is reconfigured (at least at second run) whenever the version changes @@ -288,14 +334,18 @@ function(dynamic_version) # 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_VERSION_FULL} ${${ARGS_OUTPUT_VERSION_FULL}} PARENT_SCOPE) set(${ARGS_OUTPUT_COMMIT} ${${ARGS_OUTPUT_COMMIT}} PARENT_SCOPE) set(${ARGS_OUTPUT_DISTANCE} ${${ARGS_OUTPUT_DISTANCE}} PARENT_SCOPE) + set(${ARGS_OUTPUT_SHORT_HASH} ${${ARGS_OUTPUT_SHORT_HASH}} PARENT_SCOPE) endif () return(PROPAGATE ${ARGS_OUTPUT_DESCRIBE} ${ARGS_OUTPUT_VERSION} + ${ARGS_OUTPUT_VERSION_FULL} ${ARGS_OUTPUT_COMMIT} ${ARGS_OUTPUT_DISTANCE} + ${ARGS_OUTPUT_SHORT_HASH} ) endfunction() @@ -316,8 +366,8 @@ function(get_dynamic_version) ## Synopsis ```cmake get_dynamic_version(PROJECT_SOURCE GIT_ARCHIVAL_FILE - TMP_FOLDER - [FALLBACK_VERSION ] [FALLBACK_HASH ] + TMP_FOLDER VERSION_FULL_MODE + [FALLBACK_VERSION ] [FALLBACK_HASH ] ) ``` @@ -335,6 +385,7 @@ function(get_dynamic_version) ) set(ARGS_OneValue PROJECT_SOURCE + VERSION_FULL_MODE GIT_ARCHIVAL_FILE FALLBACK_VERSION FALLBACK_HASH @@ -366,17 +417,19 @@ function(get_dynamic_version) if (DEFINED ARGS_FALLBACK_VERSION) string(JSON data SET ${data} version \"${ARGS_FALLBACK_VERSION}\") - file(WRITE ${ARGS_TMP_FOLDER}/.DynamicVersion.json ${data}) + string(JSON data SET + ${data} version-full \"${ARGS_FALLBACK_VERSION}\") 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 () + file(WRITE ${ARGS_TMP_FOLDER}/.DynamicVersion.json ${data}) + # Check git_archival.txt file is present and properly written if (NOT EXISTS ${ARGS_GIT_ARCHIVAL_FILE}) # If git_archival.txt is missing, project is ill-formed message(${error_message_type} @@ -385,8 +438,6 @@ function(get_dynamic_version) ) return() endif () - - # Get version dynamically from git_archival.txt file(STRINGS ${ARGS_GIT_ARCHIVAL_FILE} describe-name REGEX "^describe-name:.*") if (NOT describe-name) @@ -398,30 +449,65 @@ function(get_dynamic_version) 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 + # Try to get the version statically, and if it fails, get it from VCS + if (EXISTS ${ARGS_PROJECT_SOURCE}/PKG-INFO) + # Case1: Python sdist archive. Get everything from PKG-INFO file + set(mode pkg-info) + file(STRINGS ${ARGS_PROJECT_SOURCE}/PKG-INFO version + REGEX "^Version:[ ]?(([0-9\\.]+)([a-zA-Z0-9]*)?(\\.(dev|post)([0-9]+)\\+g([a-f0-9]+))?)$") # Cannot use Regex match from here, need to run string(REGEX MATCH) again # https://gitlab.kitware.com/cmake/cmake/-/issues/23770 + string(REGEX MATCH "^Version:[ ]?(([0-9\\.]+)([a-zA-Z0-9]*)?(\\.(dev|post)([0-9]+)\\+g([a-f0-9]+))?)$" version "${version}") + # Regex match groups: https://regex101.com/r/G4Ox4X/5 + # 1: Full version string + # 2: Version string + # 3: Version suffix (e.g. rc, alpha, etc.) + # 4: Development suffix + # 5: dev/post + # 6: git distance + # 7: short_hash + set(version-full ${CMAKE_MATCH_1}) + set(version ${CMAKE_MATCH_2}) + set(version-suffix ${CMAKE_MATCH_3}) + if (CMAKE_MATCH_4) + set(distance ${CMAKE_MATCH_6}) + set(short-hash ${CMAKE_MATCH_7}) + string(JSON data SET + ${data} dev-type \"${CMAKE_MATCH_5}\") + else () + set(distance 0) + endif () + message(DEBUG "Found version in PKG-INFO") + elseif (describe-name MATCHES "^describe-name:[ ]?(v?([0-9\\.]+)(-?[a-zA-z0-9]*)?(-([0-9]+)-g([a-f0-9]+))?)$") + # Case2: Git archive. Get everything from git_archival.txt file + set(mode git-archive) + # Regex match groups: https://regex101.com/r/osVZpm/4 + # 1: Git describe + # 2: Version string + # 3: Version suffix (e.g. rc, alpha, etc.) + # 4: Development suffix + # 5: git distance + # 6: short_hash + set(describe ${CMAKE_MATCH_1}) + set(version ${CMAKE_MATCH_2}) + set(version-suffix ${CMAKE_MATCH_3}) + if (CMAKE_MATCH_4) + set(distance ${CMAKE_MATCH_5}) + set(short-hash ${CMAKE_MATCH_6}) + else () + set(distance 0) + endif () + # Get commit hash file(STRINGS ${ARGS_GIT_ARCHIVAL_FILE} node REGEX "^node:[ ]?(.*)") + # Cannot use Regex match from here, need to run string(REGEX MATCH) again + # https://gitlab.kitware.com/cmake/cmake/-/issues/23770 string(REGEX MATCH "^node:[ ]?(.*)" node "${node}") - string(JSON data SET - ${data} commit \"${CMAKE_MATCH_1}\") - file(WRITE ${ARGS_TMP_FOLDER}/.git_commit ${CMAKE_MATCH_1}) - message(DEBUG - "Found appropriate tag in .git_archival.txt file" - ) + set(commit ${CMAKE_MATCH_1}) + message(DEBUG "Found version in git-archival.txt") else () - # If not it has to be computed from the git archive + # Default: Git repository. Call git commands + set(mode git) find_package(Git REQUIRED) # Test if project is a git repository execute_process(COMMAND ${GIT_EXECUTABLE} status @@ -435,41 +521,109 @@ function(get_dynamic_version) ) 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 --long --abbrev=8 --match=?[0-9.]* + execute_process(COMMAND ${GIT_EXECUTABLE} describe --tags --long --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\\.]+)-([0-9]+)-g(.*))") + if (NOT describe-name MATCHES "^(v?([0-9\\.]+)(-?[a-zA-z0-9]*)?(-([0-9]+)-g([a-f0-9]+))?)$") message(${error_message_type} "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}) - string(JSON data SET - ${data} distance \"${CMAKE_MATCH_3}\") - string(JSON data SET - ${data} short_sha \"${CMAKE_MATCH_4}\") - string(JSON data SET - ${data} commit \"${git-hash}\") - file(WRITE ${ARGS_TMP_FOLDER}/.git_commit ${git-hash}) - message(DEBUG - "Found appropriate tag from git" + # Regex match groups: https://regex101.com/r/GIfYI1/2 + # 1: Git describe + # 2: Version string + # 3: Version suffix (e.g. rc, alpha, etc.) + # 4: Development suffix + # 5: git distance + # 6: short_hash + set(describe ${CMAKE_MATCH_1}) + set(version ${CMAKE_MATCH_2}) + set(version-suffix ${CMAKE_MATCH_3}) + if (CMAKE_MATCH_4) + set(distance ${CMAKE_MATCH_5}) + set(short-hash ${CMAKE_MATCH_6}) + else () + set(distance 0) + endif () + # Get commit hash + execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse HEAD + WORKING_DIRECTORY ${ARGS_PROJECT_SOURCE} + OUTPUT_VARIABLE commit + OUTPUT_STRIP_TRAILING_WHITESPACE + COMMAND_ERROR_IS_FATAL ANY) + message(DEBUG "Found version git repo") + endif () + + # Construct the version_full if it was not already provided + if (NOT version-full) + string(REGEX REPLACE "[-_]" "" version-suffix-sanitized "${version-suffix}") + if (distance EQUAL 0) + # If the distance is 0, just use the original tag version with sanitized suffix + set(version-full "${version}${version-suffix-sanitized}") + else () + # Otherwise construct it according to VERSION_FULL_MODE + if (ARGS_VERSION_FULL_MODE STREQUAL DEV) + # In DEV mode, we bump the last digit of the version. If this is in version-suffix, like `-rcX`, then + # this must be bumped instead + if (version-suffix-sanitized MATCHES "([a-zA-Z]*)([0-9]+)") + math(EXPR bumped_number "${CMAKE_MATCH_2} + 1") + set(version-suffix-sanitized "${CMAKE_MATCH_1}${bumped_number}") + elseif (version MATCHES "([0-9\\.]*\\.)([0-9]+)") + math(EXPR bumped_number "${CMAKE_MATCH_2} + 1") + set(version "${CMAKE_MATCH_1}${bumped_number}") + else () + message(FATAL_ERROR "Assert False: version = ${version}") + endif () + set(version-full "${version}${version-suffix-sanitized}.dev${distance}+g${short-hash}") + elseif (ARGS_VERSION_FULL_MODE STREQUAL POST) + set(version-full "${version}${version-suffix-sanitized}.post${distance}+g${short-hash}") + else () + message(FATAL_ERROR "Assert False: VERSION_FULL_MODE = ${ARGS_VERSION_FULL_MODE}") + endif () + endif () + endif () + + # Construct the JSON data + string(JSON data SET ${data} + mode \"${mode}\" + ) + string(JSON data SET ${data} + version \"${version}\" + ) + file(WRITE ${ARGS_TMP_FOLDER}/.version ${version}) + string(JSON data SET ${data} + version-full \"${version-full}\" + ) + if (describe) + string(JSON data SET ${data} + describe \"${describe}\" + ) + file(WRITE ${ARGS_TMP_FOLDER}/.git_describe ${describe}) + endif () + if (commit) + string(JSON data SET ${data} + commit \"${commit}\" + ) + file(WRITE ${ARGS_TMP_FOLDER}/.git_commit ${commit}) + endif () + set(JSON data SET ${data} + version-full \"${version-full}\" + ) + string(JSON data SET ${data} + version-suffix \"${version-suffix}\" + ) + string(JSON data SET ${data} + distance ${distance} + ) + if (short-hash) + string(JSON data SET ${data} + short-hash \"${short-hash}\" ) endif () diff --git a/cmake/modules/int_computed.cmake.in b/cmake/modules/int_computed.cmake.in index 1511d08bb..51d79d527 100644 --- a/cmake/modules/int_computed.cmake.in +++ b/cmake/modules/int_computed.cmake.in @@ -28,6 +28,8 @@ set(LIBINT_MINOR_VERSION "@LIBINT_MINOR_VERSION@") set(LIBINT_MICRO_VERSION "@LIBINT_MICRO_VERSION@") set(LIBINT_VERSION ${LIBINT_MAJOR_VERSION}.${LIBINT_MINOR_VERSION}.${LIBINT_MICRO_VERSION}) +# for macros.tex +set(LIBINT_MMM_VERSION "${LIBINT_VERSION}") # <<< Dev Version >>> @@ -54,53 +56,31 @@ set(LIBINT2_SHELL_SET "@LIBINT2_SHELL_SET@") # <<< Features List >>> -if ((@LIBINT_CGSHELL_ORDERING@ EQUAL 1) AND (@LIBINT_SHELL_SET@ EQUAL 1)) - set(_ordering "ss") -elseif((@LIBINT_CGSHELL_ORDERING@ EQUAL 1) AND (@LIBINT_SHELL_SET@ EQUAL 2)) - set(_ordering "so") -elseif((@LIBINT_CGSHELL_ORDERING@ EQUAL 2) AND (@LIBINT_SHELL_SET@ EQUAL 1)) - set(_ordering "is") -elseif((@LIBINT_CGSHELL_ORDERING@ EQUAL 2) AND (@LIBINT_SHELL_SET@ EQUAL 2)) - set(_ordering "io") -elseif((@LIBINT_CGSHELL_ORDERING@ EQUAL 3) AND (@LIBINT_SHELL_SET@ EQUAL 1)) - set(_ordering "gs") -elseif((@LIBINT_CGSHELL_ORDERING@ EQUAL 3) AND (@LIBINT_SHELL_SET@ EQUAL 2)) - set(_ordering "go") -elseif((@LIBINT_CGSHELL_ORDERING@ EQUAL 4) AND (@LIBINT_SHELL_SET@ EQUAL 1)) - set(_ordering "os") -elseif((@LIBINT_CGSHELL_ORDERING@ EQUAL 4) AND (@LIBINT_SHELL_SET@ EQUAL 2)) - set(_ordering "oo") -elseif((@LIBINT_CGSHELL_ORDERING@ EQUAL 5) AND (@LIBINT_SHELL_SET@ EQUAL 1)) - set(_ordering "bs") -elseif((@LIBINT_CGSHELL_ORDERING@ EQUAL 5) AND (@LIBINT_SHELL_SET@ EQUAL 2)) - set(_ordering "bo") -elseif((@LIBINT_CGSHELL_ORDERING@ EQUAL 1) AND (@LIBINT_SHELL_SET@ EQUAL 1)) - set(_ordering "ss") -elseif((@LIBINT_CGSHELL_ORDERING@ EQUAL 1) AND (@LIBINT_SHELL_SET@ EQUAL 2)) - set(_ordering "so") -elseif((@LIBINT_CGSHELL_ORDERING@ EQUAL 2) AND (@LIBINT_SHELL_SET@ EQUAL 1)) - set(_ordering "is") -elseif((@LIBINT_CGSHELL_ORDERING@ EQUAL 2) AND (@LIBINT_SHELL_SET@ EQUAL 2)) - set(_ordering "io") -elseif((@LIBINT_CGSHELL_ORDERING@ EQUAL 3) AND (@LIBINT_SHELL_SET@ EQUAL 1)) - set(_ordering "gs") -elseif((@LIBINT_CGSHELL_ORDERING@ EQUAL 3) AND (@LIBINT_SHELL_SET@ EQUAL 2)) - set(_ordering "go") -elseif((@LIBINT_CGSHELL_ORDERING@ EQUAL 4) AND (@LIBINT_SHELL_SET@ EQUAL 1)) - set(_ordering "os") -elseif((@LIBINT_CGSHELL_ORDERING@ EQUAL 4) AND (@LIBINT_SHELL_SET@ EQUAL 2)) - set(_ordering "oo") -elseif((@LIBINT_CGSHELL_ORDERING@ EQUAL 5) AND (@LIBINT_SHELL_SET@ EQUAL 1)) - set(_ordering "bs") -elseif((@LIBINT_CGSHELL_ORDERING@ EQUAL 5) AND (@LIBINT_SHELL_SET@ EQUAL 2)) - set(_ordering "bo") +if (@LIBINT_CGSHELL_ORDERING@ EQUAL 1) + set(_o1st "s") +elseif(@LIBINT_CGSHELL_ORDERING@ EQUAL 2) + set(_o1st "i") +elseif(@LIBINT_CGSHELL_ORDERING@ EQUAL 3) + set(_o1st "g") +elseif(@LIBINT_CGSHELL_ORDERING@ EQUAL 4) + set(_o1st "o") +elseif(@LIBINT_CGSHELL_ORDERING@ EQUAL 5) + set(_o1st "b") else() - message(STATUS "int_computed.cmake: indeterminate orderings") + message(STATUS "int_computed.cmake: indeterminate orderings: cgshell") +endif() + +if (@LIBINT_SHELL_SET@ EQUAL 1) + set(_o2nd "s") +elseif(@LIBINT_SHELL_SET@ EQUAL 2) + set(_o2nd "o") +else() + message(STATUS "int_computed.cmake: indeterminate orderings: shell_set") endif() # for configuration.cc and libint2-config.cmake files set(Libint2_CONFIG_COMPONENTS "@Libint2_ERI_COMPONENTS@") -list(PREPEND Libint2_CONFIG_COMPONENTS "${_ordering}") +list(PREPEND Libint2_CONFIG_COMPONENTS "${_o1st}${_o2nd}") # <<< AM Components >>> diff --git a/cmake/modules/int_versions.cmake b/cmake/modules/int_versions.cmake index 35c6e9d13..460fd7ef9 100644 --- a/cmake/modules/int_versions.cmake +++ b/cmake/modules/int_versions.cmake @@ -13,10 +13,12 @@ # <<< 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}") +message(DEBUG "LibintRepository_VERSION ${LibintRepository_VERSION}") +message(DEBUG "LibintRepository_VERSION_FULL ${LibintRepository_VERSION_FULL}") +message(DEBUG "LibintRepository_COMMIT ${LibintRepository_COMMIT}") +message(DEBUG "LibintRepository_SHORT_HASH ${LibintRepository_SHORT_HASH}") +message(DEBUG "LibintRepository_DISTANCE ${LibintRepository_DISTANCE}") +message(DEBUG "LibintRepository_DESCRIBE ${LibintRepository_DESCRIBE}") if (LibintRepository_DISTANCE STREQUAL "0") set(LIBINT_SORTABLE_VERSION "${LibintRepository_VERSION}") @@ -24,8 +26,10 @@ 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}") +# short hash from DynVer is variable length from `git describe`. +# * use below for fixed length +# string(SUBSTRING ${LibintRepository_COMMIT} 0 7 LIBINT_GIT_COMMIT) +message(DEBUG "LIBINT_GIT_COMMIT ${LibintRepository_SHORT_HASH}") # 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 @@ -52,7 +56,7 @@ else() set(LIBINT_EXT_VERSION ${LIBINT_VERSION}) endif() -message(STATUS "Version: Full ${LIBINT_EXT_VERSION} Numeric ${LIBINT_VERSION} Sortable ${LIBINT_SORTABLE_VERSION}") +message(STATUS "Version: Full ${LIBINT_EXT_VERSION} Numeric ${LIBINT_VERSION} Sortable ${LIBINT_SORTABLE_VERSION} SCM ${LibintRepository_VERSION_FULL}") if (NOT(LibintRepository_VERSION STREQUAL LIBINT_VERSION)) message(AUTHOR_WARNING diff --git a/export/cmake/CMakeLists.txt.export b/export/cmake/CMakeLists.txt.export index 1908ff32f..6e8c0631a 100644 --- a/export/cmake/CMakeLists.txt.export +++ b/export/cmake/CMakeLists.txt.export @@ -8,7 +8,7 @@ project(Libint LANGUAGES CXX C) # Set Libint version =================================================================================================== set(LIBINT_MAJOR_VERSION 2) set(LIBINT_MINOR_VERSION 8) -set(LIBINT_MICRO_VERSION 1) +set(LIBINT_MICRO_VERSION 1) # Sync this with python/CMakeLists.txt set(LIBINT_BUILDID ) set(LIBINT_VERSION "${LIBINT_MAJOR_VERSION}.${LIBINT_MINOR_VERSION}.${LIBINT_MICRO_VERSION}") if (LIBINT_BUILDID) diff --git a/include/libint2/config.h.cmake.in b/include/libint2/config.h.cmake.in index 723f28cb2..d88b5a1e7 100644 --- a/include/libint2/config.h.cmake.in +++ b/include/libint2/config.h.cmake.in @@ -330,15 +330,15 @@ #if __has_cpp_attribute(maybe_unused) #define LIBINT_MAYBE_UNUSED [[maybe_unused]] #endif -#endif // __has_cpp_attribute +#endif /* __has_cpp_attribute */ #ifndef LIBINT_MAYBE_UNUSED #if defined __has_attribute # if __has_attribute (unused) # define LIBINT_MAYBE_UNUSED __attribute__ ((unused)) # endif -#endif // __has_attribute -#endif // LIBINT_MAYBE_UNUSED -#ifndef LIBINT_MAYBE_UNUSED // fallback +#endif /* __has_attribute */ +#endif /* LIBINT_MAYBE_UNUSED */ +#ifndef LIBINT_MAYBE_UNUSED /* fallback */ #define LIBINT_MAYBE_UNUSED #endif diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 5511d99d5..ba0bf421f 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -5,7 +5,20 @@ else() endif() cmake_policy(SET CMP0079 NEW) -project(libint2-python) +if(NOT DEFINED LIBINT_VERSION) + # for indep bld of project(libint2-python), version (for setup.py) not available from project(Libint2) + list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../cmake/modules) + include(int_computed OPTIONAL) # cmake+cmake + + if(NOT DEFINED LIBINT_VERSION) + set(LIBINT_VERSION "2.8.1") # libtool+cmake + endif() +endif() + +project( + libint2-python + VERSION ${LIBINT_VERSION} + ) if (NOT TARGET Python::Module) find_package(Python COMPONENTS Interpreter Development REQUIRED) @@ -23,6 +36,7 @@ if (NOT TARGET pybind11::pybind11) pybind11 GIT_REPOSITORY https://github.com/ValeevGroup/pybind11.git GIT_TAG ValeevGroup/v2.11 + # FIND_PACKAGE_ARGS 2.6.0 CONFIG # CMake 3.24 integrates find_package() call above into FC_Declare ) FetchContent_MakeAvailable(pybind11) @@ -40,10 +54,10 @@ if (pybind11_VERSION VERSION_GREATER_EQUAL 2.11.0) endif() pybind11_add_module( - libint2-python MODULE - #EXCLUDE_FROM_ALL - src/libint2/libint2.cc - src/libint2/engine.cc + libint2-python + MODULE + src/libint2/libint2.cc + src/libint2/engine.cc ) target_compile_features(libint2-python PRIVATE "cxx_std_17") @@ -56,9 +70,8 @@ endif() target_compile_options(libint2-python PRIVATE - # too many warnings on Windows - # $<$:/W4> - $<$>:-Wall> + #$<$:/W4> + $<$>:-Wall> ) #find_package(Eigen3 3.3 REQUIRED) @@ -66,23 +79,28 @@ target_compile_options(libint2-python set_target_properties( libint2-python PROPERTIES - #PREFIX "" - OUTPUT_NAME libint2 + #PREFIX "" + OUTPUT_NAME libint2 ) -if (TARGET libint2_obj) - set(libint2_python_target libint2_obj) +# future note: this whole block could likely be replaced by the below, but perhaps explicit better for now +# * `if(TARGET Libint2::cxx)\n target_link_libraries(libint2-python PRIVATE Libint2::cxx)\nelse()\n find_package(Libint2 ...` +if (TARGET Libint2::cxx) - if(MSVC) + + if (MSVC) target_compile_definitions(libint2-python PUBLIC _USE_MATH_DEFINES) target_compile_options(libint2-python PUBLIC "/EHsc") endif() - target_link_libraries(libint2-python PRIVATE libint2_obj) - target_link_libraries(libint2-python PRIVATE Boost::boost) + target_link_libraries( + libint2-python + PRIVATE + Libint2::cxx + ) else() - find_package(Libint2 REQUIRED) - target_link_libraries(libint2-python PRIVATE Libint2::cxx) + find_package(Libint2 COMPONENTS CXX_ho REQUIRED) + target_link_libraries(libint2-python PRIVATE Libint2::cxx) endif() # if (Eigen3::Eigen) @@ -93,12 +111,39 @@ endif() configure_file(setup.py.in ${PROJECT_BINARY_DIR}/setup.py) +# <<< Install >>> + +if (LIBINT2_PREFIX_PYTHON_INSTALL) + # * Note that this block is *Linux-style* install to `CMAKE_INSTALL_PREFIX`. + # * For *Python-style* install to `Python_EXECUTABLE`'s site-packages, use + # wheel target below from python/ directory. + # > make libint2-python-wheel + # > pip3 install dist/libint2-*.whl + + execute_process( + COMMAND ${Python_EXECUTABLE} -c + "import os, sys, sysconfig as s; spdir = s.get_path('platlib'); print(spdir.replace(os.path.commonpath([sys.prefix, spdir]), '').lstrip(os.path.sep));" + OUTPUT_VARIABLE LIBINT2_INSTALL_PYMODDIR + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + message(STATUS "Showing option LIBINT2_INSTALL_PYMODDIR: ${LIBINT2_INSTALL_PYMODDIR} (non-user-configurable until wanted)") + + install( + FILES $ + COMPONENT ${L2}_Python + DESTINATION ${LIBINT2_INSTALL_PYMODDIR} + ) +endif() + +# <<< Python Tooling >>> + add_custom_target( libint2-python-test DEPENDS libint2-python COMMAND ${Python_EXECUTABLE} -m setup test WORKING_DIRECTORY ${PROJECT_BINARY_DIR} ) +add_custom_target(check-python DEPENDS libint2-python-test) add_custom_target( libint2-python-wheel diff --git a/src/bin/CMakeLists.txt b/src/bin/CMakeLists.txt index 7de67d430..58d0a77bb 100644 --- a/src/bin/CMakeLists.txt +++ b/src/bin/CMakeLists.txt @@ -6,6 +6,7 @@ add_custom_target_subproject( USES_TERMINAL COMMAND ${CMAKE_CTEST_COMMAND} -V -R "libint2/compiler" ) +add_dependencies(check-libint2compiler build_libint) if (BUILD_TESTING) add_subdirectory(test_eri) diff --git a/src/bin/libint/CMakeLists.txt b/src/bin/libint/CMakeLists.txt index 532a6da97..ebb7983b7 100644 --- a/src/bin/libint/CMakeLists.txt +++ b/src/bin/libint/CMakeLists.txt @@ -38,7 +38,6 @@ add_library( target_compile_definitions( libint-libcompiler PUBLIC - __COMPILING_LIBINT2=1 $<$:_USE_MATH_DEFINES> # MSVC does not include constants, unless _USE_MATH_DEFINES is defined. ) @@ -85,7 +84,10 @@ add_executable( EXCLUDE_FROM_ALL build_libint.cc ) -target_link_libraries(build_libint libint-libcompiler) +target_link_libraries( + build_libint + libint-libcompiler + ) if(MSVC) # Increase stack size from 1 MB to 4 MB diff --git a/src/lib/libint/CMakeLists.txt b/src/lib/libint/CMakeLists.txt index e652bdccb..7de6dbf74 100644 --- a/src/lib/libint/CMakeLists.txt +++ b/src/lib/libint/CMakeLists.txt @@ -1,44 +1,69 @@ list(APPEND CMAKE_MODULE_PATH ${PROJECT_BINARY_DIR}/cmake/modules) +include(GNUInstallDirs) include(int_computed) # for macros.tex and features # <<< Generate Library Source >>> -file(MAKE_DIRECTORY "${EXPORT_STAGE_DIR}/src") +file(MAKE_DIRECTORY "${PROJECT_BINARY_DIR}/generated") add_custom_command( OUTPUT - "${EXPORT_STAGE_DIR}/src/libint2_params.h" + "${PROJECT_BINARY_DIR}/generated/libint2_params.h" + "${PROJECT_BINARY_DIR}/generated/HRRPart0bra0ket0pp.h" + "${PROJECT_BINARY_DIR}/generated/HRRPart0bra0ket0pp.cc" COMMAND $ - WORKING_DIRECTORY "${EXPORT_STAGE_DIR}/src" DEPENDS build_libint + WORKING_DIRECTORY "${PROJECT_BINARY_DIR}/generated" COMMENT "Generating Libint2 library source" ) -add_custom_target(libint-library-generate DEPENDS "${EXPORT_STAGE_DIR}/src/libint2_params.h") +add_custom_target(libint-library-generate DEPENDS "${PROJECT_BINARY_DIR}/generated/libint2_params.h") + # <<< Add Metadata To The Library Source >>> +# the library source to be tarballed is mainly assembled through populate.cmake. +# These following configure_file's need a few extra substitution variables that +# aren't passed to populate. + configure_file( "features.cmake.in" "${EXPORT_STAGE_DIR}/features" @ONLY) configure_file( - "${PROJECT_SOURCE_DIR}/include/libint2/basis.h.in" # TODO could move to basis.h.cmake.in when libtool retires + "${PROJECT_SOURCE_DIR}/doc/progman/macros.tex.in" + "${EXPORT_STAGE_DIR}/doc/macros.tex" + @ONLY) + +# position basis.h for local buildtree. +# * gets re-configured (CMakeLists.txt.export) for library export with its own +# CMAKE_INSTALL_PREFIX +# * gets re-re-positioned (end of this file) for case of continuous generator + +# library build (through EP) with outer CMAKE_INSTALL_PREFIX +set(DATADIR_ABSOLUTE ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/libint/${LIBINT_VERSION}) +configure_file( + "${PROJECT_SOURCE_DIR}/include/libint2/basis.h.in" # TODO basis.h.cmake.in after libtool retires "include/libint2/basis.h" @ONLY) add_custom_command( OUTPUT ${EXPORT_STAGE_DIR}/CMakeLists.txt + ${EXPORT_STAGE_DIR}/include/libint2_params.h + ${EXPORT_STAGE_DIR}/include/HRRPart0bra0ket0pp.h + ${EXPORT_STAGE_DIR}/src/HRRPart0bra0ket0pp.cc + COMMAND ${CMAKE_COMMAND} + "-DPROJECT_SOURCE_DIR=${PROJECT_SOURCE_DIR}" + "-DPROJECT_BINARY_DIR=${PROJECT_BINARY_DIR}" + "-DEXPORT_STAGE_DIR=${EXPORT_STAGE_DIR}" + -P ${CMAKE_CURRENT_SOURCE_DIR}/populate.cmake DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/populate.cmake" + "${PROJECT_BINARY_DIR}/generated/libint2_params.h" + "${PROJECT_BINARY_DIR}/generated/HRRPart0bra0ket0pp.h" + "${PROJECT_BINARY_DIR}/generated/HRRPart0bra0ket0pp.cc" libint-library-generate - COMMAND ${CMAKE_COMMAND} - "-DPROJECT_SOURCE_DIR=${PROJECT_SOURCE_DIR}" - "-DPROJECT_BINARY_DIR=${PROJECT_BINARY_DIR}" - "-DEXPORT_STAGE_DIR=${EXPORT_STAGE_DIR}" - -P ${CMAKE_CURRENT_SOURCE_DIR}/populate.cmake COMMENT "Populating Libint2 library" ) add_custom_target(libint-library-populate DEPENDS ${EXPORT_STAGE_DIR}/CMakeLists.txt) @@ -46,21 +71,25 @@ add_custom_target(libint-library-populate DEPENDS ${EXPORT_STAGE_DIR}/CMakeLists # <<< Export The Library Source >>> +set(EXPORT_TARBALL "${EXPORT_STAGE_DIR}.${EXPORT_COMPRESSOR_EXT}") add_custom_command( OUTPUT - "${EXPORT_STAGE_DIR}.${EXPORT_COMPRESSOR_EXT}" - COMMAND ${CMAKE_COMMAND} -E tar ${EXPORT_COMPRESSOR_CMD} "${EXPORT_STAGE_DIR}.${EXPORT_COMPRESSOR_EXT}" "${EXPORT_STAGE_DIR}" + "${EXPORT_TARBALL}" + COMMAND ${CMAKE_COMMAND} -E tar ${EXPORT_COMPRESSOR_CMD} "${EXPORT_TARBALL}" "${EXPORT_STAGE_DIR}" DEPENDS libint-library-generate libint-library-populate + ${EXPORT_STAGE_DIR}/CMakeLists.txt WORKING_DIRECTORY "${EXPORT_STAGE_DIR}/.." - COMMENT "Exporting tarball of Libint2 library source" + COMMENT "Exporting tarball of Libint2 library source: ${EXPORT_TARBALL}" ) - add_custom_target( - libint-library-export - DEPENDS - "${EXPORT_STAGE_DIR}.${EXPORT_COMPRESSOR_EXT}" - ) +if (LIBINT_BUILD_LIBRARY_AS_SUBPROJECT) + # rerun cmake if building as subproject to unpack and consume the library as subproject + add_custom_target(libint-library-export DEPENDS "${EXPORT_TARBALL}" + COMMAND ${CMAKE_COMMAND} -S "${CMAKE_SOURCE_DIR}" -B "${CMAKE_BINARY_DIR}") +else() + add_custom_target(libint-library-export DEPENDS "${EXPORT_TARBALL}") +endif() add_custom_target(export DEPENDS libint-library-export) @@ -74,13 +103,32 @@ else() message(FATAL_ERROR "No C language support; invoke CMake with -D CMAKE_C_COMPILER=/path/to/C/compiler to resolve") endif() +if (LIBINT_BUILD_LIBRARY_AS_SUBPROJECT) + include(FetchContent) + + if (EXISTS "${EXPORT_TARBALL}") + message("Unpacking ${EXPORT_TARBALL} and setting up as a subproject") + FetchContent_Declare( + libint-library + URL "${EXPORT_TARBALL}" + ) + FetchContent_MakeAvailable(libint-library) + else() + message(WARNING "LIBINT_BUILD_LIBRARY_AS_SUBPROJECT=ON but library has not been exported yet; build target 'libint-library-export' first, this will make library build/test targets ('check', 'install') available. Don't run 'check' with CTEST_PARALLEL_LEVEL as this will build the library multiple times.") + endif() + +else() include(ExternalProject) list(APPEND library_CMAKE_ARGS + -G${CMAKE_GENERATOR} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DBUILD_TESTING=${BUILD_TESTING} -DBUILD_SHARED_LIBS=${BUILD_SHARED_LIBS} -DLIBINT2_BUILD_SHARED_AND_STATIC_LIBS=${LIBINT2_BUILD_SHARED_AND_STATIC_LIBS} + -DLIBINT2_REQUIRE_CXX_API=${LIBINT2_REQUIRE_CXX_API} + -DLIBINT2_REQUIRE_CXX_API_COMPILED=${LIBINT2_REQUIRE_CXX_API_COMPILED} + -DLIBINT2_ENABLE_FORTRAN=${LIBINT2_ENABLE_FORTRAN} -DLIBINT2_ALIGN_SIZE=${LIBINT_ALIGN_SIZE} -DLIBINT2_REALTYPE=${LIBINT2_REALTYPE} -DLIBINT2_ENABLE_MPFR=${LIBINT2_ENABLE_MPFR} @@ -108,10 +156,13 @@ endif() if (LIBINT2_LOCAL_Eigen3_INSTALL) list(APPEND library_CMAKE_ARGS "-DLIBINT2_LOCAL_Eigen3_INSTALL=${LIBINT2_LOCAL_Eigen3_INSTALL}") endif() - - - - + if (Python_EXECUTABLE) + list(APPEND library_CMAKE_ARGS "-DPython_EXECUTABLE=${Python_EXECUTABLE}") + endif() + if (LIBINT2_ENABLE_FORTRAN AND CMAKE_Fortran_COMPILER) + # Note: not handling CMAKE_Fortran_COMPILER_ARG1 + list(APPEND library_CMAKE_ARGS "-DCMAKE_Fortran_COMPILER=${CMAKE_Fortran_COMPILER}") + endif() ExternalProject_Add( library @@ -120,7 +171,7 @@ endif() BUILD_ALWAYS 1 # download generalized to generating source by unpacking source tarball # * DOWNLOAD_DIR is tidier but fails on Windows - URL "${EXPORT_STAGE_DIR}.${EXPORT_COMPRESSOR_EXT}" + URL "${EXPORT_TARBALL}" LOG_DOWNLOAD 1 CMAKE_ARGS "${library_CMAKE_ARGS}" CMAKE_CACHE_ARGS @@ -130,10 +181,17 @@ endif() -DCMAKE_C_COMPILER_ARG1:STRING=${CMAKE_C_COMPILER_ARG1} -DCMAKE_PREFIX_PATH:STRING=${CMAKE_PREFIX_PATH} TEST_COMMAND "" - STEP_TARGETS build + USES_TERMINAL_CONFIGURE TRUE + USES_TERMINAL_BUILD TRUE + USES_TERMINAL_INSTALL TRUE + STEP_TARGETS + build + install ) ExternalProject_Get_property(library BINARY_DIR) + # instead of running tests by default, replicate check-libint2 target here + # and use it to invoke library's check-libint2 target if (BUILD_TESTING) include(AddCustomTargetSubproject) add_custom_target_subproject( @@ -144,6 +202,17 @@ endif() DEPENDS "library-build" ) + + if (LIBINT2_ENABLE_PYTHON) + find_package(Python COMPONENTS Interpreter REQUIRED) + add_custom_target_subproject( + libint2 check-python + COMMAND ${Python_EXECUTABLE} -m setup test + WORKING_DIRECTORY ${BINARY_DIR}/python + DEPENDS + "library-install" + ) + endif() endif() install( @@ -151,3 +220,4 @@ endif() "${CMAKE_CURRENT_BINARY_DIR}/include/libint2/basis.h" DESTINATION "${STAGED_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}/libint2" ) +endif() diff --git a/src/lib/libint/CMakeLists.txt.export b/src/lib/libint/CMakeLists.txt.export index 48c42d0eb..73c946647 100644 --- a/src/lib/libint/CMakeLists.txt.export +++ b/src/lib/libint/CMakeLists.txt.export @@ -29,6 +29,7 @@ set(pnv libint2) # projectnameversion # See "TARBALL" labels in INSTALL.md for elaboration of options, dependencies, & targets. ################################### Options #################################### +include(GNUInstallDirs) include(CTest) include(options) include(CheckFunctionExists) @@ -41,6 +42,12 @@ option_with_print(BUILD_SHARED_LIBS "Build Libint library as shared, not static" OFF) option_with_print(LIBINT2_BUILD_SHARED_AND_STATIC_LIBS "Build both shared and static Libint libraries in one shot. Uses -fPIC." OFF) +option_with_print(LIBINT2_REQUIRE_CXX_API + "C++11 Libint API: define library targets + test (requires Eigen3, Boost is optional but strongly recommended)" ON) +option_with_print(LIBINT2_REQUIRE_CXX_API_COMPILED + "Build C++11 Compiled (not just header-only) targets (requires Eigen3)" OFF) +option_with_print(LIBINT2_ENABLE_FORTRAN + "Build Fortran03+ Libint interface (requires C and Fortran and Python)" OFF) option_with_print(LIBINT2_ENABLE_MPFR "Use GNU MPFR library for high-precision testing (requires MPFR. EXPERTS ONLY)" OFF) option_with_print(LIBINT2_ENABLE_PYTHON @@ -52,21 +59,37 @@ option_with_print(LIBINT2_LOCAL_Eigen3_INSTALL option_with_print(CMAKE_DISABLE_FIND_PACKAGE_Boost "When Boost required for C++11 API, disable its detection, thereby forcing use of bundled Boost (Standard CMake variable: https://cmake.org/cmake/help/latest/variable/CMAKE_DISABLE_FIND_PACKAGE_PackageName.html)" OFF) - check_function_exists(posix_memalign HAVE_POSIX_MEMALIGN) if (HAVE_POSIX_MEMALIGN) option_with_default(LIBINT2_ALIGN_SIZE "(EXPERT) if posix_memalign is available, this will specify alignment of Libint data, in units of sizeof(LIBINT2_REALTYPE). Default is to use built-in heuristics." 0) mark_as_advanced(LIBINT2_ALIGN_SIZE) elseif (MSVC) - set(HAVE_POSIX_MEMALIGN 1) # aliased in memory.h + # works for Windows in memory.h set(LIBINT2_ALIGN_SIZE "0" CACHE STRING "") endif() option_with_default(LIBINT2_REALTYPE "Specifies the floating-point data type used by the library." double) +option_with_print(LIBINT_USER_DEFINED_REAL_INCLUDES + "UNTESTED Additional #includes necessary to use the real type." OFF) include(int_userreal) +if ((LIBINT2_REQUIRE_CXX_API_COMPILED OR LIBINT2_ENABLE_PYTHON) AND NOT LIBINT2_REQUIRE_CXX_API) + set(LIBINT2_REQUIRE_CXX_API 1) + message(STATUS "Setting option LIBINT2_REQUIRE_CXX_API=ON as needed by LIBINT2_REQUIRE_CXX_API_COMPILED=${LIBINT2_REQUIRE_CXX_API_COMPILED} and/or LIBINT2_ENABLE_PYTHON=${LIBINT2_ENABLE_PYTHON}.") +endif() + +if (LIBINT2_ENABLE_FORTRAN) + include(CheckLanguage) + check_language(Fortran) + if (CMAKE_Fortran_COMPILER) + enable_language(Fortran) + else() + message(FATAL_ERROR "Given LIBINT2_ENABLE_FORTRAN=ON but could not find Fortran compiler. Provide via CMAKE_Fortran_COMPILER") + endif() +endif() + # <<< Miscellaneous >>> # next one defined by `include(CTest)` @@ -106,7 +129,7 @@ if (LIBINT2_ENABLE_MPFR) message(VERBOSE "${Cyan}Found MPFR${ColourReset}: ${_loc} (found version ${MPFR_VERSION})") endif() -#if (LIBINT2_REQUIRE_CXX_API) +if (LIBINT2_REQUIRE_CXX_API) if (NOT TARGET Boost::headers) find_package(Boost 1.57) endif() @@ -129,16 +152,15 @@ endif() message(FATAL_ERROR "Failed to unpack the bundled Boost! The tar command output:\n${UNPACK_BOOST_OUTPUT}") endif() endif() -#endif() +endif() find_package(Eigen3 MODULE) if (TARGET Eigen3::Eigen) set(LIBINT_HAS_EIGEN 1) endif() -#if (LIBINT2_REQUIRE_CXX_API AND NOT ${LIBINT_HAS_CXX_API}) -if (NOT LIBINT_HAS_EIGEN) # TODO tmp wrong logic - message(FATAL_ERROR "C++ API cannot be built without Eigen3; configure (via CMake) and install Eigen3 and add the install prefix to CMAKE_PREFIX_PATH, or add -DLIBINT2_REQUIRE_CXX_API=OFF to the CMake command line if the C++ API is not required") +if (LIBINT2_REQUIRE_CXX_API AND NOT ${LIBINT_HAS_EIGEN}) + message(FATAL_ERROR "C++ API cannot be built without Eigen3; configure (via CMake) and install Eigen3 and add the install prefix to CMAKE_PREFIX_PATH, or add -D LIBINT2_REQUIRE_CXX_API=OFF to the CMake command line if the C++ API is not required") endif() # Python is optionally used for testing. @@ -168,47 +190,46 @@ configure_file( src/configuration.cc @ONLY) +set(DATADIR_ABSOLUTE ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/libint/${LIBINT_VERSION}) +configure_file( + include/libint2/basis.h.in # temp until libtool dropped and moved to basis.h.cmake.in + include/libint2/basis.h + @ONLY) + include(srclist.cmake) list(LENGTH LIBINT2_LIBRARY_CXX_SRC _source_count) message(STATUS "Loading ${_source_count} library source files from LIBINT2_LIBRARY_CXX_SRC") message(DEBUG "LIBINT2_LIBRARY_CXX_SRC=${LIBINT2_LIBRARY_CXX_SRC}") -# Notes +# Notes: # * __COMPILING_LIBINT2 is needed whenever using libint targets in the build tree # * MSVC does not include constants, unless _USE_MATH_DEFINES is defined # * /EHsc sets the exception handling model (allows "throw") +# * _CRT_* squashs some getenv, strdup, strncpy, ctime, fopen warnings + +# ==== pre-user-targets: plain/C++(compiled) ================================== add_library( int-obj OBJECT "${LIBINT2_LIBRARY_CXX_SRC}" - "src/configuration.cc" - ) -target_compile_definitions( - int-obj - PRIVATE - -D__COMPILING_LIBINT2=1 + src/configuration.cc ) target_compile_features( int-obj PUBLIC - "cxx_std_11" - # N.B. PUBLIC to make int-{static/shared} require C++11? + "cxx_std_11" # N.B. PUBLIC to make int-{static/shared} require C++11 + ) +target_compile_definitions( + int-obj + PRIVATE + __COMPILING_LIBINT2=1 ) set_target_properties( int-obj PROPERTIES UNITY_BUILD TRUE # always use unity build for int-obj ) -target_include_directories( - int-obj - PRIVATE - ${PROJECT_BINARY_DIR}/include/ - ${PROJECT_SOURCE_DIR}/src/ - ${PROJECT_SOURCE_DIR}/include/ - ${PROJECT_SOURCE_DIR}/include/libint2/ - ) - if (MSVC) set_target_properties( int-obj @@ -217,21 +238,76 @@ if (MSVC) # Increase stack size from 1 MB to 4 MB ) endif() +target_include_directories( + int-obj + PRIVATE + ${PROJECT_SOURCE_DIR}/include/ + ${PROJECT_BINARY_DIR}/include/ + ) + +if (LIBINT2_REQUIRE_CXX_API_COMPILED) + + add_library( + int-cxx-obj + OBJECT + src/engine.cpp + ) + target_compile_features( + int-cxx-obj + PUBLIC + "cxx_std_11" + ) + target_compile_definitions( + int-cxx-obj + PRIVATE + __COMPILING_LIBINT2=1 + LIBINT2_DOES_NOT_INLINE_ENGINE=1 + $<$:_USE_MATH_DEFINES> + $<$:_CRT_NONSTDC_NO_DEPRECATE> + $<$:_CRT_NONSTDC_NO_WARNINGS> + $<$:_CRT_SECURE_NO_WARNINGS> + ) + target_include_directories( + int-cxx-obj + PRIVATE + ${PROJECT_SOURCE_DIR}/include/ + ${PROJECT_BINARY_DIR}/include/ + ) + target_link_libraries ( + int-cxx-obj + PRIVATE + Eigen3::Eigen + $<$:Boost::headers> + ) + +endif() + +# plan shared, static, or both, then set fpic accordingly +if (LIBINT2_BUILD_SHARED_AND_STATIC_LIBS OR BUILD_SHARED_LIBS) + set(L2_BUILD_SHARED_LIBS 1) +endif() +if (LIBINT2_BUILD_SHARED_AND_STATIC_LIBS OR (NOT BUILD_SHARED_LIBS)) + set(L2_BUILD_STATIC_LIBS 1) +endif() +if (L2_BUILD_SHARED_LIBS OR (LIBINT2_ENABLE_PYTHON AND NOT MSVC)) + if (TARGET int-cxx-obj) + set(tgts int-obj int-cxx-obj) + else() + set(tgts int-obj) + endif() -if (BUILD_SHARED_LIBS OR LIBINT2_BUILD_SHARED_AND_STATIC_LIBS) - set(BUILD_SHARED_LIBS 1) set_target_properties( - int-obj + ${tgts} PROPERTIES POSITION_INDEPENDENT_CODE 1 ) endif() -if (NOT BUILD_SHARED_LIBS OR LIBINT2_BUILD_SHARED_AND_STATIC_LIBS) - set(BUILD_STATIC_LIBS 1) -endif() -if (BUILD_SHARED_LIBS) + +# ==== 6 user targets: plain/C++(headers)/C++(compiled) shared/static ========= + +if (L2_BUILD_SHARED_LIBS) add_library( int-shared @@ -249,28 +325,115 @@ if (BUILD_SHARED_LIBS) $ PUBLIC $<$:_USE_MATH_DEFINES> - ) + $<$:_CRT_NONSTDC_NO_DEPRECATE> + $<$:_CRT_NONSTDC_NO_WARNINGS> + $<$:_CRT_SECURE_NO_WARNINGS> + ) target_compile_options( int-shared PUBLIC $<$:/EHsc> ) + set_target_properties( + int-shared + PROPERTIES + OUTPUT_NAME "int2" + ) + if (APPLE) + set_target_properties( + int-shared + PROPERTIES + LINK_FLAGS "-undefined dynamic_lookup" + ) + endif() target_include_directories( int-shared INTERFACE $ - $ $ - $ ) - set_target_properties( - int-shared - PROPERTIES - OUTPUT_NAME "int2" - ) -endif() -if (BUILD_STATIC_LIBS) + if (LIBINT2_REQUIRE_CXX_API) + + add_library( + int-cxx-headeronly-shared + INTERFACE + ) + target_compile_features( + int-cxx-headeronly-shared + INTERFACE + "cxx_std_11" + ) + target_compile_definitions( + int-cxx-headeronly-shared + INTERFACE + $ + ) + if (NOT MSVC) + # TODO fix the DATADIR define escaping on Windows + # * below works fine in tests + # * but fails in Psi4 compile + # * prefix replacement in conda used instead on Windows + target_compile_definitions( + int-cxx-headeronly-shared + INTERFACE + $ + ) + endif() + target_link_libraries( + int-cxx-headeronly-shared + INTERFACE + int-shared + Eigen3::Eigen + $<$:Boost::headers> + ) + + if (LIBINT2_REQUIRE_CXX_API_COMPILED) + + add_library( + int-cxx-shared + SHARED + $ + ) + target_compile_definitions( + int-cxx-shared + INTERFACE + $ + PUBLIC + LIBINT2_DOES_NOT_INLINE_ENGINE=1 + LIBINT2_CONSTEXPR_STATICS=1 # LAB: needed for tests, but correct in general, EFV? + $<$:_USE_MATH_DEFINES> + ) + target_compile_options( + int-cxx-shared + PUBLIC + $<$:/EHsc> + ) + set_target_properties( + int-cxx-shared + PROPERTIES + #SOVERSION ${LIBINT_MAJOR_SOVERSION} + MACOSX_RPATH ON + OUTPUT_NAME "int2-cxx" + ) + if (APPLE) + set_target_properties( + int-cxx-shared + PROPERTIES + LINK_FLAGS "-undefined dynamic_lookup" + ) + endif() + target_link_libraries( + int-cxx-shared + INTERFACE + int-cxx-headeronly-shared + ) + + endif (LIBINT2_REQUIRE_CXX_API_COMPILED) + endif (LIBINT2_REQUIRE_CXX_API) +endif (L2_BUILD_SHARED_LIBS) + +if (L2_BUILD_STATIC_LIBS) add_library( int-static @@ -288,36 +451,231 @@ if (BUILD_STATIC_LIBS) $ PUBLIC $<$:_USE_MATH_DEFINES> + $<$:_CRT_NONSTDC_NO_DEPRECATE> + $<$:_CRT_NONSTDC_NO_WARNINGS> + $<$:_CRT_SECURE_NO_WARNINGS> ) target_compile_options( int-static PUBLIC $<$:/EHsc> ) + set_target_properties( + int-static + PROPERTIES + OUTPUT_NAME "int2" + ) target_include_directories( int-static INTERFACE $ - $ $ - $ ) - set_target_properties( - int-static - PROPERTIES - OUTPUT_NAME "int2" - ) -endif() -# Permanent aliases ============================================================= + if (LIBINT2_REQUIRE_CXX_API) + + add_library( + int-cxx-headeronly-static + INTERFACE + ) + target_compile_features( + int-cxx-headeronly-static + INTERFACE + "cxx_std_11" + ) + target_compile_definitions( + int-cxx-headeronly-static + INTERFACE + $ + ) + target_link_libraries( + int-cxx-headeronly-static + INTERFACE + int-static + Eigen3::Eigen + $<$:Boost::headers> + ) + + if (LIBINT2_REQUIRE_CXX_API_COMPILED) + + add_library( + int-cxx-static + STATIC + $ + ) + target_compile_definitions( + int-cxx-static + INTERFACE + $ + PUBLIC + LIBINT2_DOES_NOT_INLINE_ENGINE=1 + LIBINT2_CONSTEXPR_STATICS=1 # LAB: needed for tests, but correct in general, EFV? + $<$:_USE_MATH_DEFINES> + ) + target_compile_options( + int-cxx-static + PUBLIC + $<$:/EHsc> + ) + set_target_properties( + int-cxx-static + PROPERTIES + OUTPUT_NAME "int2-cxx" + #EXPORT_NAME "int2-cxx" + ) + target_link_libraries( + int-cxx-static + INTERFACE + int-cxx-headeronly-static + ) + + endif (LIBINT2_REQUIRE_CXX_API_COMPILED) + endif (LIBINT2_REQUIRE_CXX_API) +endif (L2_BUILD_STATIC_LIBS) + + +# ==== aliases ================================================================ + +# permanent aliases # * used for tests -if (BUILD_SHARED_LIBS) +if (L2_BUILD_SHARED_LIBS) add_library(${L2}::int2 ALIAS int-shared) -elseif (BUILD_STATIC_LIBS) + if (LIBINT2_REQUIRE_CXX_API) + add_library(${L2}::cxx ALIAS int-cxx-headeronly-shared) + if (LIBINT2_REQUIRE_CXX_API_COMPILED) + add_library(${L2}::int2-cxx ALIAS int-cxx-shared) + endif() + endif() +elseif (L2_BUILD_STATIC_LIBS) add_library(${L2}::int2 ALIAS int-static) + if (LIBINT2_REQUIRE_CXX_API) + add_library(${L2}::cxx ALIAS int-cxx-headeronly-static) + if (LIBINT2_REQUIRE_CXX_API_COMPILED) + add_library(${L2}::int2-cxx ALIAS int-cxx-static) + endif() + endif() endif() +# legacy (pre-2.9.0) aliases + +if (L2_BUILD_SHARED_LIBS) + add_library(libint2 ALIAS int-shared) + if (LIBINT2_REQUIRE_CXX_API) + add_library(libint2_cxx ALIAS int-cxx-headeronly-shared) + endif() +elseif (L2_BUILD_STATIC_LIBS) + add_library(libint2 ALIAS int-static) + if (LIBINT2_REQUIRE_CXX_API) + add_library(libint2_cxx ALIAS int-cxx-headeronly-static) + endif() +endif() + + +# ==== Fortran bindings ======================================================= + +if (LIBINT2_ENABLE_FORTRAN) + # specify the location of modules + set(BUILDTREE_FMODDIR "fortran/modules") + + # preprocess libint2.h ... this is a guess for UNIX systems only + # N.B. Requires C compiler! + if (UNIX) + file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/fortran) + + include(CheckLanguage) + check_language(C) + if (CMAKE_C_COMPILER) + enable_language(C) + else() + message(FATAL_ERROR "Given LIBINT2_ENABLE_FORTRAN=ON but could not find C compiler needed to generate Fortran bindings, provide via CMAKE_C_COMPILER") + endif() + + # preprocessed libint.h + add_custom_command( + OUTPUT + ${PROJECT_BINARY_DIR}/fortran/libint2.h.i + COMMAND ${CMAKE_C_COMPILER} -E + -D__COMPILING_LIBINT2 + -I${PROJECT_SOURCE_DIR}/include + -I${PROJECT_SOURCE_DIR}/src + -I${PROJECT_BINARY_DIR}/include + -I${PROJECT_BINARY_DIR}/include/libint2 + ${PROJECT_SOURCE_DIR}/include/libint2.h + -o ${PROJECT_BINARY_DIR}/fortran/libint2.h.i + DEPENDS + ${PROJECT_SOURCE_DIR}/include/libint2.h + COMMENT "Generating libint2.h.i" + ) + else() + message(FATAL_ERROR "Cannot run preprocessor on non-Unix systems, disable Fortran to proceed") + endif() + + # translated Libint_t + add_custom_command( + OUTPUT + ${PROJECT_BINARY_DIR}/fortran/libint2_types_f.h + COMMAND ${Python_EXECUTABLE} ${PROJECT_SOURCE_DIR}/fortran/c_to_f.py ${PROJECT_BINARY_DIR}/fortran/libint2.h.i ${PROJECT_BINARY_DIR}/fortran/libint2_types_f.h Libint_t + DEPENDS + ${PROJECT_BINARY_DIR}/fortran/libint2.h.i + COMMENT "Generating libint2_types_f.h" + ) + + # extracted defines from libint2_types.h + add_custom_command( + OUTPUT + ${PROJECT_BINARY_DIR}/fortran/fortran_incldefs.h + COMMAND ${Python_EXECUTABLE} ${PROJECT_SOURCE_DIR}/fortran/make_defs.py ${PROJECT_SOURCE_DIR}/include/libint2_types.h ${PROJECT_BINARY_DIR}/fortran/fortran_incldefs.h + DEPENDS + ${PROJECT_SOURCE_DIR}/include/libint2_types.h + COMMENT "Generating fortran_incldefs.h" + ) + + # build module + add_library( + libint_f + OBJECT + fortran/libint_f.F90 + ) + set_source_files_properties( + fortran/libint_f.F90 + PROPERTIES + OBJECT_DEPENDS "${PROJECT_BINARY_DIR}/fortran/libint2_types_f.h;${PROJECT_BINARY_DIR}/fortran/fortran_incldefs.h" + ) + target_compile_definitions( + libint_f + PRIVATE + __COMPILING_LIBINT2 + ) + set_target_properties( + libint_f + PROPERTIES + Fortran_MODULE_DIRECTORY ${PROJECT_BINARY_DIR}/${BUILDTREE_FMODDIR} + ) + if (BUILD_SHARED_LIBS) + set_target_properties(libint_f PROPERTIES POSITION_INDEPENDENT_CODE 1) + endif() + target_include_directories( + libint_f + PUBLIC + $ + $ + $ + $ + ) + + # Fortran tests merged into rest of tests + +endif (LIBINT2_ENABLE_FORTRAN) + + +# ==== Python bindings ======================================================== + +if (LIBINT2_ENABLE_PYTHON) + add_subdirectory(python EXCLUDE_FROM_ALL) +endif() + + if (BUILD_TESTING) add_subdirectory(tests) endif() diff --git a/src/lib/libint/engine.cpp b/src/lib/libint/engine.cpp new file mode 100644 index 000000000..b6d4735b1 --- /dev/null +++ b/src/lib/libint/engine.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2022-2024 Edward F. Valeev + * + * This file is part of Libint. + * + * Libint is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Libint is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Libint. If not, see . + * + */ + +#include + +// need to instantiate this just in case user constructs a libint2::Engine using +// default operator params +template __libint2_engine_inline libint2::any +libint2::Engine::enforce_params_type< + libint2::detail::default_operator_traits::oper_params_type>( + libint2::Operator oper, + const libint2::detail::default_operator_traits::oper_params_type& params, + bool throw_if_wrong_type); diff --git a/src/lib/libint/populate.cmake b/src/lib/libint/populate.cmake index 8e2b59ad5..38daa14de 100644 --- a/src/lib/libint/populate.cmake +++ b/src/lib/libint/populate.cmake @@ -1,19 +1,58 @@ set(LIBRARY_SOURCE_DIR ${PROJECT_SOURCE_DIR}/src/lib/libint) +# ==== /doc =================================================================== + file( INSTALL - "${LIBRARY_SOURCE_DIR}/../../../tests/" + "${PROJECT_SOURCE_DIR}/doc/progman/progman.tex" + "${PROJECT_SOURCE_DIR}/doc/progman/refs.bib" + "${PROJECT_SOURCE_DIR}/doc/Libint_Logo3_alt.pdf" + "${PROJECT_SOURCE_DIR}/doc/Libint_Logo3_alt.eps" + DESTINATION "${EXPORT_STAGE_DIR}/doc" + ) + +file( + INSTALL + "${PROJECT_SOURCE_DIR}/doc/progman/" + DESTINATION "${EXPORT_STAGE_DIR}/doc" + FILES_MATCHING + PATTERN "*.cc" + ) + +# ==== /tests ================================================================= + +file( + INSTALL + "${PROJECT_SOURCE_DIR}/tests/" DESTINATION "${EXPORT_STAGE_DIR}/tests" FILES_MATCHING PATTERN "*.c" PATTERN "*.cc" PATTERN "*.h" PATTERN "*.hpp" + PATTERN "*.F90" PATTERN "*.py" PATTERN "*.xyz" + PATTERN "hftest.cmake" PATTERN "CMakeLists.txt" + # PATTERN "ssss.nb" ) +file( + INSTALL + "${PROJECT_SOURCE_DIR}/export/fortran/fortran_example.F90" + DESTINATION "${EXPORT_STAGE_DIR}/tests/fortran" + ) + +file(READ "${PROJECT_SOURCE_DIR}/export/fortran/test.cc" _file_contents) +string(REPLACE "tests/unit" "unit" _file_contents "${_file_contents}") +file(WRITE "${EXPORT_STAGE_DIR}/tests/fortran/test.cc" "${_file_contents}") + +file(READ "${PROJECT_SOURCE_DIR}/export/fortran/test-eri.cc" _file_contents) +string(REPLACE "tests/unit" "unit" _file_contents "${_file_contents}") +string(REPLACE "tests/eri" "eri" _file_contents "${_file_contents}") +file(WRITE "${EXPORT_STAGE_DIR}/tests/fortran/test-eri.cc" "${_file_contents}") + file( INSTALL "${PROJECT_SOURCE_DIR}/src/bin/test_eri/eri.h" @@ -21,6 +60,70 @@ file( DESTINATION "${EXPORT_STAGE_DIR}/tests/eri" ) +# ==== /lib/basis ============================================================= + +file( + INSTALL + "${PROJECT_SOURCE_DIR}/lib/basis/" + DESTINATION "${EXPORT_STAGE_DIR}/lib/basis" + FILES_MATCHING + PATTERN "*.g94" + ) + +# file(INSTALL) preserves symlinks, and tar -xf fails on them on Windows (either +# the symlink extracted before the target file or problems with the stars in +# filenames themselves). To make export tarballs broadly usable, we'll copy +# the symlinked files into real files, which is what libtool effectively did. +file( + COPY_FILE + "${PROJECT_SOURCE_DIR}/lib/basis/6-31gs.g94" + "${EXPORT_STAGE_DIR}/lib/basis/6-31gs.g94" + ) +file( + COPY_FILE + "${PROJECT_SOURCE_DIR}/lib/basis/6-31gss.g94" + "${EXPORT_STAGE_DIR}/lib/basis/6-31gss.g94" + ) +file( + COPY_FILE + "${PROJECT_SOURCE_DIR}/lib/basis/6-311gss.g94" + "${EXPORT_STAGE_DIR}/lib/basis/6-311gss.g94" + ) + +# ==== /fortran =============================================================== + +file( + INSTALL + "${PROJECT_SOURCE_DIR}/export/fortran/c_to_f.py" + "${PROJECT_SOURCE_DIR}/export/fortran/make_defs.py" + "${PROJECT_SOURCE_DIR}/export/fortran/libint_f.F90" + DESTINATION "${EXPORT_STAGE_DIR}/fortran" + ) + +# ==== /python ================================================================ + +file( + INSTALL + "${PROJECT_SOURCE_DIR}/python/" + DESTINATION "${EXPORT_STAGE_DIR}/python" + FILES_MATCHING + PATTERN "*.h" + PATTERN "*.cc" + PATTERN "*.py" + PATTERN "*.py.in" + PATTERN "CMakeLists.txt" + ) + +# ==== /external ============================================================== + +file( + INSTALL + "${PROJECT_SOURCE_DIR}/external/boost.tar.gz" + DESTINATION "${EXPORT_STAGE_DIR}/external" + ) + +# ==== /cmake ================================================================= + file( INSTALL "${PROJECT_SOURCE_DIR}/cmake/modules/autocmake_safeguards.cmake" @@ -41,13 +144,7 @@ file( DESTINATION "${EXPORT_STAGE_DIR}/cmake" ) -configure_file( - "${LIBRARY_SOURCE_DIR}/CMakeLists.txt.export" - "${EXPORT_STAGE_DIR}/CMakeLists.txt" - COPYONLY) - - -# <<< Headers >>> +# ==== /include =============================================================== file( INSTALL @@ -55,30 +152,88 @@ file( "${PROJECT_SOURCE_DIR}/include/libint2.hpp" DESTINATION "${EXPORT_STAGE_DIR}/include" ) + +# Note that libint2_iface.h, libint2_params.h, libint2_types.h shift around. +# They're generated along with the integrals .h/.cc library src, then get +# exported to include/ (along with the integrals .h), then are finally +# installed (not with the integrals .h) into include/libint2/ . The +# __COMPILING_LIBINT define and the include/libint2/util/generated/libint2_*.h +# redirection headers take care of the "build tree"/"export" setup. +# In a cmake+cmake buildsystem, one could probably install these three headers +# to both locations and forego the define. file( INSTALL - "${PROJECT_SOURCE_DIR}/include/libint2/" - DESTINATION "${EXPORT_STAGE_DIR}/include/libint2" + "${PROJECT_BINARY_DIR}/generated/" + DESTINATION "${EXPORT_STAGE_DIR}/include" FILES_MATCHING PATTERN "*.h" - PATTERN "*.h.cmake.in" ) + +file( + INSTALL + "${PROJECT_SOURCE_DIR}/src/bin/libint/util_types.h" + DESTINATION "${EXPORT_STAGE_DIR}/include" + ) + file( INSTALL "${LIBRARY_SOURCE_DIR}/" - DESTINATION "${EXPORT_STAGE_DIR}/include/libint2" + DESTINATION "${EXPORT_STAGE_DIR}/include" FILES_MATCHING PATTERN "*.h" ) +# ==== /include/libint2 ======================================================= + +file( + INSTALL + "${PROJECT_SOURCE_DIR}/include/libint2/" + DESTINATION "${EXPORT_STAGE_DIR}/include/libint2" + FILES_MATCHING + PATTERN "*.h" + PATTERN "*.h.cmake.in" + PATTERN "basis.h.in" # TODO basis.h.cmake.in after libtool retires + ) -# <<< Source >>> +# ==== /src =================================================================== file( INSTALL + "${PROJECT_SOURCE_DIR}/src/lib/libint/engine.cpp" "${PROJECT_SOURCE_DIR}/src/lib/libint/configuration.cc.cmake.in" DESTINATION "${EXPORT_STAGE_DIR}/src" ) +file( + INSTALL + "${PROJECT_BINARY_DIR}/generated/" + DESTINATION "${EXPORT_STAGE_DIR}/src" + FILES_MATCHING + PATTERN "*.cc" + ) + file(GLOB generated_sources_list RELATIVE "${EXPORT_STAGE_DIR}" "${EXPORT_STAGE_DIR}/src/*.cc") file(WRITE ${EXPORT_STAGE_DIR}/srclist.cmake "set(LIBINT2_LIBRARY_CXX_SRC \"${generated_sources_list}\" )") + +# ==== / ====================================================================== + +configure_file( + "${PROJECT_SOURCE_DIR}/export/LICENSE.export" + "${EXPORT_STAGE_DIR}/LICENSE" + COPYONLY) + +file( + INSTALL + "${PROJECT_SOURCE_DIR}/INSTALL" + "${PROJECT_SOURCE_DIR}/INSTALL.md" + "${PROJECT_SOURCE_DIR}/COPYING" + "${PROJECT_SOURCE_DIR}/COPYING.LESSER" + "${PROJECT_SOURCE_DIR}/CITATION" + "${PROJECT_SOURCE_DIR}/README.md" + DESTINATION "${EXPORT_STAGE_DIR}" + ) + +configure_file( + "${LIBRARY_SOURCE_DIR}/CMakeLists.txt.export" + "${EXPORT_STAGE_DIR}/CMakeLists.txt" + COPYONLY) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 45a7c2ea7..ca2b48249 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,7 +1,7 @@ include(AddCustomTargetSubproject) add_custom_target_subproject( libint2 check # target check-libint2 - USES_TERMINAL + USES_TERMINAL COMMAND ${CMAKE_CTEST_COMMAND} -V -R "libint2/" ) @@ -13,27 +13,35 @@ add_custom_target_subproject( ## Tests ======================================================================== add_executable( - eritest-libint2 - EXCLUDE_FROM_ALL + eritest-libint2 + EXCLUDE_FROM_ALL eri/test.cc ) target_include_directories( - eritest-libint2 - PRIVATE + eritest-libint2 + PRIVATE eri # src/bin/test_eri/eri.h + # see note on eri.h at unit_tests-libint2 + ) +target_compile_definitions( + eritest-libint2 + PRIVATE + $<$:LIBINT_HAS_MPFR=1> ) target_link_libraries( - eritest-libint2 - Libint2::int2 # N.B. plain library + eritest-libint2 + PRIVATE + Libint2::int2 # N.B. plain library + $<$:Multiprecision::mpfr> + $<$:Multiprecision::gmpxx> ) - add_test( # Test #1 - NAME libint2/eritest/build + NAME libint2/eritest/build COMMAND "${CMAKE_COMMAND}" --build ${CMAKE_BINARY_DIR} --target eritest-libint2 ) set_tests_properties( - libint2/eritest/build - PROPERTIES + libint2/eritest/build + PROPERTIES FIXTURES_SETUP LIBINT2_ERITEST_EXEC ) add_test( # Test #2 @@ -62,10 +70,15 @@ set_tests_properties( libint2/eritest/run2 libint2/eritest/run3 libint2/eritest/run4 - PROPERTIES + PROPERTIES FIXTURES_REQUIRED LIBINT2_ERITEST_EXEC ) + +if (LIBINT2_REQUIRE_CXX_API) + set(utests_src + unit/catch.hpp + unit/fixture.h unit/test-1body.cc unit/test-2body.cc unit/test-basis.cc @@ -76,11 +89,227 @@ set_tests_properties( unit/test-shell-order.cc unit/test-util.cc ) + # if have C, also test C bindings + include(CheckLanguage) + check_language(C) + if (CMAKE_C_COMPILER) + enable_language(C) + list(APPEND unit_tests-src + unit/c-api.c + unit/c-api-util.cc + unit/test-c-api.cc + ) + endif() add_executable( - unit_tests-libint2 - EXCLUDE_FROM_ALL - unit/test.cc + unit_tests-libint2 + EXCLUDE_FROM_ALL + unit/test.cc ${utests_src} ) + target_compile_definitions( + unit_tests-libint2 + PRIVATE + NO_LIBINT_COMPILER_CODE # src/bin/test_eri/eri.h + # note there's two ways to use eri.h, depending on the test src. eritest-libint2 uses the other. + $<$:LIBINT_HAS_MPFR=1> + ) + target_link_libraries( + unit_tests-libint2 + PRIVATE + $,Libint2::int2-cxx,Libint2::cxx> + # N.B. cxx compiled library if LIBINT2_REQUIRE_CXX_API_COMPILED=ON else header-only library + $<$:Multiprecision::mpfr> + $<$:Multiprecision::gmpxx> + $<$:Multiprecision::gmp> + # MPFR: numeric.h:175:23: error: cannot convert 'const char*' to 'double' in initialization: To to(ss.str().c_str()); + ) + add_test( # Test #7 + NAME libint2/unit/build + COMMAND "${CMAKE_COMMAND}" --build ${CMAKE_BINARY_DIR} --target unit_tests-libint2 + ) + set_tests_properties( + libint2/unit/build + PROPERTIES + FIXTURES_SETUP LIBINT2_UNIT_TESTS_EXEC + ) + add_test( # Test #8 + NAME libint2/unit/run + COMMAND $ + ) + add_test( # Test #9 + NAME libint2/unit/sho=gaussian/run + COMMAND $ --shgshell-order=gaussian + ) + set_tests_properties( + libint2/unit/run + libint2/unit/sho=gaussian/run + PROPERTIES + FIXTURES_REQUIRED LIBINT2_UNIT_TESTS_EXEC + ) + + add_executable( + hf-libint2 + EXCLUDE_FROM_ALL + hartree-fock/hartree-fock.cc + ) + target_link_libraries( + hf-libint2 + PRIVATE + $,Libint2::int2-cxx,Libint2::cxx> + # N.B. cxx compiled library if LIBINT2_REQUIRE_CXX_API_COMPILED=ON else header-only library + ) + add_test( # Test #10 + NAME libint2/hf/build + COMMAND "${CMAKE_COMMAND}" --build ${CMAKE_BINARY_DIR} --target hf-libint2 + ) + set_tests_properties( + libint2/hf/build + PROPERTIES + FIXTURES_SETUP LIBINT2_HFTEST_EXEC + ) + if (Python_Interpreter_FOUND) + add_test( # Test #11 + NAME libint2/hf/run + COMMAND ${CMAKE_COMMAND} + -DexecName=hf-libint2 + -DtestName=hartree-fock + -DtestArgs=${PROJECT_SOURCE_DIR}/tests/hartree-fock/h2o.xyz + -DPROJECT_SOURCE_DIR=${PROJECT_SOURCE_DIR} + -DPROJECT_BINARY_DIR=${PROJECT_BINARY_DIR} + -DpythonExec=${Python_EXECUTABLE} + -P ${PROJECT_SOURCE_DIR}/tests/hartree-fock/hftest.cmake + ) + else() + add_test( # Test #11 alt. + NAME libint2/hf/run + COMMAND $ ${PROJECT_SOURCE_DIR}/tests/hartree-fock/h2o.xyz + ) + endif() + set_tests_properties( + libint2/hf/run + PROPERTIES + FIXTURES_REQUIRED LIBINT2_HFTEST_EXEC + ) + + add_executable( + hf++-libint2 + EXCLUDE_FROM_ALL + hartree-fock/hartree-fock++.cc + ) + find_package(Threads) # for some reason clang does not link in threading support even though we are using C++ threads + target_link_libraries( + hf++-libint2 + PRIVATE + Libint2::cxx + Threads::Threads + # mostly errors for int2-cxx (excpet some Linux configurations) + # $,Libint2::int2-cxx,Libint2::cxx> + ) + add_test( # Test #12 + NAME libint2/hf++/build + COMMAND "${CMAKE_COMMAND}" --build ${CMAKE_BINARY_DIR} --target hf++-libint2 + ) + set_tests_properties( + libint2/hf++/build + PROPERTIES + FIXTURES_SETUP LIBINT2_HFXXTEST_EXEC + ) + if (Python_Interpreter_FOUND) + add_test( # Test #13 + NAME libint2/hf++/run + COMMAND ${CMAKE_COMMAND} + -DexecName=hf++-libint2 + -DtestName=hartree-fock++ + -DtestArgs=${PROJECT_SOURCE_DIR}/tests/hartree-fock/h2o_rotated.xyz + -DPROJECT_SOURCE_DIR=${PROJECT_SOURCE_DIR} + -DPROJECT_BINARY_DIR=${PROJECT_BINARY_DIR} + -DpythonExec=${Python_EXECUTABLE} + -P ${PROJECT_SOURCE_DIR}/tests/hartree-fock/hftest.cmake + ) + else() + add_test( # Test #13 alt. + NAME libint2/hf++/run + COMMAND $ ${PROJECT_SOURCE_DIR}/tests/hartree-fock/h2o_rotated.xyz + ) + endif() + set_tests_properties( + libint2/hf++/run + PROPERTIES + FIXTURES_REQUIRED LIBINT2_HFXXTEST_EXEC + ) + +endif (LIBINT2_REQUIRE_CXX_API) + +if (LIBINT2_ENABLE_FORTRAN) + + # Note: if forming compile line by hand rather than using targets, you'll + # need to include the Fortran module file directory: + # `target_include_directories(... PRIVATE $)` + + add_executable( + fortran_example-libint2 + EXCLUDE_FROM_ALL + fortran/fortran_example.F90 + ) + target_link_libraries( + fortran_example-libint2 + PRIVATE + Libint2::int2 + libint_f + ) + add_test( # Test #14 + NAME libint2/fortran_example/build + COMMAND "${CMAKE_COMMAND}" --build ${CMAKE_BINARY_DIR} --target fortran_example-libint2 + ) + set_tests_properties( + libint2/fortran_example/build + PROPERTIES + FIXTURES_SETUP LIBINT2_FORTRAN_EXAMPLE_EXEC + ) + add_test( # Test #15 + NAME libint2/fortran_example/run + COMMAND $ + ) + set_tests_properties( + libint2/fortran_example/run + PROPERTIES + FIXTURES_REQUIRED LIBINT2_FORTRAN_EXAMPLE_EXEC + ) + + if (LIBINT2_REQUIRE_CXX_API) + add_executable( + fortran_test-libint2 + EXCLUDE_FROM_ALL + fortran/test.cc + fortran/test-eri.cc + $ + ) + target_link_libraries( + fortran_test-libint2 + PRIVATE + $,Libint2::int2-cxx,Libint2::cxx> + # N.B. cxx compiled library if LIBINT2_REQUIRE_CXX_API_COMPILED=ON else header-only library + libint_f + ) + add_test( # Test #16 + NAME libint2/fortran_test/build + COMMAND "${CMAKE_COMMAND}" --build ${CMAKE_BINARY_DIR} --target fortran_test-libint2 + ) + set_tests_properties( + libint2/fortran_test/build + PROPERTIES + FIXTURES_SETUP LIBINT2_FORTRAN_TEST_EXEC + ) + add_test( # Test #17 + NAME libint2/fortran_test/run + COMMAND $ + ) + set_tests_properties( + libint2/fortran_test/run + PROPERTIES + FIXTURES_REQUIRED LIBINT2_FORTRAN_TEST_EXEC + ) + endif (LIBINT2_REQUIRE_CXX_API) +endif (LIBINT2_ENABLE_FORTRAN) diff --git a/tests/hartree-fock/hftest.cmake b/tests/hartree-fock/hftest.cmake new file mode 100644 index 000000000..92442cd0e --- /dev/null +++ b/tests/hartree-fock/hftest.cmake @@ -0,0 +1,44 @@ + +macro(runtest) + + set(OUTPUT_FILE_NAME "${PROJECT_BINARY_DIR}/tests/${testName}.out") + + set(CHECK_CMD "${pythonExec}" "${PROJECT_SOURCE_DIR}/tests/hartree-fock/${testName}-validate.py") + if (testName STREQUAL "hartree-fock++") + set(CHECK_ARGS "${PROJECT_SOURCE_DIR}/features") + endif() + + execute_process(COMMAND + ${PROJECT_BINARY_DIR}/tests/${execName} ${testArgs} + OUTPUT_FILE "${OUTPUT_FILE_NAME}" + RESULT_VARIABLE TEST_RESULT) + + if(TEST_RESULT) + message(STATUS "\nOUTPUT of " ${PROJECT_BINARY_DIR}/tests/${execName} " with args " ${testArgs}) + execute_process(COMMAND + cat + ${OUTPUT_FILE_NAME} + RESULT_VARIABLE + CAT_RESULT + ) + message(FATAL_ERROR "Error running ${PROJECT_BINARY_DIR}/tests/${execName}") + endif(TEST_RESULT) + + execute_process(COMMAND + ${CHECK_CMD} ${CHECK_ARGS} ${OUTPUT_FILE_NAME} + RESULT_VARIABLE CHECK_RESULT) + + if(CHECK_RESULT) + message(STATUS "\nOUTPUT of " ${CHECK_CMD}) + execute_process(COMMAND + cat + ${OUTPUT_FILE_NAME} + RESULT_VARIABLE + CAT_RESULT + ) + message(FATAL_ERROR "Error running ${CHECK_CMD} with args " ${CHECK_ARGS} " " ${OUTPUT_FILE_NAME}) + endif(CHECK_RESULT) + +endmacro(runtest) + +runtest()