FetchContent is not setting include path - cmake

I'm trying to link a fortran project against jsonfortran. Below is a mwe which fails because the module file cannot be found. This happens when the include path is not properly set.
main.F90
program thing_program
use json_module, only : json_ck
implicit none
character(kind=json_ck, len=:), allocatable :: json_string
json_string = "{}"
write(*, *) json_string
end program thing_program
CMakeLists.txt
cmake_minimum_required(VERSION 3.21)
project(
myproj
VERSION 0.0.0
LANGUAGES Fortran
)
# don't build json-fortran docs
set(SKIP_DOC_GEN ON CACHE INTERNAL "")
#json fortran uses the compiler id as an identifier
string(TOLOWER ${CMAKE_Fortran_COMPILER_ID} compiler_id)
include(FetchContent)
FetchContent_Declare(jsonfortran-${compiler_id}
GIT_REPOSITORY https://github.com/jacobwilliams/json-fortran.git
GIT_TAG 3ab8f98209871875325c6985dd0e50085d1c82c2 # 8.3.0
# if the project is installed system-wide, use that instead of building it ourselves
FIND_PACKAGE_ARGS NAMES jsonfortran-${compiler_id}
)
FetchContent_MakeAvailable(jsonfortran-${compiler_id})
add_executable( thing main.F90 )
target_link_libraries(thing
PUBLIC
jsonfortran-${compiler_id}
)
Before using FetchContent, I was able to add jsonfortran with find_package, but I had to use target_link_libraries(thing PUBLIC jsonfortran-${compiler_id}::jsonfortran) to get cmake to generate properly. After using FetchContent, that same call to target_link_libraries fails, but works if I change it to this target_link_libraries(thing PUBLIC jsonfortran-${compiler_id}).
I'm guessing that the includes directory isn't being properly set so the fortran mod files cannot be found because the call to target_link_libraries is somehow incorrect, but I am not sure how to make it function properly. Any idea what's going on?
The actual error, if you're interested:
[ 6%] Building Fortran object CMakeFiles/thing.dir/main.F90.o
/Users/kshores/Downloads/thing/main.F90:2:7:
2 | use json_module, only : json_ck
| 1
Fatal Error: Cannot open module file 'json_module.mod' for reading at (1): No such file or directory
compilation terminated.
Update: The mod files are created and placed into _deps/jsonfortran-gnu-build when they should be placed into _deps/jsonfortran-gnu-build/include. With make VERBOSE=1, I see that in fact the include directory is added -I/Users/kshores/Downloads/thing/build/_deps/jsonfortran-gnu-build/include, but the mod files are not there for some reason.

Related

Get all include folders from cmake executable

How can I get all include directories of my executable?
I want to introduce cppcheck and I need to forward all include directories to cppcheck (also the include directories of the interface imported libraries).
So for example I have
add_executable(my_exe main.cpp)
target_link_libraries(my_exe PRIVATE RapidJSON::RapidJSON)
How can I get all corresponding include directories of my_exe, including the RapidJSON ones (e.g. ~/.conan/data/RapidJSON/1.1.0/a/b/package/x/include)?
I tried following without success :-(
get_target_property(ALL_INCLUDES my_exe INTERFACE_INCLUDE_DIRECTORIES) # NOTFOUND
get_target_property(ALL_INCLUDES my_exe INCLUDE_DIRECTORIES) # empty
add_executable(my_exe main.cpp)
target_link_libraries(my_exe PRIVATE RapidJSON::RapidJSON)
# the following line should be adapted so that the variable cppcheck_includes also contains the RapidJSON include directories
set(cppcheck_includes ${CMAKE_SOURCE_DIR}/includes)
add_custom_target(cppcheck
COMMAND cppcheck
--enable=all
--std=c++11
--library=/usr/share/cppcheck/cfg/std.cfg
--check-config
${cppcheck_includes}
main.cpp
)
I expect that there is no warning, but there is a warning:
Include file: <rapidjson/document.h> not found.
In my case I just created a target which runs Cppcheck and uses the compilation database of my project as input - see https://github.com/firewave/mame_regtest/blob/master/cppcheck.cmake.
To generate the compilation database you simply need to add
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
to your CMakeLists.txt.
You could also use the integrated support in CMake but I have no experience with that yet.

CMake Fortran Module Directory to be used with add_library

I have a CMake 3.5.2 project that creates a library: libtest.a, which is then linked to by an executable.
The source code is Fortran, and the libtest.a produces a module file: "main.mod"
The executable also needs to include this main.mod file, so to make main.mod accessible, when building the project I set the variable, CMAKE_Fortran_MODULE_DIRECTORY to a known location, and add it to the relevant include paths.
This works great when building the entire project, main.mod is built in a known location, and it is there for whatever code needs it. My usage, however, makes it necessary to only build libtest.a by itself, and leave the executable to be built by a downstream user sometimes.
The issue I am having is that when I go into the libtest source and treat it as its own CMake project, the library will build and install, but the main.mod file is always left in the BINARY_DIR and is not built in the CMAKE_Fortran_MODULE_DIRECTORY, dispite setting in the the CMakeList.txt within libtest.
Is the Fortran_MODULE_DIRECTORY only honored when add_executable() is being called? And just ignored for the library builds alone? Or am I missing something.
Thanks for the help.
EDIT: This will reproduce my issue.
test_mod.f90:
module main
implicit none
real, parameter :: pi=3.2
end module main
tt.f90:
program test
use main
implicit none
real :: a, area
a =10
area = a * 100
end program test
CMakeList.txt:
CMAKE_minimum_required( VERSION 3.5 )
enable_language( Fortran )
project( tt )
file( GLOB test_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.f90 )
add_library( tt STATIC ${test_SOURCES} )
set( CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/Mod )
install( TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/Lib/ )
If I build and install the above code, I will get a libtt.a library installed in the Lib directory, however my main.mod will remain in my build directory and is not build in a Mod folder.
Here I assume that the "user" uses cmake to build the project while having access to the source of your project.
The steps to a working build.
There is a CMakeLists.txt file for libtest that specifies CMAKE_Fortran_MODULE_DIRECTORY. This should be enough for main.mod to appear there.
There is a CMakeLists.txt file for buiding the "client" program. This file should include the libtest project with add_subdirectory.
Add target_link_libraries(NAME_OF_PROGRAM NAME_OF_LIBRARY). This only takes care of the linking of libraries and is not sufficient (for solution B below anyway) for the module to be known to the client program.
Now, make your own adventure:
Solution A: in the libtest CMakeLists.txt, place the module where "all modules will go", for instance set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/modules) (you need to do this also for the "client" CMakeLists.txt). The variable ${CMAKE_BINARY_DIR} is set by the "client" cmake invocation and will be the same for all included cmake projects. This directory will be listed in the build commands for Fortran programs.
Solution B: in the libtest CMakeLists.txt, place the module of this library in a dedicated directory. You can achieve this, for instance, with set(CMAKE_Fortran_MODULE_DIRECTORY ${PROJECT_BINARY_DIR}/modules). You need then to manually specify this location with include_directories(PATH_THAT_DEPENDS_ON_THE_NAME_OF_THE_SUBPROJECT) in the client CMakeLists.txt.
If you wish the library to be installable, you need to specify paths for installing the library and the module file. With Fortran, you should think of this with the target OS, compiler and architecture in mind.
Links to the CMake documentation:
PROJECT_BINARY_DIR
CMAKE_Fortran_MODULE_DIRECTORY
CMAKE_BINARY_DIR
Following the addition of your sample code, the following modification should do it:
CMAKE_minimum_required( VERSION 3.5 )
enable_language( Fortran )
project( tt )
set( CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/Mod )
file( GLOB test_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.f90 )
add_library( tt STATIC ${test_SOURCES} )
install( TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/Lib/ )
install(DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} DESTINATION ${CMAKE_CURRENT_SOURCE_DIR})
Make sure that set( CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/Mod ) occurs before any add_library line.
Add install(DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}) to actually install the .mod file. Module files (as header files in C) have to installed in addition to the library file.
The setup you created is a bit unusual in that you locate everything within the source build whereas "usual" install locations are made relative to CMAKE_INSTALL_PREFIX

Antlr4 with C++ and CMake

I'm trying to use antlr4 with C++. I have the following CMakeLists.txt in my root directory:
cmake_minimum_required(VERSION 3.10)
project(demo VERSION 0.1 DESCRIPTION "demo")
include_directories(${CMAKE_CURRENT_BINARY_DIR})
set(CMAKE_CXX_STANDARD 17)
#############################################################################
# ANTLR SPECIFIC CMAKE COMMANDS
# This is derived from: https://github.com/blorente/antlr-4.7-cpp-cmake-base
#############################################################################
# Set the ocation of the JAR.
set(ANTLR4CPP_JAR_LOCATION ${CMAKE_SOURCE_DIR}/antlr/jar/antlr.jar)
# Add external build for antlrcpp.
include (${CMAKE_SOURCE_DIR}/antlr/runtime/ExternalAntlr4Cpp.cmake)
# add antrl4cpp artifacts to project environment.
include_directories( ${ANTLR4CPP_INCLUDE_DIRS} )
link_directories( ${ANTLR4CPP_LIBS} )
# message(WARNING "Found antlr4cpp libs: ${ANTLR4CPP_LIBS} and includes: ${ANTLR4CPP_INCLUDE_DIRS} ")
# Build the lexer/parser .h/.cpp files off the g4 grammar files.
antlr4cpp_process_grammar(demo demoparser
${CMAKE_SOURCE_DIR}/grammar/DemoLexer.g4
${CMAKE_SOURCE_DIR}/grammar/DemoParser.g4)
# include the generated files from the grammar/lexer.
include_directories(${antlr4cpp_include_dirs_demoparser})
#############################################################################
# Build demo
add_executable(demo main.cpp ${antlr4cpp_src_files_demoparser})
# Add dependencies for antlr
add_dependencies(demo antlr4cpp antlr4cpp_generation_demoparser)
target_link_libraries(demo antlr4-runtime)
I have, more or less, copied the example from: the "official" cmake/antlr tutorial. For brevity I have omitted the contents of include (${CMAKE_SOURCE_DIR}/antlr/runtime/ExternalAntlr4Cpp.cmake)
So the files are all in the same structure, except the jar. I just include it as a project file and, as you can see from the set(ANTLR4CPP_JAR_LOCATION) command, it resides where I want it.
When I build this, I get this error:
dyld: Library not loaded: libantlr4-runtime.4.7.1.dylib
Referenced from: /path/to/demo/cmake-build-debug/demo
Reason: image not found
However, when I look in: /path/to/demo/cmake-build-debug/external/antlr4cpp/lib/ I see the libantlr4-runtime.4.7.1.dylib file that should be referenced by using the target_link_libraries(demo antlr4-runtime) command. There are static libraries in there as well.
Am I missing something? Why is CMake not finding the static or dylib files it's supposed to?
I didn't realize, right before I started this, that I ran brew install antlr4-cpp-runtime. Which installed the headers to the path. I did this on my machine, but not on any of the other machines that build this project. The outstanding problem is why is it not deterministic is if the runtime isn't installed. But it works immediately upon installing the runtime.

What are the function calls like `BCI2000_INCLUDE( "SOURCEFILTER" )` in CMake?

I want to compile the Emotiv EPOC module for BCI2000. In that module CMakeLists file contains following(below the following error log).
The compiler gives the following error,
-- Selecting Windows SDK version to target Windows 10.0.10586.
CMake Error at CMakeLists.txt:21 (BCI2000_INCLUDE):
Unknown CMake command "BCI2000_INCLUDE".
CMake Warning (dev) in CMakeLists.txt:
No cmake_minimum_required command is present. A line of code such as
cmake_minimum_required(VERSION 3.10)```
CMakeLists.txt contains this,
###########################################################################
## $Id$
## Authors: griffin.milsap#gmail.com
## Description: Build information for the Emotiv module
# Set the executable name
SET( EXECUTABLE_NAME Emotiv )
# Set the project specific sources
SET( SRC_PROJECT
EmotivADC.cpp
lib/edk.imports.cpp
)
SET( HDR_PROJECT
EmotivADC.h
lib/edk.imports.h
)
INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_SOURCE_DIR}/lib/include )
BCI2000_INCLUDE( "SOURCEFILTER" )
IF( WIN32 )
# Create the signal source module
BCI2000_ADD_SIGNAL_SOURCE_MODULE(
"${EXECUTABLE_NAME}"
"${SRC_PROJECT}" "${HDR_PROJECT}"
"${CMAKE_CURRENT_SOURCE_DIR}/dll/edk.dll"
"${CMAKE_CURRENT_SOURCE_DIR}/dll/edk_utils.dll"
)
ELSE()
MESSAGE( "--- No Emotiv libraries for this OS. Will not build." )
ENDIF()
How can I fix this?
Looks like they are function calls, but how do I import those function calls from another file if they are declared somewhere?
The project has to be compiled according to the instructions in this,
https://www.bci2000.org/mediawiki/index.php/Programming_Reference:Build_System
And it says the supported Visual Studio editions are 9(2008) and 10 only or you can compile it with CodeBlocks with MinGW.
And the supportive modules should be included into the source folder of BCI2000 and compile it as a whole project not as seperate modules. Then it recognized the functions defined in the parent project.
There are cMakeList files for submodules. But they cannot be compiled on their own.

Fortran module files not found by CMake

I have a split project in Fortran with a subdirectory as a library:
# ./CMakeLists.txt
cmake_minimum_required (VERSION 2.8)
project (Simulation Fortran)
enable_language(Fortran)
add_subdirectory(lib)
add_executable(Simulation main.f90)
include_directories(lib)
add_dependencies(Simulation physicalConstants)
target_link_libraries(Simulation physicalConstants)
The root directory contains only a single Fortran source code file:
! ./main.f90:
program simulation
use physicalConstants
implicit none
write(*,*) "Boltzmann constant:", k_b
end program simulation
And my subdirectory lib contains another CMakeLists.txt as well as a Fortran module source file:
# ./lib/CMakeLists.txt:
cmake_minimum_required (VERSION 2.8)
enable_language(Fortran)
project(physicalConstants)
add_library( physicalConstants SHARED physicalConstants.f90)
! ./lib/physicalConstants.f90:
module physicalConstants
implicit none
save
real, parameter :: k_B = 1.38e-23
end module physicalConstants
I tried to build those using cmake. Make generates the physicalconstants.mod in the lib directory, but this file is not found during the build process of main.f90.o:
Fatal Error: Can't open module file 'physicalconstants.mod' for reading at (1): No such file or directory
What am I missing here?
For target A to successfully use modules from target B, the directory where B stores module files must be among A's include directories.
Variant 1
One way to achieve that is to set the property Fortran_MODULE_DIRECTORY on target B and then add that property's contents to include directories of A.
You're claiming to support ancient CMake 2.8.0, in which you'll need to do something like this:
add_executable(Simulation main.f90)
include_directories(lib)
# note that add_dependencies call is not necessary when you're actually linking
target_link_libraries(Simulation physicalConstants)
get_property(moduleDir TARGET physicalConstants PROPERTY Fortran_MODULE_DIRECTORY)
include_directories(${moduleDir})
In more modern CMake, you could do this instead:
add_executable(Simulation main.f90)
include_directories(lib)
target_link_libraries(Simulation physicalConstants)
target_include_directories(Simulation PUBLIC $<TARGET_PROPERTY:physicalConstants,Fortran_MODULE_DIRECTORY>)
You can even create a function for it:
function(LinkFortranLibraries Target)
target_link_libraries(Target ${ARGN})
foreach(Lib IN LISTS ARGN)
target_include_directories(Simulation PUBLIC $<TARGET_PROPERTY:${Lib},Fortran_MODULE_DIRECTORY>)
endforeach()
endfunction()
and then use it like this:
add_executable(Simulation main.f90)
include_directories(lib)
LinkFortranLibraries(Simulation physicalConstants)
Variant 2
If you do not use the Fortran_MODULE_DIRECTORY property, module files are stored in the binary directory corresponding to the source directory of the target producing them. This can be retrieved from the target's property BINARY_DIR, which you could use exactly as Fortran_MODULE_DIRECTORY in variant 1.
However, CMake 2.8.0 does not support the target property BINARY_DIR, so you will have to "reconstruct" its value manually:
add_executable(Simulation main.f90)
include_directories(lib ${CMAKE_CURRENT_BINARY_DIR}/lib)
target_link_libraries(Simulation physicalConstants)