CMake Fortran Module Directory to be used with add_library - module

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

Related

How to correctly link static library build and installed previously

There is a static library called revolta which is being built and then installed into a sysroot:
set( CMAKE_INSTALL_PREFIX <path to sysroot> )
# ReVolta c++ library name
set( TARGET_LIBREVOLTA "revolta" )
add_library( ${TARGET_LIBREVOLTA} STATIC )
target_include_directories( ${TARGET_LIBREVOLTA}
PUBLIC
# Once the librevolta targets are being exported, this include directory in which the lib is installed is used
$<INSTALL_INTERFACE:${CMAKE_INSTALL_PREFIX}/include>
PRIVATE
# Include directory used privately just to build the library itself
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}>
)
target_sources( ${TARGET_LIBREVOLTA}
PUBLIC
...
)
Later then once the librevolta is built, it is installed into the sys root using:
# Install all the revolta headers into include directory and copy the built library
install( TARGETS ${TARGET_LIBREVOLTA} EXPORT ${TARGET_LIBREVOLTA}
FILE_SET HEADERS DESTINATION "${CMAKE_INSTALL_PREFIX}/include"
ARCHIVE DESTINATION "${CMAKE_INSTALL_PREFIX}/lib"
)
and the connected custom command:
# Once the librevolta is built, install it to the sysroot as specified by 'install()' commands
add_custom_command( TARGET ${TARGET_LIBREVOLTA} POST_BUILD COMMAND ${CMAKE_COMMAND} ARGS --install . )
So far so good. This works as intended, once CMake builds the "revolta" target, it is built and installed into the sysroot as installed using the ${CMAKE_INSTALL_PREFIX}.
My problem is once I try to add the target as the linked one in other lib/executable, it includes somehow automatically the librevolta source path into includes and links the library using the relative path in the build directory rather than the one installed into sysroot as performed in the step right after the librevolta build.
Some other lib/executable:
target_link_libraries( ${APP_EXECUTABLE}
PRIVATE
revolta
)
Once being built, the include path -I/home/martin/git/revolta/source/librevolta is added (the source location) even though it is stated as PRIVATE in the snipped above:
PRIVATE
# Include directory used privately just to build the library itself
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}>
and only the ${CMAKE_INSTALL_PREFIX}/include is made public...
Additionally, the library is taken from the build tree rather than from the location where it is installed:
../../librevolta/librevolta.a
instead of
/home/martin/git/revolta/sysroot/lib/librevolta.a
Could you please advice me how to correctly set the revolta target the way it correctly uses its sources for building itself but once used elsewhere it provides the sysroot installed headers and built library from the same location (respecting the standard locations)?
HINT: I also tried to remove the revolta target from the app completely, specifying only to use the sys root (gcc option --sysroot=/home/martin/git/revolta/sysroot), it works fine correct headers and lib is used BUT once the librevolta is not built and installed, the target is not run prior to app build as the dependency is not defined then...
TL;DR: You need to do what's done here:
How to create a ProjectConfig.cmake file
I see a few issues with these CMakeLists.txt files but they aren't related to your problem, because if I understand correctly what you are trying to do here, then there is no problem and it is used as intended.
Let me clarify:
You have a library project that has it's own CMakeLists.txt, where you define the target revolta
You have an executable project that has it's own CMakeLists.txt, where you define your executable target and then you add the revolta target via add_subdirectory() and target_link_libraries(my_executable revolta)
If that's the case then this is just bad:
# Once the librevolta is built, install it to the sysroot as specified by 'install()' commands
add_custom_command( TARGET ${TARGET_LIBREVOLTA} POST_BUILD COMMAND ${CMAKE_COMMAND} ARGS --install . )
Forcing your build to automatically install this library is not the way to go, ever (you for example, need elevated privileges to build it in the first place, because of this command and that poses a security risk).
That being said what is happening is perfectly fine, because from the perspective of the my_executable's CMakeLists.txt you are still building i.e. you use the BUILD_INTERFACE. It is however something you do not want to do.
What instead you want to do is:
Create generator files for a revoltaConfig.cmake file. For that I will refer you to this tutorial:
How to create a ProjectConfig.cmake file
After you create such file, i.e. after building and installing revolta. You will (in the process) also create a revoltaConfig.cmake file. Which helps you populate the my_executable project via find_package(revolta).
The above is probably what you are interested in.
The generator expressions that you use to distinguish BUILD_INTERFACE and INSTALL_INTERFACE are mainly for header file locations (or other linked libraries). Because when you build the library the header files can have a different structure then when you install it (as you already know). And as such work perfectly fine in your CMakeLists.txt, because when you think about it:
You don't want to copy changes to your library files (into the install directory) just to test ongoing development (features/bugfixes) in your executable.
And during the build of the executable if your building another target then IT IS NOT INSTALLED but rather BEING BUILT. And you are most likely adding it as a built target.
So to sum up what would most likely happen here (using your old CMakeLists.txt) is that
The moment you start building the executable which adds the target library as a dependency via add_subdirectory you are implicitly using BUILD_INTERFACE because you are building.
If you were to then install both the executable and the library it would again use the correct install paths, i.e. you would then implicitly start using INSTALL_INTERFACE.
You could hack it without the projectConfig file using the same generator expressions by mixing them up, but I don't recommend it, because then the CMakeLists.txt wouldn't work without doing some weird steps beforehand.

cmake target created with ADD_CUSTOM_TARGET() generated multiple times in parallel mode

I have project name libtld where I first create a tool¹:
project(tld_parser)
add_executable(${PROJECT_NAME}
../tools/tldc.cpp
tld_compiler.cpp
tld_file.cpp
tld_strings.c
)
Then I use that tool to generate the tld_data.c file, which is a table with all the TLDs (in my newer version of the library, this is a copy of the RIFF/TLDS binary file which is to be compiled internally as a fallback). Here is the add_custom_command() I use to generate said file:
project(tld_data)
set(TLD_DATA_C ${PROJECT_BINARY_DIR}/tld_data.c)
file(GLOB_RECURSE TLD_FILES ${CMAKE_SOURCE_DIR}/conf/tlds/*.ini)
add_custom_command(
OUTPUT ${TLD_DATA_C}
COMMAND "tld_parser"
"--source"
"${CMAKE_SOURCE_DIR}/conf/tlds"
"--verify"
"--output-json"
"--include-offsets"
"--c-file"
"${TLD_DATA_C}"
"${PROJECT_BINARY_DIR}/tlds.tld"
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
MAIN_DEPENDENCY tld_parser
DEPENDS ${TLD_FILES}
)
add_custom_target(${PROJECT_NAME} ALL DEPENDS ${TLD_DATA_C})
define_property(SOURCE
PROPERTY GENERATED
BRIEF_DOCS "The tld_data.c file is a table of all the TLDs defined in conf/tlds/... .ini files."
FULL_DOCS "In the new version, the tld_data.c is always generated on the fly since it can be done with just C/C++."
)
Next I want to generate the tld dynamic and static libraries.
set(LIBTLD_SOURCES
tld.cpp
tld_compiler.cpp
${TLD_DATA_C}
tld_domain_to_lowercase.c
tld_emails.cpp
tld_file.cpp
tld_object.cpp
tld_strings.c
)
##
## TLD library
##
project(tld)
configure_file(
tld.h.in
${PROJECT_BINARY_DIR}/tld.h
)
add_library(${PROJECT_NAME} SHARED
${LIBTLD_SOURCES}
)
set_target_properties(${PROJECT_NAME} PROPERTIES
VERSION ${LIBTLD_VERSION_MAJOR}.${LIBTLD_VERSION_MINOR}
SOVERSION ${LIBTLD_VERSION_MAJOR}
)
install(
TARGETS ${PROJECT_NAME}
LIBRARY DESTINATION lib
COMPONENT runtime
)
install(
FILES ${PROJECT_BINARY_DIR}/tld.h
DESTINATION include
COMPONENT development
)
##
## TLD static library
##
project(tld_static)
add_library(${PROJECT_NAME} STATIC
${LIBTLD_SOURCES}
)
add_dependencies(${PROJECT_NAME}
tld
)
# We need the -fPIC to use this library as extension of PHP, etc.
set_target_properties(tld_static PROPERTIES COMPILE_FLAGS -fPIC)
install(
TARGETS ${PROJECT_NAME}
ARCHIVE DESTINATION lib
COMPONENT development
)
I added the add_dependencies() to the tld_static project to depend on tld thinking that would help my case, but I still see two run of the tld_parser...
I also had a test that directly includes the tld_data.c file (that way I can directly verify that the tld() function returns what I would expect from the exact source data).
Adding the add_dependencies() to the test worked as expected. Instead of getting the tld_data.c file created three times, now it's only twice.
project(tld_internal_test)
add_executable(${PROJECT_NAME}
tld_internal_test.cpp
)
add_dependencies(${PROJECT_NAME}
tld
)
add_test(
NAME ${PROJECT_NAME}
COMMAND ${PROJECT_NAME}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
By killing the parallel build feature, it works as expected, however, there should be no good reasons for me to do that (see this cmake question for reference).
What I'm wondering, though, is:
How do you debug such an issue?
cmake's Makefile's are enormous, so reading those is quite a killer. If I knew what to search for, I suppose I could at least look at specific targets instead of trying to understand all that's happening in there (most of which has nothing to do with my issue).
¹ The code is in a branch at the moment. I'm hoping to have it all wrapped up soon at which point it will replace the main branch. The tld() function interface will not have changed very much. The implementation, however, will be quite different, more flexible, dynamically updatable.

Python library and CMake target with the same name

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)

CMake copy dll transitively

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 ...)

Making a CMake library accessible by other CMake packages automatically

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