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)
Related
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.
I'm constructing a library "mylib" that is C++ header-only and has a Python API using pybind11.
I want to use "mylib" both as CMake target, containing compile instructions, and as name of the Python API. However, this leads to a name conflict.
Problem description
Consider the following file structure:
CMakeLists.txt
include/mylib.hpp
python_api.cpp
In reality there are also tests and examples, each with their own CMakeLists.txt, but for the purpose of this example the only thing that matters is:
In the (main) CMakeLists.txt I am defining a CMake target "mylib" that has the include path to the header(s), but also 'links' the targets of dependencies. So that the user (or tests, examples, or build of the Python API) only has to 'link' the target and be good to go. (Finally, I'm also installing the target in mylibTargets.cmake when I install the headers such that there is CMake support for the end user).
Now the problem: My Python package should have the same name, "mylib". However, if I call pybind11_add_module with "mylib", CMake complains that
CMake Error at .../share/cmake/pybind11/pybind11Tools.cmake:166 (add_library):
add_library cannot create target "mylib" because another target with the
same name already exists. The existing target is an interface library
created in source directory "..".
See documentation for policy CMP0002 for more details.
It has the right to complain. At the same time I cannot use a different name for either the CMake target (since I want to install and use it using the only logical name, "mylib") or the pybind11 target (since it has to encode "mylib").
So: how do I solve this?
(The only solution I found was to rename one of targets, but as described I don't want to do this)
Detailed example
Consider the simplified, single, CMakeLists.txt:
cmake_minimum_required(VERSION 3.1..3.19)
# configure target
project(mylib)
find_package(xtensor REQUIRED)
add_library(${PROJECT_NAME} INTERFACE)
target_include_directories(${PROJECT_NAME} INTERFACE
$<INSTALL_INTERFACE:include>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>)
target_link_libraries(${PROJECT_NAME} INTERFACE xtensor)
# installation of headers and of CMake target
include(CMakePackageConfigHelpers)
include(GNUInstallDirs)
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/" DESTINATION include)
install(TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME}-targets)
install(
EXPORT ${PROJECT_NAME}-targets
FILE "${PROJECT_NAME}Targets.cmake"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
# Build Python module
find_package(pybind11 CONFIG REQUIRED)
pybind11_add_module(${PROJECT_NAME} python_api.cpp) # <- target name conflict
target_link_libraries(example PUBLIC pybind11::module)
Too limited work around
I could entirely split building (and later install) the Python API to an independent CMakeLists.txt. However, I want to use the target "mylib", that I already equipped with everything it needs, to build the Python API. Since I want to do this without being forced to install the library forced, I don't know how to do this in a 'single' CMakeLists.txt
pybind11_add_module is just a wrapper around add_library, this is explicitely written in the documentation for that function. So, most of the "tricks", which works for the common libraries, works for python modules too.
That is, if you want resulted file to be named as mylib.so but cannot afford you to use mylib as a target name, then you could use any other name for the target but adjust OUTPUT_NAME property for that target. For example:
# Python library target has suffix '_python'
pybind11_add_module(mylib_python ...)
# But name of the library file doesn't have this suffix
set_target_properties(mylib_python PROPERTIES OUTPUT_NAME mylib)
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.
Basically I want CMake to copy dependency's dll to the same directory of the executable. Suppose I have the following directory structure:
my-project/
CMakeLists.txt
lib/
CMakeLists.txt
... # Some source files
app/
CMakeLists.txt
... # Some source files
The library lib depends on some third party dll, say foo.dll. The executable in app, say app.exe, depends on lib.
I've written a FindFOO.cmake to make the third party library foo.dll an imported target named foo.
Now when I compile app, in order to run the executable, foo.dll is required to be in the same directory as app.exe. How can this be achieved automatically with cmake? And what if I want to use CPack to package the application into an installer?
CMAKE_RUNTIME_OUTPUT_DIRECTORY is your friend.
If this variable is created before creating some target, if the target is RUNTIME, it will define where the output of the target will be placed.
In your case, it can be used to force foo.dll and app.exe to be in the same folder. Your root CMakeLists.txt should look like this:
cmake_minimum_required(VERSION 3.15)
project(foo_and_app)
# app.exe and foo.dll will be in bin subfolder of build dir
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
add_subdirectory(lib)
add_subdirectory(app)
#not connected to placement, but only to package creation
include(CPack)
It should be noted that this variable is used to initialize the properties of the targets added, meaning that everything may also be achieved by directly manipulating appropriate target properties.
Regarding packaging, what you ask is possible, regardless of the placement of runtime targets, by using install cmake statement. In lib/CMakeLists.txt you should add something like this:
# suppose that the target named `foo`,
# i.e. it is added by add_library(foo SHARED .....)
install(TARGETS foo
RUNTIME DESTINATION bin
)
same should be done for app/CMakeLists.txt:
# suppose that the target named `app`,
# i.e. it is added by add_executable(app .....)
install(TARGETS app
RUNTIME DESTINATION bin
)
If you have these install statements, the final destination will be bin folder within the chosen install folder.
In the end, here are the links for CMake documentation describing:
CMAKE_RUNTIME_OUTPUT_DIRECTORY variable
RUNTIME cmake targets
install(TARGETS ...)
I have one project that produces a library:
project (myCoolLibrary)
ADD_LIBRARY(my_cool_library SHARED ${mysources_SRC})
And another project that should be using this library:
find_package (myCoolLibrary REQUIRED)
INCLUDE_DIRECTORIES("${myCoolLibrary_INCLUDE_DIRS}" )
add_executable(myCoolExe ${my_sources_SRC} )
TARGET_LINK_LIBRARIES(myCoolExe ${myCoolLibrary_LIBRARIES} )
Is there a way that I can change the first file so that the second file works automatically? That by running CMake on the first file and then running make on the output, then running CMake on the second file, CMake is able to find the package?
An answer where I just give the address of where the first project is built to the second package is also acceptable.
Taking the code found in a blog post by #daniperez - Use CMake-enabled libraries in your CMake project (III) - I've come up with the following minimal solution:
myCoolLibrary/CMakeLists.txt
cmake_minimum_required(VERSION 3.3)
project(myCoolLibrary)
function(my_export_target _target _include_dir)
file(
WRITE "${CMAKE_CURRENT_BINARY_DIR}/${_target}Config.cmake"
"
include(\"\$\{CMAKE_CURRENT_LIST_DIR\}/${_target}Targets.cmake\")
set_property(
TARGET ${_target}
APPEND PROPERTY
INTERFACE_INCLUDE_DIRECTORIES \"${_include_dir}\"
)
"
)
export(TARGETS ${_target} FILE "${CMAKE_CURRENT_BINARY_DIR}/${_target}Targets.cmake")
# NOTE: The following call can pollute your PC's CMake package registry
# See comments/alternatives below
export(PACKAGE ${_target})
endfunction(my_export_target)
...
add_library(${PROJECT_NAME} SHARED ${mysources_SRC})
my_export_target(${PROJECT_NAME} "${CMAKE_CURRENT_SOURCE_DIR}")
myCoolExe/CMakeLists.txt
cmake_minimum_required(VERSION 3.3)
project(myCoolExe)
find_package(myCoolLibrary REQUIRED)
...
add_executable(${PROJECT_NAME} ${my_sources_SRC})
target_link_libraries(${PROJECT_NAME} myCoolLibrary)
To make it reusable I have packed everything into my_export_target(). And I'm friend of self-propagating properties like INTERFACE_INCLUDE_DIRECTORIES.
As commented by #ruslo, using export(PACKAGE ...) can pollute your package registry. So alternatively you can:
Write the target configuration files directly to some dedicated place specific for a certain toolchain
See e.g. How to install your custom CMake-Find module and 0003659: FIND_PACKAGE command improvements.
Set CMAKE_MODULE_PATH via the second project's CMake command line (injecting the search path(s) from the outside). If you are building the two projects anyway with a build script, then this is the most direct way to propagate the module search path(s).
Additional References
export()
CMake/Tutorials/Package Registry
Unable to find Eigen3 with CMake
How to instruct CMake to use the build architecture compiler