How can I create a target for an _existing_ library? - cmake

In CMake, we can add_library(mylib file1.cpp file2.cpp) and have a mylib.a in the library path get built. We can also target_include_directories(mylib INTERFACE some/directory), which effects targets depending on mylib.
But what if I have a library to begin with, which I will not be compiling. How can I add a target relating to it? That what I add as a dependency, will cause the .a file to be linked against, and for which I can set target_include_directories() ?
Note: I'm asking about CMake 3.x.

CMake provide an alternate signature for libraries that are already compiled:
add_library(
mynamespace::mylib
STATIC # or it could be SHARED
IMPORTED
)
See the official CMake documentation for more details.
with that you'll be able to add properties to the target doing so
set_target_properties(
mynamespace::mylib
PROPERTIES
IMPORTED_LOCATION "path/to/libmylib.a"
)
Little precision here, if you're using a Windows DLL, you must pass the DLL file's path in IMPORTED_LOCATION and set another property IMPORTED_IMPLIB that points to the .lib file, (not very handy).
Note that there is also a equivalent properties per configuration (Debug, and Release), that will need another property to be set (IMPORTED_CONFIGURATION), e.g. IMPORTED_LOCATION_DEBUG.
See also here and here in the documentation.
To avoid missing include files you can also precise the include directory using INTERFACE_INCLUDE_DIRECTORY property
set_target_properties(
mynamespace::mylib
PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "path/to/mylib/include"
)
With this, upon link declaration using target_link_libraries(), CMake will read information of the imported target and will add include directories implicitly.
Official documentation reference.

Related

Using target_link_libraries on specific configurations (MinSizeRel, Release, etc.) instead of general ones (Optimized or debug)?

From what I see from the target_link_libraries documentation, you can set the keyword debug, optimized or general before a library to define for what configuration to link it to. The problem is that I would like to have even more control and be able to specify a different library for Release, Debug, MinSizeRel and RelWithDebInfo. Is there a way to do this?
Command target_link_libraries accepts generator expressions, using which you may express almost any configuration-dependent logic.
Actually, specifying for target_link_libraries
debug libA
is effectively the same as using following generator expression:
$<$<CONFIG:Debug>:libA>
(More precise, it effectively the same with default setting of DEBUG_CONFIGURATIONS variable.)
If you want to link different flavors of the same library dependent on configuration, then the better way is using an IMPORTED library target combined with IMPORTED_LOCATION_<CONFIG> properties setting:
add_library(libA STATIC IMPORTED)
set_target_properties(libA PROPERTIES
IMPORTED_CONFIGURATIONS "Debug;Release"
IMPORTED_LOCATION_DEBUG "/foo/bar/libA_debug.a"
IMPORTED_LOCATION_RELEASE "/foo/bar/libA_release.a"
)
After that, plain
target_link_libraries(myExe PUBLIC libA)
will link with /foo/bar/libA_debug.a in 'Debug' configuration and with /foo/bar/libA_release.a in 'Release' configuration.

Use target_link_libraries to libraries not start with lib*

I am trying to link my program(hello) with a special library(/path/abc.so) that not starts with 'lib'.
Here is my CMakeLists.txt
add_executables(hello hello.c)
target_link_libraries(hello /path/abc.so)
It works fine, but is there any other way to avoid this full path(/path/abc.so) things?
I don't want to make symlink of abc.so or modify abc.so itself.
Probably your best option is not to link directly the library, but use imported targets:
You can have your library target as
add_library(ABC SHARED IMPORTED)
set_target_properties(ABC PROPERTIES
IMPORTED_LOCATION path/to/library/abc.so
INTERFACE_INCLUDE_DIRECTORIES path/to/include
)
Then you can link it as a target:
target_link_libraries(hello ABC)
The next step would be to have a library find module, or config module, so you don't define full path in your CMakeLists.txt, but search for the library, or just include a .cmake file with all of the paths.
Have a look HERE and HERE

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.

distinguish include_directories with and without the 'SYSTEM' option in cmake

I am using get_property( … PROPERTY INCLUDE_DIRECTORIES) for debugging in a CMake project. Is there a way to tell which of the include directories have been done with and without the SYSTEM option?
In the following example I see no difference
include_directories("../B")
include_directories(SYSTEM "../A")
get_property( reqs TARGET main PROPERTY INCLUDE_DIRECTORIES)
message(STATUS "main requires ${reqs}")
prints:
-- main requires /home/pseyfert/coding/system/C/../B;/home/pseyfert/coding/system/C/../A
There is target property INTERFACE_SYSTEM_INCLUDE_DIRECTORIES, which contains those of include directories, which has been marked as SYSTEM.
As the property's name suggests (and as noted in docs), INTERFACE_SYSTEM_INCLUDE_DIRECTORIES contains only INTERFACE directories, which are propagated to the targets linked with given one. It is unclear from CMake docs, how PRIVATE include directories should be distinguished.

CMake Include dependencies of imported library without linking

I am using CMake 3.5.2.
Consider the following situation. I have an imported library Foo::Foo:
add_library(Foo::Foo UNKNOWN IMPORTED)
This imported library has been populated with appropriate properties:
set_target_properties(Foo::Foo PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "/path/to/include/blah" "/another/path/include/other"
IMPORTED_LINK_INTERFACE_LIBRARIES blah other
IMPORTED_LOCATION "/path/to/libfoo.a-or-so")
I have a convenience library called bar. I need it to include Foo::Foo's include directories, but I do not want it to link against Foo::Foo.
add_library(bar STATIC "${BAR_SOURCES}")
How can I add just the include dependencies from Foo::Foo? Here is what I have tried that has failed:
# This did not include any includes from Foo::Foo
target_link_libraries(bar INTERFACE Foo::Foo)
# This included only the first include directory from Foo::Foo
target_include_directories(bar PUBLIC "$<TARGET_PROPERTY:Foo::Foo,INTERFACE_INCLUDE_DIRECTORIES>")
I have given you example a try. You should change the code in your example to:
set_target_properties(
Foo::Foo PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES
"/path/to/include/blah;/path/to/include/other"
IMPORTED_LINK_INTERFACE_LIBRARIES "blah.a"
IMPORTED_LOCATION "/path/to/libfoo.a-or-so"
)
The call to set_target_properties() only accepts "property" / "value" pairs (with spaces as delimiter). And your example just wasn't throwing any errors because you can always define your own properties (with any naming).
Please transfer your include directory list into a "CMake List" (string with semicolon separated elements).
Alternative
If you just want to "reset" the transitive libraries you can do this with e.g.:
target_link_libraries(bar Foo::Foo)
set_target_properties(bar PROPERTIES INTERFACE_LINK_LIBRARIES "")
I've used this approach when I was building a shared library in the same project as I was linking against the same (and I did not want the library dependencies of the shared library also being linked into the target using the shared library).
References
target_link_libraries()