What does target_link_directories() do in CMake? - cmake

I'm learning standard CMake functions, and after reading the documentation, I still have this question. They say
"Specifies the paths in which the linker should search for libraries when linking a given target"
But I don't really get how linker can look for libraries, when I use target_link_libraries() which already knows where my libraries are.
Thank you.

The directories you pass to this command are used you pass something that's not a cmake target to target_link_libraries.
From the docs of target_link_libraries
This command has several signatures as detailed in subsections below. All of them have the general form
target_link_libraries(<target> ... <item>... ...)
[...]
Each <item> may be:
A library target name: [...]
A full path to a library file: [...]
A plain library name: The generated link line will ask the linker to search for the library (e.g. foo becomes -lfoo or foo.lib).
[...]
...
The third option ("A plain library name") is the one where target_link_directories gets relevant; if the linker does not find the library by default, you need to add provide the path via target_link_directories (or by similar means of modifying the LINK_DIRECTORIES target property).

Related

What is a "target" in CMake?

I would like a simple explanation of what a target is in CMake. The current terminology is something I am not all too familiar with and I am seeking a simple explanation of the word "target" used primarily with add_executable and add_library.
Target is a name for something to do. Execute or build or create, depending on the target. Typically, target names represent an executable file or library file.
The term "target" comes from make command. make takes targets names as arguments and builds them using rules that specify what to do to create a specific target/file. In make a "target" is just a path to a file to produce, for example an actual executable name. https://www.gnu.org/software/make/manual/make.html#Rule-Introduction
The build system generated by CMake "updates a target" (executes the commands needed to build the executable file or library file) when the prerequisite files have been modified since the target was last modified or the target does not exist ("the target" = the file that is the result of building the target).
A target represents either an executable (functions and data in a binary that has a main-function/entrypoint (a program)), or a library (functions and data in a binary with no main-function/entrypoint) in a buildsystem. For more information, see the docs on binary targets, and the docs for add_executable and for add_library.
Targets can be part of a project (in which case they have source files and need to be built as part of the buildsystem), or IMPORTED, in which case they already exist in binary form and have no sources and do not need to be built (they were already built outside the buildsystem to be generated for a project).
Targets can have dependencies between them. Ex. An executable target depending on a library target to use its functions and data. For more info, see the docs for target_dependencies, which can have implications for linking binaries together, and for build order: making sure dependent targets are re-built when depended-upon targets need to be rebuilt.
There are some target types which are not IMPORTED which also have nothing to build (Ex. interface libraries, and other types you can read about in the add_library docs, but those are more "advanced" features).

Do something for all targets

What is the best way to do additional stuff for all (binary) targets?
Examples:
I want to check that each library name follows a pattern.
I want to sign each executable.
I dont what the C/C++ developers to use nonstandard commands (like add_library2). I want them to use and learn the official CMake functions, but have them do additonal, project specific, stuff.
The built-in CMake functions add_library and add_executable can be overidden by defining CMake functions of the same name. E.g., to automatically sign all added executables add the following code:
function (add_executable _name)
_add_executable(${ARGV})
if (TARGET ${_name})
add_custom_command(TARGET ${_name} POST_BUILD
COMMAND sign_executable $<TARGET_FILE:${_name}>)
endif()
endfunction()
The original built-in add_executable can be invoked by prefixing it with an underscore character. The same pattern can be applied to add_library to check the name of the library.
You can overwrite any CMake command/function to extend its functionality, but please
Call Things by their Names
I strongly advocate to call the things by their names and not doing things implicitly. It will be easier for everybody using/maintaining/debugging your CMake based project.
If you want to sign your executable - and that's probably even platform specific - you create a function like add_post_build_step_sign_executable() which would add the appropriate post build steps:
add_executable(MyExe main.cpp)
if (WIN32)
add_post_build_step_sign_executable(MyExe)
endif()
And if you have to repeat yourself too often put that code snippet into a function like my_project_add_signed_executable() itself. It could still have the same parameter syntax as CMake's add_executable() command.
Runtime Checks vs. Static Code Analysis
Regarding library naming checks, I see this more like checking against your project's CMake script coding styles and would not use runtime checks for this.
For example you could use something like cmake-lint or your own external script to check for conformity.
References
How to frame the concept behind CMake?
cmake: get add_library() names
How to ensure consistent coding style for CMakeLists.txt and FindXXX.cmake

Cmake not setting RPATH when adding link_library with -L

When setting link libraries in the following manner
target_link_libraries (SOME_TARGET -L/somedir -lfoo)
cmake doesn't handle RPATHs. Is using '-L' and '-l' not best practice, or actually plain wrong? When creating my own Find*.cmake I usually use find_library() but the find script I got doesn't do this and resorts to the above form using '-L' and '-l'.
The documentation doesn't really explain how RPATHs are gathered, also the documentation isn't really clear how it handles "-l" and "-L" the only pointer you get is
"Item names starting with -, but not -l or -framework, are treated as
linker flags"
Specifying toolchain-dependent flags like -l and -L is generally not recommended, as it breaks portability and might have different effects than you expect.
The correct way to set the linker path would be the link_directories command.
The idiomatic solution in CMake is to use find_library for locating the library and then pass the full path to the linker, so you do not need to worry about link directories at all.
Now, the RPATH is a different beast, as it also determines where dynamic libraries can be located at runtime. Usually, the default settings work reasonably fine here. If you ever find yourself in the unfortunate situation where it does not, there is a number of target properties and CMake variables influencing this:
There are a few properties used to specify RPATH rules. INSTALL_RPATH
is a semicolon-separated list specifying the rpath to use in installed
targets (for platforms that support it). INSTALL_RPATH_USE_LINK_PATH
is a boolean that if set to true will append directories in the linker
search path and outside the project to the INSTALL_RPATH.
SKIP_BUILD_RPATH is a boolean specifying whether to skip automatic
generation of an rpath allowing the target to run from the build tree.
BUILD_WITH_INSTALL_RPATH is a boolean specifying whether to link the
target in the build tree with the INSTALL_RPATH. This takes precedence
over SKIP_BUILD_RPATH and avoids the need for relinking before
installation. INSTALL_NAME_DIR is a string specifying the directory
portion of the “install_name” field of shared libraries on Mac OSX to
use in the installed targets. When the target is created the values of
the variables CMAKE_INSTALL_RPATH, CMAKE_INSTALL_RPATH_USE_LINK_PATH,
CMAKE_SKIP_BUILD_RPATH, CMAKE_BUILD_WITH_INSTALL_RPATH, and
CMAKE_INSTALL_NAME_DIR are used to initialize these properties.
(From the set_target_properties docs)
Also, you might want to have a look at the CMake Wiki page for RPATH handling.
The whole RPATH business is unfortunately rather complex and a thorough explanation would require far more space than is appropriate for a StackOverflow answer, but I hope this is enough to get you started.
Basically, You're using target_link_libraries() wrong. According to documentation, You should provide target, libraries and maybe some CMake specific linkage flags.
For example something like that:
target_link_libraries(my_build_target somedir/foo.so)
If You're using Your own crafted Find*.cmake solutions, it's usualy being done like this:
find_library(foo)
//build main target somewhere here
//now link it:
target_link_libraries(my_build_target ${FOO_LIBRARIES})
NOTE: I assume Your crafted Find*.cmake files follows these guidelines and fills CMake variables like SOMELIB_LIBRARIES, and/or SOMELIB_INCLUDE_DIRS, etc.
NOTE2: for my personal opinion, target_link_directories() is pain in a butt and You should avoid using it if not really needed. It's difficult to maintain and uses paths relative to current source directory.

Using cmake, how to link an object file built by an external_project statement into another library?

In our project, we want to use a third-party library (A) which is built using autotools and which generates an object file (B) which we need # link time of one of our libraries (C).
external_project(
A
...
)
set_source_files_properties(B PROPERTIES DEPEND A)
add_library(C ... A)
add_dependency(C B)
I had the impression that this should do the trick, but the cmake command fails by stating that it cannot find file A during the check for add_library.
Any fixes or alternative solutions would be greatly appreciated! (changing the third-party library is not an option)
thanks!
There are a few issues here:
external_project should be ExternalProject_Add
Source files have no property called DEPEND available - the set_source_files_properties command has no effect here. (Here are the properties available on source files)
add_library expects a list of source files to be passed, not another CMake target (which A is)
add_dependency should be add_dependencies
Apart from those 4 lines it's all OK :-)
So the issue is going to be that you want to include the object file B in the add_library call, but it's not going to be available at configure-time (when CMake is invoked), only at build time.
I think you're going to have to do something like:
ExternalProject_Add(
A
...
)
set_source_files_properties(
${B} PROPERTIES
EXTERNAL_OBJECT TRUE # Identifies this as an object file
GENERATED TRUE # Avoids need for file to exist at configure-time
)
add_library(C ... ${B})

CMake : how to link a library WITHOUT automatic search function FIND_PACKAGE?

I wonder how to find/link a library without any FIND_PACKAGE.
Assume that we have a "personal" library called testlib :
/perso/testlib/include/testlib1.h
/perso/testlib/include/testlib2.h
/perso/testlib/lib/testlib1.a
/perso/testlib/lib/testlib2.a
How to link it with CMake ?
1) What are the functions to link it directly in the code of the CMakeLists.txt ?
2) How to allow the user to select where are the files ?
3) I have difficulties to understand what is interpreted and what it's not by CMake. For example if you define a variable ${MYVARIABLE_INCLUDE_DIR} or ${MYVARIABLE_LIBRARIES} is "INCLUDE_DIR" or "LIBRARIES" an extension interpreted by CMake or there is no difference if I call this variable ${MYVARIABLE_INCDIR} ?
4) How to do the same procedures (including a "personal" library) if you have a library that contains ten library files or more in the lib directory ?
5) And finally, when you type TARGET_LINK_LIBRARIES(myexecutable gmp), how do you know that the name of the library is "gmp". Why not "Gmp" or "GMP" ? Is the name of the library to put in this function just equal to the .a file minus "lib" and ".a" ? For example libgmp.a -> gmp ? If I want to link a library called libtestlolexample.a, do I have to type TARGET_LINK_LIBRARIES(myexecutable testlolexample) ?
Thank you very much.
You can use target_link_libraries(myexecutable mylib) to link to the library "mylib". The compiler will use its default way to find the specified library (e.g. it will look for libmylib.a on Linux). The compiler will only look in the link_directories(directory1 directory2 ...), so you could try that command to add the required directories to the search path.
When "mylib" is also compiled with CMake this will be recognized and everything should work automatically.
When you want the user to specify a directory you can use a cached CMake variable. set(MYPATH "NOT-DEFINED" CACHE PATH "docstring").
For more complex stuff it is very advisable to write a CMake find module that can be used with find_package. I suggest you take a look at the FindALSA.cmake which can be used as a good starting point.
The interesting part is at the end:
if(ALSA_FOUND)
set( ALSA_LIBRARIES ${ALSA_LIBRARY} )
set( ALSA_INCLUDE_DIRS ${ALSA_INCLUDE_DIR} )
endif()
mark_as_advanced(ALSA_INCLUDE_DIR ALSA_LIBRARY)
The ALSA_LIBRARY and ALSA_INCLUDE_DIR variables are user configurable and stored in the cache, while ALSA_LIBRARIES and ALSA_INCLUDE_DIRS as well as ALSA_FOUND get computed and are the ones that the user of the find module is supposed to use.
Typically one would use the find module like this:
find_package(ALSA REQUIRED)
include_directories(${ALSA_INCLUDE_DIRS})
target_link_libraries(myexe ${ALSA_LIBRARIES})
I'm sure you can adapt this for your personal library.
Usually when you want to link against a library that doesn't have a find_package module (e.g. it's an uncommon library, or it's your own library), then you can use the basic commands (the find_X commands) to set variables with the paths you need. Then you use those variables as with find_package (include_directories, target_link_libraries).
If you're going to be using this library from multiple packages, you may want to create a find_package module; basically it's using the same commands with certain conventions.
Either of these allow you to specify paths (in the CMake module) to look in, and they allow the user to override the paths (the variables show up as options in ccmake/cmake-gui).
I'd be glad to add an example of one or both of these methods, just let me know what you're looking for.
If you just want a quick-and-dirty solution, you could do this, but I wouldn't recommend it:
include_directories(/perso/testlib/include)
add_executable(myexecutable myexecutable.cpp)
target_link_libraries(myexecutable
/perso/testlib/lib/testlib1.a
/perso/testlib/lib/testlib2.a)
Regarding your question about target_link_libraries (#5), you can do it several ways. If you want you can provide the full name (e.g. target_link_libraries(myexe libfoo.a)), but I think it's better (more portable I suppose) to use the short name (e.g. target_link_libraries(myexe foo). You can also include linker flags; I'm not sure where I read it, but I think it may translate the -L and -l flags for different linkers.
For example, if I have a bunch of libraries in the same directory, and I know the names, I might find the directory, store it in a variable, and then do this:
# First, find and set TESTLIB_LIBRARY_DIR, e.g. with find_path
# ...
# This assumes the libraries are e.g. 'libtestlib1.a' and 'libtestlib2.a'
set(TESTLIB_LIBRARIES
-L${TESTLIB_LIBRARY_DIR)
-l testlib1
-l testlib2)
add_executable(myexecutable myexecutable.cpp)
target_link_libraries(myexecutable ${TESTLIB_LIBRARIES})
If you want to make your own find_package module (like trenki mentioned, FindALSA.cmake seems to be a good starting point), you can use it by adding the directory to the CMAKE_MODULE_PATH; for example, if you put your module(s) in a cmake/modules/ subdirectory:
# Look for extra CMake modules in a subdirectory of this project
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/" ${CMAKE_MODULE_PATH})
One possible issue with FindALSA.cmake: I'm not sure CMAKE_CURRENT_LIST_DIR will work. So I think you should make this change (the second work for me in a module I wrote):
# Change this line
include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake)
# To this (with no path/extension it will search the CMake modules path):
include(FindPackageHandleStandardArgs)
And to get the usage of FIND_PACKAGE_HANDLE_STANDARD_ARGS, look at FindPackageHandleStandardArgs.cmake in the CMake Modules directory.
CMake has a good documentation.
Static linkage (if i understand you correct) is archived by passing the STATIC keyword to add_library
I would suggest to not do that (I'm not a CMake expert) but it sounds like the expense would be to large.
There is no difference, ${MYVARIABLE_INCLUDE_DIR} ist just a variable name it whatever you want. But i would suggest that you follow the naming convention.
One libary is always one .lib/.a file so there should be no problem just use the add_library& target_link_libraries& add_dependencies function.
The library name is always the name that you pass to add_library. However Gmp or gMP would be the same as CMake is case intensitive