CMake add_library with future sources [duplicate] - cmake

This question already has an answer here:
Creating a library in CMake depending on source files not available when generating build files
(1 answer)
Closed 2 years ago.
I have a CMake file with two targets - gen and lib2. lib2 depends on gen. The purpose of gen, which is custom target, is to take some interface description XML file and generate several C++ source and header files. I know what the result file names will be, but they don't exist yet at the time the CMake configuration is run.
Can I add them to the lib2 library in add_library? Using simple list in add_library produces "Cannot find source file" error.

If it is not possible to build and run gen separately first. Perhaps you could create empty files first (since you know the file names).
For example:
# Your gen custom target
add_custom_target ( gen ... )
# Create empty file with name source.cpp
file(WRITE source.cpp) # CMake 3.0-3.11
file(TOUCH source.cpp) # CMake 3.12+
# Create target lib2
add_library(lib2 STATIC source.cpp)
# Ensures gen is built before lib2
add_dependencies(lib2 gen)

You add dependencies between targets using add_dependencies:
add_dependencies(lib2 gen)

This question is essentially a duplicate of this question.
The solution is to put the generator code into create_custom_command. Then have the gen target depend on the file that the aforementioned custom command generates. Then have lib2 depend on gen.

Related

In CMake, is it possible to add unknown generated source file to target in build time? [duplicate]

This question already has an answer here:
CMake globbing generated files
(1 answer)
Closed last year.
In the below minimal example, if I know the name of the generated source file in advance (test1.txt), I could do the below
cmake_minimum_required(VERSION 3.20)
project(example)
add_executable(example main.cpp ${CMAKE_BINARY_DIR}/test1.txt)
set_source_files_properties(${CMAKE_BINARY_DIR}/test1.txt PROPERTIES GENERATED TRUE)
However, how can I achieve the same thing if I don't know the name of the generated files in advance? For my application, it will also work if the generated files are added to another target instead of the original one. Please see below of my failed attempt
cmake_minimum_required(VERSION 3.20)
project(example)
add_executable(example main.cpp)
add_custom_target(
example-generate
COMMAND $<TARGET_FILE:example>
COMMAND "using bash commands to glob the generated files but need to find a way to output the result to a cmake variable"
DEPENDS example)
add_library(example2 OBJECT) # the target with the generated unknown sources, but I can't find a way to add sources to it in build time
add_dependencies(example2 example-generate)
You can generate a dummy source file with a concrete name that #includes all the others.
You might need to write out a DEPFILE to get accurate incremental-build dependencies (if the list of files depends on something other than the example sources/binary or command-line invocation).

CMake importing both shared and static library versions, but I only want one

I would like to use the Antlr framework in a project. I'm using CMake to build the project.
I would like to use the SHARED library version of Antlr, not the STATIC one. Its CMake file contains targets for both.
Antlr's github site explicity tells me to use the following code:
find_package(antlr4-runtime REQUIRED)
# add runtime include directories on this project.
include_directories( ${ANTLR4_INCLUDE_DIR} )
# add runtime to project dependencies
add_dependencies( Parsertest antlr4_shared )
# add runtime to project link libraries
target_link_libraries( Parsertest PRIVATE
antlr4_shared)
(another target, antlr4_static, exists, but shouldn´t be used.)
I copied it exactly like this and am getting the following error:
CMake Error at /usr/lib64/cmake/antlr4-runtime/antlr4-targets.cmake:82 (message):
The imported target "antlr4_static" references the file
"/usr/lib/libantlr4-runtime.a"
but this file does not exist.
I dont have the static library installed in my system as I have no intention of using it. Still, how do I make CMake stop looking for the wrong target in the first place? I use it nowhere in my CMakeLists.txt file and am puzzled by this behavior.

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

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