how to add BEFORE to interface included directories for exported target? - cmake

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?

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.

Include one target's include directories in another target

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

How to tell whether a given target is a library or executable?

The built-in function install(TARGETS ...) installs library targets to another location than executable targets. I want to do something similar. Given a list of target names, I want to add all library targets among them to a list variable and all runtime targets to another variable.
I couldn't found a list of CMake's default target properties, but I guess add_library() and add_executable() add a property that can be used for this kind of distinction.
How to tell whether a given target is a library or executable (or even something else)?
According to documentation, TYPE property can be used for distinguish standard CMake target types:
It will be one of STATIC_LIBRARY, MODULE_LIBRARY, SHARED_LIBRARY, EXECUTABLE or one of the internal target types.
Example:
get_target_property(target_type <target> TYPE)
if (target_type STREQUAL "EXECUTABLE")
# Process executable target
endif ()

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.