├── .github └── workflows │ └── build.yml ├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── app ├── CMakeLists.txt └── main.f90 ├── config ├── CMakeLists.txt ├── cmake │ ├── Findfortran_stdlib.cmake │ └── Findtest-drive.cmake └── template.cmake ├── src ├── CMakeLists.txt └── example.f90 └── test ├── CMakeLists.txt ├── main.f90 └── test_example.f90 /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | pull_request: 5 | schedule: 6 | - cron: '0 0 * * 6' 7 | 8 | env: 9 | CI: "ON" 10 | HOMEBREW_NO_ANALYTICS: "ON" 11 | HOMEBREW_NO_AUTO_UPDATE: "ON" 12 | HOMEBREW_NO_BOTTLE_SOURCE_FALLBACK: "ON" 13 | HOMEBREW_NO_GITHUB_API: "ON" 14 | HOMEBREW_NO_INSTALL_CLEANUP: "ON" 15 | BUILD_DIR: _build 16 | CMAKE_OPTIONS: >- 17 | -DCMAKE_BUILD_TYPE=Debug 18 | 19 | jobs: 20 | gcc-build: 21 | runs-on: ${{ matrix.os }} 22 | strategy: 23 | fail-fast: false 24 | matrix: 25 | os: [ubuntu-latest, macos-latest] 26 | 27 | steps: 28 | - name: Checkout code 29 | uses: actions/checkout@v2 30 | 31 | - uses: actions/setup-python@v1 32 | with: 33 | python-version: '3.x' 34 | 35 | - name: Set Compiler (Linux) 36 | if: contains(matrix.os, 'ubuntu') 37 | run: | 38 | echo "FC=gfortran" >> $GITHUB_ENV 39 | echo "CC=gcc" >> $GITHUB_ENV 40 | 41 | - name: Set Compiler (OSX) 42 | if: contains(matrix.os, 'macos') 43 | run: | 44 | echo "FC=gfortran-9" >> $GITHUB_ENV 45 | echo "CC=gcc-9" >> $GITHUB_ENV 46 | 47 | - name: Install cmake 48 | run: pip3 install cmake ninja fypp 49 | 50 | - name: Configure build 51 | run: >- 52 | cmake -B ${BUILD_DIR} -G Ninja 53 | -DCMAKE_INSTALL_PREFIX=${PWD}/_install 54 | ${CMAKE_OPTIONS} 55 | 56 | - name: Build project 57 | run: cmake --build ${BUILD_DIR} 58 | 59 | - name: Run regression tests 60 | run: | 61 | pushd ${BUILD_DIR} 62 | ctest -j 2 --output-on-failure 63 | popd 64 | 65 | - name: Install project 66 | run: | 67 | cmake --install ${BUILD_DIR} 68 | 69 | # Test MinGW native compilation 70 | mingw-build: 71 | runs-on: windows-latest 72 | strategy: 73 | fail-fast: false 74 | matrix: 75 | include: [ 76 | { msystem: MINGW64, arch: x86_64 }, 77 | { msystem: MINGW32, arch: i686 } 78 | ] 79 | defaults: 80 | run: 81 | shell: msys2 {0} 82 | steps: 83 | - uses: actions/checkout@v2 84 | 85 | - uses: msys2/setup-msys2@v2 86 | with: 87 | msystem: ${{ matrix.msystem }} 88 | update: false 89 | install: >- 90 | git 91 | mingw-w64-${{ matrix.arch }}-gcc-fortran 92 | mingw-w64-${{ matrix.arch }}-python 93 | mingw-w64-${{ matrix.arch }}-python-pip 94 | mingw-w64-${{ matrix.arch }}-cmake 95 | mingw-w64-${{ matrix.arch }}-ninja 96 | 97 | - name: Install fypp 98 | run: pip install fypp 99 | 100 | - name: Configure build 101 | run: >- 102 | cmake -B ${BUILD_DIR} -G Ninja 103 | -DCMAKE_INSTALL_PREFIX=${PWD}/_install 104 | ${CMAKE_OPTIONS} 105 | env: 106 | FC: gfortran 107 | CC: gcc 108 | 109 | - name: Build project 110 | run: cmake --build ${BUILD_DIR} 111 | 112 | - name: Run regression tests 113 | run: | 114 | pushd ${BUILD_DIR} 115 | ctest -j 2 --output-on-failure 116 | popd 117 | 118 | - name: Install project 119 | run: | 120 | cmake --install ${BUILD_DIR} 121 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | # Directories 35 | /build*/ 36 | /_*/ 37 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # SPDX-Identifier: MIT 2 | cmake_minimum_required(VERSION 3.14) 3 | 4 | project( 5 | "stdlib-example" 6 | LANGUAGES "Fortran" 7 | VERSION "0.1" 8 | ) 9 | 10 | # Follow GNU conventions for installing directories 11 | include(GNUInstallDirs) 12 | 13 | # General configuration information 14 | add_subdirectory("config") 15 | 16 | if(NOT TARGET "fortran_stdlib::fortran_stdlib") 17 | find_package("fortran_stdlib" REQUIRED) 18 | endif() 19 | 20 | # Collect source of the project 21 | add_subdirectory("src") 22 | 23 | # Collect source of the application 24 | add_subdirectory("app") 25 | 26 | # Export targets for other projects 27 | add_library("${PROJECT_NAME}" INTERFACE) 28 | target_link_libraries("${PROJECT_NAME}" INTERFACE "${PROJECT_NAME}-lib") 29 | install( 30 | TARGETS 31 | "${PROJECT_NAME}" 32 | EXPORT 33 | "${PROJECT_NAME}-targets" 34 | LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" 35 | ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" 36 | ) 37 | 38 | # Install exported targets 39 | install( 40 | EXPORT "${PROJECT_NAME}-targets" 41 | NAMESPACE 42 | "${PROJECT_NAME}::" 43 | DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" 44 | ) 45 | 46 | # Package license files 47 | install( 48 | FILES 49 | "LICENSE" 50 | DESTINATION "${CMAKE_INSTALL_DATADIR}/licenses/${PROJECT_NAME}" 51 | ) 52 | 53 | # Include test suite 54 | enable_testing() 55 | add_subdirectory("test") 56 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2020 Sebastian Ehlert 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Using stdlib in your project 3 | 4 | The Fortran standard library ([stdlib](https://github.com/fortran-lang/stdlib)) is developed by the [Fortran-lang community](https://github.com/fortran-lang). 5 | This projects shows how to integrate stdlib in your CMake project. 6 | 7 | For a quick start you can include stdlib as git submodule in your projects by 8 | 9 | ``` 10 | git submodule https://github.com/fortran-lang/stdlib subprojects/stdlib 11 | ``` 12 | 13 | Users must initialize the submodule themselves when building your project, unless you let CMake perform this operation on demand. 14 | 15 | Alternatively, you can use the `FetchContent` module of CMake to retrieve the git repository while configuring the project. 16 | A CMake snippet supporting both approaches is given here 17 | 18 | ```cmake 19 | # Include the stdlib project 20 | if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/stdlib/CMakeLists.txt) 21 | add_subdirectory("stdlib") 22 | else() 23 | set("stdlib-url" "https://github.com/fortran-lang/stdlib") 24 | message(STATUS "Retrieving stdlib from ${stdlib-url}") 25 | include(FetchContent) 26 | FetchContent_Declare( 27 | "stdlib" 28 | GIT_REPOSITORY "${stdlib-url}" 29 | GIT_TAG "HEAD" 30 | ) 31 | FetchContent_MakeAvailable("stdlib") 32 | endif() 33 | ``` 34 | 35 | A more elaborated CMake module for making stdlib available can be found [here](config/cmake/Findfortran_stdlib.cmake). 36 | This module makes sure the ``fortran_stdlib::fortran_stdlib`` target is always generated regardless of how the stdlib is included in the project. 37 | 38 | You can configure stdlib by setting the appropriate options before including the subproject. 39 | Important options are 40 | 41 | - `BUILD_SHARED_LIBS` should be set to off if you want to link statically against stdlib 42 | - `CMAKE_MAXIMUM_RANK` determines the maximum rank of arrays supported in stdlib, the default value is 15. 43 | To save compile time you can reduce this value to the maximum rank needed in your application. 44 | 45 | This project offers a ready to use integration of stdlib for CMake, including an exported library and a binary application. 46 | Additionally, some boilerplate text in the README is available below as well as a testing setup with GitHub actions for GCC. 47 | The general CMake style should allow you to reuse most of the CMake build files *without* modification, just change the project name, add your source files and you are ready to go. 48 | 49 | You can [just *use this template* to create new project](https://github.com/fortran-lang/stdlib-cmake-example/generate). 50 | Remove this introduction from the README afterwards and fill in your project details. 51 | 52 | For more information on stdlib visit its [documentation](https://stdlib.fortran-lang.org). 53 | 54 | 55 | 56 | ## Installation 57 | 58 | To build this project you need 59 | 60 | - A Fortran compiler supporting Fortran 2008 or later (`gfortran` or `ifort`) 61 | - CMake version 3.14 or later 62 | - The [fypp](https://github.com/aradi/fypp) preprocessor 63 | - A build backend, ninja (version 1.10 or newer) or make 64 | 65 | 66 | Configure the build with (set the `CMAKE_INSTALL_PREFIX` to your preferred install location) 67 | 68 | ``` 69 | cmake -B _build -G Ninja -DCMAKE_INSTALL_PREFIX=$HOME/.local 70 | ``` 71 | 72 | To build the project run 73 | 74 | ``` 75 | cmake --build _build 76 | ``` 77 | 78 | Finally, you can install the project with 79 | 80 | ``` 81 | cmake --install _build 82 | ``` 83 | 84 | 85 | 86 | ## License 87 | 88 | This project is free software: you can redistribute it and/or modify it under the terms of the [MIT license](LICENSE). 89 | -------------------------------------------------------------------------------- /app/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # SPDX-Identifier: MIT 2 | set(dir "${CMAKE_CURRENT_SOURCE_DIR}") 3 | 4 | set( 5 | prog 6 | "${dir}/main.f90" 7 | ) 8 | 9 | # Example application target 10 | add_executable( 11 | "${PROJECT_NAME}-exe" 12 | "${prog}" 13 | ) 14 | set_target_properties( 15 | "${PROJECT_NAME}-exe" 16 | PROPERTIES 17 | OUTPUT_NAME "${PROJECT_NAME}" 18 | ) 19 | target_link_libraries( 20 | "${PROJECT_NAME}-exe" 21 | PRIVATE 22 | "${PROJECT_NAME}-lib" 23 | ) 24 | 25 | # Only install executable in main project 26 | if(NOT is-subprojects) 27 | install( 28 | TARGETS 29 | "${PROJECT_NAME}-exe" 30 | RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" 31 | ) 32 | endif() 33 | -------------------------------------------------------------------------------- /app/main.f90: -------------------------------------------------------------------------------- 1 | ! SPDX-Identifier: MIT 2 | 3 | !> Example application 4 | program app 5 | use example 6 | implicit none 7 | 8 | call hello_stdlib 9 | 10 | end program app 11 | -------------------------------------------------------------------------------- /config/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # SPDX-Identifier: MIT 2 | 3 | option(BUILD_SHARED_LIBS "Whether the libraries built should be shared" OFF) 4 | 5 | # Set build type as CMake does not provide defaults 6 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 7 | set( 8 | CMAKE_BUILD_TYPE "RelWithDebInfo" 9 | CACHE STRING "Build type to be used." 10 | FORCE 11 | ) 12 | message( 13 | STATUS 14 | "Setting build type to '${CMAKE_BUILD_TYPE}' as none was specified." 15 | ) 16 | endif() 17 | 18 | if(WIN32) 19 | list( 20 | APPEND CMAKE_EXE_LINKER_FLAGS 21 | "-Wl,--allow-multiple-definition" 22 | ) 23 | endif() 24 | 25 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") 26 | set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" PARENT_SCOPE) 27 | install( 28 | DIRECTORY 29 | "${CMAKE_CURRENT_SOURCE_DIR}/cmake/" 30 | DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" 31 | ) 32 | 33 | include(CMakePackageConfigHelpers) 34 | configure_package_config_file( 35 | "${CMAKE_CURRENT_SOURCE_DIR}/template.cmake" 36 | "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake" 37 | INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" 38 | ) 39 | write_basic_package_version_file( 40 | "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake" 41 | VERSION "${PROJECT_VERSION}" 42 | COMPATIBILITY SameMinorVersion 43 | ) 44 | install( 45 | FILES 46 | "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake" 47 | "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake" 48 | DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" 49 | ) 50 | -------------------------------------------------------------------------------- /config/cmake/Findfortran_stdlib.cmake: -------------------------------------------------------------------------------- 1 | # SPDX-Identifier: MIT 2 | 3 | #[[.rst: 4 | Find fortran_stdlib 5 | ------------------- 6 | 7 | Makes the Fortran standard library (stdlib) project available. 8 | 9 | Imported Targets 10 | ^^^^^^^^^^^^^^^^ 11 | 12 | This module provides the following imported target, if found: 13 | 14 | ``fortran_stdlib::fortran_stdlib`` 15 | The Fortran standard library 16 | 17 | 18 | Result Variables 19 | ^^^^^^^^^^^^^^^^ 20 | 21 | This module will define the following variables: 22 | 23 | ``FORTRAN_STDLIB_FOUND`` 24 | True if the Fortran standard library is available 25 | 26 | ``FORTRAN_STDLIB_SOURCE_DIR`` 27 | Path to the source directory of the Fortran standard library project, 28 | only set if the project is included as source. 29 | 30 | ``FORTRAN_STDLIB_BINARY_DIR`` 31 | Path to the binary directory of the Fortran standard library project, 32 | only set if the project is included as source. 33 | 34 | Cache variables 35 | ^^^^^^^^^^^^^^^ 36 | 37 | The following cache variables may be set to influence the library detection: 38 | 39 | ``FORTRAN_STDLIB_FIND_METHOD`` 40 | Methods to find or make the project available. Available methods are 41 | - ``cmake``: Try to find via CMake config file 42 | - ``pkgconf``: Try to find via pkg-config file 43 | - ``subproject``: Use source in subprojects directory 44 | - ``fetch``: Fetch the source from upstream 45 | 46 | ``FORTRAN_STDLIB_DIR`` 47 | Used for searching the CMake config file 48 | 49 | ``FORTRAN_STDLIB_SUBPROJECT`` 50 | Directory to find the Fortran standard library subproject, relative to the project root 51 | 52 | #]] 53 | 54 | set(_lib "fortran_stdlib") 55 | set(_pkg "FORTRAN_STDLIB") 56 | set(_url "https://github.com/fortran-lang/stdlib") 57 | 58 | if(NOT DEFINED "${_pkg}_FIND_METHOD") 59 | if(DEFINED "${PROJECT_NAME}-dependency-method") 60 | set("${_pkg}_FIND_METHOD" "${${PROJECT_NAME}-dependency-method}") 61 | else() 62 | set("${_pkg}_FIND_METHOD" "cmake" "pkgconf" "subproject" "fetch") 63 | endif() 64 | set("_${_pkg}_FIND_METHOD") 65 | endif() 66 | 67 | foreach(method ${${_pkg}_FIND_METHOD}) 68 | if(TARGET "${_lib}::${_lib}") 69 | break() 70 | endif() 71 | 72 | if("${method}" STREQUAL "cmake") 73 | message(STATUS "${_lib}: Find installed package") 74 | if(DEFINED "${_pkg}_DIR") 75 | set("_${_pkg}_DIR") 76 | set("${_lib}_DIR" "${_pkg}_DIR") 77 | endif() 78 | find_package("${_lib}" CONFIG) 79 | if("${_lib}_FOUND") 80 | message(STATUS "${_lib}: Found installed package") 81 | break() 82 | endif() 83 | endif() 84 | 85 | if("${method}" STREQUAL "pkgconf") 86 | find_package(PkgConfig QUIET) 87 | pkg_check_modules("${_pkg}" QUIET "${_lib}") 88 | if("${_pkg}_FOUND") 89 | message(STATUS "Found ${_lib} via pkg-config") 90 | 91 | add_library("${_lib}::${_lib}" INTERFACE IMPORTED) 92 | target_link_libraries( 93 | "${_lib}::${_lib}" 94 | INTERFACE 95 | "${${_pkg}_LINK_LIBRARIES}" 96 | ) 97 | target_include_directories( 98 | "${_lib}::${_lib}" 99 | INTERFACE 100 | "${${_pkg}_INCLUDE_DIRS}" 101 | ) 102 | 103 | break() 104 | endif() 105 | endif() 106 | 107 | if("${method}" STREQUAL "subproject") 108 | if(NOT DEFINED "${_pkg}_SUBPROJECT") 109 | set("_${_pkg}_SUBPROJECT") 110 | set("${_pkg}_SUBPROJECT" "subprojects/${_lib}") 111 | endif() 112 | set("${_pkg}_SOURCE_DIR" "${PROJECT_SOURCE_DIR}/${${_pkg}_SUBPROJECT}") 113 | set("${_pkg}_BINARY_DIR" "${PROJECT_BINARY_DIR}/${${_pkg}_SUBPROJECT}") 114 | if(EXISTS "${${_pkg}_SOURCE_DIR}/CMakeLists.txt") 115 | message(STATUS "Include ${_lib} from ${${_pkg}_SUBPROJECT}") 116 | add_subdirectory( 117 | "${${_pkg}_SOURCE_DIR}" 118 | "${${_pkg}_BINARY_DIR}" 119 | ) 120 | 121 | add_library("${_lib}::${_lib}" INTERFACE IMPORTED) 122 | target_link_libraries("${_lib}::${_lib}" INTERFACE "${_lib}") 123 | 124 | # We need the module directory in the subproject before we finish the configure stage 125 | if(NOT EXISTS "${${_pkg}_BINARY_DIR}/mod_files") 126 | make_directory("${${_pkg}_BINARY_DIR}/mod_files") 127 | endif() 128 | 129 | break() 130 | endif() 131 | endif() 132 | 133 | if("${method}" STREQUAL "fetch") 134 | message(STATUS "Retrieving ${_lib} from ${_url}") 135 | include(FetchContent) 136 | FetchContent_Declare( 137 | "${_lib}" 138 | GIT_REPOSITORY "${_url}" 139 | GIT_TAG "HEAD" 140 | ) 141 | FetchContent_MakeAvailable("${_lib}") 142 | 143 | add_library("${_lib}::${_lib}" INTERFACE IMPORTED) 144 | target_link_libraries("${_lib}::${_lib}" INTERFACE "${_lib}") 145 | 146 | # We need the module directory in the subproject before we finish the configure stage 147 | FetchContent_GetProperties("${_lib}" SOURCE_DIR "${_pkg}_SOURCE_DIR") 148 | FetchContent_GetProperties("${_lib}" BINARY_DIR "${_pkg}_BINARY_DIR") 149 | if(NOT EXISTS "${${_pkg}_BINARY_DIR}/mod_files") 150 | make_directory("${${_pkg}_BINARY_DIR}/mod_files") 151 | endif() 152 | 153 | break() 154 | endif() 155 | 156 | endforeach() 157 | 158 | if(TARGET "${_lib}::${_lib}") 159 | set("${_pkg}_FOUND" TRUE) 160 | else() 161 | set("${_pkg}_FOUND" FALSE) 162 | endif() 163 | 164 | if(DEFINED "_${_pkg}_SUBPROJECT") 165 | unset("${_pkg}_SUBPROJECT") 166 | unset("_${_pkg}_SUBPROJECT") 167 | endif() 168 | if(DEFINED "_${_pkg}_DIR") 169 | unset("${_lib}_DIR") 170 | unset("_${_pkg}_DIR") 171 | endif() 172 | if(DEFINED "_${_pkg}_FIND_METHOD") 173 | unset("${_pkg}_FIND_METHOD") 174 | unset("_${_pkg}_FIND_METHOD") 175 | endif() 176 | unset(_lib) 177 | unset(_pkg) 178 | unset(_url) 179 | -------------------------------------------------------------------------------- /config/cmake/Findtest-drive.cmake: -------------------------------------------------------------------------------- 1 | # SPDX-Identifier: MIT 2 | 3 | #[[.rst: 4 | Find test-drive 5 | --------------- 6 | 7 | Makes the test-drive project available. 8 | 9 | Imported Targets 10 | ^^^^^^^^^^^^^^^^ 11 | 12 | This module provides the following imported target, if found: 13 | 14 | ``test-drive::test-drive`` 15 | The test-drive library 16 | 17 | 18 | Result Variables 19 | ^^^^^^^^^^^^^^^^ 20 | 21 | This module will define the following variables: 22 | 23 | ``TEST_DRIVE_FOUND`` 24 | True if the test-drive library is available 25 | 26 | ``TEST_DRIVE_SOURCE_DIR`` 27 | Path to the source directory of the test-drive project, 28 | only set if the project is included as source. 29 | 30 | ``TEST_DRIVE_BINARY_DIR`` 31 | Path to the binary directory of the test-drive project, 32 | only set if the project is included as source. 33 | 34 | Cache variables 35 | ^^^^^^^^^^^^^^^ 36 | 37 | The following cache variables may be set to influence the library detection: 38 | 39 | ``TEST_DRIVE_FIND_METHOD`` 40 | Methods to find or make the project available. Available methods are 41 | - ``cmake``: Try to find via CMake config file 42 | - ``pkgconf``: Try to find via pkg-config file 43 | - ``subproject``: Use source in subprojects directory 44 | - ``fetch``: Fetch the source from upstream 45 | 46 | ``TEST_DRIVE_DIR`` 47 | Used for searching the CMake config file 48 | 49 | ``TEST_DRIVE_SUBPROJECT`` 50 | Directory to find the test-drive subproject, relative to the project root 51 | 52 | #]] 53 | 54 | set(_lib "test-drive") 55 | set(_pkg "TEST_DRIVE") 56 | set(_url "https://github.com/fortran-lang/test-drive") 57 | 58 | if(NOT DEFINED "${_pkg}_FIND_METHOD") 59 | if(DEFINED "${PROJECT_NAME}-dependency-method") 60 | set("${_pkg}_FIND_METHOD" "${${PROJECT_NAME}-dependency-method}") 61 | else() 62 | set("${_pkg}_FIND_METHOD" "cmake" "pkgconf" "subproject" "fetch") 63 | endif() 64 | set("_${_pkg}_FIND_METHOD") 65 | endif() 66 | 67 | foreach(method ${${_pkg}_FIND_METHOD}) 68 | if(TARGET "${_lib}::${_lib}") 69 | break() 70 | endif() 71 | 72 | if("${method}" STREQUAL "cmake") 73 | message(STATUS "${_lib}: Find installed package") 74 | if(DEFINED "${_pkg}_DIR") 75 | set("_${_pkg}_DIR") 76 | set("${_lib}_DIR" "${_pkg}_DIR") 77 | endif() 78 | find_package("${_lib}" CONFIG) 79 | if("${_lib}_FOUND") 80 | message(STATUS "${_lib}: Found installed package") 81 | break() 82 | endif() 83 | endif() 84 | 85 | if("${method}" STREQUAL "pkgconf") 86 | find_package(PkgConfig QUIET) 87 | pkg_check_modules("${_pkg}" QUIET "${_lib}") 88 | if("${_pkg}_FOUND") 89 | message(STATUS "Found ${_lib} via pkg-config") 90 | 91 | add_library("${_lib}::${_lib}" INTERFACE IMPORTED) 92 | target_link_libraries( 93 | "${_lib}::${_lib}" 94 | INTERFACE 95 | "${${_pkg}_LINK_LIBRARIES}" 96 | ) 97 | target_include_directories( 98 | "${_lib}::${_lib}" 99 | INTERFACE 100 | "${${_pkg}_INCLUDE_DIRS}" 101 | ) 102 | 103 | break() 104 | endif() 105 | endif() 106 | 107 | if("${method}" STREQUAL "subproject") 108 | if(NOT DEFINED "${_pkg}_SUBPROJECT") 109 | set("_${_pkg}_SUBPROJECT") 110 | set("${_pkg}_SUBPROJECT" "subprojects/${_lib}") 111 | endif() 112 | set("${_pkg}_SOURCE_DIR" "${PROJECT_SOURCE_DIR}/${${_pkg}_SUBPROJECT}") 113 | set("${_pkg}_BINARY_DIR" "${PROJECT_BINARY_DIR}/${${_pkg}_SUBPROJECT}") 114 | if(EXISTS "${${_pkg}_SOURCE_DIR}/CMakeLists.txt") 115 | message(STATUS "Include ${_lib} from ${${_pkg}_SUBPROJECT}") 116 | add_subdirectory( 117 | "${${_pkg}_SOURCE_DIR}" 118 | "${${_pkg}_BINARY_DIR}" 119 | ) 120 | 121 | add_library("${_lib}::${_lib}" INTERFACE IMPORTED) 122 | target_link_libraries("${_lib}::${_lib}" INTERFACE "${_lib}") 123 | 124 | # We need the module directory in the subproject before we finish the configure stage 125 | if(NOT EXISTS "${${_pkg}_BINARY_DIR}/include") 126 | make_directory("${${_pkg}_BINARY_DIR}/include") 127 | endif() 128 | 129 | break() 130 | endif() 131 | endif() 132 | 133 | if("${method}" STREQUAL "fetch") 134 | message(STATUS "Retrieving ${_lib} from ${_url}") 135 | include(FetchContent) 136 | FetchContent_Declare( 137 | "${_lib}" 138 | GIT_REPOSITORY "${_url}" 139 | GIT_TAG "HEAD" 140 | ) 141 | FetchContent_MakeAvailable("${_lib}") 142 | 143 | add_library("${_lib}::${_lib}" INTERFACE IMPORTED) 144 | target_link_libraries("${_lib}::${_lib}" INTERFACE "${_lib}") 145 | 146 | # We need the module directory in the subproject before we finish the configure stage 147 | FetchContent_GetProperties("${_lib}" SOURCE_DIR "${_pkg}_SOURCE_DIR") 148 | FetchContent_GetProperties("${_lib}" BINARY_DIR "${_pkg}_BINARY_DIR") 149 | if(NOT EXISTS "${${_pkg}_BINARY_DIR}/include") 150 | make_directory("${${_pkg}_BINARY_DIR}/include") 151 | endif() 152 | 153 | break() 154 | endif() 155 | 156 | endforeach() 157 | 158 | if(TARGET "${_lib}::${_lib}") 159 | set("${_pkg}_FOUND" TRUE) 160 | else() 161 | set("${_pkg}_FOUND" FALSE) 162 | endif() 163 | 164 | if(DEFINED "_${_pkg}_SUBPROJECT") 165 | unset("${_pkg}_SUBPROJECT") 166 | unset("_${_pkg}_SUBPROJECT") 167 | endif() 168 | if(DEFINED "_${_pkg}_DIR") 169 | unset("${_lib}_DIR") 170 | unset("_${_pkg}_DIR") 171 | endif() 172 | if(DEFINED "_${_pkg}_FIND_METHOD") 173 | unset("${_pkg}_FIND_METHOD") 174 | unset("_${_pkg}_FIND_METHOD") 175 | endif() 176 | unset(_lib) 177 | unset(_pkg) 178 | unset(_url) 179 | -------------------------------------------------------------------------------- /config/template.cmake: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | 3 | if(NOT TARGET "@PROJECT_NAME@::@PROJECT_NAME@") 4 | include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@-targets.cmake") 5 | endif() 6 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # SPDX-Identifier: MIT 2 | set(dir "${CMAKE_CURRENT_SOURCE_DIR}") 3 | 4 | set( 5 | srcs 6 | "${dir}/example.f90" 7 | ) 8 | 9 | set( 10 | MODULE_OUTPUT_DIR 11 | "${CMAKE_CURRENT_BINARY_DIR}/include" 12 | ) 13 | 14 | # Create example library target 15 | add_library( 16 | "${PROJECT_NAME}-lib" 17 | "${srcs}" 18 | ) 19 | set_target_properties( 20 | "${PROJECT_NAME}-lib" 21 | PROPERTIES 22 | POSITION_INDEPENDENT_CODE TRUE 23 | OUTPUT_NAME "${PROJECT_NAME}" 24 | VERSION "${PROJECT_VERSION}" 25 | SOVERSION "${PROJECT_VERSION_MAJOR}" 26 | Fortran_MODULE_DIRECTORY "${MODULE_OUTPUT_DIR}" 27 | ) 28 | target_link_libraries( 29 | "${PROJECT_NAME}-lib" 30 | PRIVATE 31 | "fortran_stdlib::fortran_stdlib" 32 | ) 33 | target_include_directories( 34 | "${PROJECT_NAME}-lib" 35 | INTERFACE 36 | $ 37 | $ 38 | ) 39 | 40 | # Export targets for other projects 41 | install( 42 | TARGETS 43 | "${PROJECT_NAME}-lib" 44 | EXPORT 45 | "${PROJECT_NAME}-targets" 46 | LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" 47 | ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" 48 | ) 49 | install( 50 | DIRECTORY 51 | "${MODULE_OUTPUT_DIR}/" 52 | DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" 53 | ) 54 | -------------------------------------------------------------------------------- /src/example.f90: -------------------------------------------------------------------------------- 1 | ! SPDX-Identifier: MIT 2 | 3 | !> Example module to interact with stdlib 4 | module example 5 | use stdlib_logger 6 | implicit none 7 | public 8 | 9 | 10 | contains 11 | 12 | 13 | subroutine hello_stdlib 14 | 15 | call global_logger%log_information("This project uses the Fortran standard library!") 16 | 17 | end subroutine hello_stdlib 18 | 19 | 20 | end module example 21 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # SPDX-Identifier: MIT 2 | 3 | if(NOT TARGET "test-drive::test-drive") 4 | find_package("test-drive" REQUIRED) 5 | endif() 6 | 7 | # Unit testing 8 | set( 9 | tests 10 | "example" 11 | ) 12 | set( 13 | test-srcs 14 | "main.f90" 15 | ) 16 | foreach(t IN LISTS tests) 17 | string(MAKE_C_IDENTIFIER ${t} t) 18 | list(APPEND test-srcs "test_${t}.f90") 19 | endforeach() 20 | 21 | add_executable( 22 | "${PROJECT_NAME}-tester" 23 | "${test-srcs}" 24 | ) 25 | target_link_libraries( 26 | "${PROJECT_NAME}-tester" 27 | PRIVATE 28 | "${PROJECT_NAME}-lib" 29 | "test-drive::test-drive" 30 | ) 31 | 32 | foreach(t IN LISTS tests) 33 | add_test("${PROJECT_NAME}/${t}" "${PROJECT_NAME}-tester" "${t}") 34 | endforeach() 35 | -------------------------------------------------------------------------------- /test/main.f90: -------------------------------------------------------------------------------- 1 | ! SPDX-Identifier: MIT 2 | 3 | !> Driver for unit testing 4 | program tester 5 | use, intrinsic :: iso_fortran_env, only : error_unit 6 | use testdrive, only : run_testsuite, new_testsuite, testsuite_type, & 7 | & select_suite, run_selected, get_argument 8 | use test_example, only : collect_example 9 | implicit none 10 | integer :: stat, is 11 | character(len=:), allocatable :: suite_name, test_name 12 | type(testsuite_type), allocatable :: testsuites(:) 13 | character(len=*), parameter :: fmt = '("#", *(1x, a))' 14 | 15 | stat = 0 16 | 17 | testsuites = [ & 18 | new_testsuite("example", collect_example) & 19 | ] 20 | 21 | call get_argument(1, suite_name) 22 | call get_argument(2, test_name) 23 | 24 | if (allocated(suite_name)) then 25 | is = select_suite(testsuites, suite_name) 26 | if (is > 0 .and. is <= size(testsuites)) then 27 | if (allocated(test_name)) then 28 | write(error_unit, fmt) "Suite:", testsuites(is)%name 29 | call run_selected(testsuites(is)%collect, test_name, error_unit, stat) 30 | if (stat < 0) then 31 | error stop 1 32 | end if 33 | else 34 | write(error_unit, fmt) "Testing:", testsuites(is)%name 35 | call run_testsuite(testsuites(is)%collect, error_unit, stat) 36 | end if 37 | else 38 | write(error_unit, fmt) "Available testsuites" 39 | do is = 1, size(testsuites) 40 | write(error_unit, fmt) "-", testsuites(is)%name 41 | end do 42 | error stop 1 43 | end if 44 | else 45 | do is = 1, size(testsuites) 46 | write(error_unit, fmt) "Testing:", testsuites(is)%name 47 | call run_testsuite(testsuites(is)%collect, error_unit, stat) 48 | end do 49 | end if 50 | 51 | if (stat > 0) then 52 | write(error_unit, '(i0, 1x, a)') stat, "test(s) failed!" 53 | error stop 1 54 | end if 55 | 56 | 57 | end program tester 58 | -------------------------------------------------------------------------------- /test/test_example.f90: -------------------------------------------------------------------------------- 1 | ! SPDX-Identifier: MIT 2 | 3 | module test_example 4 | use testdrive, only : new_unittest, unittest_type, error_type, check, skip_test 5 | implicit none 6 | private 7 | 8 | public :: collect_example 9 | 10 | 11 | !> Single precision real numbers 12 | integer, parameter :: sp = selected_real_kind(6) 13 | 14 | !> Double precision real numbers 15 | integer, parameter :: dp = selected_real_kind(15) 16 | 17 | !> Char length for integers 18 | integer, parameter :: i1 = selected_int_kind(2) 19 | 20 | !> Short length for integers 21 | integer, parameter :: i2 = selected_int_kind(4) 22 | 23 | !> Length of default integers 24 | integer, parameter :: i4 = selected_int_kind(9) 25 | 26 | !> Long length for integers 27 | integer, parameter :: i8 = selected_int_kind(18) 28 | 29 | contains 30 | 31 | !> Collect all exported unit tests 32 | subroutine collect_example(testsuite) 33 | 34 | !> Collection of tests 35 | type(unittest_type), allocatable, intent(out) :: testsuite(:) 36 | 37 | testsuite = [ & 38 | new_unittest("success", test_success), & 39 | new_unittest("failure", test_failure, should_fail=.true.), & 40 | new_unittest("failure-message", test_failure_message, should_fail=.true.), & 41 | new_unittest("skipped", test_skipped), & 42 | new_unittest("expression", test_expression), & 43 | new_unittest("expression-fail", test_expression_fail, should_fail=.true.), & 44 | new_unittest("expression-message", test_expression_message, should_fail=.true.), & 45 | new_unittest("real-single-abs", test_rsp_abs), & 46 | new_unittest("real-single-rel", test_rsp_rel), & 47 | new_unittest("real-single-abs-fail", test_rsp_abs_fail, should_fail=.true.), & 48 | new_unittest("real-single-rel-fail", test_rsp_rel_fail, should_fail=.true.), & 49 | new_unittest("real-single-abs-message", test_rsp_abs_message, should_fail=.true.), & 50 | new_unittest("real-double-abs", test_rdp_abs), & 51 | new_unittest("real-double-rel", test_rdp_rel), & 52 | new_unittest("real-double-abs-fail", test_rdp_abs_fail, should_fail=.true.), & 53 | new_unittest("real-double-rel-fail", test_rdp_rel_fail, should_fail=.true.), & 54 | new_unittest("real-double-abs-message", test_rdp_abs_message, should_fail=.true.), & 55 | new_unittest("complex-single-abs", test_csp_abs), & 56 | new_unittest("complex-single-rel", test_csp_rel), & 57 | new_unittest("complex-single-abs-fail", test_csp_abs_fail, should_fail=.true.), & 58 | new_unittest("complex-single-rel-fail", test_csp_rel_fail, should_fail=.true.), & 59 | new_unittest("complex-single-abs-message", test_csp_abs_message, should_fail=.true.), & 60 | new_unittest("complex-double-abs", test_cdp_abs), & 61 | new_unittest("complex-double-rel", test_cdp_rel), & 62 | new_unittest("complex-double-abs-fail", test_cdp_abs_fail, should_fail=.true.), & 63 | new_unittest("complex-double-rel-fail", test_cdp_rel_fail, should_fail=.true.), & 64 | new_unittest("complex-double-abs-message", test_cdp_abs_message, should_fail=.true.), & 65 | new_unittest("integer-char", test_i1), & 66 | new_unittest("integer-char-fail", test_i1_fail, should_fail=.true.), & 67 | new_unittest("integer-char-message", test_i1_message, should_fail=.true.), & 68 | new_unittest("integer-short", test_i2), & 69 | new_unittest("integer-short-fail", test_i2_fail, should_fail=.true.), & 70 | new_unittest("integer-short-message", test_i2_message, should_fail=.true.), & 71 | new_unittest("integer-default", test_i4), & 72 | new_unittest("integer-default-fail", test_i4_fail, should_fail=.true.), & 73 | new_unittest("integer-default-message", test_i4_message, should_fail=.true.), & 74 | new_unittest("integer-long", test_i8), & 75 | new_unittest("integer-long-fail", test_i8_fail, should_fail=.true.), & 76 | new_unittest("integer-long-message", test_i8_message, should_fail=.true.), & 77 | new_unittest("logical-default-true", test_l4_true), & 78 | new_unittest("logical-default-false", test_l4_false), & 79 | new_unittest("logical-default-fail", test_l4_fail, should_fail=.true.), & 80 | new_unittest("logical-default-message", test_l4_message, should_fail=.true.), & 81 | new_unittest("character", test_char), & 82 | new_unittest("character-fail", test_char_fail, should_fail=.true.), & 83 | new_unittest("character-message", test_char_message, should_fail=.true.) & 84 | ] 85 | 86 | end subroutine collect_example 87 | 88 | 89 | subroutine test_success(error) 90 | 91 | !> Error handling 92 | type(error_type), allocatable, intent(out) :: error 93 | 94 | call check(error, 0) 95 | 96 | end subroutine test_success 97 | 98 | 99 | subroutine test_failure(error) 100 | 101 | !> Error handling 102 | type(error_type), allocatable, intent(out) :: error 103 | 104 | call check(error, 7) 105 | 106 | end subroutine test_failure 107 | 108 | 109 | subroutine test_failure_message(error) 110 | 111 | !> Error handling 112 | type(error_type), allocatable, intent(out) :: error 113 | 114 | call check(error, 4, "Custom message describing the error") 115 | 116 | end subroutine test_failure_message 117 | 118 | 119 | subroutine test_skipped(error) 120 | 121 | !> Error handling 122 | type(error_type), allocatable, intent(out) :: error 123 | 124 | call skip_test(error, "This test is always skipped") 125 | 126 | end subroutine test_skipped 127 | 128 | 129 | subroutine test_expression(error) 130 | 131 | !> Error handling 132 | type(error_type), allocatable, intent(out) :: error 133 | 134 | call check(error, index("info!", "!") > 0) 135 | 136 | end subroutine test_expression 137 | 138 | 139 | subroutine test_expression_fail(error) 140 | 141 | !> Error handling 142 | type(error_type), allocatable, intent(out) :: error 143 | 144 | call check(error, index("info!", "?") > 0) 145 | 146 | end subroutine test_expression_fail 147 | 148 | 149 | subroutine test_expression_message(error) 150 | 151 | !> Error handling 152 | type(error_type), allocatable, intent(out) :: error 153 | 154 | call check(error, index("info!", "!") == 0, 'index("info!", "!") == 0') 155 | 156 | end subroutine test_expression_message 157 | 158 | 159 | subroutine test_rsp_abs(error) 160 | 161 | !> Error handling 162 | type(error_type), allocatable, intent(out) :: error 163 | 164 | real(sp) :: val 165 | 166 | val = 3.3_sp 167 | 168 | call check(error, val, 3.3_sp, thr=sqrt(epsilon(val))) 169 | 170 | end subroutine test_rsp_abs 171 | 172 | 173 | subroutine test_rsp_rel(error) 174 | 175 | !> Error handling 176 | type(error_type), allocatable, intent(out) :: error 177 | 178 | real(sp) :: val 179 | 180 | val = 3.3_sp 181 | 182 | call check(error, val, 3.3_sp, rel=.true.) 183 | 184 | end subroutine test_rsp_rel 185 | 186 | 187 | subroutine test_rsp_abs_fail(error) 188 | 189 | !> Error handling 190 | type(error_type), allocatable, intent(out) :: error 191 | 192 | real(sp) :: val 193 | 194 | val = 1.0_sp 195 | 196 | call check(error, val, 2.0_sp) 197 | 198 | end subroutine test_rsp_abs_fail 199 | 200 | 201 | subroutine test_rsp_rel_fail(error) 202 | 203 | !> Error handling 204 | type(error_type), allocatable, intent(out) :: error 205 | 206 | real(sp) :: val 207 | 208 | val = 1.0_sp 209 | 210 | call check(error, val, 1.5_sp, rel=.true.) 211 | 212 | end subroutine test_rsp_rel_fail 213 | 214 | 215 | subroutine test_rsp_abs_message(error) 216 | 217 | !> Error handling 218 | type(error_type), allocatable, intent(out) :: error 219 | 220 | real(sp) :: val 221 | 222 | val = 1.0_sp 223 | 224 | call check(error, val, 1.5_sp, message="Actual value is not 1.5") 225 | 226 | end subroutine test_rsp_abs_message 227 | 228 | 229 | subroutine test_rdp_abs(error) 230 | 231 | !> Error handling 232 | type(error_type), allocatable, intent(out) :: error 233 | 234 | real(dp) :: val 235 | 236 | val = 3.3_dp 237 | 238 | call check(error, val, 3.3_dp, thr=sqrt(epsilon(val))) 239 | 240 | end subroutine test_rdp_abs 241 | 242 | 243 | subroutine test_rdp_rel(error) 244 | 245 | !> Error handling 246 | type(error_type), allocatable, intent(out) :: error 247 | 248 | real(dp) :: val 249 | 250 | val = 3.3_dp 251 | 252 | call check(error, val, 3.3_dp, rel=.true.) 253 | 254 | end subroutine test_rdp_rel 255 | 256 | 257 | subroutine test_rdp_abs_fail(error) 258 | 259 | !> Error handling 260 | type(error_type), allocatable, intent(out) :: error 261 | 262 | real(dp) :: val 263 | 264 | val = 1.0_dp 265 | 266 | call check(error, val, 2.0_dp) 267 | 268 | end subroutine test_rdp_abs_fail 269 | 270 | 271 | subroutine test_rdp_rel_fail(error) 272 | 273 | !> Error handling 274 | type(error_type), allocatable, intent(out) :: error 275 | 276 | real(dp) :: val 277 | 278 | val = 1.0_dp 279 | 280 | call check(error, val, 1.5_dp, rel=.true.) 281 | 282 | end subroutine test_rdp_rel_fail 283 | 284 | 285 | subroutine test_rdp_abs_message(error) 286 | 287 | !> Error handling 288 | type(error_type), allocatable, intent(out) :: error 289 | 290 | real(dp) :: val 291 | 292 | val = 1.0_dp 293 | 294 | call check(error, val, 1.5_dp, message="Actual value is not 1.5") 295 | 296 | end subroutine test_rdp_abs_message 297 | 298 | 299 | subroutine test_csp_abs(error) 300 | 301 | !> Error handling 302 | type(error_type), allocatable, intent(out) :: error 303 | 304 | complex(sp) :: val 305 | 306 | val = cmplx(3.3_sp, 1.0_sp, sp) 307 | 308 | call check(error, val, cmplx(3.3_sp, 1.0_sp, sp), thr=sqrt(epsilon(abs(val)))) 309 | 310 | end subroutine test_csp_abs 311 | 312 | 313 | subroutine test_csp_rel(error) 314 | 315 | !> Error handling 316 | type(error_type), allocatable, intent(out) :: error 317 | 318 | complex(sp) :: val 319 | 320 | val = cmplx(3.3_sp, 1.0_sp, sp) 321 | 322 | call check(error, val, cmplx(3.3_sp, 1.0_sp, sp), rel=.true.) 323 | 324 | end subroutine test_csp_rel 325 | 326 | 327 | subroutine test_csp_abs_fail(error) 328 | 329 | !> Error handling 330 | type(error_type), allocatable, intent(out) :: error 331 | 332 | complex(sp) :: val 333 | 334 | val = cmplx(1.0_sp, 2.0_sp, sp) 335 | 336 | call check(error, val, cmplx(2.0_sp, 1.0_sp, sp)) 337 | 338 | end subroutine test_csp_abs_fail 339 | 340 | 341 | subroutine test_csp_rel_fail(error) 342 | 343 | !> Error handling 344 | type(error_type), allocatable, intent(out) :: error 345 | 346 | complex(sp) :: val 347 | 348 | val = cmplx(1.0_sp, 1.5_sp, sp) 349 | 350 | call check(error, val, cmplx(1.5_sp, 1.0_sp, sp), rel=.true.) 351 | 352 | end subroutine test_csp_rel_fail 353 | 354 | 355 | subroutine test_csp_abs_message(error) 356 | 357 | !> Error handling 358 | type(error_type), allocatable, intent(out) :: error 359 | 360 | complex(sp) :: val 361 | 362 | val = cmplx(1.0_sp, 1.5_sp, sp) 363 | 364 | call check(error, val, cmplx(1.5_sp, 1.0_sp, sp), message="Actual value is not 1.5+1.0i") 365 | 366 | end subroutine test_csp_abs_message 367 | 368 | 369 | subroutine test_cdp_abs(error) 370 | 371 | !> Error handling 372 | type(error_type), allocatable, intent(out) :: error 373 | 374 | complex(dp) :: val 375 | 376 | val = cmplx(3.3_dp, 1.0_dp, dp) 377 | 378 | call check(error, val, cmplx(3.3_dp, 1.0_dp, dp), thr=sqrt(epsilon(real(val)))) 379 | 380 | end subroutine test_cdp_abs 381 | 382 | 383 | subroutine test_cdp_rel(error) 384 | 385 | !> Error handling 386 | type(error_type), allocatable, intent(out) :: error 387 | 388 | complex(dp) :: val 389 | 390 | val = cmplx(3.3_dp, 1.0_dp, dp) 391 | 392 | call check(error, val, cmplx(3.3_dp, 1.0_dp, dp), rel=.true.) 393 | 394 | end subroutine test_cdp_rel 395 | 396 | 397 | subroutine test_cdp_abs_fail(error) 398 | 399 | !> Error handling 400 | type(error_type), allocatable, intent(out) :: error 401 | 402 | complex(dp) :: val 403 | 404 | val = cmplx(1.0_dp, 2.0_dp, dp) 405 | 406 | call check(error, val, cmplx(2.0_dp, 1.0_dp, dp)) 407 | 408 | end subroutine test_cdp_abs_fail 409 | 410 | 411 | subroutine test_cdp_rel_fail(error) 412 | 413 | !> Error handling 414 | type(error_type), allocatable, intent(out) :: error 415 | 416 | complex(dp) :: val 417 | 418 | val = cmplx(1.0_dp, 1.5_dp, dp) 419 | 420 | call check(error, val, cmplx(1.5_dp, 1.0_dp, dp), rel=.true.) 421 | 422 | end subroutine test_cdp_rel_fail 423 | 424 | 425 | subroutine test_cdp_abs_message(error) 426 | 427 | !> Error handling 428 | type(error_type), allocatable, intent(out) :: error 429 | 430 | complex(dp) :: val 431 | 432 | val = cmplx(1.0_dp, 1.5_dp, dp) 433 | 434 | call check(error, val, cmplx(1.5_dp, 1.0_dp, dp), message="Actual value is not 1.5+1.0i") 435 | 436 | end subroutine test_cdp_abs_message 437 | 438 | 439 | subroutine test_i1(error) 440 | 441 | !> Error handling 442 | type(error_type), allocatable, intent(out) :: error 443 | 444 | integer(i1) :: val 445 | 446 | val = 3_i1 447 | 448 | call check(error, val, 3_i1) 449 | 450 | end subroutine test_i1 451 | 452 | 453 | subroutine test_i1_fail(error) 454 | 455 | !> Error handling 456 | type(error_type), allocatable, intent(out) :: error 457 | 458 | integer(i1) :: val 459 | 460 | val = 3_i1 461 | 462 | call check(error, val, -4_i1) 463 | 464 | end subroutine test_i1_fail 465 | 466 | 467 | subroutine test_i1_message(error) 468 | 469 | !> Error handling 470 | type(error_type), allocatable, intent(out) :: error 471 | 472 | integer(i1) :: val 473 | 474 | val = -3_i1 475 | 476 | call check(error, val, 7_i1, "Actual value is not seven") 477 | 478 | end subroutine test_i1_message 479 | 480 | 481 | subroutine test_i2(error) 482 | 483 | !> Error handling 484 | type(error_type), allocatable, intent(out) :: error 485 | 486 | integer(i2) :: val 487 | 488 | val = 3_i2 489 | 490 | call check(error, val, 3_i2) 491 | 492 | end subroutine test_i2 493 | 494 | 495 | subroutine test_i2_fail(error) 496 | 497 | !> Error handling 498 | type(error_type), allocatable, intent(out) :: error 499 | 500 | integer(i2) :: val 501 | 502 | val = 3_i2 503 | 504 | call check(error, val, -4_i2) 505 | 506 | end subroutine test_i2_fail 507 | 508 | 509 | subroutine test_i2_message(error) 510 | 511 | !> Error handling 512 | type(error_type), allocatable, intent(out) :: error 513 | 514 | integer(i2) :: val 515 | 516 | val = -3_i2 517 | 518 | call check(error, val, 7_i2, "Actual value is not seven") 519 | 520 | end subroutine test_i2_message 521 | 522 | 523 | subroutine test_i4(error) 524 | 525 | !> Error handling 526 | type(error_type), allocatable, intent(out) :: error 527 | 528 | integer(i4) :: val 529 | 530 | val = 3_i4 531 | 532 | call check(error, val, 3_i4) 533 | 534 | end subroutine test_i4 535 | 536 | 537 | subroutine test_i4_fail(error) 538 | 539 | !> Error handling 540 | type(error_type), allocatable, intent(out) :: error 541 | 542 | integer(i4) :: val 543 | 544 | val = 3_i4 545 | 546 | call check(error, val, -4_i4) 547 | 548 | end subroutine test_i4_fail 549 | 550 | 551 | subroutine test_i4_message(error) 552 | 553 | !> Error handling 554 | type(error_type), allocatable, intent(out) :: error 555 | 556 | integer(i4) :: val 557 | 558 | val = -3_i4 559 | 560 | call check(error, val, 7_i4, "Actual value is not seven") 561 | 562 | end subroutine test_i4_message 563 | 564 | 565 | subroutine test_i8(error) 566 | 567 | !> Error handling 568 | type(error_type), allocatable, intent(out) :: error 569 | 570 | integer(i8) :: val 571 | 572 | val = 3_i8 573 | 574 | call check(error, val, 3_i8) 575 | 576 | end subroutine test_i8 577 | 578 | 579 | subroutine test_i8_fail(error) 580 | 581 | !> Error handling 582 | type(error_type), allocatable, intent(out) :: error 583 | 584 | integer(i8) :: val 585 | 586 | val = 3_i8 587 | 588 | call check(error, val, -4_i8) 589 | 590 | end subroutine test_i8_fail 591 | 592 | 593 | subroutine test_i8_message(error) 594 | 595 | !> Error handling 596 | type(error_type), allocatable, intent(out) :: error 597 | 598 | integer(i8) :: val 599 | 600 | val = -3_i8 601 | 602 | call check(error, val, 7_i8, "Actual value is not seven") 603 | 604 | end subroutine test_i8_message 605 | 606 | 607 | subroutine test_l4_true(error) 608 | 609 | !> Error handling 610 | type(error_type), allocatable, intent(out) :: error 611 | 612 | call check(error, .true., .true.) 613 | 614 | end subroutine test_l4_true 615 | 616 | 617 | subroutine test_l4_false(error) 618 | 619 | !> Error handling 620 | type(error_type), allocatable, intent(out) :: error 621 | 622 | call check(error, .false., .false.) 623 | 624 | end subroutine test_l4_false 625 | 626 | 627 | subroutine test_l4_fail(error) 628 | 629 | !> Error handling 630 | type(error_type), allocatable, intent(out) :: error 631 | 632 | call check(error, .true., .false.) 633 | 634 | end subroutine test_l4_fail 635 | 636 | 637 | subroutine test_l4_message(error) 638 | 639 | !> Error handling 640 | type(error_type), allocatable, intent(out) :: error 641 | 642 | call check(error, .false., .true., "Logical value is not true") 643 | 644 | end subroutine test_l4_message 645 | 646 | 647 | subroutine test_char(error) 648 | 649 | !> Error handling 650 | type(error_type), allocatable, intent(out) :: error 651 | 652 | character(len=:), allocatable :: val 653 | 654 | val = "positive" 655 | 656 | call check(error, val, "positive") 657 | 658 | end subroutine test_char 659 | 660 | 661 | subroutine test_char_fail(error) 662 | 663 | !> Error handling 664 | type(error_type), allocatable, intent(out) :: error 665 | 666 | character(len=:), allocatable :: val 667 | 668 | val = "positive" 669 | 670 | call check(error, val, "negative") 671 | 672 | end subroutine test_char_fail 673 | 674 | 675 | subroutine test_char_message(error) 676 | 677 | !> Error handling 678 | type(error_type), allocatable, intent(out) :: error 679 | 680 | character(len=:), allocatable :: val 681 | 682 | val = "positive" 683 | 684 | call check(error, val, "negative", "Character string should be negative") 685 | 686 | end subroutine test_char_message 687 | 688 | 689 | end module test_example 690 | --------------------------------------------------------------------------------