cmakefortran

cmake configuration failure with FetchContent of pFUnit in `extern/pFUnit`, succeeds FetchContent if in root project directory


I would like to use pFUnit in my project and download using FetchContent in extern/pFunit/CMakeLists.txt. My project structure is as follows:

broken/
├── CMakeLists.txt
├── extern
│   ├── CMakeLists.txt
│   └── pFUnit
│       └── CMakeLists.txt
├── src
│   ├── CMakeLists.txt
│   └── square.f90
└── test
    ├── CMakeLists.txt
    └── test_square.pf

when attempting cmake -S . -B build -DCMAKE_Fortran_COMPILER=mpif90, pFUnit does not configure correctly (maybe I open an issue in their github, but since i'm new to cmake, my issue could be a more basic misunderstanding) and throws the error preceded by some debugging information from variable_watch(PFUNIT_DRIVER):

CMake Debug Log at build/_deps/pfunit-src/CMakeLists.txt:223 (set):                           
  Variable "PFUNIT_DRIVER" was accessed using MODIFIED_ACCESS with value                      
  "/home/jf01/dev/minimal-fetch-content-pfunit/broken/build/_deps/pfunit-src/include/driver.F9
0".                                                                                           
                                                                                              
                                                                                              
CMake Debug Log at build/_deps/pfunit-src/include/add_pfunit_ctest.cmake:70 (configure_file): 
  Variable "PFUNIT_DRIVER" was accessed using UNKNOWN_READ_ACCESS with value                  
  "".                                                                                         
Call Stack (most recent call first):                                                          
  test/CMakeLists.txt:1 (add_pfunit_ctest)                                                    
                                                                                              
                                                                                              
CMake Error: File /home/jf01/dev/minimal-fetch-content-pfunit/broken/test/.in does not exist. 
CMake Error at build/_deps/pfunit-src/include/add_pfunit_ctest.cmake:70 (configure_file):     
  configure_file Problem configuring file                                                     
Call Stack (most recent call first):                                                          
  test/CMakeLists.txt:1 (add_pfunit_ctest) 
                       

It's odd to me that PFUNIT_DRIVER is correctly read and set in one file, but is an empty string in the other. Any suggestions for resolving this error?

Below are the contents of the files in my broken example, but for convenience, you can also pull my code from jfdev001/minimal-fetch-content-pfunit and reproduce a working (yet not ideally structured) and broken example as needed.

###########################
# @file CMakeLists.txt
###########################
cmake_minimum_required(VERSION 3.15)

project(
    TestPFUNIT
    VERSION 0.1.0 
    LANGUAGES Fortran
)

variable_watch(PFUNIT_DRIVER)
add_subdirectory(src)

enable_testing()
add_subdirectory(extern)
add_subdirectory(test)

###########################
# @file extern/CMakeLists.txt
###########################
add_subdirectory(pFUnit)

###########################
# @file extern/pFUnit/CMakeLists.txt
###########################
include(FetchContent)
set(PFUNIT_VERSION "v4.9.0")
FetchContent_Declare(
  PFUNIT
  GIT_REPOSITORY "https://github.com/Goddard-Fortran-Ecosystem/pFUnit"
  GIT_TAG ${PFUNIT_VERSION}
)  
FetchContent_MakeAvailable(PFUNIT)

###########################
# @file src/CMakeLists.txt
###########################
add_library(sut
    square.f90
)

set_target_properties(sut 
    PROPERTIES
    Fortran_MODULE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
)

# Allows successful linking of sut in the tests/CMakeLists.txt
target_include_directories(sut 
    PUBLIC ${CMAKE_CURRENT_BINARY_DIR}
)

###########################
# @file src/square.f90
###########################
MODULE Square_mod

    IMPLICIT NONE

CONTAINS

    PURE REAL FUNCTION square(x)
        REAL, INTENT(in) :: x
        square = x**2
    END FUNCTION square

END MODULE Square_mod

###########################
# @file test/CMakeLists.txt
###########################
add_pfunit_ctest (test_square
    TEST_SOURCES test_square.pf
    LINK_LIBRARIES sut 
    LABELS "myproject" 
)

###########################
# @file test/test_square.pf 
###########################
@test
SUBROUTINE test_square()
    USE Square_mod
    USE funit

    @assertEqual(9., square(3.), 'square(3)')

END SUBROUTINE test_square

A working example can be compiled by placing the pfunit fetch content in the toplevel CMakeLists.txt and structuring the project as shown below, but for organizational purposes, it seems like it would be a better practice to put such dependencies in a separate folder (see e.g., fortran-lang/fftpack, jchristopherson/dynamics).

working/
├── CMakeLists.txt
├── src
│   ├── CMakeLists.txt
│   └── square.f90
└── test
    ├── CMakeLists.txt
    └── test_square.pf

Solution

  • Turns out this was a bug in the library itself and not just a basic misunderstanding of cmake. The problem is addressed in https://github.com/Goddard-Fortran-Ecosystem/pFUnit/pull/485

    As pointed out by @Tsyvarev, the scoping of PFUNIT_DRIVER was the source of the problem. The sledgehammer solution was to cache this variable (i.e., using the CACHE) so that the variable is visible at all scopes.