From fc9337f3ea7a16343a39455c86a3803391a6e4ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A1lint=20Aradi?= Date: Wed, 25 Sep 2024 22:59:38 +0200 Subject: [PATCH] Add MPI interface --- .github/workflows/ci.yml | 132 ++++++++++---- CMakeLists.txt | 24 ++- cmake/fortuno-mpi.pc.in | 7 + devel/fpm-export/fpm-export.sh | 19 ++ devel/fpm-export/mpi/fpm.toml | 14 ++ devel/fpm-export/mpi/rsync.ignore | 5 + devel/fpm-export/mpi/rsync.include | 14 ++ fpm.toml => devel/fpm-export/serial/fpm.toml | 12 -- devel/fpm-export/serial/rsync.ignore | 5 + devel/fpm-export/serial/rsync.include | 14 ++ example/CMakeLists.txt | 41 ++--- example/meson.build | 27 +-- example/mpi-fypp/CMakeLists.txt | 49 +++++ example/mpi-fypp/mylib.f90 | 59 ++++++ example/mpi-fypp/test_simple_fypp.fypp | 58 ++++++ example/mpi-fypp/testapp.f90 | 13 ++ example/mpi/CMakeLists.txt | 31 ++++ example/mpi/meson.build | 23 +++ example/mpi/mylib.f90 | 59 ++++++ example/mpi/test_simple.f90 | 53 ++++++ example/mpi/testapp.f90 | 13 ++ example/{fypp => serial-fypp}/CMakeLists.txt | 22 +-- example/{fypp => serial-fypp}/mylib.f90 | 0 .../test_fixtured_fypp.fypp | 0 .../test_parametrized_fypp.fypp | 0 .../test_simple_fypp.fypp | 0 .../testapp.f90} | 0 example/serial/CMakeLists.txt | 33 ++++ example/serial/meson.build | 26 +++ example/{ => serial}/mylib.f90 | 0 example/{ => serial}/test_fixtured.f90 | 0 example/{ => serial}/test_fixtured_module.f90 | 0 example/{ => serial}/test_fixtured_suite.f90 | 0 example/{ => serial}/test_parametrized.f90 | 0 example/{ => serial}/test_simple.f90 | 0 example/{ => serial}/testapp.f90 | 0 include/CMakeLists.txt | 19 +- include/fortuno_mpi.fypp | 107 +++++++++++ include/fortuno_serial.fpp | 34 ++++ meson.build | 25 ++- meson.options | 2 + src/CMakeLists.txt | 29 +++ src/fortuno_mpi.f90 | 15 ++ src/fortuno_mpi/CMakeLists.txt | 18 ++ src/fortuno_mpi/meson.build | 16 ++ src/fortuno_mpi/mpibasetypes.f90 | 99 ++++++++++ src/fortuno_mpi/mpicase.f90 | 61 +++++++ src/fortuno_mpi/mpicmdapp.f90 | 81 +++++++++ src/fortuno_mpi/mpiconlogger.f90 | 120 ++++++++++++ src/fortuno_mpi/mpicontext.f90 | 151 ++++++++++++++++ src/fortuno_mpi/mpidriver.f90 | 126 +++++++++++++ src/fortuno_mpi/mpienv.f90 | 147 +++++++++++++++ src/fortuno_mpi/mpiglobalctx.f90 | 171 ++++++++++++++++++ src/fortuno_mpi/mpisuite.f90 | 31 ++++ src/fortuno_mpi/mpitestinfo.f90 | 75 ++++++++ src/meson.build | 5 + test/export/mpi/CMakeLists.txt | 30 +++ test/export/mpi/app/CMakeLists.txt | 21 +++ test/export/mpi/app/meson.build | 7 + test/export/mpi/app/testapp.f90 | 46 +++++ test/export/mpi/app/testapp_fypp.fypp | 50 +++++ test/export/mpi/fpm.toml | 28 +++ test/export/mpi/meson.build | 26 +++ test/export/{ => serial}/CMakeLists.txt | 0 test/export/{ => serial}/app/CMakeLists.txt | 0 test/export/{ => serial}/app/meson.build | 0 test/export/{ => serial}/app/testapp.f90 | 6 +- test/export/serial/app/testapp_fpp.F90 | 43 +++++ .../export/{ => serial}/app/testapp_fypp.fypp | 0 test/export/{ => serial}/fpm.toml | 2 +- test/export/{ => serial}/meson.build | 0 71 files changed, 2228 insertions(+), 116 deletions(-) create mode 100644 cmake/fortuno-mpi.pc.in create mode 100755 devel/fpm-export/fpm-export.sh create mode 100644 devel/fpm-export/mpi/fpm.toml create mode 100644 devel/fpm-export/mpi/rsync.ignore create mode 100644 devel/fpm-export/mpi/rsync.include rename fpm.toml => devel/fpm-export/serial/fpm.toml (63%) create mode 100644 devel/fpm-export/serial/rsync.ignore create mode 100644 devel/fpm-export/serial/rsync.include create mode 100644 example/mpi-fypp/CMakeLists.txt create mode 100644 example/mpi-fypp/mylib.f90 create mode 100644 example/mpi-fypp/test_simple_fypp.fypp create mode 100644 example/mpi-fypp/testapp.f90 create mode 100644 example/mpi/CMakeLists.txt create mode 100644 example/mpi/meson.build create mode 100644 example/mpi/mylib.f90 create mode 100644 example/mpi/test_simple.f90 create mode 100644 example/mpi/testapp.f90 rename example/{fypp => serial-fypp}/CMakeLists.txt (58%) rename example/{fypp => serial-fypp}/mylib.f90 (100%) rename example/{fypp => serial-fypp}/test_fixtured_fypp.fypp (100%) rename example/{fypp => serial-fypp}/test_parametrized_fypp.fypp (100%) rename example/{fypp => serial-fypp}/test_simple_fypp.fypp (100%) rename example/{fypp/testapp_fypp.f90 => serial-fypp/testapp.f90} (100%) create mode 100644 example/serial/CMakeLists.txt create mode 100644 example/serial/meson.build rename example/{ => serial}/mylib.f90 (100%) rename example/{ => serial}/test_fixtured.f90 (100%) rename example/{ => serial}/test_fixtured_module.f90 (100%) rename example/{ => serial}/test_fixtured_suite.f90 (100%) rename example/{ => serial}/test_parametrized.f90 (100%) rename example/{ => serial}/test_simple.f90 (100%) rename example/{ => serial}/testapp.f90 (100%) create mode 100644 include/fortuno_mpi.fypp create mode 100644 include/fortuno_serial.fpp create mode 100644 src/fortuno_mpi.f90 create mode 100644 src/fortuno_mpi/CMakeLists.txt create mode 100644 src/fortuno_mpi/meson.build create mode 100644 src/fortuno_mpi/mpibasetypes.f90 create mode 100644 src/fortuno_mpi/mpicase.f90 create mode 100644 src/fortuno_mpi/mpicmdapp.f90 create mode 100644 src/fortuno_mpi/mpiconlogger.f90 create mode 100644 src/fortuno_mpi/mpicontext.f90 create mode 100644 src/fortuno_mpi/mpidriver.f90 create mode 100644 src/fortuno_mpi/mpienv.f90 create mode 100644 src/fortuno_mpi/mpiglobalctx.f90 create mode 100644 src/fortuno_mpi/mpisuite.f90 create mode 100644 src/fortuno_mpi/mpitestinfo.f90 create mode 100644 test/export/mpi/CMakeLists.txt create mode 100644 test/export/mpi/app/CMakeLists.txt create mode 100644 test/export/mpi/app/meson.build create mode 100644 test/export/mpi/app/testapp.f90 create mode 100644 test/export/mpi/app/testapp_fypp.fypp create mode 100644 test/export/mpi/fpm.toml create mode 100644 test/export/mpi/meson.build rename test/export/{ => serial}/CMakeLists.txt (100%) rename test/export/{ => serial}/app/CMakeLists.txt (100%) rename test/export/{ => serial}/app/meson.build (100%) rename test/export/{ => serial}/app/testapp.f90 (87%) create mode 100644 test/export/serial/app/testapp_fpp.F90 rename test/export/{ => serial}/app/testapp_fypp.fypp (100%) rename test/export/{ => serial}/fpm.toml (93%) rename test/export/{ => serial}/meson.build (100%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a0d2395..13bcebf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,6 +10,7 @@ on: env: BUILD_DIR: _build INSTALL_DIR: _install + FPM_EXPORT_DIR: _fpm_export jobs: @@ -22,12 +23,41 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest, macos-latest] - compiler: [intel, gnu] - exclude: - # MacOS has no Intel compiler - - os: macos-latest - compiler: intel + include: + - os: ubuntu-latest + compiler: gnu + mpi: nompi + interface: serial + - os: ubuntu-latest + compiler: gnu + mpi: mpich + interface: mpi + # CMake config file of OpenMPI is broken on ubuntu 22.04 + # - os: ubuntu-latest + # compiler: gnu + # mpi: openmpi + # interface: mpi + - os: ubuntu-latest + compiler: intel + mpi: nompi + interface: serial + - os: ubuntu-latest + compiler: intel + mpi: impi + interface: mpi + - os: macos-latest + compiler: gnu + mpi: nompi + interface: serial + # MPICH on MacOS lacks mpi_f08 interface + # - os: macos-latest + # compiler: gnu + # mpi: mpich + # interface: mpi + - os: macos-latest + compiler: gnu + mpi: openmpi + interface: mpi steps: @@ -38,9 +68,10 @@ jobs: if: ${{ contains(matrix.compiler, 'intel') }} uses: rscohn2/setup-oneapi@v0 with: - # Note: intel 2024.1 and 2024.2 fail to build Fortuno properly due to a compiler bug, - # see https://community.intel.com/t5/Intel-Fortran-Compiler/Compiler-bug-Procedure-pointer-association-status-gets-lost/m-p/1612121#M172850 - components: ifx@2024.0.0 + components: | + ifx + icx + impi - name: Setup Intel environment if: ${{ contains(matrix.compiler, 'intel') }} @@ -49,6 +80,9 @@ jobs: printenv >> ${GITHUB_ENV} echo "FC=ifx" >> ${GITHUB_ENV} echo "FPM_FC=ifx" >> ${GITHUB_ENV} + # Overriding default FPM_FFLAGS as default setting contains '-standard-semantics' + # which is incompatible with intel MPI. + echo "FPM_FFLAGS='-warn all -check all,nouninit -error-limit 1 -O0 -g -stand f18 -traceback'" >> ${GITHUB_ENV} - name: Setup GNU compiler if: ${{ contains(matrix.compiler, 'gnu') }} @@ -63,59 +97,93 @@ jobs: echo "FC=${{ env.FC }}" >> ${GITHUB_ENV} echo "FPM_FC=${{ env.FC }}" >> ${GITHUB_ENV} - - name: Setup build tools + - name: Setup MPICH on Ubuntu + if: ${{ contains(matrix.os, 'ubuntu') && contains(matrix.mpi, 'mpich') }} run: | - pip install cmake fpm meson ninja fypp + sudo apt-get update + sudo apt-get install -y mpich - - name: Build Fortuno with CMake + - name: Setup OpenMPI on Ubuntu + if: ${{ contains(matrix.os, 'ubuntu') && contains(matrix.mpi, 'openmpi') }} run: | - cmake -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR} -B ${BUILD_DIR} -G Ninja - cmake --build ${BUILD_DIR} - cmake --install ${BUILD_DIR} - rm -rf ${BUILD_DIR} + sudo apt-get update + sudo apt-get install -y openmpi-common openmpi-bin - - name: Build Fortuno with Fpm + - name: Setup MPICH on MacOS + if: ${{ contains(matrix.os, 'macos') && contains(matrix.mpi, 'mpich') }} run: | - fpm build - rm -rf build + brew install mpich - - name: Build Fortuno with Meson + - name: Setup OpenMPI on MacOS + if: ${{ contains(matrix.os, 'macos') && contains(matrix.mpi, 'openmpi') }} run: | - meson setup -Dbuild_examples=true ${BUILD_DIR} - ninja -C ${BUILD_DIR} + brew install openmpi + + - name: Setup serial interface options + if: ${{ contains(matrix.interface, 'serial') }} + run: | + echo "CMAKE_OPTIONS=-DFORTUNO_BUILD_SERIAL_INTERFACE=ON" >> ${GITHUB_ENV} + echo "MESON_OPTIONS=-Dbuild_serial_interface=true" >> ${GITHUB_ENV} + echo "MESON_FALLBACK_OPTIONS=-Dfortuno:build_serial_interface=true" >> ${GITHUB_ENV} + echo "INTERFACE=serial" >> ${GITHUB_ENV} + echo "RUN_PREFIX=" >> ${GITHUB_ENV} + + - name: Setup mpi interface options + if: ${{ contains(matrix.interface, 'mpi') }} + run: | + echo "CMAKE_OPTIONS=-DFORTUNO_BUILD_MPI_INTERFACE=ON" >> ${GITHUB_ENV} + echo "MESON_OPTIONS=-Dbuild_mpi_interface=true" >> ${GITHUB_ENV} + echo "MESON_FALLBACK_OPTIONS=-Dfortuno:build_mpi_interface=true" >> ${GITHUB_ENV} + echo "INTERFACE=mpi" >> ${GITHUB_ENV} + echo "RUN_PREFIX=mpirun -n 2" >> ${GITHUB_ENV} + + - name: Setup build tools + run: | + pip install cmake fpm meson ninja fypp + + - name: Build Fortuno + run: | + cmake ${CMAKE_OPTIONS} -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR} -B ${BUILD_DIR} -G Ninja + cmake --build ${BUILD_DIR} + cmake --install ${BUILD_DIR} rm -rf ${BUILD_DIR} - name: Test CMake export run: | - CMAKE_PREFIX_PATH=${INSTALL_DIR} cmake -B ${BUILD_DIR} -G Ninja test/export + CMAKE_PREFIX_PATH=${INSTALL_DIR} cmake -B ${BUILD_DIR} -G Ninja test/export/${INTERFACE} cmake --build ${BUILD_DIR} - ${BUILD_DIR}/app/testapp - ${BUILD_DIR}/app/testapp_fypp + ${RUN_PREFIX} ${BUILD_DIR}/app/testapp + ${RUN_PREFIX} ${BUILD_DIR}/app/testapp_fypp rm -rf ${BUILD_DIR} - name: Test fpm export run: | - cd test/export - fpm run testapp + ./devel/fpm-export/fpm-export.sh ${INTERFACE} ${FPM_EXPORT_DIR} + fpm run -C ${FPM_EXPORT_DIR}/test/export/${INTERFACE} testapp + rm -rf ${FPM_EXPORT_DIR} - name: Test Meson pkgconfig export + # Meson only detects OpenMPI reliably (as of version 1.3.2) + if: ${{ !contains(matrix.interface, 'mpi') || contains(matrix.mpi, 'openmpi') }} run: | export PKG_CONFIG_PATH="${PWD}/${INSTALL_DIR}/lib/pkgconfig:${PKG_CONFIG_PATH}" - cd test/export + cd test/export/${INTERFACE} meson setup --wrap-mode nofallback ${BUILD_DIR} ninja -C ${BUILD_DIR} - ${BUILD_DIR}/testapp + ${RUN_PREFIX} ${BUILD_DIR}/testapp rm -rf ./${BUILD_DIR} - name: Test Meson subproject export + # Meson only detects OpenMPI reliably (as of version 1.3.2) + if: ${{ !contains(matrix.interface, 'mpi') || contains(matrix.mpi, 'openmpi') }} run: | FORTUNO_DIR=${PWD} GIT_REV=$(git rev-parse HEAD) - cd test/export + cd test/export/${INTERFACE} mkdir subprojects echo -e "[wrap-git]\ndirectory=fortuno\n" > subprojects/fortuno.wrap echo -e "url=file://${FORTUNO_DIR}\nrevision=${GIT_REV}\n" >> subprojects/fortuno.wrap - meson setup --wrap-mode forcefallback ${BUILD_DIR} + meson setup ${MESON_FALLBACK_OPTIONS} --wrap-mode forcefallback ${BUILD_DIR} ninja -C ${BUILD_DIR} - ${BUILD_DIR}/testapp + ${RUN_PREFIX} ${BUILD_DIR}/testapp rm -rf subprojects ${BUILD_DIR} diff --git a/CMakeLists.txt b/CMakeLists.txt index 21757f7..11dd176 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,10 +31,16 @@ option(FORTUNO_BUILD_SHARED_LIBS "Fortuno: Build as shared library" ${PROJECT_IS option( FORTUNO_BUILD_SERIAL_INTERFACE - "Fortuno: whether serial interface should be built (or only core library)" + "Fortuno: whether serial interface should be built" ON ) +option( + FORTUNO_BUILD_MPI_INTERFACE + "Fortuno: whether MPI interface should be built" + OFF +) + cmake_dependent_option( FORTUNO_BUILD_TESTS "Fortuno: Build test suite" ${PROJECT_IS_TOP_LEVEL} "FORTUNO_BUILD_SERIAL_INTERFACE" OFF @@ -79,6 +85,13 @@ find_program(FYPP fypp) set(BUILD_SHARED_LIBS ${FORTUNO_BUILD_SHARED_LIBS}) fortuno_setup_build_type("RelWithDebInfo") +if (FORTUNO_BUILD_MPI_INTERFACE) + find_package(MPI REQUIRED) + if(NOT MPI_FORTRAN_FOUND) + message(FATAL_ERROR "Failed to detect MPI-framework for Fortran") + endif() +endif () + #[=================================================================================================[ # Main definition # ]=================================================================================================] @@ -112,6 +125,15 @@ if (FORTUNO_INSTALL) ) endif () + if (FORTUNO_BUILD_MPI_INTERFACE) + configure_file(cmake/fortuno-mpi.pc.in fortuno-mpi.pc @ONLY) + install( + FILES ${CMAKE_CURRENT_BINARY_DIR}/fortuno-mpi.pc + DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig + COMPONENT Fortuno_development + ) + endif () + # cmake export files write_basic_package_version_file( ${CMAKE_CURRENT_BINARY_DIR}/FortunoConfigVersion.cmake diff --git a/cmake/fortuno-mpi.pc.in b/cmake/fortuno-mpi.pc.in new file mode 100644 index 0000000..b48e12a --- /dev/null +++ b/cmake/fortuno-mpi.pc.in @@ -0,0 +1,7 @@ +Name: @PROJECT_NAME@ +Description: @PROJECT_DESCRIPTION@ +Version: @PROJECT_VERSION@ + +Requires: fortuno +Cflags: -I@CMAKE_INSTALL_FULL_LIBDIR@/@FORTUNO_MPI_INSTALL_MODULEDIR@ +Libs: -L@CMAKE_INSTALL_FULL_LIBDIR@ -lfortuno-mpi diff --git a/devel/fpm-export/fpm-export.sh b/devel/fpm-export/fpm-export.sh new file mode 100755 index 0000000..c7faf6e --- /dev/null +++ b/devel/fpm-export/fpm-export.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +SCRIPT_DIR="$(readlink -f $(dirname ${BASH_SOURCE[0]}))" + +configdir=$1 +destdir=$2 + +if [[ $# != 2 ]]; then + echo "Need two arguments." >&2 + exit 1 +fi + +rsync \ + -av \ + --exclude-from=${SCRIPT_DIR}/${configdir}/rsync.ignore \ + --include-from=${SCRIPT_DIR}/${configdir}/rsync.include \ + --exclude='*' \ + ${SCRIPT_DIR}/../../ ${destdir} +cp ${SCRIPT_DIR}/${configdir}/fpm.toml ${destdir} \ No newline at end of file diff --git a/devel/fpm-export/mpi/fpm.toml b/devel/fpm-export/mpi/fpm.toml new file mode 100644 index 0000000..6cdc12d --- /dev/null +++ b/devel/fpm-export/mpi/fpm.toml @@ -0,0 +1,14 @@ +name = "fortuno-mpi" +version = "0.1.0" +license = "BSD-2-Clause-Patent" +author = "Fortuno authors" +maintainer = "aradi@uni-bremen.de" +copyright = "Copyright 2024, Fortuno authors" + +[fortran] +# Enabling the implicit options seems to be necessary for using fpm with meta-dependency mpi. +# Do not worry, the project does not make use of them. ;-) +implicit-typing = true +implicit-external = true +source-form = "free" + diff --git a/devel/fpm-export/mpi/rsync.ignore b/devel/fpm-export/mpi/rsync.ignore new file mode 100644 index 0000000..d4d112c --- /dev/null +++ b/devel/fpm-export/mpi/rsync.ignore @@ -0,0 +1,5 @@ +*~ +*.mod +*.a +*.o + diff --git a/devel/fpm-export/mpi/rsync.include b/devel/fpm-export/mpi/rsync.include new file mode 100644 index 0000000..93646e8 --- /dev/null +++ b/devel/fpm-export/mpi/rsync.include @@ -0,0 +1,14 @@ +fpm.serial.toml +LICENSE +src/ +src/fortuno.f90 +src/fortuno/ +src/fortuno/** +src/fortuno_mpi.f90 +src/fortuno_mpi/ +src/fortuno_mpi/** +test/ +test/export/ +test/export/mpi/ +test/export/mpi/** + diff --git a/fpm.toml b/devel/fpm-export/serial/fpm.toml similarity index 63% rename from fpm.toml rename to devel/fpm-export/serial/fpm.toml index c092d7a..6b22a17 100644 --- a/fpm.toml +++ b/devel/fpm-export/serial/fpm.toml @@ -5,19 +5,7 @@ author = "Fortuno authors" maintainer = "aradi@uni-bremen.de" copyright = "Copyright 2024, Fortuno authors" -[build] -auto-executables = false -auto-tests = true -auto-examples = false -module-naming = false - -[install] -library = true - [fortran] implicit-typing = false implicit-external = false source-form = "free" - -[example] -name = "testapp" diff --git a/devel/fpm-export/serial/rsync.ignore b/devel/fpm-export/serial/rsync.ignore new file mode 100644 index 0000000..d4d112c --- /dev/null +++ b/devel/fpm-export/serial/rsync.ignore @@ -0,0 +1,5 @@ +*~ +*.mod +*.a +*.o + diff --git a/devel/fpm-export/serial/rsync.include b/devel/fpm-export/serial/rsync.include new file mode 100644 index 0000000..51a4429 --- /dev/null +++ b/devel/fpm-export/serial/rsync.include @@ -0,0 +1,14 @@ +fpm.serial.toml +LICENSE +src/ +src/fortuno.f90 +src/fortuno/ +src/fortuno/** +src/fortuno_serial.f90 +src/fortuno_serial/ +src/fortuno_serial/** +test/ +test/export/ +test/export/serial/ +test/export/serial/** + diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index 522ea5c..18a9aca 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -4,33 +4,16 @@ list(APPEND CMAKE_MESSAGE_CONTEXT Example) -add_library(fortuno_example_mylib) -set_target_properties( - fortuno_example_mylib PROPERTIES - OUTPUT_NAME mylib -) -target_sources( - fortuno_example_mylib PRIVATE - mylib.f90 -) +if (FORTUNO_BUILD_SERIAL_INTERFACE) + add_subdirectory(serial) + if (FYPP) + add_subdirectory(serial-fypp) + endif () +endif () -add_executable(fortuno_example_testapp) -set_target_properties( - fortuno_example_testapp PROPERTIES - OUTPUT_NAME testapp -) -target_sources( - fortuno_example_testapp PRIVATE - testapp.f90 - test_fixtured.f90 - test_fixtured_module.f90 - test_fixtured_suite.f90 - test_parametrized.f90 - test_simple.f90 -) -target_link_libraries(fortuno_example_testapp PRIVATE fortuno_example_mylib Fortuno::fortuno_serial) - - -if (FYPP) - add_subdirectory(fypp) -endif () \ No newline at end of file +if (FORTUNO_BUILD_MPI_INTERFACE) + add_subdirectory(mpi) + if (FYPP) + add_subdirectory(mpi-fypp) + endif () +endif () diff --git a/example/meson.build b/example/meson.build index e5fc200..2e41b5b 100644 --- a/example/meson.build +++ b/example/meson.build @@ -2,25 +2,10 @@ # Licensed under the BSD-2-Clause Plus Patent license. # SPDX-License-Identifier: BSD-2-Clause-Patent -example_mylib_lib = library( - 'mylib', - sources: ['mylib.f90'], - install: false -) -example_mylib_dep = declare_dependency( - link_with: example_mylib_lib, -) +if build_serial_interface + subdir('serial') +endif -example_testapp_exe = executable( - 'testapp', - sources: [ - 'test_fixtured_module.f90', - 'test_fixtured_suite.f90', - 'test_fixtured.f90', - 'test_parametrized.f90', - 'test_simple.f90', - 'testapp.f90', - ], - dependencies: [example_mylib_dep, fortuno_serial_dep], - install: false, -) +if build_mpi_interface + subdir('mpi') +endif diff --git a/example/mpi-fypp/CMakeLists.txt b/example/mpi-fypp/CMakeLists.txt new file mode 100644 index 0000000..ff3caff --- /dev/null +++ b/example/mpi-fypp/CMakeLists.txt @@ -0,0 +1,49 @@ +# This file is part of Fortuno. +# Licensed under the BSD-2-Clause Plus Patent license. +# SPDX-License-Identifier: BSD-2-Clause-Patent + +list(APPEND CMAKE_MESSAGE_CONTEXT MpiFypp) + +add_library(fortuno_example_mpi_fypp_mylib) +set_target_properties( + fortuno_example_mpi_fypp_mylib PROPERTIES + OUTPUT_NAME mylib +) +target_sources( + fortuno_example_mpi_fypp_mylib PRIVATE + mylib.f90 +) +target_link_libraries(fortuno_example_mpi_fypp_mylib PRIVATE MPI::MPI_Fortran) + +add_executable(fortuno_example_mpi_fypp_testapp) +set_target_properties( + fortuno_example_mpi_fypp_testapp PROPERTIES + OUTPUT_NAME testapp +) + +set( + fypp-sources + test_simple_fypp.fypp +) + +get_target_property( + _fortuno_incdir + Fortuno::fortuno_include_dir + INTERFACE_INCLUDE_DIRECTORIES +) +fortuno_preprocess( + ${FYPP} "-I${_fortuno_incdir};--file-var-root=${CMAKE_SOURCE_DIR}" + .fypp .f90 + "${fypp-sources}" fypp-f90-sources +) + +target_sources( + fortuno_example_mpi_fypp_testapp PRIVATE + ${fypp-f90-sources} + testapp.f90 +) +target_link_libraries( + fortuno_example_mpi_fypp_testapp + PRIVATE + fortuno_example_mpi_fypp_mylib Fortuno::fortuno_mpi MPI::MPI_Fortran +) diff --git a/example/mpi-fypp/mylib.f90 b/example/mpi-fypp/mylib.f90 new file mode 100644 index 0000000..b7e5a77 --- /dev/null +++ b/example/mpi-fypp/mylib.f90 @@ -0,0 +1,59 @@ +! This file is part of Fortuno. +! Licensed under the BSD-2-Clause Plus Patent license. +! SPDX-License-Identifier: BSD-2-Clause-Patent + +!> Demo module/library to be tested +module mylib + use mpi_f08, only : MPI_Allreduce, MPI_Bcast, MPI_Comm, MPI_COMM_WORLD, MPI_IN_PLACE,& + & MPI_INTEGER, MPI_SUM + implicit none + + private + public :: allreduce_sum, broadcast + +contains + + + !> Broadcasts a scalar integer + subroutine broadcast(comm, buffer, source) + + !> MPI communicator to use + type(MPI_Comm), intent(in) :: comm + + !> Item to broadcast + integer, intent(inout) :: buffer + + !> Source rank (default = 0) + integer, optional, intent(in) :: source + + integer :: source_ + integer :: ierror + + if (present(source)) then + source_ = source + else + source_ = 0 + end if + call MPI_Bcast(buffer, 1, MPI_INTEGER, source_, comm, ierror) + if (ierror /= 0) error stop "MPI_Bcast failed in mylib/broadcast" + + end subroutine broadcast + + + !> Reduces a scalar integer by summation on all ranks + subroutine allreduce_sum(comm, val) + + !> MPI communicator + type(MPI_Comm), intent(in) :: comm + + !> Value to reduce by summation + integer, intent(inout) :: val + + integer :: ierror + + call MPI_Allreduce(MPI_IN_PLACE, val, 1, MPI_INTEGER, MPI_SUM, comm, ierror) + if (ierror /= 0) error stop "MPI_Allreduce failed in mylib/allreduce_sum" + + end subroutine allreduce_sum + +end module mylib diff --git a/example/mpi-fypp/test_simple_fypp.fypp b/example/mpi-fypp/test_simple_fypp.fypp new file mode 100644 index 0000000..2deb367 --- /dev/null +++ b/example/mpi-fypp/test_simple_fypp.fypp @@ -0,0 +1,58 @@ +! This file is part of Fortuno. +! Licensed under the BSD-2-Clause Plus Patent license. +! SPDX-License-Identifier: BSD-2-Clause-Patent + +#:include "fortuno_mpi.fypp" + +module test_simple_fypp + use mylib, only : broadcast + use fortuno_mpi, only : as_char, global_comm, is_equal, suite => mpi_suite_item, test_list,& + & this_rank + $:FORTUNO_MPI_IMPORTS() + implicit none + +contains + + + $:TEST("broadcast") + integer, parameter :: sourcerank = 0, sourceval = 1, otherval = -1 + integer :: buffer + + character(:), allocatable :: msg + + ! GIVEN source rank contains a different integer value as all other ranks + if (this_rank() == sourcerank) then + buffer = sourceval + else + buffer = otherval + end if + + ! WHEN source rank broadcasts its value + call broadcast(global_comm(), buffer, sourcerank) + + ! Make every third rank fail for demonstration purposes + if (mod(this_rank(), 3) == 2) then + buffer = sourceval + 1 + msg = "Failing on rank " // as_char(this_rank()) // " on purpose" + end if + + ! THEN each rank must contain source rank's value + @:CHECK(is_equal(buffer, sourceval), msg=msg) + + $:END_TEST() + + + ! Returns the test items from this module + function tests() + type(test_list) :: tests + + tests = test_list([& + suite("simple_fypp", test_list([& + $:TEST_ITEMS() + ]))& + ]) + $:STOP_ON_MISSING_TEST_ITEMS() + + end function tests + +end module test_simple_fypp diff --git a/example/mpi-fypp/testapp.f90 b/example/mpi-fypp/testapp.f90 new file mode 100644 index 0000000..d59311f --- /dev/null +++ b/example/mpi-fypp/testapp.f90 @@ -0,0 +1,13 @@ +! This file is part of Fortuno. +! Licensed under the BSD-2-Clause Plus Patent license. +! SPDX-License-Identifier: BSD-2-Clause-Patent + +!> Test app, collecting and executing the tests +program testapp + use fortuno_mpi, only : execute_mpi_cmd_app + use test_simple_fypp, only : tests + implicit none + + call execute_mpi_cmd_app(tests()) + +end program testapp diff --git a/example/mpi/CMakeLists.txt b/example/mpi/CMakeLists.txt new file mode 100644 index 0000000..f5689af --- /dev/null +++ b/example/mpi/CMakeLists.txt @@ -0,0 +1,31 @@ +# This file is part of Fortuno. +# Licensed under the BSD-2-Clause Plus Patent license. +# SPDX-License-Identifier: BSD-2-Clause-Patent + +list(APPEND CMAKE_MESSAGE_CONTEXT Mpi) + +add_library(fortuno_example_mpi_mylib) +set_target_properties( + fortuno_example_mpi_mylib PROPERTIES + OUTPUT_NAME mylib +) +target_sources( + fortuno_example_mpi_mylib PRIVATE + mylib.f90 +) +target_link_libraries(fortuno_example_mpi_mylib PRIVATE MPI::MPI_Fortran) + +add_executable(fortuno_example_mpi_testapp) +set_target_properties( + fortuno_example_mpi_testapp PROPERTIES + OUTPUT_NAME testapp +) +target_sources( + fortuno_example_mpi_testapp PRIVATE + test_simple.f90 + testapp.f90 +) +target_link_libraries( + fortuno_example_mpi_testapp PRIVATE + fortuno_example_mpi_mylib Fortuno::fortuno_mpi MPI::MPI_Fortran +) diff --git a/example/mpi/meson.build b/example/mpi/meson.build new file mode 100644 index 0000000..f11fa61 --- /dev/null +++ b/example/mpi/meson.build @@ -0,0 +1,23 @@ +# This file is part of Fortuno. +# Licensed under the BSD-2-Clause Plus Patent license. +# SPDX-License-Identifier: BSD-2-Clause-Patent + +example_mylib_lib = library( + 'mylib', + sources: ['mylib.f90'], + dependencies: [mpi_fortran_dep], + install: false, +) +example_mylib_dep = declare_dependency( + link_with: example_mylib_lib, +) + +example_testapp_exe = executable( + 'testapp', + sources: [ + 'test_simple.f90', + 'testapp.f90', + ], + dependencies: [example_mylib_dep, fortuno_mpi_dep], + install: false, +) diff --git a/example/mpi/mylib.f90 b/example/mpi/mylib.f90 new file mode 100644 index 0000000..b7e5a77 --- /dev/null +++ b/example/mpi/mylib.f90 @@ -0,0 +1,59 @@ +! This file is part of Fortuno. +! Licensed under the BSD-2-Clause Plus Patent license. +! SPDX-License-Identifier: BSD-2-Clause-Patent + +!> Demo module/library to be tested +module mylib + use mpi_f08, only : MPI_Allreduce, MPI_Bcast, MPI_Comm, MPI_COMM_WORLD, MPI_IN_PLACE,& + & MPI_INTEGER, MPI_SUM + implicit none + + private + public :: allreduce_sum, broadcast + +contains + + + !> Broadcasts a scalar integer + subroutine broadcast(comm, buffer, source) + + !> MPI communicator to use + type(MPI_Comm), intent(in) :: comm + + !> Item to broadcast + integer, intent(inout) :: buffer + + !> Source rank (default = 0) + integer, optional, intent(in) :: source + + integer :: source_ + integer :: ierror + + if (present(source)) then + source_ = source + else + source_ = 0 + end if + call MPI_Bcast(buffer, 1, MPI_INTEGER, source_, comm, ierror) + if (ierror /= 0) error stop "MPI_Bcast failed in mylib/broadcast" + + end subroutine broadcast + + + !> Reduces a scalar integer by summation on all ranks + subroutine allreduce_sum(comm, val) + + !> MPI communicator + type(MPI_Comm), intent(in) :: comm + + !> Value to reduce by summation + integer, intent(inout) :: val + + integer :: ierror + + call MPI_Allreduce(MPI_IN_PLACE, val, 1, MPI_INTEGER, MPI_SUM, comm, ierror) + if (ierror /= 0) error stop "MPI_Allreduce failed in mylib/allreduce_sum" + + end subroutine allreduce_sum + +end module mylib diff --git a/example/mpi/test_simple.f90 b/example/mpi/test_simple.f90 new file mode 100644 index 0000000..d44d728 --- /dev/null +++ b/example/mpi/test_simple.f90 @@ -0,0 +1,53 @@ +! This file is part of Fortuno. +! Licensed under the BSD-2-Clause Plus Patent license. +! SPDX-License-Identifier: BSD-2-Clause-Patent + +module test_simple + use mylib, only : broadcast + use fortuno_mpi, only : as_char, global_comm, is_equal, test => mpi_case_item,& + & check => mpi_check, test_list, this_rank + implicit none + +contains + + + !> Returns the tests from this module + function tests() + type(test_list) :: tests + + tests = test_list([& + test("broadcast", test_broadcast)& + ]) + + end function tests + + + !> Simple test with collective communication + subroutine test_broadcast() + integer, parameter :: sourcerank = 0, sourceval = 1, otherval = -1 + integer :: buffer + + character(:), allocatable :: msg + + ! GIVEN source rank contains a different integer value as all other ranks + if (this_rank() == sourcerank) then + buffer = sourceval + else + buffer = otherval + end if + + ! WHEN source rank broadcasts its value + call broadcast(global_comm(), buffer, sourcerank) + + ! Make every third rank fail for demonstration purposes + if (mod(this_rank(), 3) == 2) then + buffer = sourceval + 1 + msg = "Failing on rank " // as_char(this_rank()) // " on purpose" + end if + + ! THEN each rank must contain source rank's value + call check(is_equal(buffer, sourceval), msg=msg) + + end subroutine test_broadcast + +end module test_simple diff --git a/example/mpi/testapp.f90 b/example/mpi/testapp.f90 new file mode 100644 index 0000000..4b2186d --- /dev/null +++ b/example/mpi/testapp.f90 @@ -0,0 +1,13 @@ +! This file is part of Fortuno. +! Licensed under the BSD-2-Clause Plus Patent license. +! SPDX-License-Identifier: BSD-2-Clause-Patent + +!> Test app, collecting and executing the tests +program testapp + use fortuno_mpi, only : execute_mpi_cmd_app + use test_simple, only : simple_tests => tests + implicit none + + call execute_mpi_cmd_app(simple_tests()) + +end program testapp diff --git a/example/fypp/CMakeLists.txt b/example/serial-fypp/CMakeLists.txt similarity index 58% rename from example/fypp/CMakeLists.txt rename to example/serial-fypp/CMakeLists.txt index 7897bd3..2944930 100644 --- a/example/fypp/CMakeLists.txt +++ b/example/serial-fypp/CMakeLists.txt @@ -2,22 +2,22 @@ # Licensed under the BSD-2-Clause Plus Patent license. # SPDX-License-Identifier: BSD-2-Clause-Patent -list(APPEND CMAKE_MESSAGE_CONTEXT Fypp) +list(APPEND CMAKE_MESSAGE_CONTEXT SerialFypp) -add_library(fortuno_example_fypp_mylib) +add_library(fortuno_example_serial_fypp_mylib) set_target_properties( - fortuno_example_fypp_mylib PROPERTIES + fortuno_example_serial_fypp_mylib PROPERTIES OUTPUT_NAME mylib ) target_sources( - fortuno_example_fypp_mylib PRIVATE + fortuno_example_serial_fypp_mylib PRIVATE mylib.f90 ) -add_executable(fortuno_example_fypp_testapp) +add_executable(fortuno_example_serial_fypp_testapp) set_target_properties( - fortuno_example_fypp_testapp PROPERTIES - OUTPUT_NAME testapp_fypp + fortuno_example_serial_fypp_testapp PROPERTIES + OUTPUT_NAME testapp ) set( @@ -35,12 +35,12 @@ fortuno_preprocess( ) target_sources( - fortuno_example_fypp_testapp PRIVATE + fortuno_example_serial_fypp_testapp PRIVATE ${fypp-f90-sources} - testapp_fypp.f90 + testapp.f90 ) target_link_libraries( - fortuno_example_fypp_testapp + fortuno_example_serial_fypp_testapp PRIVATE - fortuno_example_fypp_mylib Fortuno::fortuno_serial + fortuno_example_serial_fypp_mylib Fortuno::fortuno_serial ) diff --git a/example/fypp/mylib.f90 b/example/serial-fypp/mylib.f90 similarity index 100% rename from example/fypp/mylib.f90 rename to example/serial-fypp/mylib.f90 diff --git a/example/fypp/test_fixtured_fypp.fypp b/example/serial-fypp/test_fixtured_fypp.fypp similarity index 100% rename from example/fypp/test_fixtured_fypp.fypp rename to example/serial-fypp/test_fixtured_fypp.fypp diff --git a/example/fypp/test_parametrized_fypp.fypp b/example/serial-fypp/test_parametrized_fypp.fypp similarity index 100% rename from example/fypp/test_parametrized_fypp.fypp rename to example/serial-fypp/test_parametrized_fypp.fypp diff --git a/example/fypp/test_simple_fypp.fypp b/example/serial-fypp/test_simple_fypp.fypp similarity index 100% rename from example/fypp/test_simple_fypp.fypp rename to example/serial-fypp/test_simple_fypp.fypp diff --git a/example/fypp/testapp_fypp.f90 b/example/serial-fypp/testapp.f90 similarity index 100% rename from example/fypp/testapp_fypp.f90 rename to example/serial-fypp/testapp.f90 diff --git a/example/serial/CMakeLists.txt b/example/serial/CMakeLists.txt new file mode 100644 index 0000000..fa92075 --- /dev/null +++ b/example/serial/CMakeLists.txt @@ -0,0 +1,33 @@ +# This file is part of Fortuno. +# Licensed under the BSD-2-Clause Plus Patent license. +# SPDX-License-Identifier: BSD-2-Clause-Patent + +list(APPEND CMAKE_MESSAGE_CONTEXT Serial) + +add_library(fortuno_example_serial_mylib) +set_target_properties( + fortuno_example_serial_mylib PROPERTIES + OUTPUT_NAME mylib +) +target_sources( + fortuno_example_serial_mylib PRIVATE + mylib.f90 +) + +add_executable(fortuno_example_serial_testapp) +set_target_properties( + fortuno_example_serial_testapp PROPERTIES + OUTPUT_NAME testapp +) +target_sources( + fortuno_example_serial_testapp PRIVATE + testapp.f90 + test_fixtured.f90 + test_fixtured_module.f90 + test_fixtured_suite.f90 + test_parametrized.f90 + test_simple.f90 +) +target_link_libraries( + fortuno_example_serial_testapp PRIVATE fortuno_example_serial_mylib Fortuno::fortuno_serial +) diff --git a/example/serial/meson.build b/example/serial/meson.build new file mode 100644 index 0000000..e5fc200 --- /dev/null +++ b/example/serial/meson.build @@ -0,0 +1,26 @@ +# This file is part of Fortuno. +# Licensed under the BSD-2-Clause Plus Patent license. +# SPDX-License-Identifier: BSD-2-Clause-Patent + +example_mylib_lib = library( + 'mylib', + sources: ['mylib.f90'], + install: false +) +example_mylib_dep = declare_dependency( + link_with: example_mylib_lib, +) + +example_testapp_exe = executable( + 'testapp', + sources: [ + 'test_fixtured_module.f90', + 'test_fixtured_suite.f90', + 'test_fixtured.f90', + 'test_parametrized.f90', + 'test_simple.f90', + 'testapp.f90', + ], + dependencies: [example_mylib_dep, fortuno_serial_dep], + install: false, +) diff --git a/example/mylib.f90 b/example/serial/mylib.f90 similarity index 100% rename from example/mylib.f90 rename to example/serial/mylib.f90 diff --git a/example/test_fixtured.f90 b/example/serial/test_fixtured.f90 similarity index 100% rename from example/test_fixtured.f90 rename to example/serial/test_fixtured.f90 diff --git a/example/test_fixtured_module.f90 b/example/serial/test_fixtured_module.f90 similarity index 100% rename from example/test_fixtured_module.f90 rename to example/serial/test_fixtured_module.f90 diff --git a/example/test_fixtured_suite.f90 b/example/serial/test_fixtured_suite.f90 similarity index 100% rename from example/test_fixtured_suite.f90 rename to example/serial/test_fixtured_suite.f90 diff --git a/example/test_parametrized.f90 b/example/serial/test_parametrized.f90 similarity index 100% rename from example/test_parametrized.f90 rename to example/serial/test_parametrized.f90 diff --git a/example/test_simple.f90 b/example/serial/test_simple.f90 similarity index 100% rename from example/test_simple.f90 rename to example/serial/test_simple.f90 diff --git a/example/testapp.f90 b/example/serial/testapp.f90 similarity index 100% rename from example/testapp.f90 rename to example/serial/testapp.f90 diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt index a320a9d..d6c207e 100644 --- a/include/CMakeLists.txt +++ b/include/CMakeLists.txt @@ -13,13 +13,22 @@ target_include_directories( add_library(Fortuno::fortuno_include_dir ALIAS fortuno_include_dir) if (FORTUNO_INSTALL) - install( - FILES fortuno_serial.fypp - DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" - COMPONENT Fortuno_development - ) install( TARGETS fortuno_include_dir EXPORT FortunoTargets ) + if (FORTUNO_BUILD_SERIAL_INTERFACE) + install( + FILES fortuno_serial.fypp + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" + COMPONENT Fortuno_development + ) + endif () + if (FORTUNO_BUILD_MPI_INTERFACE) + install( + FILES fortuno_mpi.fypp + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" + COMPONENT Fortuno_development + ) + endif () endif () \ No newline at end of file diff --git a/include/fortuno_mpi.fypp b/include/fortuno_mpi.fypp new file mode 100644 index 0000000..1aa8a68 --- /dev/null +++ b/include/fortuno_mpi.fypp @@ -0,0 +1,107 @@ +#! This file is part of Fortuno. +#! Licensed under the BSD-2-Clause Plus Patent license. +#! SPDX-License-Identifier: BSD-2-Clause-Patent + +#:mute +#:if not defined('_FORTUNO_MPI_FYPP_') +#:set _FORTUNO_MPI_FYPP_ + +#! Internal variable storing registered test items +#:set _fortuno_mpi_registered_tests = {} +#:set _fortuno_mpi_constructed_tests = set() + + +#! Imports the prerequisites needed by the macro definitions in this file +#:def FORTUNO_MPI_IMPORTS() + use fortuno_mpi, only : mpi_case_item, mpi_check, mpi_failed +#:enddef + + +#! Starts a test procedure +#! +#! Args: +#! name: Test name +#! label: Label of the test +#! args: Arguments of the test procedure as a list of strings +#! +#:def TEST(name, label="", args=None) + #:set proc = "test" + ("_" + label if label else "") + "_" + name + #:set argstr = ", ".join(args) if args is not None else "" + $:_fortuno_mpi_registered_tests.setdefault(label, []).append({"name": name, "proc": proc}) + subroutine ${proc}$(${argstr}$) +#:enddef + + +#! Ends a test procedure +#:def END_TEST() + end subroutine +#:enddef + + +#! Gives a list of tests associated with a given label. +#! +#! Args: +#! label: Label of the tests items to generate (default: "") +#! suffix: Suffix to add after the last test (e.g. comma, if further tests will follow) +#! constructor: Name of the constructor to use (default: "test") +#! +#:def TEST_ITEMS(label="", suffix="", constructor="mpi_case_item ") + #:set items = _fortuno_mpi_registered_tests.get(label) + #:if items is None + #:stop "Undefined label ('{}') used in TEST_ITEMS()".format(label) + #:endif + $:_fortuno_mpi_constructed_tests.update(set([(label, item['name']) for item in items])) + #:set calls = [f"{constructor}('{item['name']}', {item['proc']})" for item in items] + #:set lines = " " + ",&\n ".join(calls) + suffix + "&\n" + $:lines +#:enddef + + +#! Calls the check routine and passes file and line nr. information +#! +#! Args: +#! cond: Condition to check +#! **kwargs: any keyword arguments, which should be passed to mpi_check() +#! +#:def CHECK(cond, **kwargs) +#:if kwargs + #:set kwargstr = ", ".join([f"{name}={value}" for name, value in kwargs.items()]) + call mpi_check(${cond}$, file="${_FILE_}$", line=${_LINE_}$, ${kwargstr}$) +#:else + call mpi_check(${cond}$, file="${_FILE_}$", line=${_LINE_}$) +#:endif +#:enddef + + +#! Calls the check() routine, passes file and line nr. information and calls return on failure. +#! +#! Args: +#! cond: Condition to check +#! **kwargs: any keyword arguments, which should be passed to check() +#! +#:def ASSERT(cond, **kwargs) +$:CHECK(cond, **kwargs) +if (mpi_failed()) return +#:enddef + + +#! Stops if not all registered test items had been constructed via TEST_ITEMS() calls +#! +#:def STOP_ON_MISSING_TEST_ITEMS() +#:set _all_items = set() +#:for label, items in _fortuno_mpi_registered_tests.items() + #:for item in items + $:_all_items.add((label, item['name'])) + #:endfor +#:endfor +#:set _uninst = set(_all_items) - _fortuno_mpi_constructed_tests +#:if _uninst + #:set _uninst_list = [(label + "/" + name if label else name) for label, name in _uninst] + #:set _uninst_str = ", ".join(_uninst_list) + #:stop "Some tests have no corresponding test_item constructors (" + _uninst_str + ")" +#:endif +#:enddef + + +#:endif +#:endmute diff --git a/include/fortuno_serial.fpp b/include/fortuno_serial.fpp new file mode 100644 index 0000000..8da5787 --- /dev/null +++ b/include/fortuno_serial.fpp @@ -0,0 +1,34 @@ +! This file is part of Fortuno. +! Licensed under the BSD-2-Clause Plus Patent license. +! SPDX-License-Identifier: BSD-2-Clause-Patent + +#ifndef __FORTUNO_SERIAL_FPP__ +#define __FORTUNO_SERIAL_FPP__ + +#define CHECK(cond)\ + block;\ + use fortuno_serial, only : serial_check;\ + call serial_check(cond, file=__FILE__, line=__LINE__);\ + end block; + +#define CHECK_MSG(cond, msg)\ + block;\ + use fortuno_serial, only : serial_check;\ + call serial_check(cond, msg, file=__FILE__, line=__LINE__);\ + end block; + +#define ASSERT(cond)\ + block;\ + use fortuno_serial, only : serial_check, serial_check_failed;\ + call serial_check(cond, file=__FILE__, line=__LINE__);\ + if (serial_check_failed()) return;\ + end block; + +#define ASSERT_MSG(cond, msg)\ + block;\ + use fortuno_serial, only : serial_check, serial_check_failed;\ + call serial_check(cond, msg, file=__FILE__, line=__LINE__);\ + if (serial_check_failed()) return;\ + end block; + +#endif diff --git a/meson.build b/meson.build index 9e9eca3..535c7e9 100644 --- a/meson.build +++ b/meson.build @@ -9,10 +9,12 @@ project( ) build_serial_interface = get_option('build_serial_interface') +build_mpi_interface = get_option('build_mpi_interface') build_examples = get_option('build_examples') fortuno_sources = [] fortuno_serial_sources = [] +fortuno_mpi_sources = [] subdir('src') thread_safe_compile_flags = get_option('thread_safe_compile_flags') @@ -48,6 +50,27 @@ if build_serial_interface endif -if build_serial_interface and build_examples +if build_mpi_interface + + fortuno_mpi_deps = [fortuno_dep] + + mpi_fortran_dep = dependency('mpi', language : 'fortran', required: true) + fortuno_mpi_deps += mpi_fortran_dep + + fortuno_mpi_lib = library( + 'fortuno_mpi', + version: meson.project_version(), + sources: fortuno_mpi_sources, + dependencies: fortuno_mpi_deps, + ) + + fortuno_mpi_dep = declare_dependency( + link_with: fortuno_mpi_lib, + dependencies: fortuno_mpi_deps, + ) + +endif + +if build_examples subdir('example') endif diff --git a/meson.options b/meson.options index a34812c..0e88a8b 100644 --- a/meson.options +++ b/meson.options @@ -4,6 +4,8 @@ option('build_serial_interface', type: 'boolean', value: true, description: 'Build serial interface') +option('build_mpi_interface', type: 'boolean', value: false, description: 'Build MPI interface') + option('build_examples', type: 'boolean', value: false, description: 'Build examples') option( diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 387dc8b..e0e2e9e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -57,6 +57,35 @@ if (FORTUNO_BUILD_SERIAL_INTERFACE) endif () + +if (FORTUNO_BUILD_MPI_INTERFACE) + + add_library(fortuno_mpi) + set_target_properties( + fortuno_mpi PROPERTIES + VERSION ${PROJECT_VERSION} + # TODO: change to ${PROJECT_VERSION_MAJOR} once project version reaches 1.0 + SOVERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR} + EXPORT_NAME fortuno_mpi + OUTPUT_NAME fortuno-mpi + Fortran_MODULE_DIRECTORY "${moduledir}" + ) + target_link_libraries(fortuno_mpi PRIVATE MPI::MPI_Fortran) + target_link_libraries(fortuno_mpi PUBLIC Fortuno::fortuno) + target_include_directories( + fortuno_mpi PUBLIC + $ + $ + ) + target_sources(fortuno_mpi PRIVATE fortuno_mpi.f90) + add_subdirectory(fortuno_mpi) + + add_library(Fortuno::fortuno_mpi ALIAS fortuno_mpi) + list(APPEND _installed_targets "fortuno_mpi") + +endif () + + if (FORTUNO_INSTALL) install( TARGETS ${_installed_targets} diff --git a/src/fortuno_mpi.f90 b/src/fortuno_mpi.f90 new file mode 100644 index 0000000..2a4fb78 --- /dev/null +++ b/src/fortuno_mpi.f90 @@ -0,0 +1,15 @@ +! This file is part of Fortuno. +! Licensed under the BSD-2-Clause Plus Patent license. +! SPDX-License-Identifier: BSD-2-Clause-Patent + +!> Exports the MPI-dependent part of Fortuno +module fortuno_mpi + use fortuno + use fortuno_mpi_mpicmdapp, only : mpi_cmd_app, execute_mpi_cmd_app + use fortuno_mpi_mpicontext, only : mpi_context + use fortuno_mpi_mpiglobalctx, only : global_comm, global_comm_id, mpi_check, mpi_check_failed,& + & mpi_failed, mpi_skip, mpi_scope_pointers, num_ranks, this_rank + use fortuno_mpi_mpicase, only : mpi_case, mpi_case_item + use fortuno_mpi_mpisuite, only : mpi_suite, mpi_suite_item + implicit none +end module diff --git a/src/fortuno_mpi/CMakeLists.txt b/src/fortuno_mpi/CMakeLists.txt new file mode 100644 index 0000000..3eba2c9 --- /dev/null +++ b/src/fortuno_mpi/CMakeLists.txt @@ -0,0 +1,18 @@ +# This file is part of Fortuno. +# Licensed under the BSD-2-Clause Plus Patent license. +# SPDX-License-Identifier: BSD-2-Clause-Patent + +target_sources( + fortuno_mpi + PRIVATE + mpibasetypes.f90 + mpicmdapp.f90 + mpiconlogger.f90 + mpicontext.f90 + mpidriver.f90 + mpienv.f90 + mpiglobalctx.f90 + mpicase.f90 + mpitestinfo.f90 + mpisuite.f90 +) diff --git a/src/fortuno_mpi/meson.build b/src/fortuno_mpi/meson.build new file mode 100644 index 0000000..96e1fd8 --- /dev/null +++ b/src/fortuno_mpi/meson.build @@ -0,0 +1,16 @@ +# This file is part of Fortuno. +# Licensed under the BSD-2-Clause Plus Patent license. +# SPDX-License-Identifier: BSD-2-Clause-Patent + +fortuno_mpi_sources += files( + 'mpibasetypes.f90', + 'mpicmdapp.f90', + 'mpiconlogger.f90', + 'mpicontext.f90', + 'mpidriver.f90', + 'mpienv.f90', + 'mpiglobalctx.f90', + 'mpicase.f90', + 'mpitestinfo.f90', + 'mpisuite.f90', +) diff --git a/src/fortuno_mpi/mpibasetypes.f90 b/src/fortuno_mpi/mpibasetypes.f90 new file mode 100644 index 0000000..412928c --- /dev/null +++ b/src/fortuno_mpi/mpibasetypes.f90 @@ -0,0 +1,99 @@ +! This file is part of Fortuno. +! Licensed under the BSD-2-Clause Plus Patent license. +! SPDX-License-Identifier: BSD-2-Clause-Patent + +!> Contains base type for mpi test suites +module fortuno_mpi_mpibasetypes + use fortuno, only : test_case_base, test_suite_base + implicit none + + private + public :: as_mpi_case_base, mpi_case_base, mpi_case_base_run + public :: as_mpi_suite_base, mpi_suite_base + + + !> Base class for all mpi test cases + type, extends(test_case_base), abstract :: mpi_case_base + contains + !> Procedure to be invoked by the driver to run the test + procedure(mpi_case_base_run), deferred :: run + end type mpi_case_base + + + abstract interface + + subroutine mpi_case_base_run(this) + import mpi_case_base + implicit none + class(mpi_case_base), intent(in) :: this + end subroutine mpi_case_base_run + + end interface + + + !> Base type for all mpi test suites + type, extends(test_suite_base), abstract :: mpi_suite_base + contains + procedure :: set_up => mpi_suite_base_set_up + procedure :: tear_down => mpi_suite_base_tear_down + end type mpi_suite_base + +contains + + + !> Returns an mpi_context class pointer to a generic context pointer + function as_mpi_case_base(trg) result(ptr) + + !> Target to point at + class(test_case_base), pointer, intent(in) :: trg + + !> Class specific pointer + class(mpi_case_base), pointer :: ptr + + select type (trg) + class is (mpi_case_base) + ptr => trg + class default + error stop "Invalid test context type obtained in as_mpi_case_base" + end select + + end function as_mpi_case_base + + + !> Returns an mpi_context class pointer to a generic context pointer + function as_mpi_suite_base(trg) result(ptr) + + !> Target to point at + class(test_suite_base), pointer, intent(in) :: trg + + !> Class specific pointer + class(mpi_suite_base), pointer :: ptr + + select type (trg) + class is (mpi_suite_base) + ptr => trg + class default + error stop "Invalid test context type obtained in as_mpi_suite_base" + end select + + end function as_mpi_suite_base + + + !> Sets up the test suite, before the first test in the suite is run + subroutine mpi_suite_base_set_up(this) + + !> Instance + class(mpi_suite_base), intent(inout) :: this + + end subroutine mpi_suite_base_set_up + + + !> Tears down the test suite, after the last test had been run + subroutine mpi_suite_base_tear_down(this) + + !> Instance + class(mpi_suite_base), intent(inout) :: this + + end subroutine mpi_suite_base_tear_down + +end module fortuno_mpi_mpibasetypes diff --git a/src/fortuno_mpi/mpicase.f90 b/src/fortuno_mpi/mpicase.f90 new file mode 100644 index 0000000..e0e32a3 --- /dev/null +++ b/src/fortuno_mpi/mpicase.f90 @@ -0,0 +1,61 @@ +! This file is part of Fortuno. +! Licensed under the BSD-2-Clause Plus Patent license. +! SPDX-License-Identifier: BSD-2-Clause-Patent + +!> Contains the simplest possible parameterless serial test implementation +module fortuno_mpi_mpicase + use fortuno, only : test_item + use fortuno_mpi_mpibasetypes, only : mpi_case_base + implicit none + + private + public :: mpi_case, mpi_case_item + + + !> Serial test case with simple (parameterless) test procedure + type, extends(mpi_case_base) :: mpi_case + + !> Test procedure to call when test is run + procedure(mpi_case_proc), nopass, pointer :: proc => null() + + contains + + !> Runs the test case + procedure, public :: run => mpi_case_run + + end type mpi_case + + + abstract interface + + !> Simple parameterless test procedure + subroutine mpi_case_proc() + end subroutine mpi_case_proc + + end interface + +contains + + + !> Creates a serial test case as a generic test item + function mpi_case_item(name, proc) result(testitem) + character(len=*), intent(in) :: name + procedure(mpi_case_proc) :: proc + type(test_item) :: testitem + + call testitem%init(mpi_case(name=name, proc=proc)) + + end function mpi_case_item + + + !> Runs the test case, by invoking the registered test procedure + subroutine mpi_case_run(this) + + !> Instance + class(mpi_case), intent(in) :: this + + call this%proc() + + end subroutine mpi_case_run + +end module fortuno_mpi_mpicase diff --git a/src/fortuno_mpi/mpicmdapp.f90 b/src/fortuno_mpi/mpicmdapp.f90 new file mode 100644 index 0000000..911bcfa --- /dev/null +++ b/src/fortuno_mpi/mpicmdapp.f90 @@ -0,0 +1,81 @@ +! This file is part of Fortuno. +! Licensed under the BSD-2-Clause Plus Patent license. +! SPDX-License-Identifier: BSD-2-Clause-Patent + +!> Contains the command line app for driving mpi tests +module fortuno_mpi_mpicmdapp + use fortuno, only : cmd_app, test_list + use fortuno_mpi_mpidriver, only : init_mpi_driver, mpi_driver + use fortuno_mpi_mpienv, only : init_mpi_env, final_mpi_env, mpi_env + use fortuno_mpi_mpiconlogger, only : init_mpi_console_logger, mpi_console_logger + implicit none + + private + public :: init_mpi_cmd_app, execute_mpi_cmd_app, mpi_cmd_app + + + !> App for driving mpi tests through command line app + type, extends(cmd_app) :: mpi_cmd_app + end type mpi_cmd_app + +contains + + !> Convenience wrapper setting up and running the mpi command line up + !! + !! Note: This routine stops the code during execution and never returns. + !! + subroutine execute_mpi_cmd_app(tests) + + !> Items to be considered by the app + type(test_list), intent(in) :: tests + + integer :: exitcode + + call run_mpi_cmd_app(tests, exitcode) + stop exitcode, quiet=.true. + + end subroutine execute_mpi_cmd_app + + + !> Sets up and runs the mpi command line up + subroutine run_mpi_cmd_app(tests, exitcode) + + !> Items to be considered by the app + type(test_list), intent(in) :: tests + + !> Exit code + integer, intent(out) :: exitcode + + type(mpi_cmd_app) :: app + type(mpi_env) :: mpienv + + call init_mpi_env(mpienv) + call init_mpi_cmd_app(app, mpienv) + call app%run(tests, exitcode) + call final_mpi_env(mpienv) + + end subroutine run_mpi_cmd_app + + + !> Set up the mpi command line app + subroutine init_mpi_cmd_app(this, mpienv) + + !> Instance + type(mpi_cmd_app), intent(out) :: this + + !> MPI environment + type(mpi_env), intent(in) :: mpienv + + type(mpi_console_logger), allocatable :: logger + type(mpi_driver), allocatable :: driver + + allocate(logger) + call init_mpi_console_logger(logger, mpienv) + call move_alloc(logger, this%logger) + allocate(driver) + call init_mpi_driver(driver, mpienv) + call move_alloc(driver, this%driver) + + end subroutine init_mpi_cmd_app + +end module fortuno_mpi_mpicmdapp diff --git a/src/fortuno_mpi/mpiconlogger.f90 b/src/fortuno_mpi/mpiconlogger.f90 new file mode 100644 index 0000000..fbcc9de --- /dev/null +++ b/src/fortuno_mpi/mpiconlogger.f90 @@ -0,0 +1,120 @@ +! This file is part of Fortuno. +! Licensed under the BSD-2-Clause Plus Patent license. +! SPDX-License-Identifier: BSD-2-Clause-Patent + +!> Contains the implementation of the test logger for the mpi driver +module fortuno_mpi_mpiconlogger + use fortuno, only : as_char, console_logger, failure_info, nl + use fortuno_mpi_mpienv, only : mpi_env + use fortuno_mpi_mpitestinfo, only : mpi_failure_location + implicit none + + private + public :: init_mpi_console_logger, mpi_console_logger + + + !> Implements an mpi logger which logs to a formatted unit + type, extends(console_logger) :: mpi_console_logger + type(mpi_env) :: mpienv + contains + procedure :: is_active => mpi_console_logger_is_active + procedure :: start_drive => mpi_console_logger_start_drive + procedure :: get_failure_info_repr => mpi_console_logger_get_failure_info_repr + end type mpi_console_logger + +contains + + !> Initializes an mpi_console_logger instance + subroutine init_mpi_console_logger(this, mpienv) + + !> Instance + type(mpi_console_logger), intent(out) :: this + + !> MPI environment + type(mpi_env), intent(in) :: mpienv + + this%mpienv = mpienv + + end subroutine init_mpi_console_logger + + + !> Returns whether the logger is active + function mpi_console_logger_is_active(this) result(isactive) + + !> Instance + class(mpi_console_logger), intent(in) :: this + + !> Whether logger is active + logical :: isactive + + isactive = this%mpienv%rank == 0 + + end function mpi_console_logger_is_active + + + !> Starts the logging, called before any other procedures are called + subroutine mpi_console_logger_start_drive(this) + + !> Instance + class(mpi_console_logger), intent(inout) :: this + + if (.not. this%is_active()) return + call this%console_logger%start_drive() + call this%log_message(nl // "Nr. of ranks: " // as_char(this%mpienv%nranks)) + + end subroutine mpi_console_logger_start_drive + + + !> Returns the representation of the failure information (called collectively) + subroutine mpi_console_logger_get_failure_info_repr(this, failureinfo, location, message,& + & details) + + !> Instance + class(mpi_console_logger), intent(in) :: this + + !> Failure info to get the representation from + type(failure_info), allocatable, intent(in) :: failureinfo + + !> Location string (unallocated if not available or not relevant) + character(:), allocatable, intent(out) :: location + + !> Message string (unallocated if not available or not relevant) + character(:), allocatable, intent(out) :: message + + !> Details string (unallocated if not available or not relevant) + character(:), allocatable, intent(out) :: details + + character(:), allocatable :: buffer + integer, allocatable :: failedranks(:) + + select type (loc => failureinfo%location) + class is (mpi_failure_location) + failedranks = loc%failedranks + class default + error stop "invalid failureinfo%location type in mpi_console_logger_get_failure_info_repr" + end select + + if (failedranks(1) == 0) then + if (this%mpienv%rank == 0) then + buffer = failureinfo%location%as_char() + if (len(buffer) > 0) call move_alloc(buffer, location) + if (allocated(failureinfo%message)) message = failureinfo%message + if (allocated(failureinfo%details)) details = failureinfo%details%as_char() + end if + else if (this%mpienv%rank == 0) then + call this%mpienv%recv_alloc_char(location, failedranks(1)) + call this%mpienv%recv_alloc_char(message, failedranks(1)) + call this%mpienv%recv_alloc_char(details, failedranks(1)) + else if (this%mpienv%rank == failedranks(1)) then + buffer = failureinfo%location%as_char() + if (len(buffer) == 0) deallocate(buffer) + call this%mpienv%send_alloc_char(buffer, 0) + call this%mpienv%send_alloc_char(failureinfo%message, 0) + if (allocated(buffer)) deallocate(buffer) + if (allocated(failureinfo%details)) buffer = failureinfo%details%as_char() + call this%mpienv%send_alloc_char(buffer, 0) + end if + + end subroutine mpi_console_logger_get_failure_info_repr + +end module fortuno_mpi_mpiconlogger \ No newline at end of file diff --git a/src/fortuno_mpi/mpicontext.f90 b/src/fortuno_mpi/mpicontext.f90 new file mode 100644 index 0000000..7941826 --- /dev/null +++ b/src/fortuno_mpi/mpicontext.f90 @@ -0,0 +1,151 @@ +! This file is part of Fortuno. +! Licensed under the BSD-2-Clause Plus Patent license. +! SPDX-License-Identifier: BSD-2-Clause-Patent + +!> Contains context extended for the MPI-case +module fortuno_mpi_mpicontext + use fortuno, only : context_factory, failure_location, test_context + use fortuno_mpi_mpienv, only : mpi_env + use fortuno_mpi_mpitestinfo, only : init_mpi_failure_location, mpi_failure_location + implicit none + + private + public :: as_mpi_context, mpi_context + public :: init_mpi_context_factory, mpi_context_factory + + + !> Extends the context with the MPI-specific data + type, extends(test_context) :: mpi_context + private + + !> Main characteristics of the MPI environment + type(mpi_env), public :: mpienv + + ! Mask of failed ranks + integer, allocatable :: failedranks_(:) + + contains + procedure :: check_logical => mpi_context_check_logical + procedure :: create_failure_location => mpi_context_create_failure_location + end type mpi_context + + + !> Factory to create mpi context instances + type, extends(context_factory) :: mpi_context_factory + type(mpi_env) :: mpienv + contains + procedure :: create_context => mpi_context_factory_create_context + end type mpi_context_factory + +contains + + + !> Returns an mpi_context class pointer to a generic context pointer + function as_mpi_context(trg) result(ptr) + + !> Target to point at + class(test_context), pointer, intent(in) :: trg + + !> Class specific pointer + type(mpi_context), pointer :: ptr + + select type (trg) + type is (mpi_context) + ptr => trg + class default + error stop "Invalid test context type obtained in as_mpi_context" + end select + + end function as_mpi_context + + + !> Executes a check (using logical value as check result) + subroutine mpi_context_check_logical(this, cond, msg, file, line) + + !> Instance + class(mpi_context), intent(inout) :: this + + !> Whether check condition is fulfilled (check is successful) + logical, intent(in) :: cond + + !> Check message + character(*), optional, intent(in) :: msg + + !> Source file name + character(*), optional, intent(in) :: file + + !> Line information + integer, optional, intent(in) :: line + + logical :: globalcond(this%mpienv%nranks) + integer :: ii + + globalcond(:) = .true. + ! Rank enumeration starts from zero, array position of a rank is shifted by one + globalcond(this%mpienv%rank + 1) = cond + call this%mpienv%reduce_and(globalcond) + this%failedranks_ = pack([(ii - 1, ii = 1, size(globalcond))], .not. globalcond) + call this%register_check(.not. all(globalcond), msg, file, line) + + end subroutine mpi_context_check_logical + + + !> Registers the location of the failure using the appropriate failure_location type + subroutine mpi_context_create_failure_location(this, failureloc, file, line) + + !> Instance + class(mpi_context), intent(inout) :: this + + !> Allocated and populated failure location on exit + class(failure_location), allocatable, intent(out) :: failureloc + + !> File where failure occured + character(*), optional, intent(in) :: file + + !> Line where failure occured + integer, optional, intent(in) :: line + + type(mpi_failure_location), allocatable :: mpifailureloc + + allocate(mpifailureloc) + call init_mpi_failure_location(mpifailureloc, this%failedranks_, this%nchecks, file=file,& + & line=line) + call move_alloc(mpifailureloc, failureloc) + + end subroutine mpi_context_create_failure_location + + + !> Initializes an mpi_context_factory instance + subroutine init_mpi_context_factory(this, mpienv) + + !> Instance + type(mpi_context_factory), intent(out) :: this + + !> Communicator to use for the context + type(mpi_env), intent(in) :: mpienv + + integer :: ierror + + this%mpienv = mpienv + + end subroutine init_mpi_context_factory + + + !> Creates an mpi context instance + subroutine mpi_context_factory_create_context(this, ctx) + + !> Instance + class(mpi_context_factory), intent(inout) :: this + + !> Created context on exit + class(test_context), allocatable, intent(out) :: ctx + + type(mpi_context), allocatable :: mpictx + + allocate(mpictx) + mpictx%mpienv = this%mpienv + call move_alloc(mpictx, ctx) + + end subroutine mpi_context_factory_create_context + +end module fortuno_mpi_mpicontext diff --git a/src/fortuno_mpi/mpidriver.f90 b/src/fortuno_mpi/mpidriver.f90 new file mode 100644 index 0000000..900c4a6 --- /dev/null +++ b/src/fortuno_mpi/mpidriver.f90 @@ -0,0 +1,126 @@ +! This file is part of Fortuno. +! Licensed under the BSD-2-Clause Plus Patent license. +! SPDX-License-Identifier: BSD-2-Clause-Patent + +!> Implements a driver for mpi tests +module fortuno_mpi_mpidriver + use fortuno, only : init_test_driver, test_case_base, test_context, test_driver, test_suite_base,& + & test_runner + use fortuno_mpi_mpibasetypes, only : as_mpi_case_base, as_mpi_suite_base,& + & mpi_case_base, mpi_suite_base + use fortuno_mpi_mpicontext, only : as_mpi_context, init_mpi_context_factory, mpi_context,& + & mpi_context_factory + use fortuno_mpi_mpienv, only : mpi_env + use fortuno_mpi_mpiglobalctx, only : set_mpi_global_context + implicit none + + private + public :: init_mpi_driver, mpi_driver + + + !> Runner implementation for mpi tests and test suites + type, extends(test_runner) :: mpi_runner + contains + procedure :: set_up_suite => mpi_runner_set_up_suite + procedure :: tear_down_suite => mpi_runner_tear_down_suite + procedure :: run_test => mpi_runner_run_test + end type mpi_runner + + + !> Driver for mpi tests + type, extends(test_driver) :: mpi_driver + end type mpi_driver + +contains + + + !> Initializes an mpi driver instance + subroutine init_mpi_driver(this, mpienv) + + !> Instance + type(mpi_driver), intent(out) :: this + + !> Mpi communicator to be used by the driver + type(mpi_env), intent(in) :: mpienv + + type(mpi_context_factory) :: ctxfactory + type(mpi_runner) :: runner + + call init_mpi_context_factory(ctxfactory, mpienv) + call init_test_driver(this%test_driver, ctxfactory, runner) + + end subroutine init_mpi_driver + + + !> Sets up an mpi test suite + subroutine mpi_runner_set_up_suite(this, testsuite, ctx) + + !> Instance + class(mpi_runner), intent(in) :: this + + !> Test suite to set up + class(test_suite_base), pointer, intent(in) :: testsuite + + !> Context to use + class(test_context), pointer, intent(in) :: ctx + + type(mpi_context), pointer :: pctx, poldctx + class(mpi_suite_base), pointer :: psuite + + pctx => as_mpi_context(ctx) + psuite => as_mpi_suite_base(testsuite) + call set_mpi_global_context(pctx, oldctx=poldctx) + call psuite%set_up() + call set_mpi_global_context(poldctx) + + end subroutine mpi_runner_set_up_suite + + + !> Sets up an mpi test suite + subroutine mpi_runner_tear_down_suite(this, testsuite, ctx) + + !> Instance + class(mpi_runner), intent(in) :: this + + !> Test suite to tear down + class(test_suite_base), pointer, intent(in) :: testsuite + + !> Context to use + class(test_context), pointer, intent(in) :: ctx + + type(mpi_context), pointer :: pctx, poldctx + class(mpi_suite_base), pointer :: psuite + + pctx => as_mpi_context(ctx) + psuite => as_mpi_suite_base(testsuite) + call set_mpi_global_context(pctx, oldctx=poldctx) + call psuite%tear_down() + call set_mpi_global_context(poldctx) + + end subroutine mpi_runner_tear_down_suite + + + !> Runs a test case + subroutine mpi_runner_run_test(this, test, ctx) + + !> Instance + class(mpi_runner), intent(in) :: this + + !> Test suite to tear down + class(test_case_base), pointer, intent(in) :: test + + !> Context to use + class(test_context), pointer, intent(in) :: ctx + + type(mpi_context), pointer :: pctx, poldctx + class(mpi_case_base), pointer :: ptest + + pctx => as_mpi_context(ctx) + ptest => as_mpi_case_base(test) + call set_mpi_global_context(pctx, oldctx=poldctx) + call ptest%run() + call set_mpi_global_context(poldctx) + + end subroutine mpi_runner_run_test + +end module fortuno_mpi_mpidriver \ No newline at end of file diff --git a/src/fortuno_mpi/mpienv.f90 b/src/fortuno_mpi/mpienv.f90 new file mode 100644 index 0000000..36d15d0 --- /dev/null +++ b/src/fortuno_mpi/mpienv.f90 @@ -0,0 +1,147 @@ +! This file is part of Fortuno. +! Licensed under the BSD-2-Clause Plus Patent license. +! SPDX-License-Identifier: BSD-2-Clause-Patent + +!> Contains a type to deal with the mpi environment +module fortuno_mpi_mpienv + use mpi_f08, only : MPI_Allreduce, MPI_CHAR, MPI_Comm, MPI_Comm_rank, MPI_Comm_size,& + & MPI_COMM_WORLD, MPI_IN_PLACE, MPI_Init, MPI_INTEGER, MPI_Finalize, MPI_PROD, MPI_Recv,& + & MPI_Send, MPI_Status + implicit none + + private + public :: init_mpi_env, final_mpi_env, mpi_env + + + !> Contains main charateristics of the MPI environment + type :: mpi_env + + !> MPI communicator + type(MPI_Comm) :: comm + + !> Nr. of ranks in the communicator + integer :: nranks + + !> Rank of the current process + integer :: rank + + contains + procedure :: reduce_and => mpi_env_reduce_and + procedure :: send_alloc_char => mpi_env_send_alloc_char + procedure :: recv_alloc_char => mpi_env_recv_alloc_char + end type mpi_env + +contains + + !> Initializes the MPI environment + subroutine init_mpi_env(this) + + !> Instance + type(mpi_env), intent(out) :: this + + integer :: ierror + + call MPI_Init(ierror) + if (ierror /= 0) error stop "MPI_Init failed in init_mpi_env" + this%comm = MPI_COMM_WORLD + call MPI_Comm_size(this%comm, this%nranks, ierror) + if (ierror /= 0) error stop "MPI_Comm_size failed in init_mpi_env" + call MPI_Comm_rank(this%comm, this%rank, ierror) + if (ierror /= 0) error stop "MPI_Comm_rank failed in init_mpi_env" + + end subroutine init_mpi_env + + + !> Finalizes the MPI environment + subroutine final_mpi_env(this) + + !> Instance + type(mpi_env), intent(out) :: this + + integer :: ierror + + call MPI_Finalize(ierror) + if (ierror /= 0) error stop "MPI_Finalize failed in execute_mpi_cmd_app" + + end subroutine final_mpi_env + + + !> Reduces a logical array using the and operator + subroutine mpi_env_reduce_and(this, array) + + !> Instance + class(mpi_env), intent(in) :: this + + !> Array to reduce with the and operator + logical, intent(inout) :: array(:) + + integer :: tmpint(size(array)) + integer :: ierror + + ! workaround: ifort and intelmpi + ! allreduce() with MPI_LOGICALS and MPI_LAND seems to result in some broken logical + ! representation resulting in incorrect findloc(..., dim=1) results. Therefore, map logicals + ! to integers before reduction and map them back afterwards again. + tmpint(:) = merge(1, 0, array) + call MPI_Allreduce(MPI_IN_PLACE, tmpint, size(tmpint), MPI_INTEGER, MPI_PROD, this%comm, ierror) + if (ierror /= 0) error stop "MPI_Allreduce failed in mpi_env_reduce_and" + array(:) = tmpint == 1 + + end subroutine mpi_env_reduce_and + + + !> Sends an allocatable character to a process + subroutine mpi_env_send_alloc_char(this, buffer, dest) + + !> Instance + class(mpi_env), intent(in) :: this + + !> Buffer to send (can be unallocated) + character(:), allocatable, intent(in) :: buffer + + !> Destination rank + integer, intent(in) :: dest + + integer :: charlen, ierror + + if (allocated(buffer)) then + charlen = len(buffer) + else + charlen = -1 + end if + call MPI_Send(charlen, 1, MPI_INTEGER, dest, 1, this%comm, ierror) + if (ierror /= 0) error stop "MPI_Send(1) failed in mpi_env_send_alloc_char" + if (charlen > 0) then + call MPI_Send(buffer, charlen, MPI_CHAR, dest, 2, this%comm, ierror) + if (ierror /= 0) error stop "MPI_Send(2) failed in mpi_env_send_alloc_char" + end if + + end subroutine mpi_env_send_alloc_char + + + !> Receives an allocatable character from a process + subroutine mpi_env_recv_alloc_char(this, buffer, src) + + !> Instance + class(mpi_env), intent(in) :: this + + !> Received string (might be unallocated if it was unallocated on the sender side) + character(:), allocatable, intent(out) :: buffer + + !> Source rank + integer, intent(in) :: src + + integer :: charlen, ierror + type(MPI_Status) :: mpistat + + call MPI_Recv(charlen, 1, MPI_INTEGER, src, 1, this%comm, mpistat, ierror) + if (ierror /= 0) error stop "MPI_Recv(1) failed in mpi_env_recv_alloc_char" + if (charlen >= 0) allocate(character(charlen) :: buffer) + if (charlen > 0) then + call MPI_Recv(buffer, charlen, MPI_CHAR, src, 2, this%comm, mpistat, ierror) + if (ierror /= 0) error stop "MPI_Recv(2) failed in mpi_env_recv_alloc_char" + end if + + end subroutine mpi_env_recv_alloc_char + +end module fortuno_mpi_mpienv diff --git a/src/fortuno_mpi/mpiglobalctx.f90 b/src/fortuno_mpi/mpiglobalctx.f90 new file mode 100644 index 0000000..d13fef2 --- /dev/null +++ b/src/fortuno_mpi/mpiglobalctx.f90 @@ -0,0 +1,171 @@ +! This file is part of Fortuno. +! Licensed under the BSD-2-Clause Plus Patent license. +! SPDX-License-Identifier: BSD-2-Clause-Patent + +!> Global serial context to avoid explicit passing of context when using non-threaded serial driver +module fortuno_mpi_mpiglobalctx + use mpi_f08, only : mpi_comm + use fortuno, only : check_result, test_ptr_item + use fortuno_mpi_mpicontext, only : mpi_context + implicit none + + private + public :: mpiglobalctx + public :: set_mpi_global_context + public :: mpi_check, mpi_check_failed, mpi_failed, mpi_skip + public :: mpi_scope_pointers + public :: global_comm, global_comm_id + public :: num_ranks, this_rank + + + !> Generic check interface accessing the global context + interface mpi_check + module procedure mpi_check_logical, mpi_check_check_result + end interface mpi_check + + + !> The global context + type(mpi_context), pointer, protected :: mpiglobalctx => null() + +contains + + + !> Sets the global context + subroutine set_mpi_global_context(newctx, oldctx) + + !> New context to use as global context + type(mpi_context), pointer, intent(in) :: newctx + + !> Old context used so far as global context + type(mpi_context), pointer, optional, intent(out) :: oldctx + + if (present(oldctx)) oldctx => mpiglobalctx + mpiglobalctx => newctx + + end subroutine set_mpi_global_context + + + !> Check using global context logical input + subroutine mpi_check_logical(cond, msg, file, line) + + !> Whether check condition is fulfilled (check is successful) + logical, intent(in) :: cond + + !> Check message + character(*), optional, intent(in) :: msg + + !> Source file name + character(*), optional, intent(in) :: file + + !> Line information + integer, optional, intent(in) :: line + + call mpiglobalctx%check(cond, msg=msg, file=file, line=line) + + end subroutine mpi_check_logical + + + !> Check using global context check_result input + subroutine mpi_check_check_result(checkresult, msg, file, line) + + !> Whether check condition is fulfilled (check is successful) + type(check_result), intent(in) :: checkresult + + !> Check message + character(*), optional, intent(in) :: msg + + !> Source file name + character(*), optional, intent(in) :: file + + !> Line information + integer, optional, intent(in) :: line + + call mpiglobalctx%check(checkresult, msg=msg, file=file, line=line) + + end subroutine mpi_check_check_result + + + !> Whether the last check has failed + function mpi_check_failed() result(check_failed) + + !> True, if last check has failed + logical :: check_failed + + check_failed = mpiglobalctx%check_failed() + + end function mpi_check_failed + + + !> Whether the test/context has failed + function mpi_failed() result(failed) + + !> True, if test has failed already (e.g. due to previous failing checks) + logical :: failed + + failed = mpiglobalctx%failed() + + end function mpi_failed + + + !> Mark current test/context as skipped + subroutine mpi_skip() + + call mpiglobalctx%skip() + + end subroutine mpi_skip + + + !> Returns the enclosing suite pointers + function mpi_scope_pointers() result(scopeptrs) + + !> Pointers to enclosing suites + type(test_ptr_item), allocatable :: scopeptrs(:) + + scopeptrs = mpiglobalctx%scope_pointers() + + end function mpi_scope_pointers + + + !> Returns the global communicator + function global_comm() result(comm) + + !> Global communicator + type(mpi_comm) :: comm + + comm = mpiglobalctx%mpienv%comm + + end function global_comm + + + !> Returns the global communicators integer id (to be used in F77/F90 MPI routines) + function global_comm_id() result(id) + + integer :: id + + id = mpiglobalctx%mpienv%comm%mpi_val + + end function global_comm_id + + + !> Returns the rank of the current process + function this_rank() result(rank) + + !> Rank + integer :: rank + + rank = mpiglobalctx%mpienv%rank + + end function this_rank + + + !> Returns the number of all ranks in current global communicator + function num_ranks() result(ranks) + + !> Nr. of ranks + integer :: ranks + + ranks = mpiglobalctx%mpienv%nranks + + end function num_ranks + +end module fortuno_mpi_mpiglobalctx diff --git a/src/fortuno_mpi/mpisuite.f90 b/src/fortuno_mpi/mpisuite.f90 new file mode 100644 index 0000000..6c8cd1e --- /dev/null +++ b/src/fortuno_mpi/mpisuite.f90 @@ -0,0 +1,31 @@ +! This file is part of Fortuno. +! Licensed under the BSD-2-Clause Plus Patent license. +! SPDX-License-Identifier: BSD-2-Clause-Patent + +!> Contains a trivial implementation for a serial suite +module fortuno_mpi_mpisuite + use fortuno, only : test_item, test_list + use fortuno_mpi_mpibasetypes, only : mpi_suite_base + implicit none + + private + public :: mpi_suite, mpi_suite_item + + + !> Base type for all serial suites + type, extends(mpi_suite_base) :: mpi_suite + end type mpi_suite + +contains + + !> Returns a serial suite instance wrapped as test_item + function mpi_suite_item(name, tests) result(testitem) + character(*), intent(in) :: name + type(test_list), intent(in) :: tests + type(test_item) :: testitem + + call testitem%init(mpi_suite(name=name, tests=tests)) + + end function mpi_suite_item + +end module fortuno_mpi_mpisuite diff --git a/src/fortuno_mpi/mpitestinfo.f90 b/src/fortuno_mpi/mpitestinfo.f90 new file mode 100644 index 0000000..52d9227 --- /dev/null +++ b/src/fortuno_mpi/mpitestinfo.f90 @@ -0,0 +1,75 @@ +! This file is part of Fortuno. +! Licensed under the BSD-2-Clause Plus Patent license. +! SPDX-License-Identifier: BSD-2-Clause-Patent + +!> Contains the MPI-extensions of test info structures +module fortuno_mpi_mpitestinfo + use fortuno, only : as_char, init_failure_location, failure_location, nl + implicit none + + private + public :: init_mpi_failure_location, mpi_failure_location + + + !> Adds info about failed nodes to failure location + type, extends(failure_location) :: mpi_failure_location + + !> Mask for failed nodes + integer, allocatable :: failedranks(:) + + contains + procedure :: as_char => mpi_failure_location_as_char + end type mpi_failure_location + +contains + + + !> Initializes an mpi_failure_location instance + subroutine init_mpi_failure_location(this, failedranks, checknr, file, line) + + !> Instance + type(mpi_failure_location), intent(out) :: this + + !> List of failed ranks + integer, intent(in) :: failedranks(:) + + !> Nr. of checks made so far + integer, intent(in) :: checknr + + !> File where failure occured (if available) + character(*), optional, intent(in) :: file + + !> Line where failure occured (if available) + integer, optional, intent(in) :: line + + call init_failure_location(this%failure_location, checknr, file, line) + this%failedranks = failedranks + + end subroutine init_mpi_failure_location + + + !> Character representation of the failure location + function mpi_failure_location_as_char(this) result(repr) + + !> Instance + class(mpi_failure_location), intent(in) :: this + + !> Character representation + character(:), allocatable :: repr + + integer :: firstfailed, totalfailed + + repr = this%failure_location%as_char() + if (.not. allocated(this%failedranks)) return + firstfailed = this%failedranks(1) + totalfailed = size(this%failedranks) + if (totalfailed > 1) then + repr = "Rank: " // as_char(firstfailed) // " (+ " // as_char(totalfailed - 1) // " more)"& + & // nl // repr + else + repr = "Rank: " // as_char(firstfailed) // nl // repr + end if + + end function mpi_failure_location_as_char + +end module fortuno_mpi_mpitestinfo diff --git a/src/meson.build b/src/meson.build index 22438ce..bc4cc19 100644 --- a/src/meson.build +++ b/src/meson.build @@ -11,3 +11,8 @@ fortuno_serial_sources += files( 'fortuno_serial.f90' ) subdir('fortuno_serial') + +fortuno_mpi_sources += files( + 'fortuno_mpi.f90' +) +subdir('fortuno_mpi') \ No newline at end of file diff --git a/test/export/mpi/CMakeLists.txt b/test/export/mpi/CMakeLists.txt new file mode 100644 index 0000000..c855de0 --- /dev/null +++ b/test/export/mpi/CMakeLists.txt @@ -0,0 +1,30 @@ +# This file is part of Fortuno. +# Licensed under the BSD-2-Clause Plus Patent license. +# SPDX-License-Identifier: BSD-2-Clause-Patent + +cmake_minimum_required(VERSION 3.22...3.28) + +project( + FortunoMpi_Test_Export + VERSION 0.0.0 + DESCRIPTION "Testing the CMake build info exported by Fortuno" + LANGUAGES Fortran +) + +find_package(MPI REQUIRED) +find_package(Fortuno REQUIRED) +if (NOT TARGET Fortuno::fortuno_mpi) + message(FATAL_ERROR "Fortuno library was built without the MPI interface") +endif () + +find_program(FYPP fypp REQUIRED) +if (FYPP) + get_target_property( + _fortuno_incdir + Fortuno::fortuno_include_dir + INTERFACE_INCLUDE_DIRECTORIES + ) + set(FYPP_INCOPTS "-I${_fortuno_incdir}") +endif () + +add_subdirectory(app) diff --git a/test/export/mpi/app/CMakeLists.txt b/test/export/mpi/app/CMakeLists.txt new file mode 100644 index 0000000..faedf8d --- /dev/null +++ b/test/export/mpi/app/CMakeLists.txt @@ -0,0 +1,21 @@ +# This file is part of Fortuno. +# Licensed under the BSD-2-Clause Plus Patent license. +# SPDX-License-Identifier: BSD-2-Clause-Patent + +add_executable(testapp) +target_sources(testapp PRIVATE testapp.f90) +target_link_libraries(testapp PRIVATE Fortuno::fortuno_mpi MPI::MPI_Fortran) + +if (FYPP) + add_executable(testapp_fypp) + + set(_oldfile "testapp_fypp.fypp") + string(REGEX REPLACE ".fypp$" ".f90" _newfile "${_oldfile}") + add_custom_command( + OUTPUT ${_newfile} + COMMAND ${FYPP} ${FYPP_INCOPTS} ${CMAKE_CURRENT_SOURCE_DIR}/${_oldfile} ${_newfile} + MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/${_oldfile} + ) + target_sources(testapp_fypp PRIVATE ${_newfile}) + target_link_libraries(testapp_fypp PRIVATE Fortuno::fortuno_mpi MPI::MPI_Fortran) +endif () diff --git a/test/export/mpi/app/meson.build b/test/export/mpi/app/meson.build new file mode 100644 index 0000000..ac041e5 --- /dev/null +++ b/test/export/mpi/app/meson.build @@ -0,0 +1,7 @@ +# This file is part of Fortuno. +# Licensed under the BSD-2-Clause Plus Patent license. +# SPDX-License-Identifier: BSD-2-Clause-Patent + +testapp_srcs += files( + 'testapp.f90' +) diff --git a/test/export/mpi/app/testapp.f90 b/test/export/mpi/app/testapp.f90 new file mode 100644 index 0000000..1f3b7eb --- /dev/null +++ b/test/export/mpi/app/testapp.f90 @@ -0,0 +1,46 @@ +! This file is part of Fortuno. +! Licensed under the BSD-2-Clause Plus Patent license. +! SPDX-License-Identifier: BSD-2-Clause-Patent + +!> Unit tests +module testapp_mpi_tests + use mpi_f08, only : mpi_bcast, MPI_INTEGER + use fortuno_mpi, only : global_comm, is_equal, test => mpi_case_item, check => mpi_check,& + & test_list, this_rank + implicit none + +contains + + subroutine test_success() + + integer :: buffer + + buffer = 0 + if (this_rank() == 0) buffer = 1 + call mpi_bcast(buffer, 1, MPI_INTEGER, 0, global_comm()) + call check(is_equal(buffer, 1)) + + end subroutine test_success + + + function tests() + type(test_list) :: tests + + tests = test_list([& + test("success", test_success)& + ]) + + end function tests + +end module testapp_mpi_tests + + +!> Test app driving Fortuno unit tests +program testapp + use fortuno_mpi, only : execute_mpi_cmd_app + use testapp_mpi_tests, only : tests + implicit none + + call execute_mpi_cmd_app(tests()) + +end program testapp diff --git a/test/export/mpi/app/testapp_fypp.fypp b/test/export/mpi/app/testapp_fypp.fypp new file mode 100644 index 0000000..7e5819b --- /dev/null +++ b/test/export/mpi/app/testapp_fypp.fypp @@ -0,0 +1,50 @@ +! This file is part of Fortuno. +! Licensed under the BSD-2-Clause Plus Patent license. +! SPDX-License-Identifier: BSD-2-Clause-Patent + +#:include "fortuno_mpi.fypp" + +!> Unit tests +module testapp_mpi_fypp_tests + use mpi_f08, only : mpi_bcast, MPI_INTEGER + use fortuno_mpi, only : global_comm, is_equal, test_list, this_rank + $:FORTUNO_MPI_IMPORTS() + implicit none + +contains + + + $:TEST("success") + + integer :: buffer + + buffer = 0 + if (this_rank() == 0) buffer = 1 + call mpi_bcast(buffer, 1, MPI_INTEGER, 0, global_comm()) + @:CHECK(is_equal(buffer, 1)) + + $:END_TEST() + + + function tests() + type(test_list) :: tests + + tests = test_list([& + $:TEST_ITEMS() + ]) + $:STOP_ON_MISSING_TEST_ITEMS() + + end function tests + +end module testapp_mpi_fypp_tests + + +!> Test app driving Fortuno unit tests +program testapp + use fortuno_mpi, only : execute_mpi_cmd_app + use testapp_mpi_fypp_tests, only : tests + implicit none + + call execute_mpi_cmd_app(tests()) + +end program testapp diff --git a/test/export/mpi/fpm.toml b/test/export/mpi/fpm.toml new file mode 100644 index 0000000..9a822db --- /dev/null +++ b/test/export/mpi/fpm.toml @@ -0,0 +1,28 @@ +name = "fortuno-mpi-test-export" +version = "0.0.0" +license = "BSD-2-Clause-Patent" +author = "Fortuno authors" +maintainer = "aradi@uni-bremen.de" +copyright = "Copyright 2024, Fortuno authors" + +[build] +auto-executables = false +auto-tests = false +auto-examples = false +module-naming = false + +[install] +library = false + +[fortran] +implicit-typing = true +implicit-external = true +source-form = "free" + +[dependencies] +fortuno-mpi = { path = "../../../" } +mpi = "*" + +[[executable]] +name = "testapp" +main = "testapp.f90" \ No newline at end of file diff --git a/test/export/mpi/meson.build b/test/export/mpi/meson.build new file mode 100644 index 0000000..5ff2066 --- /dev/null +++ b/test/export/mpi/meson.build @@ -0,0 +1,26 @@ +# This file is part of Fortuno. +# Licensed under the BSD-2-Clause Plus Patent license. +# SPDX-License-Identifier: BSD-2-Clause-Patent + +project( + 'fortuno_mpi_test_export', + 'fortran', + version: '0.0.0', +) + +testapp_deps = [] + +fortran_mpi_dep = dependency('mpi', language: 'fortran', required: true) +testapp_deps += fortran_mpi_dep + +fortuno_mpi_dep = dependency('fortuno-mpi', fallback: ['fortuno', 'fortuno_mpi_dep']) +testapp_deps += fortuno_mpi_dep + +testapp_srcs = [] +subdir('app') + +testapp_exe = executable( + 'testapp', + sources: testapp_srcs, + dependencies: testapp_deps, +) diff --git a/test/export/CMakeLists.txt b/test/export/serial/CMakeLists.txt similarity index 100% rename from test/export/CMakeLists.txt rename to test/export/serial/CMakeLists.txt diff --git a/test/export/app/CMakeLists.txt b/test/export/serial/app/CMakeLists.txt similarity index 100% rename from test/export/app/CMakeLists.txt rename to test/export/serial/app/CMakeLists.txt diff --git a/test/export/app/meson.build b/test/export/serial/app/meson.build similarity index 100% rename from test/export/app/meson.build rename to test/export/serial/app/meson.build diff --git a/test/export/app/testapp.f90 b/test/export/serial/app/testapp.f90 similarity index 87% rename from test/export/app/testapp.f90 rename to test/export/serial/app/testapp.f90 index 8b2aa0c..c1dcd4a 100644 --- a/test/export/app/testapp.f90 +++ b/test/export/serial/app/testapp.f90 @@ -3,7 +3,7 @@ ! SPDX-License-Identifier: BSD-2-Clause-Patent !> Unit tests -module testapp_tests +module testapp_serial_tests use fortuno_serial, only : is_equal, test => serial_case_item, check => serial_check, test_list implicit none @@ -27,12 +27,12 @@ function tests() end function tests -end module testapp_tests +end module testapp_serial_tests !> Test app driving Fortuno unit tests program testapp - use testapp_tests, only : tests + use testapp_serial_tests, only : tests use fortuno_serial, only : execute_serial_cmd_app implicit none diff --git a/test/export/serial/app/testapp_fpp.F90 b/test/export/serial/app/testapp_fpp.F90 new file mode 100644 index 0000000..4b36ebc --- /dev/null +++ b/test/export/serial/app/testapp_fpp.F90 @@ -0,0 +1,43 @@ +! This file is part of Fortuno. +! Licensed under the BSD-2-Clause Plus Patent license. +! SPDX-License-Identifier: BSD-2-Clause-Patent + +#include "fortuno_serial.fpp" + +!> Unit tests +module testapp_fpp_tests + use fortuno_serial, only : is_equal, test => serial_case_item, test_item + implicit none + + private + public :: test_items + +contains + + + subroutine test_success() + CHECK(is_equal(1, 1)) + end subroutine test_success + + + function test_items() result(testitems) + type(test_item), allocatable :: testitems(:) + + testitems = [& + test("success", test_success)& + ] + + end function test_items + +end module testapp_fpp_tests + + +!> Test app driving Fortuno unit tests +program testapp_fpp + use testapp_fpp_tests, only : test_items + use fortuno_serial, only : execute_serial_cmd_app + implicit none + + call execute_serial_cmd_app(testitems=test_items()) + +end program testapp_fpp diff --git a/test/export/app/testapp_fypp.fypp b/test/export/serial/app/testapp_fypp.fypp similarity index 100% rename from test/export/app/testapp_fypp.fypp rename to test/export/serial/app/testapp_fypp.fypp diff --git a/test/export/fpm.toml b/test/export/serial/fpm.toml similarity index 93% rename from test/export/fpm.toml rename to test/export/serial/fpm.toml index 5836eef..c8fa96a 100644 --- a/test/export/fpm.toml +++ b/test/export/serial/fpm.toml @@ -20,7 +20,7 @@ implicit-external = false source-form = "free" [dependencies] -fortuno = { path = "../../" } +fortuno = { path = "../../../" } [[executable]] name = "testapp" diff --git a/test/export/meson.build b/test/export/serial/meson.build similarity index 100% rename from test/export/meson.build rename to test/export/serial/meson.build