diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c3da79c0482..aad0c7da258 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,6 +37,56 @@ jobs: - '!docs/**' - '!**/*.md' predicate-quantifier: 'every' + + cmake-dependencies: + needs: filter-changes + if: ${{ needs.filter-changes.outputs.source_changed == 'true' }} + runs-on: ubuntu-22.04 + strategy: + fail-fast: false + matrix: + include: + # Every dependency is downloaded and built from the pinned sources. + - source: fetched + apt: '' + cmake_args: '-DOPENMC_FORCE_FETCHCONTENT=ON' + # fmt and pugixml are found as installed packages. Ubuntu 22.04 only + # packages Catch2 2.x, so it is rejected and fetched instead, which + # exercises both code paths in a single configuration. + - source: installed + apt: 'libfmt-dev libpugixml-dev catch2' + cmake_args: '' + name: "CMake dependencies (${{ matrix.source }})" + steps: + - name: Setup minimum supported CMake + uses: jwlawson/actions-setup-cmake@v2 + with: + cmake-version: '3.22.6' + + - name: Checkout repository + uses: actions/checkout@v6 + with: + # Full history and tags are needed for git describe based versioning + fetch-depth: 0 + + - name: Install build dependencies + run: | + sudo apt -y update + sudo apt install -y libhdf5-dev libpng-dev ${{ matrix.apt }} + + - name: Configure + run: | + cmake -S . -B build-dependencies \ + -DOPENMC_BUILD_TESTS=ON \ + -DOPENMC_USE_OPENMP=OFF \ + ${{ matrix.cmake_args }} + + - name: Build + run: cmake --build build-dependencies --parallel 2 + + - name: Test + run: ctest --test-dir build-dependencies --output-on-failure + main: needs: filter-changes if: ${{ needs.filter-changes.outputs.source_changed == 'true' }} @@ -229,7 +279,7 @@ jobs: fail-on-error: false ci-pass: - needs: [filter-changes, main, coverage] + needs: [filter-changes, cmake-dependencies, main, coverage] name: Check CI status if: ${{ always() }} runs-on: ubuntu-latest @@ -240,7 +290,7 @@ jobs: echo "Documentation-only change - CI skipped successfully" exit 0 fi - if [[ "${{ needs.main.result }}" == "success" && "${{ needs.coverage.result }}" == "success" ]]; then + if [[ "${{ needs.cmake-dependencies.result }}" == "success" && "${{ needs.main.result }}" == "success" && "${{ needs.coverage.result }}" == "success" ]]; then echo "CI passed" exit 0 fi diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 2bc12389436..00000000000 --- a/.gitmodules +++ /dev/null @@ -1,9 +0,0 @@ -[submodule "vendor/pugixml"] - path = vendor/pugixml - url = https://github.com/zeux/pugixml.git -[submodule "vendor/fmt"] - path = vendor/fmt - url = https://github.com/fmtlib/fmt.git -[submodule "vendor/Catch2"] - path = vendor/Catch2 - url = https://github.com/catchorg/Catch2.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 62c2ac8a151..a346a449692 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.16 FATAL_ERROR) +cmake_minimum_required(VERSION 3.22 FATAL_ERROR) project(openmc C CXX) # Set module path @@ -25,11 +25,6 @@ if("${CMAKE_EXPORT_COMPILE_COMMANDS}" STREQUAL "") set(CMAKE_EXPORT_COMPILE_COMMANDS ON) endif() -# Enable correct usage of CXX_EXTENSIONS -if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.22) - cmake_policy(SET CMP0128 NEW) -endif() - #=============================================================================== # Command line options #=============================================================================== @@ -42,7 +37,7 @@ option(OPENMC_USE_DAGMC "Enable support for DAGMC (CAD) geometry" option(OPENMC_USE_LIBMESH "Enable support for libMesh unstructured mesh tallies" OFF) option(OPENMC_USE_MPI "Enable MPI" OFF) option(OPENMC_USE_UWUW "Enable UWUW" OFF) -option(OPENMC_FORCE_VENDORED_LIBS "Explicitly use submodules defined in 'vendor'" OFF) +option(OPENMC_FORCE_FETCHCONTENT "Fetch dependencies instead of finding packages" OFF) option(OPENMC_ENABLE_STRICT_FP "Enable strict FP flags to improve test portability" OFF) message(STATUS "OPENMC_USE_OPENMP ${OPENMC_USE_OPENMP}") @@ -53,9 +48,27 @@ message(STATUS "OPENMC_USE_DAGMC ${OPENMC_USE_DAGMC}") message(STATUS "OPENMC_USE_LIBMESH ${OPENMC_USE_LIBMESH}") message(STATUS "OPENMC_USE_MPI ${OPENMC_USE_MPI}") message(STATUS "OPENMC_USE_UWUW ${OPENMC_USE_UWUW}") -message(STATUS "OPENMC_FORCE_VENDORED_LIBS ${OPENMC_FORCE_VENDORED_LIBS}") +message(STATUS "OPENMC_FORCE_FETCHCONTENT ${OPENMC_FORCE_FETCHCONTENT}") message(STATUS "OPENMC_ENABLE_STRICT_FP ${OPENMC_ENABLE_STRICT_FP}") +# Warnings for options removed along with the vendored git submodules +if(DEFINED OPENMC_FORCE_VENDORED_LIBS) + message(WARNING "The OpenMC CMake option 'OPENMC_FORCE_VENDORED_LIBS' has " + "been deprecated. Its value will be ignored. Dependencies are no longer " + "vendored as git submodules. " + "Please use '-DOPENMC_FORCE_FETCHCONTENT=${OPENMC_FORCE_VENDORED_LIBS}' " + "instead.") + unset(OPENMC_FORCE_VENDORED_LIBS CACHE) +endif() + +if(DEFINED GIT_SUBMODULE) + message(WARNING "The OpenMC CMake option 'GIT_SUBMODULE' has been " + "deprecated. Its value (${GIT_SUBMODULE}) will be ignored. OpenMC no " + "longer uses git submodules for its dependencies, so they are never " + "checked out during configuration.") + unset(GIT_SUBMODULE CACHE) +endif() + # Warnings for deprecated options foreach(OLD_OPT IN ITEMS "openmp" "profile" "coverage" "dagmc" "libmesh") if(DEFINED ${OLD_OPT}) @@ -142,19 +155,6 @@ if(OPENMC_USE_MPI) cmake_pop_check_state() endif() -#=============================================================================== -# Helper macro for finding a dependency -#=============================================================================== - -macro(find_package_write_status pkg) - find_package(${pkg} QUIET NO_SYSTEM_ENVIRONMENT_PATH) - if(${pkg}_FOUND) - message(STATUS "Found ${pkg}: ${${pkg}_DIR} (version ${${pkg}_VERSION})") - else() - message(STATUS "Did not find ${pkg}, will use submodule instead") - endif() -endmacro() - #=============================================================================== # DAGMC Geometry Support - need DAGMC/MOAB #=============================================================================== @@ -271,72 +271,10 @@ message(STATUS "OpenMC Linker flags: ${ldflags}") endif() #=============================================================================== -# Update git submodules as needed +# CMake dependencies #=============================================================================== -if(GIT_FOUND AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.git") - option(GIT_SUBMODULE "Check submodules during build" ON) - if(GIT_SUBMODULE) - message(STATUS "Submodule update") - execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - RESULT_VARIABLE GIT_SUBMOD_RESULT) - if(NOT GIT_SUBMOD_RESULT EQUAL 0) - message(FATAL_ERROR "git submodule update --init failed with \ - ${GIT_SUBMOD_RESULT}, please checkout submodules") - endif() - endif() -endif() -# Check to see if submodules exist (by checking one) -if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/vendor/pugixml/CMakeLists.txt") - message(FATAL_ERROR "The git submodules were not downloaded! GIT_SUBMODULE was \ - turned off or failed. Please update submodules and try again.") -endif() - -#=============================================================================== -# pugixml library -#=============================================================================== - -if(OPENMC_FORCE_VENDORED_LIBS) - add_subdirectory(vendor/pugixml) - set_target_properties(pugixml PROPERTIES CXX_STANDARD 14 CXX_EXTENSIONS OFF) -else() - find_package_write_status(pugixml) - if (NOT pugixml_FOUND) - add_subdirectory(vendor/pugixml) - set_target_properties(pugixml PROPERTIES CXX_STANDARD 14 CXX_EXTENSIONS OFF) - endif() -endif() - -#=============================================================================== -# {fmt} library -#=============================================================================== - -if(OPENMC_FORCE_VENDORED_LIBS) - set(FMT_INSTALL ON CACHE BOOL "Generate the install target.") - add_subdirectory(vendor/fmt) -else() - find_package_write_status(fmt) - if (NOT fmt_FOUND) - set(FMT_INSTALL ON CACHE BOOL "Generate the install target.") - add_subdirectory(vendor/fmt) - endif() -endif() - -#=============================================================================== -# Catch2 library -#=============================================================================== - -if(OPENMC_BUILD_TESTS) - if (OPENMC_FORCE_VENDORED_LIBS) - add_subdirectory(vendor/Catch2) - else() - find_package_write_status(Catch2) - if (NOT Catch2_FOUND) - add_subdirectory(vendor/Catch2) - endif() - endif() -endif() +include(cmake/Dependencies.cmake) #=============================================================================== # RPATH information @@ -561,11 +499,7 @@ endif() target_link_libraries(libopenmc_objects PUBLIC ${ldflags} ${HDF5_LIBRARIES} ${HDF5_HL_LIBRARIES} fmt::fmt ${CMAKE_DL_LIBS}) -if(TARGET pugixml::pugixml) - target_link_libraries(libopenmc_objects PUBLIC pugixml::pugixml) -else() - target_link_libraries(libopenmc_objects PUBLIC pugixml) -endif() +target_link_libraries(libopenmc_objects PUBLIC pugixml::pugixml) if(OPENMC_USE_DAGMC) target_compile_definitions(libopenmc_objects PUBLIC OPENMC_DAGMC_ENABLED) diff --git a/Dockerfile b/Dockerfile index 688e5a370f8..684ff4725aa 100644 --- a/Dockerfile +++ b/Dockerfile @@ -183,7 +183,7 @@ ENV LIBMESH_INSTALL_DIR=$HOME/LIBMESH # clone and install openmc RUN mkdir -p ${HOME}/OpenMC && cd ${HOME}/OpenMC \ - && git clone --shallow-submodules --recurse-submodules --single-branch -b ${openmc_branch} ${OPENMC_REPO} \ + && git clone --single-branch -b ${openmc_branch} ${OPENMC_REPO} \ && mkdir build && cd build ; \ if [ ${build_dagmc} = "on" ] && [ ${build_libmesh} = "on" ]; then \ cmake ../openmc \ diff --git a/MANIFEST.in b/MANIFEST.in index cdc7e2abcf0..12f5f6c7e8f 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -35,13 +35,5 @@ recursive-include tests *.h5 recursive-include tests *.h5m recursive-include tests *.py recursive-include tests *.xml -recursive-include vendor CMakeLists.txt -recursive-include vendor *.cc -recursive-include vendor *.cpp -recursive-include vendor *.h -recursive-include vendor *.hh -recursive-include vendor *.hpp -recursive-include vendor *.pc.in -recursive-include vendor *.natvis prune docs/build prune docs/source/pythonapi/generated/ diff --git a/cmake/Dependencies.cmake b/cmake/Dependencies.cmake new file mode 100644 index 00000000000..64c0872c24f --- /dev/null +++ b/cmake/Dependencies.cmake @@ -0,0 +1,79 @@ +include(FetchContent) + +# Use the download time for extracted files when supported. This avoids stale +# dependency build products if a URL changes while retaining CMake 3.22 support. +if(POLICY CMP0135) + cmake_policy(SET CMP0135 NEW) +endif() + +# Declare every dependency before making any of them available. FetchContent +# honors the first declaration for a given name, so a project that pulls OpenMC +# in as a subproject can override any of these by declaring them beforehand. +FetchContent_Declare( + pugixml + URL https://github.com/zeux/pugixml/archive/refs/tags/v1.15.tar.gz + URL_HASH SHA256=b39647064d9e28297a34278bfb897092bf33b7c487906ddfc094c9e8868bddcb +) +FetchContent_Declare( + fmt + URL https://github.com/fmtlib/fmt/archive/refs/tags/11.0.2.tar.gz + URL_HASH SHA256=6cb1e6d37bdcb756dbbe59be438790db409cdb4868c66e888d5df9f13f7c027f +) +FetchContent_Declare( + Catch2 + URL https://github.com/catchorg/Catch2/archive/refs/tags/v3.16.0.tar.gz + URL_HASH SHA256=0957cae5821b17ce07f0833aaa52b5137643a8382203221f363a8303c109af34 +) + +# Make a dependency available as ${target}, preferring an installed package and +# falling back to the pinned sources declared above. LEGACY_TARGET names an +# unnamespaced target exported by older releases of a dependency, which is +# aliased to ${target} so that callers only ever refer to the namespaced name. +function(openmc_find_or_fetch name target) + set(one_value_args LEGACY_TARGET VERSION) + cmake_parse_arguments(DEPENDENCY "" "${one_value_args}" "" ${ARGN}) + + if(TARGET "${target}") + message(STATUS "Using existing ${name} target ${target}") + return() + endif() + + if(NOT OPENMC_FORCE_FETCHCONTENT) + if(DEPENDENCY_VERSION) + find_package(${name} ${DEPENDENCY_VERSION} CONFIG QUIET + NO_SYSTEM_ENVIRONMENT_PATH) + else() + find_package(${name} CONFIG QUIET NO_SYSTEM_ENVIRONMENT_PATH) + endif() + + if(DEPENDENCY_LEGACY_TARGET + AND TARGET "${DEPENDENCY_LEGACY_TARGET}" + AND NOT TARGET "${target}") + add_library("${target}" ALIAS "${DEPENDENCY_LEGACY_TARGET}") + endif() + + if(TARGET "${target}") + set(version_variable "${name}_VERSION") + if(DEFINED ${version_variable}) + message(STATUS "Found ${name} ${${version_variable}}") + else() + message(STATUS "Found ${name}") + endif() + return() + endif() + endif() + + message(STATUS "Fetching ${name}") + FetchContent_MakeAvailable(${name}) + + if(NOT TARGET "${target}") + message(FATAL_ERROR "${name} did not provide expected target ${target}") + endif() +endfunction() + +openmc_find_or_fetch(pugixml pugixml::pugixml LEGACY_TARGET pugixml) +openmc_find_or_fetch(fmt fmt::fmt) + +if(OPENMC_BUILD_TESTS) + openmc_find_or_fetch(Catch2 Catch2::Catch2WithMain VERSION 3) +endif() diff --git a/docs/source/devguide/workflow.rst b/docs/source/devguide/workflow.rst index c49326a2090..32e74c3cbe7 100644 --- a/docs/source/devguide/workflow.rst +++ b/docs/source/devguide/workflow.rst @@ -63,7 +63,7 @@ features and bug fixes. The general steps for contributing are as follows: .. code-block:: sh - git clone --recurse-submodules git@github.com:yourusername/openmc.git + git clone git@github.com:yourusername/openmc.git cd openmc git checkout -b newbranch develop diff --git a/docs/source/quickinstall.rst b/docs/source/quickinstall.rst index 6cb774b5b07..17cb75c3051 100644 --- a/docs/source/quickinstall.rst +++ b/docs/source/quickinstall.rst @@ -143,7 +143,7 @@ download and install OpenMC by entering the following commands in a terminal: .. code-block:: sh - git clone --recurse-submodules https://github.com/openmc-dev/openmc.git + git clone https://github.com/openmc-dev/openmc.git cd openmc mkdir build && cd build cmake .. diff --git a/docs/source/usersguide/install.rst b/docs/source/usersguide/install.rst index 2a0d301d4bc..e9721eba000 100644 --- a/docs/source/usersguide/install.rst +++ b/docs/source/usersguide/install.rst @@ -386,7 +386,7 @@ how to set up git to work with GitHub since this involves setting up ssh_ keys. With git installed and setup, the following command will download the full source code from the GitHub repository:: - git clone --recurse-submodules https://github.com/openmc-dev/openmc.git + git clone https://github.com/openmc-dev/openmc.git By default, the cloned repository will be set to the development branch. To switch to the source of the latest stable release, run the following commands:: @@ -466,10 +466,24 @@ OPENMC_ENABLE_STRICT_FP suite. By default (off), the compiler is free to use all optimizations for best performance. (Default: off) -OPENMC_FORCE_VENDORED_LIBS - Forces OpenMC to use the submodules located in the vendor directory, as - opposed to searching the system for already installed versions of those - modules. +OPENMC_FORCE_FETCHCONTENT + Forces OpenMC to download and build its pinned versions of fmt, pugixml, and + Catch2 rather than searching for installed packages. Catch2 is only needed + when ``OPENMC_BUILD_TESTS`` is enabled. (Default: off) + +OpenMC searches for installed CMake packages for fmt, pugixml, and Catch2 +before downloading pinned sources with CMake's ``FetchContent`` module. Thus, +network access is only needed during configuration when a required package is +not installed. For offline builds, install the dependencies ahead of time or +provide unpacked sources through ``FETCHCONTENT_SOURCE_DIR_FMT``, +``FETCHCONTENT_SOURCE_DIR_PUGIXML``, and ``FETCHCONTENT_SOURCE_DIR_CATCH2``. + +Two further ``FetchContent`` variables are useful when packaging OpenMC. +Setting ``FETCHCONTENT_FULLY_DISCONNECTED=ON`` makes configuration fail rather +than silently download anything, which is typically what is wanted in a +sandboxed build that must rely only on installed packages. Setting +``FETCHCONTENT_BASE_DIR`` to a shared location allows downloads to be reused +across multiple build directories. To set any of these options (e.g., turning on profiling), the following form should be used: diff --git a/tests/cpp_unit_tests/CMakeLists.txt b/tests/cpp_unit_tests/CMakeLists.txt index 991f219f528..d67e598dc67 100644 --- a/tests/cpp_unit_tests/CMakeLists.txt +++ b/tests/cpp_unit_tests/CMakeLists.txt @@ -18,4 +18,9 @@ foreach(test ${TEST_NAMES}) add_executable(${test} ${test}.cpp) target_link_libraries(${test} Catch2::Catch2WithMain libopenmc_test) add_test(NAME ${test} COMMAND ${test} WORKING_DIRECTORY ${UNIT_TEST_BIN_OUTPUT_DIR}) + + # Catch2 uses exit code 4 when all test cases in a binary are skipped. Treat + # that result as a skipped CTest test rather than a failure when an optional + # dependency is unavailable. + set_tests_properties(${test} PROPERTIES SKIP_RETURN_CODE 4) endforeach() diff --git a/vendor/Catch2 b/vendor/Catch2 deleted file mode 160000 index 5a40b2275ca..00000000000 --- a/vendor/Catch2 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 5a40b2275caa05cf809bf04df848764a9d7df2e2 diff --git a/vendor/fmt b/vendor/fmt deleted file mode 160000 index 0c9fce2ffef..00000000000 --- a/vendor/fmt +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 0c9fce2ffefecfdce794e1859584e25877b7b592 diff --git a/vendor/pugixml b/vendor/pugixml deleted file mode 160000 index ee86beb30e4..00000000000 --- a/vendor/pugixml +++ /dev/null @@ -1 +0,0 @@ -Subproject commit ee86beb30e4973f5feffe3ce63bfa4fbadf72f38