Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cmake configuration fails when pfunit is **not** in toplevel CMakeLists.txt #483

Open
jfdev001 opened this issue Mar 5, 2025 · 11 comments
Assignees

Comments

@jfdev001
Copy link

jfdev001 commented Mar 5, 2025

I would like to use pFUnit in my project and download it 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 (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
@mathomp4
Copy link
Collaborator

mathomp4 commented Mar 5, 2025

@jfdev001 First, I really don't think this is the issue, but at least for pFUnit, we never specify the MPI wrapper as the Fortran compiler. In CMake the find_package(MPI COMPONENTS Fortran) should figure out everything CMake needs for linking to MPI.

That said, I think I might understand the issue. Let me ponder on it.

@mathomp4
Copy link
Collaborator

mathomp4 commented Mar 5, 2025

@jfdev001 Well, as we can't really test this easily without help from you, I decided to make a branch with some changes in CMake to see if it helps or not.

Can you try the branch bugfix/mathomp4/483-project-driver in your FetchContent and see what happens? Hopefully some behavior changes that can point us further.

@jfdev001
Copy link
Author

jfdev001 commented Mar 5, 2025

@mathomp4 I tried it with your branch corresponding to the commit 72f9f47 at https://github.com/jfdev001/minimal-fetch-content-pfunit/tree/use-bugfix-mathomp4 and this unfortunately did not fix the problem :(
If you were to pull my branch at the github i linked and then change the test/pFUnit/CMakeLists.txt accordingly, would this help you test? In what other ways can I be of assistance here? I am not very familiar with cmake, so admittedly this problem is a bit challenging for me to debug myself.

@mathomp4
Copy link
Collaborator

mathomp4 commented Mar 5, 2025

@jfdev001 Well, my branch is just our develop with the one change:

develop...bugfix/mathomp4/483-project-driver

Let me try grabbing your repo and building myself.

@mathomp4
Copy link
Collaborator

mathomp4 commented Mar 5, 2025

@jfdev001 This might take more than a day. I'm seeing...weird stuff putting in prints. I might even need to consult people like @ZedThree who I believe added our first FetchContent support.

But I am seeing even more fundamental weirdness. Like I set a variable and try to print it on the next line...and the print is empty.

@mathomp4
Copy link
Collaborator

mathomp4 commented Mar 5, 2025

That said, I am beginning to think that PARENT_SCOPE might be part of the issue. But why that would care if a FetchContent is run from one directory vs another is unknown.

@mathomp4
Copy link
Collaborator

mathomp4 commented Mar 6, 2025

@jfdev001 Can you try the bugfix/mathomp4/483-cache-internal branch? It seems to work for me in your small tester, but it would be nice if you can test it a more complete code as well.

Note: I am still doing internal testing myself, but hopefully it's good on our end too.

@jfdev001
Copy link
Author

jfdev001 commented Mar 7, 2025

The toy example (jfdev001/minimal-fetch-content-pfunit/fixed) works as expected. I don't really have a particularly complete codebase to use to test your fix since I really just wanted to have a sort generic fortran cmake project, but a "slightly" more complete example I provide here ( jfdev001/hello-sparse-blas and build with ./config/intel_ubuntu) and this project also works with your fix!

One thing that is a bit odd though, when calling

# in build/ dir 
ctest -L myproject 

all of the pfunit tests get built even when the intention is to just test my tests (labeled "myproject"). Is this intended behavior?

Test project /home/hades/Dev/minimal-fetch-content-pfunit/fixed/build
[  0%] Built target posix_predefined.x
[  0%] Built target generate_posix_parameters
[  7%] Built target m4_type_includes
[ 33%] Built target gftl-shared-v2
[ 60%] Built target gftl-shared-v2-as-default
[ 61%] Built target fargparse
[ 69%] Built target funit-core
[ 75%] Built target fhamcrest
[ 83%] Built target asserts
[ 84%] Built target funit-main
[ 84%] Built target funit
[ 84%] Built target pfunit-core
[ 84%] Built target pfunit
[ 85%] Built target new_ptests
[ 85%] Built target new_ptests.x
[ 85%] Built target other_shared
[ 87%] Built target funit_tests
[ 87%] Built target funit_tests.x
[ 87%] Built target robust
[ 87%] Built target remote.x
[ 88%] Built target robust_tests.x
[ 94%] Built target new_tests.x
[ 97%] Built target fhamcrest_tests.x
[ 98%] Built target pfunittests
[100%] Built target parallel_tests.x
[100%] Built target build-tests
    Start 33: test_square
1/1 Test #33: test_square ......................   Passed    0.00 sec

100% tests passed, 0 tests failed out of 1

Label Time Summary:
myproject    =   0.00 sec*proc (1 test)

Total Test time (real) =   0.01 sec

@tclune
Copy link
Member

tclune commented Mar 7, 2025

"intended" may not be the right word here. But we did do some non-canonical things with tests in our formative CMake years when we did not quite understand how things work.

On our end, we build and install these layers separately and do not combine them into a single project. Thus don't really see that problem. With a bit of work, I think I can point you to the bits that cause the above behavior. If you find a fix, I'm happy to accept so long as it does not interfere with our other uses.

@jfdev001
Copy link
Author

jfdev001 commented Mar 7, 2025

@tclune

With a bit of work, I think I can point you to the bits that cause the above behavior. If you find a fix, I'm happy to accept so long as it does not interfere with our other uses.

I could look into this so I would appreciate you pointing me in the right direction. I could open a separate issue for this.

@tclune
Copy link
Member

tclune commented Mar 7, 2025

I suspect that it is related to this bit of code in the topmost CMakeLists.txt:

  # The following forces tests to be built when using "make ctest" even if some targets
  # are EXCLUDE_FROM_ALL
  # From https://stackoverflow.com/questions/733475/cmake-ctest-make-test-doesnt-build-tests/56448477#56448477
  build_command(CTEST_CUSTOM_PRE_TEST TARGET build-tests)
  string(CONFIGURE \"@CTEST_CUSTOM_PRE_TEST@\" CTEST_CUSTOM_PRE_TEST_QUOTED ESCAPE_QUOTES)
  file(WRITE "${CMAKE_BINARY_DIR}/CTestCustom.cmake" "set(CTEST_CUSTOM_PRE_TEST ${CTEST_CUSTOM_PRE_TEST_QUOTED})" "\n")

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants