What is a "target" in CMake? - 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).

Related

Predeclare search location for anticipated find_library()-call

I want to include an external library as a subproject into my own project and link its target(s) statically against my own lib.
The said project somewhere in its CMake calls the following find-functions:
find_library(MBEDTLS_LIBRARY mbedtls)
find_library(MBEDX509_LIBRARY mbedx509)
find_library(MBEDCRYPTO_LIBRARY mbedcrypto)
The subproject expects mbedtls to already be installed somewhere on the system, but it didn't consider the fact that I want to link statically. My approach is to now FetchContent mbedtls and provide the find_library() calls with the prebuilt static libraries.
Now I need a way provide those find_library-calls with the right search directory, of course without modifying its source code. Can I somehow set a prefix path? I know I could probably set CMAKE_PREFIX_PATH but that seems like an ugly hack and it would probably affect other find_library()-calls within the project which also exist. Is there a more "constrained" way?
Can I somehow set a prefix path?
Setting a prefix path won't help find_library to locate the library, because command find_library searches the file at configuration stage, but the library is built only on build stage.
Instead, you may write the target name to the CACHE variable, which is passed to find_library as the first argument:
When find the result variable to be already set, find_library won't search the library file.
In most cases a project uses result of find_library in the call to target_link_libraries, so having the library target in the result variable will fit to the project's expectations.
Example:
FetchContent_Declare(mbedtls ...)
FetchContent_MakeAvailable(mbedtls)
set(MBEDTLS_LIBRARY MbedTLS::mbedtls CACHE INTERNAL "mbedtls library target")
With such setup the following
find_library(MBEDTLS_LIBRARY mbedtls)
will do nothing, since the variable MBEDTLS_LIBRARY is already set.
And if the project will use this variable like
target_link_libraries(<executable> ${MBEDTLS_LIBRARY})
then it effectively gets
target_link_libraries(<executable> MbedTLS::mbedtls)
Name of the target which should be assigned to the variable could sometime be found from the project's documentation, but otherwise you need to look into the project's sources (CMakeLists.txt).
E.g. in case of mbedtls project, the library target mbedtls is created with add_library() call, and MbedTLS::mbedtls is created as ALIAS for it.

Why do we need one more add_custom_command in cmake? [duplicate]

In CMake semantics there is some sort of distinction between "targets" and commands" that is baffling me. In Makefiles, there is no such distinction:
targetname:dependency
command
i.e. Targets correspond to a generated file of the same name.
In CMake you have commands like "add_custom_command" and "add_custom_target" that have overlapping functionality, and even in the official documentation the semantics are confused, i.e. in "Mastering CMake, 5th edition", page 110 under "Adding a custom target":
The DEPENDS argument sets up a dependency between the custom target
and the custom commands.
My understanding is that targets (generated files) have dependencies (other files, generated or no), and a command to actually do the generation. It is nonsensical to say a target depends on a command. To make matters worse, there are two flavors of "add_custom_command" that either attach an additional command to an existing target, or spit the command out into the ether.
Can someone please explain why this distinction even exists?
Targets
In general, targets comprise executables or libraries which are defined by calling add_executable or add_library and which can have many properties set.
They can have dependencies on one another, which for targets such as these just means that dependent ones will be built after their dependencies.
However, you can also define "custom targets" via add_custom_target. From the docs:
Adds a target with the given name that executes the given commands. The target has no output file and is ALWAYS CONSIDERED OUT OF DATE even if the commands try to create a file with the name of the target. Use ADD_CUSTOM_COMMAND to generate a file with dependencies. By default nothing depends on the custom target. Use ADD_DEPENDENCIES to add dependencies to or from other targets.
So these are different from "normal" targets in that they don't represent things which will produce an exe or lib, but they still benefit from all the properties that targets can have, including having or being dependencies. They appear as a target which can be built (e.g. make MyCustomTarget or msbuild MyCustomTarget.vcxproj). When you build them, you're simply invoking the commands that have been set for them. If they have dependencies on other targets (normal or custom), then these will be built first.
Custom Commands
A custom command defined via add_custom_command is quite different in that it's not a "buildable" object, and doesn't have settable properties in the way that a target does - it's not a named object which can be explicitly referred to again after it's added in the CMakeLists.txt.
It is basically a command (or set of commands) which will be invoked before building a dependent target. That's all that "depends" really means here (at least that's how I view it) - it's just saying that if A depends on B, then B will be built/executed before A is built.
The dependees of a custom command can be either set explicitly using the add_custom_command(TARGET target ... form, or implicitly by creating targets which include the files generated via the add_custom_command(OUTPUT output1 ... form.
In the first case, every time target is built, the custom command is executed first.
In the second case, it's a little more complex. If the custom command has targets which depend on its output file (and the output file doesn't already exist), it is invoked before these dependent objects are built. The dependencies are implicitly created when you do e.g. add_library(MyLib output1.h ... ) where output1.h is a file generated via add_custom_command(OUTPUT output1.h ... ).
add_custom_command adds a callable function that can have defined outputs (using the OUTPUT and BYPRODUCTS arguments). It can also have dependencies that will be run before the function is called.
Notice that it does NOT do things that you may think it does due to strange documentation (the makefile examples are very misleading). In particular, it does not have any guarantees about numbers of times it executes. For example, imagine this:
add_custom_command(OUTPUT /tmp/touched COMMAND echo touch COMMAND touch /tmp/touched)
add_custom_target(touched-one ALL DEPENDS /tmp/touched)
add_custom_target(touched-two ALL DEPENDS /tmp/touched)
How many times will "touch" be printed? You don't know, since it's not specified anywhere; make -j2 will print it twice, probably, but it's timing-dependent:
Scanning dependencies of target touched-two
Scanning dependencies of target touched-one
[ 50%] Generating touched
touch
[100%] Generating touched
touch
[100%] Built target touched-two
[100%] Built target touched-one
But Ninja will only print it once, probably:
# rm -rf * && cmake -GNinja ../c ; cmake --build . -- -j 5
[1/1] Generating touched
touch
Usually, you'll do an add_custom_command to do some work and that defines an OUTPUT, and then you'll have an add_custom_target that depends on the output of the custom command. Anyone who wants the output depends on the target, and that does give you the guarantees you want.
Caveat: see this bug for an great example of why building cross-platform metabuild tools is REALLY HARD.

An extra interface_include_directories for schema files. Is it possible?

I have successfully used target_include_directories() to pass on usage requirements to dependent C++ libraries. It all works very well. But here is a slightly different use case.
Is there a way to have a second set of include directories in addition to the normal one (i.e. the one for C/C++ headers)?
This could be convenient if you for instance have C++ libraries that are generated out of a schema language (e.g. Protobuf, Capnproto) with a
add_custom_command(). The schema files can reference each other by import, so I would like to install the schema files together with the libraries.
It would be nice to pass on information to dependent libraries about the schema files directories as well as the directories for the C++ headers.
Using target_include_directories() to track directories that don't contain C/C++ headers might be a misuse of that command. But maybe there is some other way of achieving this?
Edit 1:
Just a clarification: My goal is to have two set of directory paths, one for the C++ include headers and another one for the schema files. (The schema files and the C++ headers might not be in the same directories). The property INTERFACE_INCLUDE_DIRECTORIES will handle the C++ headers, but how should I handle the schema files?
yet_to_be_written_ADD_CAPNP_TARGET(
NAME target1
SCHEMA ${CMAKE_CURRENT_SOURCE_DIR}/a1.capnp
)
yet_to_be_written_ADD_CAPNP_TARGET(
NAME target2
SCHEMA ${CMAKE_CURRENT_SOURCE_DIR}/a2.capnp
DEPENDENCIES target1
)
A workaround could be to let
yet_to_be_written_ADD_CAPNP_TARGET() create two libraries
add_library(${name}_cxx)
add_library(${name}_schema INTERFACE)
instead of one library because I would then have two INTERFACE_INCLUDE_DIRECTORIES that could remember directory paths. The purpose of the ${name}_schema would just be to remember the schema directory paths. They could later be retrieved by the generator expression
$<TARGET_PROPERTY:target2_schema,INTERFACE_INCLUDE_DIRECTORIES>
and be given to the schema compiler (/usr/bin/capnp) in a
add_custom_command()
Of course this whole exercise is rather pointless if all schema files are available in the source code tree of the software project at hand but I am thinking of a scenario when cmake targets would have been imported from different external software projects. The schema files might be installed in different directories.
Another workaround would be to specify the external schema directory paths directly (without using targets to pass this information).
Of course. I do this myself in a project I worked on with ProtoBuf files:
set(Aperture_PROTO
protoinclude/bcl.proto
protoinclude/StratusAsset.proto)
protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS ${Aperture_PROTO})
List the proto files as source files, so they are handled with the target:
LIST(APPEND Aperture_SRCS ${Aperture_PROTO})
Make sure to add HEADER_FILE_ONLY properties to the file group:
SET_SOURCE_FILES_PROPERTIES(${Aperture_PROTO}
PROPERTIES HEADER_FILE_ONLY TRUE)
Group them distinctly in an IDE using source_group for bonus points:
source_group("Prototype Files" FILES ${Aperture_PROTO})

Can CMakeLists.txt depend on a file parsed by a function?

I am rather new to CMake, starting off for the first time with a larger project consisting of many subprojects.
For particular reasons (described below for the curious) I already have a set of include files that contain info about the source files needed for each CMake target (lib or exe) – and, for now, I prefer to (re)use these files (reason also described below)
Writing a function to parse these files and add their content as source files to the targets was a surprisingly easy task.
But – now the Problem:
Obviously I want to have each targets CMakeLists.txt depend on the particular include file, that generates the list of source files, so that changes on the include file will be detected as if it were changes to CMakeLists.txt itself, but I simply can’t find any references on how to accomplish that.
N.B.: I found AddFileDependencies but that is for adding dependencies on source files, not the CMakeLists.txt. However, CMake itself can figure out dependencies to included .cmake file somehow, so I figured, it should be possible to do somehow.
Background for the curious:
For this project (quite a number of libraries used by quite a number of executable targets, all organized as subprojects) I was using QMake (without actually using Qt itself) for setting up makefiles. Doing so I was able to use Qt Creator while still being able to generate Visual Studio Solution/Project files automagically. We’re also still in progress of evaluating different IDEs and the choice has not been made yet. But the most important reason to use a generator like QMake / CMake was not being forced to set up the VS files for all these subprojects manually.
Although I needed to trick QMake sometimes to do what I wanted to, things went quite well - even for the VS solution - except for one thing: Visual Studio messes up dependencies on Flex/Bison and other files using custom build rules. It keeps recompiling the Flex/Bison/other files saying „command line changed“ – which I gave up trying to fix.
For this reason I thougt, I’d try CMake as a generator instead, which looks very promising so far – although not having builtin precompiled header support in CMake is somewhat ridiculous these days (off topic, I know).
Since Qt Creators CMake support is by far not as good as the support for QMake projects, I firgured, using the approach of parsing the .pri files containing the source file list would enable me using QMake and CMake side by side – especially since the remaining project settings are rather less complicated than on most open source projects.
There's a nice trick which does exactly what you need. It's based on the idea I found in the git-revision module of #rpavlik see this so question
This is the overall idea:
Create a dummy timestamp file
Add a custom command which touches the timestamp whenever the input .pri file changes
include the timestamp file in your CMakeLists.txt
A possible implementation:
set(input_pri_file <path-to-the-input-pri-file>)
set(timestamp_file ${CMAKE_CURRENT_BINARY_DIR}/timestamp.cmake)
add_custom_command(
OUTPUT ${timestamp_file}
COMMAND ${CMAKE_COMMAND} -E touch ${timestamp_file}
MAIN_DEPENDENCY ${input_pri_file}
VERBATIM
COMMENT "Updating timestamp.cmake"
)
if(NOT EXISTS "${timestamp_file}")
file(WRITE ${timestamp_file} "") # create initial empty file
endif()
include(${timestamp_file})
# create the file list from input_pri_file
....
# use the file list
add_executable(main ${filelist})
Here's what happens when the .pri file changes:
the change triggers the execution of the custom command
which updates the timestamp
because the CMakeLists includes the timestamp it is dependent on it
so updating the timestamp triggers a re-configuration of the CMakeLists.txt
I use the configure_file() if I have some input that should retrigger CMake's configuration process. See e.g. How to make CMake reconfiguration depend on custom file? and configure_file()'s unit test
So in your case it would look something like:
configure_file(SomeInput.pri ${CMAKE_CURRENT_BINARY_DIR}/SomeInput.pri)
Then you use ${CMAKE_CURRENT_BINARY_DIR}/SomeInput.pri to generate the sources. Just make sure you do not add COPYONLY, because then configuration won't retrigger on changes of SomeInput.pri.
EDIT: Alternatively use - a relative new addition - the CMAKE_CONFIGURE_DEPENDS directory property.

Why does CMake make a distinction between a "target" and a "command"?

In CMake semantics there is some sort of distinction between "targets" and commands" that is baffling me. In Makefiles, there is no such distinction:
targetname:dependency
command
i.e. Targets correspond to a generated file of the same name.
In CMake you have commands like "add_custom_command" and "add_custom_target" that have overlapping functionality, and even in the official documentation the semantics are confused, i.e. in "Mastering CMake, 5th edition", page 110 under "Adding a custom target":
The DEPENDS argument sets up a dependency between the custom target
and the custom commands.
My understanding is that targets (generated files) have dependencies (other files, generated or no), and a command to actually do the generation. It is nonsensical to say a target depends on a command. To make matters worse, there are two flavors of "add_custom_command" that either attach an additional command to an existing target, or spit the command out into the ether.
Can someone please explain why this distinction even exists?
Targets
In general, targets comprise executables or libraries which are defined by calling add_executable or add_library and which can have many properties set.
They can have dependencies on one another, which for targets such as these just means that dependent ones will be built after their dependencies.
However, you can also define "custom targets" via add_custom_target. From the docs:
Adds a target with the given name that executes the given commands. The target has no output file and is ALWAYS CONSIDERED OUT OF DATE even if the commands try to create a file with the name of the target. Use ADD_CUSTOM_COMMAND to generate a file with dependencies. By default nothing depends on the custom target. Use ADD_DEPENDENCIES to add dependencies to or from other targets.
So these are different from "normal" targets in that they don't represent things which will produce an exe or lib, but they still benefit from all the properties that targets can have, including having or being dependencies. They appear as a target which can be built (e.g. make MyCustomTarget or msbuild MyCustomTarget.vcxproj). When you build them, you're simply invoking the commands that have been set for them. If they have dependencies on other targets (normal or custom), then these will be built first.
Custom Commands
A custom command defined via add_custom_command is quite different in that it's not a "buildable" object, and doesn't have settable properties in the way that a target does - it's not a named object which can be explicitly referred to again after it's added in the CMakeLists.txt.
It is basically a command (or set of commands) which will be invoked before building a dependent target. That's all that "depends" really means here (at least that's how I view it) - it's just saying that if A depends on B, then B will be built/executed before A is built.
The dependees of a custom command can be either set explicitly using the add_custom_command(TARGET target ... form, or implicitly by creating targets which include the files generated via the add_custom_command(OUTPUT output1 ... form.
In the first case, every time target is built, the custom command is executed first.
In the second case, it's a little more complex. If the custom command has targets which depend on its output file (and the output file doesn't already exist), it is invoked before these dependent objects are built. The dependencies are implicitly created when you do e.g. add_library(MyLib output1.h ... ) where output1.h is a file generated via add_custom_command(OUTPUT output1.h ... ).
add_custom_command adds a callable function that can have defined outputs (using the OUTPUT and BYPRODUCTS arguments). It can also have dependencies that will be run before the function is called.
Notice that it does NOT do things that you may think it does due to strange documentation (the makefile examples are very misleading). In particular, it does not have any guarantees about numbers of times it executes. For example, imagine this:
add_custom_command(OUTPUT /tmp/touched COMMAND echo touch COMMAND touch /tmp/touched)
add_custom_target(touched-one ALL DEPENDS /tmp/touched)
add_custom_target(touched-two ALL DEPENDS /tmp/touched)
How many times will "touch" be printed? You don't know, since it's not specified anywhere; make -j2 will print it twice, probably, but it's timing-dependent:
Scanning dependencies of target touched-two
Scanning dependencies of target touched-one
[ 50%] Generating touched
touch
[100%] Generating touched
touch
[100%] Built target touched-two
[100%] Built target touched-one
But Ninja will only print it once, probably:
# rm -rf * && cmake -GNinja ../c ; cmake --build . -- -j 5
[1/1] Generating touched
touch
Usually, you'll do an add_custom_command to do some work and that defines an OUTPUT, and then you'll have an add_custom_target that depends on the output of the custom command. Anyone who wants the output depends on the target, and that does give you the guarantees you want.
Caveat: see this bug for an great example of why building cross-platform metabuild tools is REALLY HARD.