diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 950392b794..108a85e0dd 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -40,10 +40,13 @@ The Smith project release numbers follow [Semantic Versioning](http://semver.org - Reworked the trust-region solver to use an MFEM/LAPACK subspace solve with cached reduced problems and simplified Steihaug-Toint CG internals. - [Changed `TimeDiscretizedWeakForm` class name to `FunctionalWeakForm`](https://github.com/llnl/smith/pull/1609/). +- Required third-party libraries (Camp, Axom, MFEM, and Adiak/Caliper when profiling) are now found via the generic + `find_package` search paths (e.g. `CMAKE_PREFIX_PATH`) when their `_DIR` variable is not given. ### Fixed - FunctionalWeakForm::jvp() to include shape-displacement contributions, which were previously ignored. +- Given `_DIR` variables now take precedence over packages installed in system paths. ## [Version 0.1.0] - Release date 2026-04-28 diff --git a/cmake/SmithMacros.cmake b/cmake/SmithMacros.cmake index e9982b2205..e4238bda30 100644 --- a/cmake/SmithMacros.cmake +++ b/cmake/SmithMacros.cmake @@ -273,6 +273,78 @@ macro(smith_assert_find_succeeded) endmacro(smith_assert_find_succeeded) +##------------------------------------------------------------------------------ +## smith_find_package(NAME +## PROJECT_NAME +## TARGET +## DIR_VARIABLE +## [PATH_SUFFIXES ...]) +## +## Finds a required package that provides a CMake config file. If DIR_VARIABLE +## is set, it is searched first. The generic find_package() search paths +## (e.g. _ROOT, CMAKE_PREFIX_PATH, and system paths) are always searched. +## +## Sets _FOUND. If the package was found without +## DIR_VARIABLE, DIR_VARIABLE is set to the found install prefix so it is +## recorded in the installed smith-config.cmake. +## +## Fails with a helpful message when the package could not be found. +##------------------------------------------------------------------------------ +macro(smith_find_package) + + set(options) + set(singleValueArgs NAME PROJECT_NAME TARGET DIR_VARIABLE) + set(multiValueArgs PATH_SUFFIXES) + + # Parse the arguments to the macro + # Note: Use a unique prefix since this macro calls other macros that use 'arg' + cmake_parse_arguments(_sfp + "${options}" "${singleValueArgs}" "${multiValueArgs}" ${ARGN}) + + string(TOUPPER ${_sfp_NAME} _sfp_upper_name) + + set(_sfp_find_args) + if(${_sfp_DIR_VARIABLE}) + smith_assert_is_directory(DIR_VARIABLE ${_sfp_DIR_VARIABLE}) + set(_sfp_dir_given TRUE) + set(_sfp_dir "${${_sfp_DIR_VARIABLE}}") + list(APPEND _sfp_find_args HINTS "${_sfp_dir}") + else() + set(_sfp_dir_given FALSE) + endif() + if(_sfp_PATH_SUFFIXES) + list(APPEND _sfp_find_args PATH_SUFFIXES ${_sfp_PATH_SUFFIXES}) + endif() + + find_package(${_sfp_NAME} ${_sfp_find_args}) + + if(TARGET ${_sfp_TARGET}) + if(NOT _sfp_dir_given) + # Record the install prefix of the found config file, e.g. /lib/cmake/ + string(REGEX REPLACE "/(lib[^/]*|share)/cmake(/[^/]*)?/?$" "" + _sfp_dir "${${_sfp_NAME}_DIR}") + set(${_sfp_DIR_VARIABLE} "${_sfp_dir}" CACHE PATH "" FORCE) + endif() + smith_assert_find_succeeded(PROJECT_NAME ${_sfp_PROJECT_NAME} + TARGET ${_sfp_TARGET} + DIR_VARIABLE ${_sfp_DIR_VARIABLE}) + message(STATUS "${_sfp_PROJECT_NAME} support is ON") + set(${_sfp_upper_name}_FOUND TRUE) + elseif(_sfp_dir_given) + message(FATAL_ERROR "${_sfp_PROJECT_NAME} failed to load from given ${_sfp_DIR_VARIABLE}: ${_sfp_dir}") + else() + message(FATAL_ERROR "Could not find ${_sfp_PROJECT_NAME}. Either set ${_sfp_DIR_VARIABLE} to its " + "install prefix or add the install prefix to CMAKE_PREFIX_PATH.") + endif() + + unset(_sfp_dir) + unset(_sfp_dir_given) + unset(_sfp_find_args) + unset(_sfp_upper_name) + +endmacro(smith_find_package) + + ##------------------------------------------------------------------------------ ## smith_convert_to_native_escaped_file_path( path output ) ## diff --git a/cmake/thirdparty/FindMFEM.cmake b/cmake/thirdparty/FindMFEM.cmake index 06197d27ac..980a765bd2 100644 --- a/cmake/thirdparty/FindMFEM.cmake +++ b/cmake/thirdparty/FindMFEM.cmake @@ -14,16 +14,27 @@ # STRUMPACK_FOUND - If MFEM was built with STRUMPACK #------------------------------------------------------------------------------ -if(NOT MFEM_DIR) - message(FATAL_ERROR "MFEM support needs explicit MFEM_DIR") +if(MFEM_DIR) + message(STATUS "Looking for MFEM using MFEM_DIR = ${MFEM_DIR}") + smith_assert_is_directory(DIR_VARIABLE MFEM_DIR) + + set(_MFEM_DIR ${MFEM_DIR}) # Save MFEM_DIR as a non-cache variable + find_package(MFEM CONFIG NO_DEFAULT_PATH PATHS "${MFEM_DIR}/lib/cmake/mfem") + # find_package will overwrite MFEM_DIR, so restore it here + set(MFEM_DIR ${_MFEM_DIR} CACHE PATH "" FORCE) +else() + # Without MFEM_DIR, we can only find an MFEM that was built with CMake + message(STATUS "Looking for MFEM's CMake config file in the default search paths") + find_package(MFEM CONFIG) + if(NOT MFEM_FOUND) + message(FATAL_ERROR "Could not find MFEM. Either set MFEM_DIR to its install prefix " + "or add the install prefix to CMAKE_PREFIX_PATH.") + endif() + # find_package sets MFEM_DIR to the config file directory, so set it to the install prefix + get_filename_component(_MFEM_DIR "${MFEM_DIR}/../../.." ABSOLUTE) + set(MFEM_DIR ${_MFEM_DIR} CACHE PATH "" FORCE) endif() -message(STATUS "Looking for MFEM using MFEM_DIR = ${MFEM_DIR}") -smith_assert_is_directory(DIR_VARIABLE MFEM_DIR) - -set(_MFEM_DIR ${MFEM_DIR}) # Save MFEM_DIR as a non-cache variable -find_package(MFEM CONFIG NO_DEFAULT_PATH PATHS "${MFEM_DIR}/lib/cmake/mfem") -# find_package will overwrite MFEM_DIR, so restore it here -set(MFEM_DIR ${_MFEM_DIR} CACHE PATH "" FORCE) +unset(_MFEM_DIR) set(_mfem_uses_mpi FALSE) set(_mfem_uses_openmp FALSE) diff --git a/cmake/thirdparty/SetupSmithThirdParty.cmake b/cmake/thirdparty/SetupSmithThirdParty.cmake index 0299542373..222e22e866 100644 --- a/cmake/thirdparty/SetupSmithThirdParty.cmake +++ b/cmake/thirdparty/SetupSmithThirdParty.cmake @@ -77,33 +77,19 @@ if (NOT SMITH_THIRD_PARTY_LIBRARIES_FOUND) #------------------------------------------------------------------------------ # Camp #------------------------------------------------------------------------------ - if (NOT CAMP_DIR) - message(FATAL_ERROR "CAMP_DIR is required.") - endif() - - smith_assert_is_directory(DIR_VARIABLE CAMP_DIR) - - find_dependency(camp REQUIRED PATHS "${CAMP_DIR}") - - smith_assert_find_succeeded(PROJECT_NAME Camp - TARGET camp - DIR_VARIABLE CAMP_DIR) - message(STATUS "Camp support is ON") - set(CAMP_FOUND TRUE) + smith_find_package(NAME camp + PROJECT_NAME Camp + TARGET camp + DIR_VARIABLE CAMP_DIR) #------------------------------------------------------------------------------ # Umpire #------------------------------------------------------------------------------ if(UMPIRE_DIR) - smith_assert_is_directory(DIR_VARIABLE UMPIRE_DIR) - find_dependency(umpire REQUIRED PATHS "${UMPIRE_DIR}") - - smith_assert_find_succeeded(PROJECT_NAME Umpire - TARGET umpire - DIR_VARIABLE UMPIRE_DIR) - - message(STATUS "Umpire support is ON") - set(UMPIRE_FOUND TRUE) + smith_find_package(NAME umpire + PROJECT_NAME Umpire + TARGET umpire + DIR_VARIABLE UMPIRE_DIR) else() message(STATUS "Umpire support is OFF") set(UMPIRE_FOUND FALSE) @@ -113,32 +99,15 @@ if (NOT SMITH_THIRD_PARTY_LIBRARIES_FOUND) # RAJA #------------------------------------------------------------------------------ if(RAJA_DIR) - smith_assert_is_directory(DIR_VARIABLE RAJA_DIR) - find_dependency(RAJA REQUIRED PATHS ${RAJA_DIR}) - smith_assert_find_succeeded(PROJECT_NAME RAJA - TARGET RAJA - DIR_VARIABLE RAJA_DIR) - message(STATUS "RAJA support is ON") - set(RAJA_FOUND TRUE) + smith_find_package(NAME RAJA + PROJECT_NAME RAJA + TARGET RAJA + DIR_VARIABLE RAJA_DIR) else() message(STATUS "RAJA support is OFF") set(RAJA_FOUND FALSE) endif() - #------------------------------------------------------------------------------ - # Conduit (found via Axom) - #------------------------------------------------------------------------------ - if(NOT CONDUIT_DIR) - MESSAGE(FATAL_ERROR "Could not find Conduit. Conduit requires explicit CONDUIT_DIR.") - endif() - - #------------------------------------------------------------------------------ - # HDF5 (found via Axom) - #------------------------------------------------------------------------------ - if (NOT HDF5_DIR) - MESSAGE(FATAL_ERROR "Could not find HDF5. HDF5 requires explicit HDF5_DIR.") - endif() - #------------------------------------------------------------------------------ # Sundials #------------------------------------------------------------------------------ @@ -206,13 +175,11 @@ if (NOT SMITH_THIRD_PARTY_LIBRARIES_FOUND) # Enzyme #------------------------------------------------------------------------------ if (ENZYME_DIR) - smith_assert_is_directory(DIR_VARIABLE ENZYME_DIR) - set(Enzyme_ROOT ${ENZYME_DIR} CACHE PATH "") - find_dependency(Enzyme REQUIRED) - - smith_assert_find_succeeded(PROJECT_NAME Enzyme - TARGET ClangEnzymeFlags - DIR_VARIABLE ENZYME_DIR) + # Note: Enzyme is only searched for when given since it requires a Clang-based compiler + smith_find_package(NAME Enzyme + PROJECT_NAME Enzyme + TARGET ClangEnzymeFlags + DIR_VARIABLE ENZYME_DIR) message(STATUS "Checking for Target 'ClangEnzymeFlags' plugin target exists..") get_target_property(_clangenzyme_opts ClangEnzymeFlags INTERFACE_COMPILE_OPTIONS) @@ -228,9 +195,6 @@ if (NOT SMITH_THIRD_PARTY_LIBRARIES_FOUND) else() message(STATUS "Skipped check. `ClangEnzymeFlags` target does not reference another target") endif() - - message(STATUS "Enzyme support is ON") - set(ENZYME_FOUND TRUE) else() message(STATUS "Enzyme support is OFF") set(ENZYME_FOUND FALSE) @@ -241,7 +205,6 @@ if (NOT SMITH_THIRD_PARTY_LIBRARIES_FOUND) #------------------------------------------------------------------------------ if(NOT SMITH_ENABLE_CODEVELOP) message(STATUS "Using installed MFEM") - smith_assert_is_directory(DIR_VARIABLE MFEM_DIR) include(${CMAKE_CURRENT_LIST_DIR}/FindMFEM.cmake) smith_assert_find_succeeded(PROJECT_NAME MFEM TARGET mfem @@ -427,14 +390,11 @@ if (NOT SMITH_THIRD_PARTY_LIBRARIES_FOUND) #------------------------------------------------------------------------------ if(NOT SMITH_ENABLE_CODEVELOP) message(STATUS "Using installed Axom") - smith_assert_is_directory(DIR_VARIABLE AXOM_DIR) - - find_dependency(axom REQUIRED PATHS "${AXOM_DIR}/lib/cmake") - - smith_assert_find_succeeded(PROJECT_NAME Axom - TARGET axom - DIR_VARIABLE AXOM_DIR) - message(STATUS "Axom support is ON") + smith_find_package(NAME axom + PROJECT_NAME Axom + TARGET axom + DIR_VARIABLE AXOM_DIR + PATH_SUFFIXES lib/cmake) # # Check for optional Axom headers that are required for Smith @@ -443,7 +403,7 @@ if (NOT SMITH_THIRD_PARTY_LIBRARIES_FOUND) # sol.hpp find_path( _sol_found sol.hpp - PATHS ${AXOM_DIR}/include/axom + PATHS ${AXOM_INCLUDE_DIRS}/axom NO_DEFAULT_PATH NO_CMAKE_ENVIRONMENT_PATH NO_CMAKE_PATH @@ -451,14 +411,14 @@ if (NOT SMITH_THIRD_PARTY_LIBRARIES_FOUND) NO_CMAKE_SYSTEM_PATH ) if (NOT _sol_found) - message(FATAL_ERROR "Given AXOM_DIR did not contain a required header: axom/sol.hpp" + message(FATAL_ERROR "Axom install (${AXOM_DIR}) did not contain a required header: axom/sol.hpp" "\nTry building Axom with '-DBLT_CXX_STD=c++14' or higher\n ") endif() # LuaReader.hpp find_path( _luareader_found LuaReader.hpp - PATHS ${AXOM_DIR}/include/axom/inlet + PATHS ${AXOM_INCLUDE_DIRS}/axom/inlet NO_DEFAULT_PATH NO_CMAKE_ENVIRONMENT_PATH NO_CMAKE_PATH @@ -466,7 +426,7 @@ if (NOT SMITH_THIRD_PARTY_LIBRARIES_FOUND) NO_CMAKE_SYSTEM_PATH ) if (NOT _luareader_found) - message(FATAL_ERROR "Given AXOM_DIR did not contain a required header: axom/inlet/LuaReader.hpp" + message(FATAL_ERROR "Axom install (${AXOM_DIR}) did not contain a required header: axom/inlet/LuaReader.hpp" "\nTry building Axom with '-DLUA_DIR=path/to/lua/install'\n ") endif() set(LUA_FOUND TRUE) @@ -474,7 +434,7 @@ if (NOT SMITH_THIRD_PARTY_LIBRARIES_FOUND) # MFEMSidreDataCollection.hpp find_path( _mfemdatacollection_found MFEMSidreDataCollection.hpp - PATHS ${AXOM_DIR}/include/axom/sidre/core + PATHS ${AXOM_INCLUDE_DIRS}/axom/sidre/core NO_DEFAULT_PATH NO_CMAKE_ENVIRONMENT_PATH NO_CMAKE_PATH @@ -482,7 +442,7 @@ if (NOT SMITH_THIRD_PARTY_LIBRARIES_FOUND) NO_CMAKE_SYSTEM_PATH ) if (NOT _mfemdatacollection_found) - message(FATAL_ERROR "Given AXOM_DIR did not contain a required header: axom/sidre/core/MFEMSidreDataCollection.hpp" + message(FATAL_ERROR "Axom install (${AXOM_DIR}) did not contain a required header: axom/sidre/core/MFEMSidreDataCollection.hpp" "\nTry building Axom with '-DAXOM_ENABLE_MFEM_SIDRE_DATACOLLECTION=ON'\n ") endif() @@ -490,6 +450,14 @@ if (NOT SMITH_THIRD_PARTY_LIBRARIES_FOUND) set(ENABLE_FORTRAN OFF CACHE BOOL "" FORCE) # Otherwise we use the submodule message(STATUS "Using Axom submodule") + if(NOT CONDUIT_DIR) + message(FATAL_ERROR "CONDUIT_DIR is required to use the Axom submodule" + "\nTry running CMake with '-DCONDUIT_DIR=path/to/conduit/install'\n ") + endif() + if(NOT HDF5_DIR) + message(FATAL_ERROR "HDF5_DIR is required to use the Axom submodule" + "\nTry running CMake with '-DHDF5_DIR=path/to/hdf5/install'\n ") + endif() if(NOT LUA_DIR) message(FATAL_ERROR "LUA_DIR is required to use the Axom submodule" "\nTry running CMake with '-DLUA_DIR=path/to/lua/install'\n ") @@ -601,15 +569,12 @@ if (NOT SMITH_THIRD_PARTY_LIBRARIES_FOUND) set(TRIBOL_FOUND FALSE) elseif(TRIBOL_DIR) message(STATUS "Enabling pre-built Tribol in '${TRIBOL_DIR}'" ) - smith_assert_is_directory(DIR_VARIABLE TRIBOL_DIR) - - find_dependency(tribol REQUIRED PATHS "${TRIBOL_DIR}/lib/cmake") - - smith_assert_find_succeeded(PROJECT_NAME Tribol - TARGET tribol - DIR_VARIABLE TRIBOL_DIR) + smith_find_package(NAME tribol + PROJECT_NAME Tribol + TARGET tribol + DIR_VARIABLE TRIBOL_DIR + PATH_SUFFIXES lib/cmake) blt_convert_to_system_includes(TARGET tribol) - set(TRIBOL_FOUND TRUE) else() set(TRIBOL_ENABLE_DOCS OFF CACHE BOOL "") set(TRIBOL_ENABLE_TESTS OFF CACHE BOOL "") @@ -746,20 +711,11 @@ if (NOT SMITH_THIRD_PARTY_LIBRARIES_FOUND) #------------------------------------------------------------------------------ # Adiak #------------------------------------------------------------------------------ - if(SMITH_ENABLE_PROFILING AND NOT ADIAK_DIR) - message(FATAL_ERROR "SMITH_ENABLE_PROFILING cannot be ON without ADIAK_DIR defined. Either specify a host \ - config with ADIAK_DIR, or rebuild Smith TPLs with +adiak variant.") - endif() - - if(ADIAK_DIR AND SMITH_ENABLE_PROFILING) - smith_assert_is_directory(DIR_VARIABLE ADIAK_DIR) - - find_dependency(adiak REQUIRED PATHS "${ADIAK_DIR}") - smith_assert_find_succeeded(PROJECT_NAME Adiak - TARGET adiak::adiak - DIR_VARIABLE ADIAK_DIR) - message(STATUS "Adiak support is ON") - set(ADIAK_FOUND TRUE) + if(SMITH_ENABLE_PROFILING) + smith_find_package(NAME adiak + PROJECT_NAME Adiak + TARGET adiak::adiak + DIR_VARIABLE ADIAK_DIR) else() message(STATUS "Adiak support is OFF") set(ADIAK_FOUND FALSE) @@ -768,14 +724,7 @@ if (NOT SMITH_THIRD_PARTY_LIBRARIES_FOUND) #------------------------------------------------------------------------------ # Caliper #------------------------------------------------------------------------------ - if(SMITH_ENABLE_PROFILING AND NOT CALIPER_DIR) - message(FATAL_ERROR "SMITH_ENABLE_PROFILING cannot be ON without CALIPER_DIR defined. Either specify a host \ - config with CALIPER_DIR, or rebuild Smith TPLs with +caliper variant.") - endif() - - if(CALIPER_DIR AND SMITH_ENABLE_PROFILING) - smith_assert_is_directory(DIR_VARIABLE CALIPER_DIR) - + if(SMITH_ENABLE_PROFILING) # Should this logic be in the Caliper CMake package? # If CMake version doesn't support CUDAToolkit the libraries # are just "baked in" @@ -787,12 +736,10 @@ if (NOT SMITH_THIRD_PARTY_LIBRARIES_FOUND) endif() endif() - find_dependency(caliper REQUIRED PATHS "${CALIPER_DIR}") - smith_assert_find_succeeded(PROJECT_NAME Caliper - TARGET caliper - DIR_VARIABLE CALIPER_DIR) - message(STATUS "Caliper support is ON") - set(CALIPER_FOUND TRUE) + smith_find_package(NAME caliper + PROJECT_NAME Caliper + TARGET caliper + DIR_VARIABLE CALIPER_DIR) else() message(STATUS "Caliper support is OFF") set(CALIPER_FOUND FALSE)