CMake all_object_files target - cmake

I want to create a CMake target to build all object files.
To do this, I first modified all targets to build an object file lib and link against it:
add_library(exe_object_files OBJECT ${sources})
add_executable(exe $<TARGET_OBJECTS:exe_object_files>)
Since I have multiple executables, I added a target for all object files and added the targets for each executable as dependency:
add_custom_target(all_exe_object_files)
add_dependencies(all_exe_object_files exe_object_files)
This adds a all_exe_object_files target I can make, but making the target does nothing.
Then I tried adding the object files as sources:
get_target_property(all_exe_object_files_sources all_exe_object_files SOURCES)
if(NOT all_exe_object_files_sources)
set(all_exe_object_files_sources)
endif()
list(APPEND all_exe_object_files_sources $<TARGET_OBJECTS:exe_object_files>)
set_target_properties(all_exe_object_files PROPERTIES SOURCES "${all_exe_object_files_sources}")
This did not help.
I guess the reason is that add_library(... OBJECT ...) behaves similar to add_custom_command and the files are only build if a target uses them (and my custom target probably does not count as using the generated files).
What am I doing wrong and how can I create a target to build all object files?

Related

Run cmake function or add_custom_command only once

I have a ugly solution for copying .dlls to a bin folder that uses add_custom_command.
It works but in parallel build I am sometimes getting
Error copying directory from
My guess(I can not know since CMake will not tell me what failed) is that because calls to copy are done at same time so filesystem error occur.
Is there a way in cmake to specify that only first invocation of function or add_custom_command should run?
If it matters this is my copy dlls logic(every target needing those dlls calls the function with itself as argument):
function(copyBla projectName)
add_custom_command(TARGET ${projectName} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${SDK_PATH}/Bla/${BLA_VERSION}/x64/bin ${CMAKE_BINARY_DIR}/bin/$<CONFIGURATION>)
endfunction()
P.S.
I do not want to limit myself to just one target calling copyBla since that is fragile(other targets depend on this target just because they want to use same dlls).
I think they fixed this pretty well in 3.21
$<TARGET_RUNTIME_DLLS:tgt>
New in version 3.21.
List of DLLs that the target depends on at runtime. This is determined by the locations of all the SHARED and MODULE targets in the target's transitive dependencies. Using this generator expression on targets other than executables, SHARED libraries, and MODULE libraries is an error. On non-DLL platforms, it evaluates to an empty string.
This generator expression can be used to copy all of the DLLs that a target depends on into its output directory in a POST_BUILD custom command. For example:
find_package(foo CONFIG REQUIRED) # package generated by install(EXPORT)
add_executable(exe main.c)
target_link_libraries(exe PRIVATE foo::foo foo::bar)
add_custom_command(TARGET exe POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_RUNTIME_DLLS:exe> $<TARGET_FILE_DIR:exe>
COMMAND_EXPAND_LISTS
)
Note Imported Targets are supported only if they know the location of their .dll files. An imported SHARED or MODULE library must have IMPORTED_LOCATION set to its .dll file. See the add_library imported libraries section for details. Many Find Modules produce imported targets with the UNKNOWN type and therefore will be ignored.
Link to docs as requested

How to set include directories

I have a directory with various pre-built libraries (mostly headers and libs/dlls, all built without CMake or copied without installing using CMake). For my next project i want to use these libraries with CMake.
I am writing a script that iterates over all the libraries and generates the needed <name>Config.cmake and <name>ConfigVersion.cmake files referencing the existing directory structure.
A minimal config file may look like:
SET(ROOT_DIR_MYLIB "${CMAKE_CURRENT_LIST_DIR}/../../../../MyLib")
add_library(MyLib STATIC IMPORTED)
set_target_properties(MyLib PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${ROOT_DIR_MYLIB}/include"
IMPORTED_LOCATION "${ROOT_DIR_MYLIB}/Lib/MyLib.lib"
IMPORTED_LOCATION_DEBUG "${ROOT_DIR_MYLIB}/Lib/MyLibd.lib"
)
I have copied the line where the property INTERFACE_INCLUDE_DIRECTORIES is set from another project where this worked, but for some reason it does not for MyLib. When i attempt to include its header files i get compile errors that the include files can not be found.
What am i missing here?

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)

Why use add_library({tgt} IMPORTED) versus target_link_libraries( -l {.so | .a})?

What is the purpose of using the statement:
add_library(<tgt> [SHARED|STATIC] IMPORTED)
From what I have found even if you create an imported library target above you still would need to specify the specific location of the actual .so or .a. This would take at least 3 cmake commands to link to an executable and the compiler still would not automatically search through the common include directories on your OS.
Example:
cmake code to link IMPORTED lib
From the CMake documentation I understand there are really 3 ways to link a library that is not built as a target in a subproject of your overall application/library.
CMake target_link_libraries() documentation
Using a CMake package for one of the shipped package scripts.
Using a linker flag:
target_link_libraries(<tgt> [SHARED|STATIC|...] -lncursesw)
Or using the IMPORTED library method (showcased in code at top).
A major difference when using the second method is that it only takes a single line of code and will search through all of your compiler's predefined include directories on you OS. Could anyone help me understand why the add_library() method is used?
Additional Realated SO Posts:
Include directories for IMPORTED libs
CMake imported library behavior
You should use add_library(<tgt> [SHARED|STATIC] IMPORTED) whenever you need to set properties such as dependencies, compile definitions, compile flags etc for <tgt>, and/or by extension, any targets that are linking against <tgt>.
Let's say you have two static libraries; libfoobar.a and libraboof.a, where libfoobar.a requires libraboof.a. Let's also say that these libraries contain some features that are enabled by -DSOME_FEATURE.
add_library(raboof STATIC IMPORTED)
set_target_properties(raboof PROPERTIES
IMPORTED_LOCATION <path-to-libraboof.a>
INTERFACE_COMPILE_DEFINITIONS "SOME_FEATURE"
)
add_library(foobar STATIC IMPORTED)
set_target_properties(foobar PROPERTIES
IMPORTED_LOCATION <path-to-libfoobar.a>
INTERFACE_LINK_LIBRARIES raboof
)
So when you link against libfoobar.a:
add_executable(my_app main.cpp)
target_link_libraries(my_app foobar)
CMake will make sure to link all dependencies in the correct order and will in this case also append -DSOME_FEATURE to the compile flags when you build my_app. Note that since we added libraboof.a as a dependency to libfoobar.a, -DSOME_FEATURE is added to any target that link against libfoobar.a through the transitive property.
If you don't use add_library(<tgt> <SHARED|STATIC> IMPORTED) in a scenario like this, you would have to manage any dependencies and required build options yourself for each target, which is quite error-prone.
This method is also often used in Config-modules for multi-component libraries to manage dependencies between the components.

Set output directory for CMake OBJECT libraries

In my CMake file I've specified an object library:
add_library(core OBJECT ${sourcefiles})
I refer to this set of object file in a shared library further on:
add_library(sharedlib SHARED $<TARGET_OBJECTS:core>)
This works fine, however I would like to reuse the build artefacts between different project.
By setting the LIBRARY_OUTPUT_PROPERTY on the sharedlib I can direct the generated .so file to a common directory:
set_target_properties(sharedlib PROPERTIES LIBRARY_OUTPUT_NAME /commondir)
However, I cannot seem to do the same thing for the core (OBJECT) library - the .o files always end up in the (project-specific) generated cmake-build directories.
This means every project will have to rebuild the (large) shared library anyway...
Am I doing something wrong, or is this not (yet?) possible with CMake?
You can't change the output directory of object/intermediate files in CMake. That's by design (to allow several identical output names in one project):
"There is no way to change that name.
CMake creates an exact copy of the source tree in
the binary tree."
But with version 3.9 CMake learned to export object libraries:
cmake_minimum_required(VERSION 3.9)
project(CorePorject)
file(WRITE "core.cpp" "")
file(WRITE "helper.cpp" "")
set(sourcefiles "core.cpp" "helper.cpp")
add_library(core OBJECT ${sourcefiles})
export(
TARGETS core
FILE core.cmake
NAMESPACE Core_
)
References
Set C++ object file output location with CMake
Set the directory for Intermediate files (like .obj) in CMake
Making cmake library accessible by other cmake packages automatically