From c894347411c131d3ac019432492667ca8372634c Mon Sep 17 00:00:00 2001 From: Tammo van der Heide Date: Thu, 15 Sep 2022 19:25:34 +0200 Subject: [PATCH] Prepare infrastructure for conda-forge packaging --- CMakeLists.txt | 43 +++++++++++------- cmake/FortnetUtils.cmake | 45 +++++++++++++++++++ config.cmake | 8 +++- external/fsockets/CMakeLists.txt | 1 + external/xmlf90/CMakeLists.txt | 1 + prog/fortnet/CMakeLists.txt | 4 ++ .../fortnet/integration/cmake/CMakeLists.txt | 10 +++++ .../prog/fortnet/integration/cmake/runtest.sh | 28 ++++++++++++ .../fortnet/integration/cmake/test_build.f90 | 5 +++ .../fortnet/integration/pkgconfig/runtest.sh | 45 +++++++++++++++++++ .../integration/pkgconfig/test_build.f90 | 5 +++ utils/export/fortnet-config.cmake.in | 27 +++++++++++ utils/export/fortnet.pc.in | 9 ++++ utils/test/travis-extbuild.sh | 36 +++++++++++++++ utils/test/travis-integration.sh | 19 ++++++++ utils/test/travis-regression.sh | 30 +++++++++++++ 16 files changed, 300 insertions(+), 16 deletions(-) create mode 100644 test/prog/fortnet/integration/cmake/CMakeLists.txt create mode 100755 test/prog/fortnet/integration/cmake/runtest.sh create mode 100644 test/prog/fortnet/integration/cmake/test_build.f90 create mode 100755 test/prog/fortnet/integration/pkgconfig/runtest.sh create mode 100644 test/prog/fortnet/integration/pkgconfig/test_build.f90 create mode 100644 utils/export/fortnet-config.cmake.in create mode 100644 utils/export/fortnet.pc.in create mode 100755 utils/test/travis-extbuild.sh create mode 100755 utils/test/travis-integration.sh create mode 100755 utils/test/travis-regression.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index 71c9913..7d927f4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,18 +14,17 @@ project(fortnet VERSION ${FORTNET_VERSION} LANGUAGES Fortran C) fnet_setup_build_type() fnet_load_toolchain_settings() fnet_setup_global_compiler_flags() +fnet_ensure_config_consistency() fnet_get_release_name(RELEASE) find_package(HDF5 REQUIRED COMPONENTS Fortran_HL) -find_package(MPI QUIET) -if(WITH_MPI AND NOT MPI_FORTRAN_FOUND) - message(FATAL_ERROR "Compiler ${CMAKE_Fortran_COMPILER} is not MPI capable but is specified for " - "a WITH_MPI=TRUE build") -elseif(NOT WITH_MPI AND MPI_FORTRAN_FOUND) - message(WARNING "MPI enabled compiler ${CMAKE_Fortran_COMPILER} found for a non-MPI build. Your " - "build will NOT be MPI-parallelised. Set WITH_MPI=TRUE in order to obtain an MPI-parallelised " - "build.") +if(WITH_MPI) + find_package(MPI REQUIRED) + if(NOT MPI_FORTRAN_FOUND) + message(FATAL_ERROR "Compiler ${CMAKE_Fortran_COMPILER} is not MPI capable but is specified " + "for a WITH_MPI=TRUE build") + endif() endif() # @@ -43,6 +42,10 @@ set(PKG_CONFIG_REQUIRES) set(PKG_CONFIG_LIBS) set(PKG_CONFIG_LIBS_PRIVATE) +find_package(CustomBlas REQUIRED) +find_package(CustomLapack REQUIRED) +list(APPEND PKG_CONFIG_LIBS_PRIVATE ${BLAS_LIBRARY} ${LAPACK_LIBRARY}) + # # Preprocessor details @@ -62,28 +65,36 @@ set(PYTHON_INTERPRETER "python3" CACHE STRING # # Add optional external components # +if(BUILD_SHARED_LIBS) + set(exclude) + option(INSTALL_INCLUDE_FILES "Whether include files should be installed" FALSE) +else() + set(exclude EXCLUDE_FROM_ALL) + option(INSTALL_INCLUDE_FILES "Whether include files should be installed" FALSE) +endif() + # Follow GNU conventions for installing directories include(GNUInstallDirs) +add_subdirectory(external/xmlf90 EXCLUDE_FROM_ALL) + if(WITH_SOCKETS) add_subdirectory(external/fsockets EXCLUDE_FROM_ALL) endif() +# If INCLUDE_INDIRECT_DEPS is non-empty, indirect dependencies must also be explicitely treated +string(REGEX MATCH "(^|;)[Ss]ubmodule(^|;)" INCLUDE_INDIRECT_DEPS "${HYBRID_CONFIG_METHODS}") + # Note: GIT_TAG hashes below must be updated with the utils/test/check_submodule_commits script! if(WITH_MPI) set(MPIFX_GIT_REPOSITORY "https://github.com/dftbplus/mpifx.git") - set(MPIFX_GIT_TAG "0cb07ee08cbb20f3f7bb2527152a4ec317c579ad") # do not change manually! + set(MPIFX_GIT_TAG "da51073aa87831a91ae756a9773e39ea000d1c3a") # do not change manually! fnet_config_hybrid_dependency(MpiFx MpiFx::MpiFx "${HYBRID_CONFIG_METHODS}" "QUIET" external/mpifx "${exclude}" "${MPIFX_GIT_REPOSITORY}" "${MPIFX_GIT_TAG}") endif() -find_package(CustomBlas REQUIRED) -find_package(CustomLapack REQUIRED) - -add_subdirectory(external/xmlf90 EXCLUDE_FROM_ALL) - # # Add internal components @@ -96,4 +107,6 @@ add_subdirectory(prog) # string(CONFIGURE "${TEST_RUNNER_TEMPLATE}" TEST_RUNNER) enable_testing() -add_subdirectory(test) +if(NOT BUILD_EXPORTED_TARGETS_ONLY) + add_subdirectory(test) +endif() diff --git a/cmake/FortnetUtils.cmake b/cmake/FortnetUtils.cmake index 5c2ec14..4b65c3e 100644 --- a/cmake/FortnetUtils.cmake +++ b/cmake/FortnetUtils.cmake @@ -59,6 +59,10 @@ function (fnet_add_fypp_defines fyppflags) list(APPEND _fyppflags -DWITH_SOCKETS) endif() + if(BUILD_SHARED_LIBS) + list(APPEND _fyppflags -DBUILD_SHARED_LIBS) + endif() + set(${fyppflags} ${_fyppflags} PARENT_SCOPE) endfunction() @@ -193,6 +197,25 @@ function(fnet_library_linking_flags libraries linkflags) endfunction() +# Checks the build configuration on consistency and stops in case of inconsistencies +function (fnet_ensure_config_consistency) + + # Check minimal compiler versions + set(fortran_minimal_versions "GNU;8.3" "Intel;20.2") + fnet_check_minimal_compiler_version("Fortran" "${fortran_minimal_versions}") + + set(pkgconfig_languages C Fortran) + list(FIND pkgconfig_languages "${PKGCONFIG_LANGUAGE}" pos) + if(pos EQUAL -1) + string(REPLACE ";" "\", \"" pkgconfig_languages_str "${pkgconfig_languages}") + set(pkgconfig_languages_str "\"${pkgconfig_languages_str}\"") + message(FATAL_ERROR + "Invalid language \"${PKGCONFIG_LANGUAGE}\" for PKGCONFIG_LANGUAGE (possible choices: ${pkgconfig_languages_str})") + endif() + +endfunction() + + # Stops the code if the source and the build folders are identical. # function(fnet_ensure_out_of_source_build) @@ -443,3 +466,25 @@ macro(fnet_config_hybrid_dependency package target config_methods findpkgopts su unset(_config_lower) endmacro() + + +# Checks whether the current compiler fullfills minimal version requirements. +# +# +# Arguments: +# lang [in]: Language for which the compiler should be checked (e.g. Fortran, C, CXX) +# compiler_versions [in]: List with alternating compiler ids and minimal version numbers, e.g. +# "Intel;19.0;GNU;9.0". If the compiler is amoung the listed ones and its version number is +# less than the specified one, a fatal error message will be issued. Otherwise the function +# returns silently. +# +function (fnet_check_minimal_compiler_version lang compiler_versions) + while(compiler_versions) + list(POP_FRONT compiler_versions compiler version) + if("${CMAKE_${lang}_COMPILER_ID}" STREQUAL "${compiler}" + AND CMAKE_${lang}_COMPILER_VERSION VERSION_LESS "${version}") + message(FATAL_ERROR "${compiler} ${lang} compiler is too old " + "(found \"${CMAKE_${lang}_COMPILER_VERSION}\", required >= \"${version}\")") + endif() + endwhile() +endfunction() diff --git a/config.cmake b/config.cmake index e95e470..86424fb 100644 --- a/config.cmake +++ b/config.cmake @@ -9,7 +9,13 @@ option(WITH_MPI "Whether Fortnet should support MPI-parallelism" TRUE) -option(WITH_SOCKETS "Whether socket communication should be allowed for" FALSE) +option(WITH_SOCKETS "Whether socket communication should be allowed for" TRUE) + +option(BUILD_SHARED_LIBS "Whether the libraries built should be shared" FALSE) +# Turn this on, if the Fortnet library (and other compiled libraries) should be shared libraries and +# dynamically linked to their applications. This results in smaller applications, but the libraries +# must be present at run-time (and the correct LD_LIBRARY_PATH environment variable must be set, so +# that they can be found by the operating system). # diff --git a/external/fsockets/CMakeLists.txt b/external/fsockets/CMakeLists.txt index b5915d9..cbc8460 100644 --- a/external/fsockets/CMakeLists.txt +++ b/external/fsockets/CMakeLists.txt @@ -7,5 +7,6 @@ add_library(fsockets_objlib OBJECT ${sources}) set(includedir ${CMAKE_CURRENT_BINARY_DIR}/include) set_target_properties(fsockets_objlib PROPERTIES Fortran_MODULE_DIRECTORY ${includedir}) +set_target_properties(fsockets_objlib PROPERTIES POSITION_INDEPENDENT_CODE ${BUILD_SHARED_LIBS}) target_include_directories(fsockets_objlib PUBLIC ${includedir}) diff --git a/external/xmlf90/CMakeLists.txt b/external/xmlf90/CMakeLists.txt index f60303e..e97416b 100644 --- a/external/xmlf90/CMakeLists.txt +++ b/external/xmlf90/CMakeLists.txt @@ -37,5 +37,6 @@ add_library(xmlf90_objlib OBJECT ${sources}) set(includedir ${CMAKE_CURRENT_BINARY_DIR}/include) set_target_properties(xmlf90_objlib PROPERTIES Fortran_MODULE_DIRECTORY ${includedir}) +set_target_properties(xmlf90_objlib PROPERTIES POSITION_INDEPENDENT_CODE ${BUILD_SHARED_LIBS}) target_include_directories(xmlf90_objlib PUBLIC ${includedir}) diff --git a/prog/fortnet/CMakeLists.txt b/prog/fortnet/CMakeLists.txt index 1b1e5bc..86ef409 100644 --- a/prog/fortnet/CMakeLists.txt +++ b/prog/fortnet/CMakeLists.txt @@ -71,6 +71,10 @@ target_include_directories(fortnet PUBLIC ${OTHER_INCLUDE_DIRS}) # # Installation # +if(BUILD_SHARED_LIBS) + install(TARGETS fortnet DESTINATION "${CMAKE_INSTALL_LIBDIR}" EXPORT fortnet-targets) +endif() + list(APPEND PKG_CONFIG_LIBS fortnet) set(PKG_CONFIG_LIBS "${PKG_CONFIG_LIBS}" PARENT_SCOPE) diff --git a/test/prog/fortnet/integration/cmake/CMakeLists.txt b/test/prog/fortnet/integration/cmake/CMakeLists.txt new file mode 100644 index 0000000..52f32de --- /dev/null +++ b/test/prog/fortnet/integration/cmake/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.16) + +project(TestFortnetBuild LANGUAGES Fortran) + +find_package(Fortnet REQUIRED) + +add_executable(test_build test_build.f90) +target_link_libraries(test_build Fortnet::Fortnet) + + diff --git a/test/prog/fortnet/integration/cmake/runtest.sh b/test/prog/fortnet/integration/cmake/runtest.sh new file mode 100755 index 0000000..f7b149a --- /dev/null +++ b/test/prog/fortnet/integration/cmake/runtest.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +# +# Tests whether the installed Fortnet library can be used within a CMake project. +# +# Arguments: +# +# - building directory (will be created, should not exist) +# +# Requirements: +# +# - Environment variables FC & CC contain the same compiler as used for Fortnet +# +# - Environment variable CMAKE_PREFIX_PATH contains the Fortnet install root. +# +SCRIPTDIR=$(dirname $0) +SCRIPTNAME=$(basename $0) +BUILDDIR=$1 +shift + +if [ -d ${BUILDDIR} ]; then + echo "${SCRIPTNAME}: Test build directory '${BUILDDIR}' already exists." >&2 + exit 1 +fi + +FC=$FC CC=$CC cmake -B ${BUILDDIR} ${SCRIPTDIR} -DCMAKE_MODULE_PATH=${CMAKE_MODULE_PATH} "$@" \ + || { echo "Configuration step failed" >&2; exit 1; } +cmake --build ${BUILDDIR} -- VERBOSE=1 || { echo "Build step failed" >&2; exit 1; } +echo "CMake build succeeded!" diff --git a/test/prog/fortnet/integration/cmake/test_build.f90 b/test/prog/fortnet/integration/cmake/test_build.f90 new file mode 100644 index 0000000..4b1183c --- /dev/null +++ b/test/prog/fortnet/integration/cmake/test_build.f90 @@ -0,0 +1,5 @@ +program test_build + use fortnet + implicit none + +end program test_build diff --git a/test/prog/fortnet/integration/pkgconfig/runtest.sh b/test/prog/fortnet/integration/pkgconfig/runtest.sh new file mode 100755 index 0000000..cdc6299 --- /dev/null +++ b/test/prog/fortnet/integration/pkgconfig/runtest.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash +# +# Tests whether the installed Fortnet library can be used with pkg-config based builds. +# +# Arguments: +# +# - building directory (will be created if it does not exist) +# +# Requirements: +# +# - Environment variables FC and CC contain the same Fortran and C compilers as used for Fortnet +# +# - Environment variable PKG_CONFIG_PATH contains the lib/pkgconfig folder within +# the installed Fortnet tree. +# +# - You pass all linker options as arguments, which are needed to link an MPI-binary +# with your compiler. Alternatively, you can specify the name of the MPI-wrappers +# as your compilers in FC and CC. +# +SCRIPTDIR=$(dirname $0) +SCRIPTNAME=$(basename $0) +BUILDDIR=$1 +shift +CUSTOMLIBS=$* + +if [ ! -d ${BUILDDIR} ]; then + mkdir ${BUILDDIR} || { echo "Could not create build dir '${BUILDDIR}'" >&2; exit 1; } +fi + +# Make sure scriptdir is absolute +cd ${SCRIPTDIR} +SCRIPTDIR=${PWD} +cd - + +cd ${BUILDDIR} || { echo "Could not change to build dir '${BUILDDIR}'" >&2; exit 1; } +pkg-config --exists fortnet || { echo "No PKG-CONFIG found for Fortnet" >&2; exit 1; } + +cflags=$(pkg-config --cflags fortnet) +libs=$(pkg-config --libs fortnet) + +cmd="${FC} ${cflags} ${SCRIPTDIR}/test_build.f90 ${libs} ${CUSTOMLIBS}" + +echo "Build command: ${cmd}" +${cmd} || { echo "Build command failed" >&2; exit 1; } +echo "PKG-CONFIG build succeeded." diff --git a/test/prog/fortnet/integration/pkgconfig/test_build.f90 b/test/prog/fortnet/integration/pkgconfig/test_build.f90 new file mode 100644 index 0000000..4b1183c --- /dev/null +++ b/test/prog/fortnet/integration/pkgconfig/test_build.f90 @@ -0,0 +1,5 @@ +program test_build + use fortnet + implicit none + +end program test_build diff --git a/utils/export/fortnet-config.cmake.in b/utils/export/fortnet-config.cmake.in new file mode 100644 index 0000000..90a42c5 --- /dev/null +++ b/utils/export/fortnet-config.cmake.in @@ -0,0 +1,27 @@ +@PACKAGE_INIT@ + +# Global Fortnet config options +set(Fortnet_WITH_MPI @WITH_MPI@) +set(Fortnet_WITH_SOCKETS @WITH_SOCKETS@) + +include(CMakeFindDependencyMacro) + +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/Modules) + +if(NOT TARGET Fortnet::Fortnet) + + if(NOT TARGET BLAS::BLAS) + find_dependency(CustomBlas) + endif() + + if(NOT TARGET LAPACK::LAPACK) + find_dependency(CustomLapack) + endif() + + if(Fortnet_WITH_MPI AND NOT TARGET MpiFx::MpiFx) + find_dependency(MpiFx) + endif() + + include("${CMAKE_CURRENT_LIST_DIR}/fortnet-targets.cmake") + +endif() diff --git a/utils/export/fortnet.pc.in b/utils/export/fortnet.pc.in new file mode 100644 index 0000000..18542ed --- /dev/null +++ b/utils/export/fortnet.pc.in @@ -0,0 +1,9 @@ +Name: Fortnet +Description: Library interface to Fortnet, a software package for training Behler-Parrinello neural networks +Version: @PROJECT_VERSION@ +URL: https://github.com/vanderhe/fortnet + +Requires: @PKGCONFIG_REQUIRES@ +Libs: @PKGCONFIG_LIBS@ +Libs.private: @PKGCONFIG_LIBS_PRIVATE@ +Cflags: @PKGCONFIG_C_FLAGS@ diff --git a/utils/test/travis-extbuild.sh b/utils/test/travis-extbuild.sh new file mode 100755 index 0000000..d605f6e --- /dev/null +++ b/utils/test/travis-extbuild.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash +# +# Build external components first and uses them as external dependencies during build +# +# Expects following env-vars: +# +# WITH_MPI ("false"/"true"), SOURCE_DIR, BUILD_DIR, INSTALL_DIR +# +set -ex + +if [ "${WITH_MPI}" == "true" ]; then + + cmake -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR} \ + -DBUILD_EXPORTED_TARGETS_ONLY=True -DCMAKE_BUILD_TYPE=Debug \ + -B ${BUILD_DIR}/mpifx ${SOURCE_DIR}/external/mpifx/origin + cmake --build ${BUILD_DIR}/mpifx -- -j + cmake --install ${BUILD_DIR}/mpifx + + cmake -DWITH_MPI=True \ + -DHYBRID_CONFIG_METHODS='Find' \ + -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR} \ + -DCMAKE_BUILD_TYPE=Debug \ + -B ${BUILD_DIR}/fortnet ${SOURCE_DIR} + cmake --build ${BUILD_DIR}/fortnet -- VERBOSE=1 fnet + +else + + cmake -DWITH_MPI=False \ + -DHYBRID_CONFIG_METHODS='Find' \ + -DBUILD_SHARED_LIBS=False \ + -DCMAKE_BUILD_TYPE=Debug \ + -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR} \ + -B ${BUILD_DIR}/fortnet ${SOURCE_DIR} + cmake --build ${BUILD_DIR}/fortnet -- VERBOSE=1 fnet + +fi diff --git a/utils/test/travis-integration.sh b/utils/test/travis-integration.sh new file mode 100755 index 0000000..b67a981 --- /dev/null +++ b/utils/test/travis-integration.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +# +# Must be called, after Fortnet had been installed +# +# Expects following env-vars: +# +# FC, CC, ("false"/"true") +# SOURCE_DIR (Fortnet source dir), BUILD_DIR, +# INSTALL_DIR (Fortnet install dir with already installed Fortnet) +# +set -ex + +# Integration test for CMake builds +CMAKE_PREFIX_PATH="${INSTALL_DIR}:${CMAKE_PREFIX_PATH}" \ + ${SOURCE_DIR}/test/prog/fortnet/integration/cmake/runtest.sh ${BUILD_DIR}/cmake + +# Integration test for PKG-CONFIG builds +PKG_CONFIG_PATH="${INSTALL_DIR}/lib/pkgconfig:$PKG_CONFIG_PATH" \ + ${SOURCE_DIR}/test/prog/fortnet/integration/pkgconfig/runtest.sh ${BUILD_DIR}/pkgconfig diff --git a/utils/test/travis-regression.sh b/utils/test/travis-regression.sh new file mode 100755 index 0000000..9004bb3 --- /dev/null +++ b/utils/test/travis-regression.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +# +# Expects following env-vars: +# +# FC, CC, WITH_MPI ("false"/"true") +# SOURCE_DIR, BUILD_DIR, INSTALL_DIR +# +set -ex + +if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then + BUILD_TYPE="Debug" +else + BUILD_TYPE="Release" +fi + +cmake_options=( + "-DCMAKE_INSTALL_PREFIX=${INSTALL_DIR}" + "-DWITH_MPI=${WITH_MPI}" + "-DWITH_API=true" + "-DFYPP_FLAGS='-DTRAVIS'" + "-DHYBRID_CONFIG_METHODS='Submodule'" + "-DCMAKE_BUILD_TYPE=${BUILD_TYPE}" +) + +cmake -B ${BUILD_DIR} "${cmake_options[@]}" ${SOURCE_DIR} +cmake --build ${BUILD_DIR} -- -j +pushd ${BUILD_DIR} +ctest -j --output-on-failure +popd +cmake --install ${BUILD_DIR}