Include one target's include directories in another target - cmake

How can I include target1's include directories (made by target_include_directories(target1 t1/include)) into target2?
I know only one way, which is to create a variable and set() include directories in it, and then reuse the variable for another target. But this looks too non-flexible, and I think CMake must have an much easier way.
My targets are shared libraries.

"$<TARGET_PROPERTY:TargetName,INTERFACE_INCLUDE_DIRECTORIES>"
is what I looked for
using like that:
target_include_directories(MyTarget
PRIVATE
"$<TARGET_PROPERTY:OtherTarget,INTERFACE_INCLUDE_DIRECTORIES>"
)
it allows me to include PUBLIC and INTERFACE include directories of target OtherTarget into MyTarget

Related

Creating a executable out of a library in CMake, and linking the library in another library

I am trying to link foo-lib library into another target bar-lib however doing the following results in an error.
(add_executable):
Cannot find source file: foo-lib
How can I create an executable out of a library and the same library can be linked into another target?
add_library(foo-lib STATIC src/foo.cpp)
add_executable(foo-ut foo-lib)
target_include_directories(foo-ut PRIVATE include)
target_link_libraries(foo-ut PUBLIC lib1 lib2)
# second library that links foo-lib
add_library(bar-lib STATIC src/bar.cpp)
add_executable(bar-ut bar-lib)
target_include_directories(bar-ut PRIVATE include)
target_link_libraries(bar-ut PUBLIC foo-lib)
This worked the way I wanted but I am not sure if I should be adding foo.cpp for the bar-ut target
add_executable(bar-ut src/foo.cpp src/bar.cpp)
I'm not going to question the design choices and what you need it for and if it is a good idea in any way. I will just provide you with a "scalable" way of doing this.
As the others pointed out add_executable() requires source files.
Now assuming that the source files that you use to create a static library contain a main() function. Then you can create (out of the same source files) an executable, by passing to the add_executable the same source files as you would to add_library.
As the program grows these would get lengthy, so what you should do is something that is no longer recommended by "CMake best practices" and that is to introduce a SOURCES variable. I.e.:
set(PROJECT_SOURCES source1.cpp source2.cpp source3.cpp)
set(PROJECT_HEADERS header1.h header2.h header3.h)
add_library(foo-lib STATIC ${PROJECT_SOURCES} ${PROJECT_HEADERS})
add_executable(foo-ut ${PROJECT_SOURCES} ${PROJECT_HEADERS}
As your program grows you would just add the respective files into the designated variables. Now as to possible improvements:
fabian mentioned a very good thing which is OBJECT libraries, since you are rebuilding the same files for both the executable and library you could just create an object library and link it. This would make it twice as fast (you only need to compile once).
Since these SOURCES are already once passed to some target, you could just get them from the target's properties via get_target_properties(MY_SOURCES foo-lib SOURCES) this would give you a variable MY_SOURCES that contains sources which are used by the target library.

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 to add BEFORE to interface included directories for exported target?

I have a exported target A::A, there is a INTERFACE_INCLUDE_DIRECTORIES, A_includes.
set_target_properties(A::A PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${A_INCLUDE_DIRS}")
With old style I can add BEFORE to target_include_directories like following, to make sure the A_includes come before the system ones:
target_include_directories(
some_target
BEFORE
PRIVATE
${A_includes}
)
But I want to use the modern style with the exported target:
target_link_libraries(
some_target
PRIVATE
A::A
)
However, the modern one does not allow me to pass BEFORE. Is there any way to set the BEFORE during target exporting? Is there a way to make includes form A::A go before system one when I use it in a project?

"config-style" cmake find_package unusable in parent scope

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

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.