diff --git a/.github/workflows/extbuild.yml b/.github/workflows/extbuild.yml index da5033e..104b056 100644 --- a/.github/workflows/extbuild.yml +++ b/.github/workflows/extbuild.yml @@ -33,6 +33,7 @@ jobs: sudo apt-get install libxml2-utils sudo apt-get install netcdf-bin libnetcdf-dev libnetcdff-dev sudo apt-get install pnetcdf-bin libpnetcdf-dev + sudo apt-get install libblas-dev - name: Cache PARALLELIO id: cache-PARALLELIO uses: actions/cache@v4 @@ -63,20 +64,6 @@ jobs: esmpy: false cache: true - - name: Build SHARE - uses: ./.github/actions/buildshare - with: - esmfmkfile: $ESMFMKFILE - pio_path: ${GITHUB_WORKSPACE}/pio - src_root: ${GITHUB_WORKSPACE} - cmake_flags: " -Wno-dev -DCMAKE_BUILD_TYPE=DEBUG -DWERROR=ON -DCMAKE_Fortran_FLAGS=\"-DCPRGNU -g -Wall \ - -ffree-form -ffree-line-length-none -fallow-argument-mismatch\" \ - -DCMAKE_MODULE_PATH=$ESMF_ROOT/cmake" - - name: Test CDEPS - run: | - cd build-share - make VERBOSE=1 - - name: Cache PFUNIT id: cache-pfunit uses: actions/cache@v4 @@ -98,33 +85,48 @@ jobs: cmake --build build popd - - name: Checkout cime - uses: actions/checkout@v4 + - name: Build SHARE + uses: ./.github/actions/buildshare with: - path: cime - ref: master - repository: ESMCI/cime + esmfmkfile: $ESMFMKFILE + pio_path: ${GITHUB_WORKSPACE}/pio + src_root: ${GITHUB_WORKSPACE} + pfunit_root: + cmake_flags: " -DUNITTESTS=ON -Wno-dev -DCMAKE_BUILD_TYPE=DEBUG -DWERROR=ON -DCMAKE_Fortran_FLAGS=\"-DCPRGNU -g -Wall \ + -ffree-form -ffree-line-length-none -fallow-argument-mismatch\" \ + -DCMAKE_MODULE_PATH=$ESMF_ROOT/cmake" + - name: Test share + run: | + cd build-share + make VERBOSE=1 - - name: Checkout ccs_config - uses: actions/checkout@v4 - with: - path: ccs_config - ref: main - repository: ESMCI/ccs_config_cesm +# - name: Checkout cime +# uses: actions/checkout@v4 +# with: +# path: cime +# ref: master +# repository: ESMCI/cime - - name: run unit testing - run: | - # this line is to satisfy the config_files.xml path structure - ln -fs . share - pushd cime - export CMAKE_PREFIX_PATH=$ESMF_ROOT:/usr - export CIME_MODEL=cesm - mkdir ftest - python ./scripts/fortran_unit_testing/run_tests.py --build-dir `pwd`/ftest --machine ubuntu-latest +# - name: Checkout ccs_config +# uses: actions/checkout@v4 +# with: +# path: ccs_config +# ref: main +# repository: ESMCI/ccs_config_cesm + +# - name: run unit testing +# run: | +# # this line is to satisfy the config_files.xml path structure +# ln -fs . share +# pushd cime +# export CMAKE_PREFIX_PATH=$ESMF_ROOT:/usr +# export CIME_MODEL=cesm +# mkdir ftest +# python ./scripts/fortran_unit_testing/run_tests.py --build-dir `pwd`/ftest --machine ubuntu-latest # the following can be used by developers to login to the github server in case of errors # see https://github.com/marketplace/actions/debugging-with-tmate for further details - - name: Setup tmate session - if: ${{ failure() }} - uses: mxschmitt/action-tmate@v3 +# - name: Setup tmate session +# if: ${{ failure() }} +# uses: mxschmitt/action-tmate@v3 diff --git a/CMakeLists.txt b/CMakeLists.txt index bf3f61d..6db30bf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,92 +2,103 @@ cmake_minimum_required(VERSION 3.10) include(ExternalProject) include(FetchContent) -option(WERROR "add the -Werror flag to compiler (works with gnu)" OFF) -enable_language(Fortran) - -if (DEFINED CIMEROOT) - message("Using CIME in ${CIMEROOT} with compiler ${COMPILER}") - include(${CASEROOT}/Macros.cmake) - if (${PIO_VERSION} LESS 2) - message( FATAL_ERROR "Version 2 of the PIO library required") - endif() - if (MPILIB STREQUAL mpi-serial) - set(CMAKE_Fortran_COMPILER ${SFC}) - set(CMAKE_C_COMPILER ${SCC}) - else() - set(CMAKE_Fortran_COMPILER ${MPIFC}) - set(CMAKE_C_COMPILER ${MPICC}) - endif() - set(CMAKE_Fortran_FLAGS "${FFLAGS} ${CPPDEFS} -I${LIBROOT}/include -I${LIBROOT}/nuopc/esmf/${NINST_VALUE}/include") - add_compile_definitions(CESMCOUPLED) - list(APPEND CMAKE_MODULE_PATH ${SRC_ROOT}/cime/CIME/non_py/src/CMake) -else() - set(BLD_STANDALONE TRUE) - project(SHARE LANGUAGES Fortran C VERSION 0.1) - list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) - string (TOUPPER "${CMAKE_Fortran_COMPILER_ID}" CMAKE_Fortran_COMPILER_NAME) - if (CMAKE_Fortran_COMPILER_NAME STREQUAL "XL") - set (CMAKE_Fortran_COMPILER_NAME "IBM") - endif () - if (CMAKE_Fortran_COMPILER_NAME STREQUAL "INTELLLVM") - set (CMAKE_Fortran_COMPILER_NAME "INTEL") + + + option(WERROR "add the -Werror flag to compiler (works with gnu)" OFF) + option(UNITTESTS "build the pfunit based tests (must have PFUNIT_ROOT)" OFF) + enable_language(Fortran) + set(ENABLE_GENF90 ON) + +# if (DEFINED CIMEROOT) +# message("Using CIME in ${CIMEROOT} with compiler ${COMPILER}") +# include(${CASEROOT}/Macros.cmake) +# if (${PIO_VERSION} LESS 2) +# message( FATAL_ERROR "Version 2 of the PIO library required") +# endif() +# if (MPILIB STREQUAL mpi-serial) +# set(CMAKE_Fortran_COMPILER ${SFC}) +# set(CMAKE_C_COMPILER ${SCC}) +# else() +# set(CMAKE_Fortran_COMPILER ${MPIFC}) +# set(CMAKE_C_COMPILER ${MPICC}) +# endif() +# set(CMAKE_Fortran_FLAGS "${FFLAGS} ${CPPDEFS} -I${LIBROOT}/include -I${LIBROOT}/nuopc/esmf/${NINST_VALUE}/include") +# add_compile_definitions(CESMCOUPLED) +# list(APPEND CMAKE_MODULE_PATH ${SRC_ROOT}/cime/CIME/non_py/src/CMake) +# else() +# set(BLD_STANDALONE TRUE) + project(SHARE LANGUAGES Fortran C VERSION 0.1) + list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) + include(Sourcelist_utils) + include(Compilers) + include(genf90_utils) +#===== genf90 ===== + if (DEFINED GENF90_PATH) + add_custom_target(genf90 + DEPENDS ${GENF90_PATH}/genf90.pl) + else () + ExternalProject_Add (genf90 + PREFIX ${CMAKE_CURRENT_BINARY_DIR}/genf90 + GIT_REPOSITORY https://github.com/PARALLELIO/genf90 + GIT_TAG update_cmake_interface + UPDATE_COMMAND "" + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "") + ExternalProject_Get_Property (genf90 SOURCE_DIR) + set (GENF90_PATH ${SOURCE_DIR}) + unset (SOURCE_DIR) endif () + + set(GENF90 "${GENF90_PATH}/genf90.pl") - set (CMAKE_Fortran_COMPILER_DIRECTIVE "CPR${CMAKE_Fortran_COMPILER_NAME}" - CACHE STRING "Fortran compiler name preprocessor directive") -endif() -message("CMAKE_Fortran_COMPILER_DIRECTIVE is ${CMAKE_Fortran_COMPILER_DIRECTIVE}") -option(WERROR "add the -Werror flag to compiler (works with gcc and intel)" OFF) - -if (DEFINED ENV{PIO_ROOT}) - message("PIO_ROOT is $ENV{PIO_ROOT}") -else() - if (DEFINED PIO) - set(PIO_PATH ${PIO}) - else() - set(PIO_PATH $ENV{PIO}) - endif() - find_package(PIO REQUIRED COMPONENT C Fortran PATH ${PIO_PATH}) -endif() -if (NOT DEFINED MPILIB OR NOT ${MPILIB} STREQUAL "mpi-serial") - find_package(MPI REQUIRED) -endif() + string (TOUPPER "${CMAKE_Fortran_COMPILER_ID}" CMAKE_Fortran_COMPILER_NAME) + if (CMAKE_Fortran_COMPILER_NAME STREQUAL "XL") + set (CMAKE_Fortran_COMPILER_NAME "IBM") + endif () + if (CMAKE_Fortran_COMPILER_NAME STREQUAL "INTELLLVM") + set (CMAKE_Fortran_COMPILER_NAME "INTEL") + endif () + set (CMAKE_Fortran_COMPILER_DIRECTIVE "-DCPR${CMAKE_Fortran_COMPILER_NAME}" + CACHE STRING "Fortran compiler name preprocessor directive") + message("CMAKE_Fortran_COMPILER_DIRECTIVE is ${CMAKE_Fortran_COMPILER_DIRECTIVE}") + set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${CMAKE_Fortran_COMPILER_DIRECTIVE} -I${CMAKE_SOURCE_DIR}/include -I${CMAKE_BINARY_DIR}") + + add_subdirectory(src) + -if (DEFINED ENV{ESMF_ROOT}) - list(APPEND CMAKE_MODULE_PATH $ENV{ESMF_ROOT}/cmake) -endif() -message("ESMF cmake is ${CMAKE_MODULE_PATH}") -find_package(ESMF REQUIRED) -set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${ESMF_F90COMPILEPATHS}") +# if (DEFINED ENV{PIO_ROOT}) +# message("PIO_ROOT is $ENV{PIO_ROOT}") +# else() +# if (DEFINED PIO) +# set(PIO_PATH ${PIO}) +# else() +# set(PIO_PATH $ENV{PIO}) +# endif() +# find_package(PIO REQUIRED COMPONENT C Fortran PATH ${PIO_PATH}) +# endif() -if("${COMPILER}" STREQUAL "nag") - set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -D__NAG__") -endif() -set(GENF90SOURCES src/shr_assert_mod.F90 src/shr_frz_mod.F90 src/shr_infnan_mod.F90) -set(ENABLE_GENF90 ON) +# if (NOT DEFINED MPILIB OR NOT ${MPILIB} STREQUAL "mpi-serial") +# find_package(MPI REQUIRED) +# endif() + +# if (DEFINED ENV{ESMF_ROOT}) +# list(APPEND CMAKE_MODULE_PATH $ENV{ESMF_ROOT}/cmake) +# endif() +# message("ESMF cmake is ${CMAKE_MODULE_PATH}") +# find_package(ESMF REQUIRED) +# set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${ESMF_F90COMPILEPATHS}") + + + + if("${COMPILER}" STREQUAL "nag") + set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -D__NAG__") + endif() + set(GENF90SOURCES src/shr_assert_mod.F90 src/shr_frz_mod.F90 src/shr_infnan_mod.F90) -#===== genf90 ===== -if (DEFINED GENF90_PATH) - add_custom_target(genf90 - DEPENDS ${GENF90_PATH}/genf90.pl) -else () - ExternalProject_Add (genf90 - PREFIX ${CMAKE_CURRENT_BINARY_DIR}/genf90 - GIT_REPOSITORY https://github.com/PARALLELIO/genf90 - GIT_TAG update_cmake_interface - UPDATE_COMMAND "" - CONFIGURE_COMMAND "" - BUILD_COMMAND "" - INSTALL_COMMAND "") - ExternalProject_Get_Property (genf90 SOURCE_DIR) - set (GENF90_PATH ${SOURCE_DIR}) - unset (SOURCE_DIR) -endif () - -set(GENF90 "${GENF90_PATH}/genf90.pl") #===== Fortran Source Generation with GenF90 ===== @@ -98,27 +109,33 @@ foreach (SRC_FILE IN LISTS GENF90SOURCES) ${CMAKE_CURRENT_SOURCE_DIR}/${SRC_FILE}.in > ${SRC_FILE} DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${SRC_FILE}.in genf90) endforeach () + + file(GLOB FSOURCES "src/*.F90" "src/water_isotopes/*.F90" "RandNum/src/*.F90" "RandNum/src/*/*.F90") + file(GLOB CSOURCES "src/*.c" "RandNum/src/*/*.c") -file(GLOB FSOURCES "src/*.F90" "src/water_isotopes/*.F90" "RandNum/src/*.F90" "RandNum/src/*/*.F90") -file(GLOB CSOURCES "src/*.c" "RandNum/src/*/*.c") - -file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/src) + file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/src) -add_library(share STATIC ${CSOURCES} ${FSOURCES} ${SHAREGENF90SRC}) + add_library(csm_share STATIC ${CSOURCES} ${FSOURCES} ${SHAREGENF90SRC} ) -if(WERROR) - set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -Werror --warn-no-unused-dummy-argument --warn-no-missing-include-dirs -ffree-line-length-none") +# if(WERROR) +# set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -Werror --warn-no-unused-dummy-argument --warn-no-missing-include-dirs -ffree-line-length-none") - set_source_files_properties(src/shr_mpi_mod.F90 src/shr_reprosum_mod.F90 PROPERTIES COMPILE_OPTIONS "-Wno-error;-fallow-argument-mismatch") - # This flag seems to be needed for temp variables generated by the compiler version in jammy - set_source_files_properties(src/shr_assert_mod.F90 PROPERTIES COMPILE_OPTIONS "-Wno-error=maybe-uninitialized") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror -Wno-error=cpp") - set_source_files_properties(src/shr_cal_mod.F90 PROPERTIES COMPILE_OPTIONS "-Wno-error=conversion") +# set_source_files_properties(src/shr_mpi_mod.F90 src/shr_reprosum_mod.F90 PROPERTIES COMPILE_OPTIONS "-Wno-error;-fallow-argument-mismatch") +# # This flag seems to be needed for temp variables generated by the compiler version in jammy +# set_source_files_properties(src/shr_assert_mod.F90 PROPERTIES COMPILE_OPTIONS "-Wno-error=maybe-uninitialized") +# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror -Wno-error=cpp") +# set_source_files_properties(src/shr_cal_mod.F90 PROPERTIES COMPILE_OPTIONS "-Wno-error=conversion") + +# endif() + add_dependencies (csm_share genf90) + target_include_directories(csm_share PRIVATE include RandNum/include ${CMAKE_BINARY_DIR}) + + if(UNITTESTS) + find_package(PFUNIT REQUIRED) + set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${CMAKE_Fortran_COMPILER_DIRECTIVE} -I${CMAKE_BINARY_DIR}/unittests/shr_assert_test/mod/assert/ ") + add_subdirectory(${CMAKE_SOURCE_DIR}/unit_test_stubs/util csm_share_stubs) + add_subdirectory(${CMAKE_SOURCE_DIR}/test/unit ${CMAKE_BINARY_DIR}/unittests) endif() -add_dependencies (share genf90) -target_include_directories(share PRIVATE include RandNum/include ${CMAKE_BINARY_DIR}) -target_compile_definitions (share PUBLIC ${CMAKE_Fortran_COMPILER_DIRECTIVE}) - diff --git a/cmake/Compilers.cmake b/cmake/Compilers.cmake new file mode 100644 index 0000000..3c71611 --- /dev/null +++ b/cmake/Compilers.cmake @@ -0,0 +1,71 @@ +# Flags for builds with different machines/compilers. +# +# This module is currently a catch-all for compiler-specific functionality +# needed by CESM. It defines OS and compiler CPP macros and CESM build +# types, as well as including the file containing CESM compiler flags, if +# necessary. +# +# There is also one function intended for CTest test writers, described +# below. +# +#========================================================================== +# +# define_Fortran_stop_failure +# +# Arguments: +# test_name - Name of a CTest test. +# +# Ensures that if the named test uses "STOP 1" to signal failure, that this +# is detected by CTest. Currently this is only necessary for NAG, which +# prints the stop code rather than using it as an error code. +# +#========================================================================== + +#========================================================================== +# Copyright (c) 2013-2014, University Corporation for Atmospheric Research +# +# This software is distributed under a two-clause BSD license, with no +# warranties, express or implied. See the accompanying LICENSE file for +# details. +#========================================================================== + +#================================================= +# Utility functions. +#================================================= + +# Add flags to space-separated list rather than normal CMake list. +function(add_flags list) + string(REPLACE ";" " " flags "${ARGN}") + set(${list} "${${list}} ${flags}" PARENT_SCOPE) +endfunction() + + +# Add configuration-specific preprocessor definitions. +function(add_config_definitions configuration) + get_directory_property(cppdefs COMPILE_DEFINITIONS_${configuration}) + foreach(flag IN LISTS ARGN) + string(REPLACE "-D" "" def "${flag}") + list(APPEND cppdefs ${def}) + endforeach() + set_directory_properties(PROPERTIES COMPILE_DEFINITIONS_${configuration} + "${cppdefs}") +endfunction() + + +#================================================= +# Help CTest tests recognize when "stop X" is called with non-zero X. +#================================================= + +# Detect "STOP" for CTest. +if("${CMAKE_Fortran_COMPILER_ID}" STREQUAL NAG) + # NAG prints the stop code instead of yielding a non-zero return, so we + # have to use a regex to catch that. + function(define_Fortran_stop_failure test_name) + set_tests_properties(${test_name} PROPERTIES + FAIL_REGULAR_EXPRESSION "STOP: [1-9]") + endfunction(define_Fortran_stop_failure) +else() + # Usually, stop /= 0 is already detected with the return code. + function(define_Fortran_stop_failure test_name) + endfunction(define_Fortran_stop_failure) +endif() diff --git a/cmake/Sourcelist_utils.cmake b/cmake/Sourcelist_utils.cmake new file mode 100644 index 0000000..6f65edf --- /dev/null +++ b/cmake/Sourcelist_utils.cmake @@ -0,0 +1,134 @@ +# Utility functions to work with accumulated lists of sources. +# +#========================================================================== +# +# sourcelist_to_parent +# +# Arguments: +# source_list_name - Name of list to send. +# +# Expands relative paths to absolute locations in the source list, then +# copies the value of the list to the list with the same name in the parent +# scope. +# +#========================================================================== +# +# extract_sources +# +# Arguments: +# sources_needed - Base names of sources required for a target. +# all_sources - Absolute locations of available source files. +# source_list_name - Absolute locations of sources to build the target. +# +# Scans through a list of all source files, selects files with the +# requested names, and *appends* them to an output list. If there is more +# than one file with the same name, the *last* match is selected. +# +# This allows you to simulate Makefile idioms that choose from multiple +# versions of a file, based on the order in which they are encountered. +# +#========================================================================== +# +# declare_generated_dependencies. +# +# Arguments: +# target - The target that needs to depend on generated files. +# generated_list - The generated source code files the target requires. +# +# Ensures that generated sources in a different directory are produced +# before compiling and linking the target. This is done by assuming that +# each generated source file corresponds to an existing target. For +# instance, a file called "foo.F90" would be generated by a target called +# "generate_foo". The input target can then be made to depend on each of +# the "generate" targets. +# +# This is unnecessary when source code generation occurs in the directory +# where the target was added. However, CMake does not propagate information +# about source code generation to parent directories, so the intermediate +# "generate" targets must be created to enforce generation in the correct +# order. +# +#========================================================================== + +#========================================================================== +# Copyright (c) 2013-2014, University Corporation for Atmospheric Research +# +# This software is distributed under a two-clause BSD license, with no +# warranties, express or implied. See the accompanying LICENSE file for +# details. +#========================================================================== + +# For each relative path in ${file_list}, prepend ${base_directory} to make +# an absolute path, and put result in list named by ${new_list_name}. +function(expand_relative_paths file_list base_directory new_list_name) + + unset(${new_list_name}) + foreach(file IN LISTS file_list) + if(IS_ABSOLUTE "${file}") + set(new_file "${file}") + else() + set(new_file "${base_directory}/${file}") + endif() + list(APPEND ${new_list_name} "${new_file}") + endforeach() + + set(${new_list_name} "${${new_list_name}}" PARENT_SCOPE) + +endfunction(expand_relative_paths) + +# Expand relative paths in a named source list, and export to parent scope. +# The idea here is to communicate the list between a directory added with +# add_subdirectory, and the directory above it. +macro(sourcelist_to_parent source_list_name) + expand_relative_paths("${${source_list_name}}" + ${CMAKE_CURRENT_SOURCE_DIR} ${source_list_name}) + set(${source_list_name} "${${source_list_name}}" PARENT_SCOPE) +endmacro(sourcelist_to_parent) + +# Find an absolute file path in ${all_sources} for each base name in +# ${sources_needed}, and append found paths to the list named by +# ${source_list_name}. +function(extract_sources sources_needed all_sources source_list_name) + + foreach(needed_source IN LISTS sources_needed) + + set(source_match source-NOTFOUND) + + foreach(source IN LISTS all_sources) + get_filename_component(basename ${source} NAME) + if(${basename} STREQUAL ${needed_source}) + set(source_match ${source}) + endif() + endforeach() + + if(NOT source_match) + message(FATAL_ERROR + "Source file not found: ${needed_source} +After searching in list: ${${all_sources}}") + endif() + + list(APPEND ${source_list_name} ${source_match}) + + endforeach() + + set(${source_list_name} "${${source_list_name}}" PARENT_SCOPE) + +endfunction(extract_sources) + +# Handles dependencies between files generated in one directory and a +# target in another. +# Given a target and a list of files, sets the GENERATED property for each +# file, and makes the target depend on a custom target generated from the +# extensionless base file name. (E.g. for /path/to/foo.F90, it will assume +# that it is generated by a custom target called generate_foo). +function(declare_generated_dependencies target generated_list) + foreach(file IN LISTS generated_list) + + set_source_files_properties(${file} PROPERTIES GENERATED 1) + + get_filename_component(stripped_name ${file} NAME_WE) + + add_dependencies(${target} generate_${stripped_name}) + + endforeach() +endfunction(declare_generated_dependencies) diff --git a/cmake/add_pfunit_ctest.cmake b/cmake/add_pfunit_ctest.cmake new file mode 100644 index 0000000..d2da0ae --- /dev/null +++ b/cmake/add_pfunit_ctest.cmake @@ -0,0 +1,169 @@ +# Function : add_pfunit_ctest +# +# Description : Helper function for compiling and adding pFUnit tests +# to the CTest testing framework. Any libraries needed +# in testing should be linked to manually. +# +# Arguments : - test_name: Name of the test package +# +# Example usage: enable_testing() +# add_pfunit_ctest (myTests +# TEST_SOURCES testMyLib.pf +# OTHER_SOURCES other.F90 yet_another.c +# REGISTRY test_suites.inc +# LINK_LIBRARIES mylib +# EXTRA_USE ... +# EXTRA_INITIALIZE ... +# EXTRA_FINALIZE ... +# LABELS ... +# MAX_PES 5 +# WORKING_DIRECTORY working_directory +# ) +# +# TEST_SOURCES items with relative paths are treated as relative to +# CMAKE_CURRENT_SOURCE_DIR, and the processed file is placed into a +# file with the same basename but relative to +# CMAKE_CURRENT_BINARY_DIR. (And with .F90 suffix, of course.) +# +# TEST_SOURCES items with absolute paths are trated as being within +# the build tree, and the processed file is placed in the same directory. +# (And with the .F90 suffix, of course.) +# +# Note: If REGISTRY is not provided, then a default testSuites.inc +# will be created based on the PFUNIT_SOURCES file names. It is +# assumed that the module name is the same as the file basename. +# For example, the file testSomething.pf should contain the +# module testSomething. +# +# +# +# +# Compile the tests: make myTests +# Run the tests with CTest: ctest -R myTests --verbose +# + +include (add_pfunit_sources) + +function (add_pfunit_ctest test_package_name) + set (oneValueArgs REGISTRY MAX_PES EXTRA_USE EXTRA_INITIALIZE EXTRA_FINALIZE WORKING_DIRECTORY) + set (multiValueArgs TEST_SOURCES OTHER_SOURCES LINK_LIBRARIES LABELS) + cmake_parse_arguments (PF_TEST "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + set (test_sources_f90) + set (test_suites_inc "") + + # Create contents of the test_suites files + foreach (pf_file ${PF_TEST_TEST_SOURCES}) + get_filename_component (basename ${pf_file} NAME_WE) + set (test_suites_inc "${test_suites_inc}ADD_TEST_SUITE(${basename}_suite)\n") + + endforeach() + + # Preprocess test files + # F90 files are set in test_sources_f90 + add_pfunit_sources(test_sources_f90 ${PF_TEST_TEST_SOURCES}) + + if (PF_TEST_EXTRA_USE) + set(PFUNIT_EXTRA_USE ${PF_TEST_EXTRA_USE}) + endif() + set(driver "${test_package_name}_driver.F90") + configure_file(${PFUNIT_DRIVER}.in ${driver}) + + add_executable (${test_package_name} + ${test_sources_f90} + ${PF_TEST_OTHER_SOURCES} + ${driver} + ) + set (mod_dir ${CMAKE_CURRENT_BINARY_DIR}/mod/${test_package_name}) + set_target_properties (${test_package_name} PROPERTIES Fortran_MODULE_DIRECTORY ${mod_dir}) + target_include_directories(${test_package_name} PRIVATE ${mod_dir}) + + if (PF_TEST_REGISTRY) + set (test_suite_inc_file ${PF_TEST_REGISTRY}) + else () + set (should_write_inc_file True) + set (test_suite_inc_file "${CMAKE_CURRENT_BINARY_DIR}/${test_package_name}.inc") + # Check if .inc file already has been generated. If so, only write new + # file if contents has changed. This avoid tests recompiling after reconfiguring cmake. + if (EXISTS ${test_suite_inc_file}) + file (READ ${test_suite_inc_file} existing_file) + if (${existing_file} STREQUAL ${test_suites_inc}) + set (should_write_inc_file False) + endif (${existing_file} STREQUAL ${test_suites_inc}) + endif () + + if (${should_write_inc_file}) + file (WRITE ${test_suite_inc_file} ${test_suites_inc}) + endif (${should_write_inc_file}) + endif () + + target_compile_definitions (${test_package_name} PRIVATE -D_TEST_SUITES="${test_suite_inc_file}") + target_include_directories (${test_package_name} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) + + if (PF_TEST_EXTRA_USE) + target_compile_definitions (${test_package_name} PRIVATE -DPFUNIT_EXTRA_USE=${PF_TEST_EXTRA_USE}) + endif() + + if (PF_TEST_EXTRA_INITIALIZE) + target_compile_definitions (${test_package_name} PRIVATE -DPFUNIT_EXTRA_INITIALIZE=${PF_TEST_EXTRA_INITIALIZE}) + endif() + + if (PF_TEST_EXTRA_FINALIZE) + target_compile_definitions (${test_package_name} PRIVATE -DPFUNIT_EXTRA_FINALIZE=${PF_TEST_EXTRA_FINALIZE}) + endif() + + if (PF_TEST_LINK_LIBRARIES) + target_link_libraries (${test_package_name} ${PF_TEST_LINK_LIBRARIES}) + endif () + + if (PF_TEST_WORKING_DIRECTORY) + set(workdir ${PF_TEST_WORKING_DIRECTORY}) + else () + set(workdir ${CMAKE_CURRENT_BINARY_DIR}) + endif () + + ################################################# + # Define test in CTest system # + ################################################# + if (PF_TEST_MAX_PES AND PFUNIT_MPI_FOUND) + target_link_libraries (${test_package_name} ${PFUNIT_LIBRARIES}) + if (NOT MPIEXEC_EXECUTABLE) + if (PFUNIT_MPI_USE_MPIEXEC) + set(MPIEXEC_EXECUTABLE ${PFUNIT_MPI_USE_MPIEXEC}) + else() # best guess + set(MPIEXEC_EXECUTABLE mpirun) + endif() + endif() + if (NOT MPIEXEC_NUMPROC_FLAG) + if (PFUNIT_MPI_USE_MPIEXEC) + set(MPIEXEC_EXECUTABLE ${PFUNIT_MPI_USE_MPIEXEC}) + else() # best guess + set(MPIEXEC_NUMPROC_FLAG "-np") + endif() + endif() + if (MPIEXEC_EXECUTABLE MATCHES ".*openmpi*") + list(APPEND MPIEXEC_PREFLAGS "--oversubscribe") + endif() + add_test (NAME ${test_package_name} + WORKING_DIRECTORY ${workdir} + COMMAND ${MPIEXEC_EXECUTABLE} ${MPIEXEC_PREFLAGS} ${MPIEXEC_NUMPROC_FLAG} ${PF_TEST_MAX_PES} ${CMAKE_CURRENT_BINARY_DIR}/${test_package_name} --verbose + ) + else() + target_link_libraries (${test_package_name} ${PFUNIT_SERIAL_LIBRARIES}) + add_test (NAME ${test_package_name} + WORKING_DIRECTORY ${workdir} + COMMAND ${test_package_name} --verbose + ) + endif() + + set_property (TEST ${test_package_name} + PROPERTY FAIL_REGULAR_EXPRESSION "Encountered 1 or more failures/errors during testing" + ) + + if (PF_TEST_LABELS) + set_property (TEST ${test_package_name} + PROPERTY LABELS ${PF_TEST_LABELS} + ) + endif() + +endfunction()