"config-style" cmake find_package unusable in parent scope - cmake

I have the following structure
project_root/
CMakeLists.txt (A)
ext/
CMakeLists.txt (B)
apps/
CMakeLists.txt (C)
The setup seems to be the fundamental issue, only when adding this new "config-style" library.
TL;DR: when find_package(foo) in (B) defines foo::foo as the library, how can I make foo::foo available in the parent scope so that target_link_libraries(tgt foo) will work for both (A) and (C)?
List (A) defines my project's options, such as what drivers to compile support for.
add_subdirectory(ext) takes place, and the needed external libraries are found. They are a mixture of add_subdirectory and find_package. List (B) populates lists for extra include directories, libraries, and compile time definitions, making them available to (A) (and subsequently (C)) with
set(MYPROJ_EXTRA_INC_DIRS "${MYPROJ_EXTRA_INC_DIRS}" PARENT_SCOPE)
set(MYPROJ_EXTRA_LIBS "${MYPROJ_EXTRA_LIBS}" PARENT_SCOPE)
set(MYPROJ_EXTRA_DEFINES "${MYPROJ_EXTRA_DEFINES}" PARENT_SCOPE)
List (A) now adds my library, including these extra directories, adding these extra definitions, and ultimately
target_link_libraries(${MYPROJ_LIB_NAME} ${MYPROJ_EXTRA_LIBS})
When the applications are requested to be built, add_subdirectory(apps) takes place, and list (C) defines a simple macro that creates an executable using the specified dependencies. The relevant part
target_link_libraries(${appName} ${MYPROJ_LIB_NAME} ${MYPROJ_EXTRA_LIBS})
This has been working very well for a long time. However, I added support for a new library that uses config-style find_package definitions, and I can't figure out how to use it correctly.
Call this new library dependency foo. It ultimately defines a single foo_LIBRARY which is foo::foo. My understanding was that I would need to do target_link_libraries(tgt foo), which works in list (A) for my library. However, it does not work for the applications, and in the macro I have to do find_package(foo) again for every executable.
Is there a way to use the existing approach (list(APPEND MYPROJ_EXTRA_LIBS <something>)) that does not require running find_package every time?
I've exhausted every reasonable option, and either get that -lfoo is not defined (if I just append foo to the list like I thought I should be), or find_package() is missing for an IMPORTED or ALIAS target. AKA since find_package(foo) happens in (B), by the time we reach (C) this target is not available. I tried making an ALIAS, but the error was then something that amounts to ALIAS cannot be created to an IMPORTED library.

Results of find_package call(both CONFIG and MODULE) are intended be used in the same directory or below. You are just lucky in that simple propagating of variables into PARENT_SCOPE makes results of find_package usable by the parent.
add_subdirectory(ext) takes place, and the needed external libraries are found.
Instead of ext/CMakeLists.txt included with add_subdirectory create CMake file (e.g. external.cmake) for being included via include. Because include command doesn't introduce new variable's scope, its find_package calls works for the main CMakeLists.txt.
Many existed projects process their dependencies in include files.
Another approach would be propagating results of find_package calls from subdirectory to the parent by creating INTERFACE library target which itself uses these results:
add_library(MyLibExtra INTERFACE)
target_link_libraries(MyLibExtra INTERFACE ${MYPROJ_EXTRA_LIBS})
target_include_directories(MyLibExtra INTERFACE ${MYPROJ_EXTRA_INC_DIRS})
target_compile_definitions(MyLibExtra INTERFACE ${MYPROJ_EXTRA_DEFINES})

Related

How to link a shared library without having to export its internal targets

The following repro cmake project fail to configure. The goal is to create a shared library that internally consists of a couple of static libraries. I want the internal symbols and include paths etc to be exported from the shared library. To do that I make the internal libs PUBLIC link libraries. But then cmake tells me I need to export my target. I don't want to "pollute" my package config with a bunch of internal targets. Is there no way to "merge" internal targets (static libs) into the public exported target (shared lib) or hide it in the config targets file?
The SystemC::systemc imported target does not show up in my target file and it also don't generate any error. I assume that is because this target is already exported in its own package? If so, would that mean I need to make my internal libs packages and so imported targets to make them go away from my exported targets list?
cmake_minimum_required(VERSION 3.16)
project(mylib)
find_package(SystemCLanguage 2.3.3 CONFIG REQUIRED)
add_library(mysublib STATIC mysublib.cpp)
add_library(mylib SHARED mylib.cpp)
target_link_libraries(mylib
PUBLIC SystemC::systemc
PUBLIC mysublib
)
export(
TARGETS mylib
NAMESPACE MyLib::
FILE MyLibTargets.cmake
)
Error generated at configure
CMake Error in CMakeLists.txt:
export called with target "mylib" which requires target "mysublib" that is
not in any export set.
The goal is to create a shared library that internally consists of a couple of static libraries. I want the internal symbols and include paths etc to be exported from the shared library. To do that I make the internal libs PUBLIC link libraries.
So, first, I'll note that PUBLIC has nothing at all to do with symbol visibility. Your example mysublib doesn't have any INTERFACE properties, so you could make it PRIVATE to mylib and avoid the need to export mysublib.
However, if you do need mysublib in the INTERFACE of mylib, then you do need to export it, period. There is no way to, as you say "'merge' internal targets [...] into the public exported target [...] or hide it in the config targets file". But this is also not a real problem.
If you're concerned about people relying on mysublib, then you can set the EXPORT_NAME property of mysublib to something that indicates it's not meant to be used, like _private_mysublib:
set_target_properties(mysublib PROPERTIES EXPORT_NAME _private_mysublib)
If you want to be really aggressive about it, you could even make the name random:
string(RANDOM LENGTH 12 mysublib_export)
set_target_properties(mysublib PROPERTIES EXPORT_NAME "x${mysublib_export}")
Prepending an x makes sure it doesn't start with a number. There is also a remote chance of collision if you do this a lot. If you want to be absolutely safe, you should write a function that records the names it's already returned in a global property and tries again if it chooses a collision.
The SystemC::systemc imported target does not show up in my target file and it also don't generate any error. I assume that is because this target is already exported in its own package? If so, would that mean I need to make my internal libs packages and so imported targets to make them go away from my exported targets list?
You are correct: because SystemC::systemc is imported, it does not need to be (and in fact cannot be) re-exported. You're expected to call find_dependency(SystemCLanguage 2.3.3) in your MyLibConfig.cmake file, both within the build tree (export) and after install (install(EXPORT)).
If you want this behavior to apply to mysublib, then yes, you will need to split your projects apart. I don't see a compelling reason to do that, though. Either make mysublib PRIVATE or just live with it being renamed in your package config. Targets aren't precious and they're namespaced, so there's no real reason to worry.

How can I make `target_link_libraries` dependencies transitive from OBJECT libraries in cmake?

If I specify a dependency chain of OBJECT libraries in CMake, only the target_link_libraries dependencies of the very last one is used in the target executable.
Minimal Example:
main depends on objB, which depends on objA.
Both objA and objB are OBJECT libraries in CMake.
I would expect main to link with both object files. It doesn't.
cmake_minimum_required(VERSION 3.13)
project(transitive-object-library-link-issue VERSION 1.0.0 LANGUAGES C)
add_library(objA OBJECT a.c)
add_library(objB OBJECT b.c)
target_link_libraries(objB PUBLIC objA)
# Should link with 'a.o', since 'objA' is "linked" by 'objB'
add_executable(main main.c)
target_link_libraries(main objB)
Note: A working set of files are available at https://github.com/scraimer/cmake-transitive-object-library-link-issue
If I change objA to a non-OBJECT library, the problem is eliminated by getting rid of the chain of OBJECT library dependencies. In other words, change the line
add_library(objA OBJECT a.c)
To be:
add_library(objA a.c)
So it's something specific to OBJECT libraries. How should I be specifying the dependencies to make main link with objA? (Without having to specify objA in every executable target that uses objB)
Object library targets are not real libraries, they are simply a collection of objects, but they are not really linked together until they are used to build a real target like an executable or shared/static library. Citing the linked documentation:
add_library(<name> OBJECT <src>...)
Creates an Object Library. An object library compiles source files but does not archive or link their object files into a library.
In despite of that, you can apply a target_link_library() command to an object library, but only to specify dependencies of their sources on other libraries. The linked documentation explains your problem:
The object library’s usage requirements are propagated transitively [...], but its object files are not.
Object Libraries may “link” to other object libraries to get usage requirements, but since they do not have a link step nothing is done with their object files.
So, the transitive propagation affects only to compile definitions and dependencies of other real libraries, not the objects themselves.
One solution is to define an INTERFACE library for each OBJECT library. Dependencies of interface libraries are transitive.
# Defines an object library named ${T}_obj,
# interface library named ${T} linked to it,
# and sets ${varname} to ${T}_obj.
macro(add_object varname T)
add_library(${T}_obj OBJECT ${ARGN})
add_library(${T} INTERFACE)
target_link_libraries(${T} INTERFACE ${T}_obj $<TARGET_OBJECTS:${T}_obj>)
set(${varname} ${T}_obj)
endmacro(add_object)
Usage
add_object(T a a.c)
add_object(T b b.c) # sets T to b_obj
target_link_libraries(${T} a)
This stems from another issue with OBJECT libraries: duplication of object code. As Brad King explains:
we'd need to be careful to de-duplicate use of [example item's] object files
If transitive propagation was the default, you could accidentally cause the linker to try linking multiple copies of the same code, thus dooming your project to be non-compilable by CMake.
That is a good enough reason not to allow transitive dependencies of OBJECT libraries. It's tricky, so best avoided. So much so that it's mentioned as a Good Practice in his excellent book "Professional CMake: A Practical Guide":
If a target uses something from a library, it should always link directly to that library. Even if the
library is already a link dependency of something else the target links to, do not rely on an indirect
link dependency for something a target uses directly.
He also added in another issue:
he transitive nature of object libraries is not the same as regular libraries. The build-system manual words it like this:
The link (or archiving) step of those other targets will use the object files from object libraries that are directly linked.
The key part of that is the "directly linked". In your example, main gets the objects from b because it links directly to b, but because it does not link directly to a, it does not get a's objects, even though b links to a.
The reason for this is related to problems where deep transivity can result in object files being added multiple times, which will cause an error. This particular aspect has come up a few times in the issue tracker, but I don't have the issue numbers at hand (you can probably search for them and track down the various discussions).
So the solution seems to be to avoid relying on target_link_library for OBJECT libraries. There are two ways: First is to simply not use OBJECT libraries. Second is to explicitly specify objects to link in the hierarchy, as proposed by Hiroshi Miura:
add_library(a OBJECT a.c)
add_library(b OBJECT b.c)
target_sources(b PUBLIC $<TARGET_OBJECTS:a>)
add_executable(main main.c)
target_sources(main PRIVATE $<TARGET_OBJECTS:b>)
This is explicit, but doesn't even solve the problem I have! I suspect this also might run into duplication issues due to diamond patterns, but I haven't tested that.
Maybe over the next few months I'll figure out a better solution using generator expressions based on the LINK_LIBRARIES property of the objA. Not sure how to do that, though.

Adding object-file dependencies

I have a CMake project. For some reason (that I won't say here, but which I can provide upon request), I need some object files that are part of the same library to be compiled before others. Specifically:
FILES is a list of source files
file_a.c is a member of FILES
file_d.c is a member of FILES
file_a.o MUST exist on disk before file_d.c is compiled
This is what I have now:
set_source_files_properties(
file_a.c
PROPERTIES
OBJECT_OUTPUTS file_a.o
)
set_source_files_properties(
file_d.c
PROPERTIES
OBJECT_DEPENDS file_a.o
)
This works well for Makefiles, but it doesn't appear to play nice with Ninja; I get a circular dependency error and complaints about multiple rules.
Don't try to declare dependencies between object files. If there are files that have a dependency, break them out into a separate library with add_library and then declare the dependency with add_dependencies and target_link_libraries. There is no extra cost for doing this.
Particularly, consider looking at Object Libraries.

Is there a LINK_DIRECTORIES or equivalent property in CMake?

I have created a project with a large number of link_directories() commands. I'd now like to store the resulting string of directories into a variable. For include_directories() this is easy using
get_property( test_INCLUDE_DIRECTORIES TARGET test PROPERTY INCLUDE_DIRECTORIES )
however there seems to be no LINK_DIRECTORIES property to do
get_property( test_LINK_DIRECTORIES TARGET test PROPERTY LINK_DIRECTORIES )
Is there a way to get a list of link directories used for a target?
(Note: I realize I could manually track the link directories in a variable myself and then use a single link_directories() but it doesn't seem very clean)
Take a look at the LINK_DIRECTORIES directory property.
The point is that link_directories operates on a per-directory basis (the command affects all targets defined in the same CMakeLists, as well as targets from all of its subdirectories), unlike, for instance, target_include_directories which works on a per-target basis.
You can query the value of the property with:
get_property(test_LINK_DIRECTORIES DIRECTORY PROPERTY LINK_DIRECTORIES)
if called from the same directory as the link_directories call. Otherwise, you need to give the (full or relative) path as an additional argument after DIRECTORY. Unfortunately, I know of no way to obtain the matching directory for an existing target.
Note that in general the use of link_directories is discouraged in CMake, which is probably the main reason why it's not supported very well. Unless you have very good reasons not to, you should always stick with full library paths passed to target_link_libraries. It will save you lots of headaches in the long run.

What is the modern method for setting general compile flags in CMake?

There are multiple mechanisms offered by CMake for getting flags to the compiler:
CMAKE_<LANG>_FLAGS_<CONFIG> variables
add_compile_options command
set_target_properties command
Is there one method that is preferred over the other in modern use? If so why? Also, how can this method be used with multiple configuration systems such as MSVC?
For modern CMake (versions 2.8.12 and up) you should use target_compile_options, which uses target properties internally.
CMAKE_<LANG>_FLAGS is a global variable and the most error-prone to use. It also does not support generator expressions, which can come in very handy.
add_compile_options is based on directory properties, which is fine in some situations, but usually not the most natural way to specify options.
target_compile_options works on a per-target basis (through setting the COMPILE_OPTIONS and INTERFACE_COMPILE_OPTIONS target properties), which usually results in the cleanest CMake code, as the compile options for a source file are determined by which project the file belongs to (rather than which directory it is placed in on the hard disk). This has the additional advantage that it automatically takes care of passing options on to dependent targets if requested.
Even though they are little bit more verbose, the per-target commands allow a reasonably fine-grained control over the different build options and (in my personal experience) are the least likely to cause headaches in the long run.
In theory, you could also set the respective properties directly using set_target_properties, but target_compile_options is usually more readable.
For example, to set the compile options of a target foo based on the configuration using generator expressions you could write:
target_compile_options(foo PUBLIC "$<$<CONFIG:DEBUG>:${MY_DEBUG_OPTIONS}>")
target_compile_options(foo PUBLIC "$<$<CONFIG:RELEASE>:${MY_RELEASE_OPTIONS}>")
The PUBLIC, PRIVATE, and INTERFACE keywords define the scope of the options. E.g., if we link foo into bar with target_link_libraries(bar foo):
PRIVATE options will only be applied to the target itself (foo) and not to other libraries (consumers) linking against it.
INTERFACE options will only be applied to the consuming target bar
PUBLIC options will be applied to both, the original target foo and the consuming target bar