How can I avoid clashes with targets "imported" with FetchContent_MakeAvailable? - cmake

Suppose I'm writing an app, and managing its build with CMake; and I also want to use a library, mylib, via the FetchContent mechanism.
Now, my own CMakeLists.txt defines a bunch of targets, and so does mylib's CMakeLists.txt. If I were to install mylib, then find_package(mylib), I would only get its exported targets, and even those would be prefixed with mylib:: (customarily, at least). But with FetchContent, both my app's and mylib's (internal and export-intended) targets are in the "global namespace", and may clash.
So, what can I do to separate those targets - other than meticulously name all of my own app's targets defensively?
I would really like it if it were possible to somehow "shove" all the mylib targets into a namespace of my choice.
Note: Relates to: How to avoid namespace collision when using CMake FetchContent?

In the current CMake (<=3.24) world, there are no features for adjusting the names of the targets in other black-box projects, whether included via find_package, add_subdirectory, or FetchContent. Thus, for now, it is incumbent on you to avoid name-clashes in targets, install components, test names, and anywhere else this could be a problem.
Craig Scott says as much in his (very good) talk at CppCon 2019, see here: https://youtu.be/m0DwB4OvDXk?t=2186
The convention he proposes is to use names that are prefixed with SomeProj_. He doesn't suggest to literally use ${PROJECT_NAME}_, and I wouldn't either, because doing so makes the code harder to read and grep (which is extremely useful for understanding a 3rd-party build).
To be a good add_subdirectory or FetchContent citizen, however, it is not enough to simply namespace your targets as SomeProj_Target; you must also provide an ALIAS target SomeProj::Target. There are a few reasons for this:
Your imported targets from find_package will almost certainly be named SomeProj::Target. It should be possible for consumers of your library to switch between FetchContent and find_package easily, without changing other parts of their code. The ALIAS target lets you expose the same interface in both cases. This will become especially pressing when CMake 3.24 lands with its new find_package-to-FetchContent redirection features.
CMake's target_link_libraries function treats names that contain :: as target names always and will throw configure-time error if the target does not exist. Without the ::, it will be treated as a target preferentially, but will turn into a linker flag if the target doesn't exist. Thus, it is preferable to link to targets with :: in their names.
Yet, only IMPORTED and ALIAS targets may have :: in their names.
Points (2) and (3) are good enough for me to define aliases.
Unfortunately, many (most?) CMake builds are not good FetchContent citizens and will flaunt this convention. Following this convention yourself reduces the chance of integration issues between your project and any other, but obviously does nothing to prevent issues between two third party projects that might define conflicting targets. In these cases, you're just out of luck.
An example of defining a library called Target that will play nice with FetchContent:
add_library(SomeProj_Target ${sources})
add_library(SomeProj::Target ALIAS SomeProj_Target)
set_target_properties(
SomeProj_Target
PROPERTIES
EXPORT_NAME Target
OUTPUT_NAME Target # optional: makes the file libTarget.so on disk
)
install(TARGETS SomeProj_Target EXPORT SomeProj_Targets)
install(EXPORT SomeProj_Targets NAMESPACE SomeProj::)
For a more complete example that plays nice with install components, include paths, and dual shared/static import, see my blog post.
See these upstream issues to track the progress/discussion of these problems.
#22687 Project-level namespaces
#16414 Namespace support for target names in nested projects

As #AlexReinking , and, in fact, Craig Scott, suggest - there's no decent current solution.
You can follow the following CMake issues through which the solution will likely be achieved:
#22687 Project-level namespaces (more current)
#16414 Namespace support for target names in nested projects (longer discussion which influenced the above, recommended reading)

Related

Hierarchical CMake project that also works when building an "inner" (non-root) project

Suppose I have a hierarchical CMake project, composed of n different projects:
CMakeLists.txt
proj-1/CMakeLists.txt
proj-2/CMakeLists.txt
(...)
proj-n/CMakeLists.txt
Evidently there'll be the source files for each project as well.
I'd ensure all commands of interest are added to the root CMakeLists.txt file -- say, CMAKE_CXX_STANDARD, enable_testing(), add_compile_options(), etc. If I understand correctly, whichever options are included in the root CMakeLists.txt file are also applied to all children CMakeLists.txt file -- please correct me if I'm wrong, since I'm counting on this behavior. The root CMakeLists.txt also contains an add_subdirectory(proj-X) statement for each X = 1, ..., n.
Anyway. Suppose, for some reason, that I would like to build only one of the proj-X folders, say proj-1. Maybe the build is broken in one of the other projects, or maybe I need to fix a bug on proj-1, it doesn't depend on the other projects, and it would take forever to build all projects.
The point is: I would like to run cmake on proj-1/CMakeLists.txt rather than on the root CMakeLists.txt file, and yet I would like to ensure that proj-1 is built in exactly the same way it would be build, had I run cmake on the root CMakeLists.txt file. This is an issue since the root CMakeLists.txt contains statements that the children CMakeLists.txt should "inherit" from in the regular situation where it's built from the root, and yet in this scenario I'm building directly from proj-1/CMakeLists.txt (the root CMakeLists.txt file doesn't come into the picture in this scenario.)
One possibility, as I understand, would be to copy all options from the root CMakeLists.txt file to every other proj-X/CMakeLists.txt file. Of course, this is a hack and a maintenance nightmare, but I suppose it would work.
Are there other possible solutions? Can I, say, create a file containing all the common options and save it to the root, and then do the CMake equivalent of #include within each of the proj-X/CMakeLists.txt files? Would there be an issue due to running the same commands twice (once on the root CMakeLists.txt and another on the proj-X/CMakeLists.txt file, when starting the build from the root)?
You may need to rework some of your CMakeLists.txt files.
I would recommend watching Daniel Pfeifer's Effective CMake talk at CPPcon (slides available here).
The gist of it is that all of your projects should provide everything they need in order to be build or compiled, in essence build requirements and usage requirements. To achieve this in a maintainable and scalable way you have to move away from variables and setting global options (add_compile_options, include_directories, etc) and instead focus on targets (target_compile_options, target_include_directories, etc).
So, in your case proj-1/CMakeLists.txt will provide one target (let's call it proj::proj1) that sets the proper PUBLIC and INTERFACE options (by options I mean needed compiler features, dependencies, include directories, etc).
An abstract example:
project(proj1)
add_library(proj1 src.cpp)
# This are private include files, whoever uses this library does not need them
target_include_directories(proj1 PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include)
# These are public, needed both by this target and by whoever uses it.
target_include_directories(proj1 PUBLIC
# This is used when building the target
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/public/include>
# This is used when the target is installed
$<INSTALL_INTERFACE:include>)
# Instead of asking directly for a language standard we ask for a compiler feature. We make this public so whoever depends on this target knows they also need this feature.
target_compile_features(proj1 PUBLIC cxx_strong_enums)
# As above, but this is needed only by this target during the build.
target_compile_features(proe1 PRIVATE cxx_lambdas)
# Add an alias, users can use target_link_libraries(target PRIVATE|PUBLIC proj::proj1) to add this target as a dependency (this will propagate all the PUBLIC include paths, compile options, compile features, dependencies, etc.
add_library(proj::proj1 ALIAS proj1)
This is highly abstract, it depends on what you're actually doing in your build scripts, it's hard to give a better explanation than Daniel Pfeifer, so I recommend watching his talk or at least reading the slides. It will make your build scripts a lot easier to write, read, and use.
Another great resource is this site.

Change toolchain in a subfolder with cmake [duplicate]

It seems like CMake is fairly entrenched in its view that there should be one, and only one, CMAKE_CXX_COMPILER for all C++ source files. I can't find a way to override this on a per-target basis. This makes a mix of host-and-cross compiling in a single CMakeLists.txt very difficult with the built-in CMake facilities.
So, my question is: what's the best way to use multiple compilers for the same language (i.e. C++)?
It's impossible to do this with CMake.
CMake only keeps one set of compiler properties which is shared by all targets in a CMakeLists.txt file. If you want to use two compilers, you need to run CMake twice. This is even true for e.g. building 32bit and 64bit binaries from the same compiler toolchain.
The quick-and-dirty way around this is using custom commands. But then you end up with what are basically glorified shell-scripts, which is probably not what you want.
The clean solution is: Don't put them in the same CMakeLists.txt! You can't link between different architectures anyway, so there is no need for them to be in the same file. You may reduce redundancies by refactoring common parts of the CMake scripts into separate files and include() them.
The main disadvantage here is that you lose the ability to build with a single command, but you can solve that by writing a wrapper in your favorite scripting language that takes care of calling the different CMake-makefiles.
You might want to look at ExternalProject:
http://www.kitware.com/media/html/BuildingExternalProjectsWithCMake2.8.html
Not impossible as the top answer suggests. I have the same problem as OP. I have some sources for cross compiling for a raspberry pi pico, and then some unit tests that I am running on my host system.
To make this work, I'm using the very shameful "set" to override the compiler in the CMakeLists.txt for my test folder. Works great.
if(DEFINED ENV{HOST_CXX_COMPILER})
set(CMAKE_CXX_COMPILER $ENV{HOST_CXX_COMPILER})
else()
set(CMAKE_CXX_COMPILER "g++")
endif()
set(CMAKE_CXX_FLAGS "")
The cmake devs/community seems very against using set to change the compiler since for some reason. They assume that you need to use one compiler for the entire project which is an incorrect assumption for embedded systems projects.
My solution above works, and fits the philosophy I think. Users can still change their chosen compiler via environment variables, if it's not set then I do assume g++. set only changes variables for the current scope, so this doesn't affect the rest of the project.
To extend #Bill Hoffman's answer:
Build your project as a super-build, by using some kind of template like the one here https://github.com/Sarcasm/cmake-superbuild
which will configure both the dependencies and your project as an ExternalProject (standalone cmake configure/build/install environment).

Naming convention for components and namespaces in cmake

In Short:
Is there any preferred naming convention for cmake library targets - in particular when using namespaces?
Note:
Unless there is really an objective reason for it, I'm not asking about personal preferences, but whether there is either an "official" (e.g. recommended by kitware) or established (which might deviate) convention.
Details:
Lets say I have a library/framework foo which has the individual components bar and baz. So far, my naming convention looks like this:
add_library(foo-bar src1.cpp, scr2.cpp)
add_library(foo-baz src3.cpp, src4.cpp)
Now I want to add alias targets using the namespace (::) convention. E.g.
add_library(Foo::Bar ALIAS foo-bar)
add_library(Foo::Baz ALIAS foo-baz)
(Of course the question also extends to export sets, but I didn't want to complicate the question)
What I couldn't really find out however, is if there is a preferred or even official naming convention for those targets.
Things I've seen:
Namespace part:
some projects seem to capitalize the first letter, some not (the former seems to be more common)
Component part:
in some projects, the component name is the same as the binary name
with or without the "lib" prefix (libfoo-bar vs foo-bar)
with or without the namespace (foo-bar vs bar)
some projects capitalize the first letter
some projects use CamelCase some snake_case, even if the binaries or project names don't follow those conventions.
I guess the main problem is that there is no naming convention for libraries in general so that makes it hard to come up with a naming convention in CMake, but at least the capitilization for the first letter of the namespace and the component seem to be pretty common, so I was wondering if there is some guideline I should follow for future projects.
The cmake-developer documentation gives the following advice on namespaces:
When providing imported targets, these should be namespaced (hence the Foo:: prefix); CMake will recognize that values passed to target_link_libraries() that contain :: in their name are supposed to be imported targets (rather than just library names), and will produce appropriate diagnostic messages if that target does not exist (see policy CMP0028).
And the CMP0028 policy documentation says on the "common pattern" in the use of namespaces:
The use of double-colons is a common pattern used to namespace IMPORTED targets and ALIAS targets. When computing the link dependencies of a target, the name of each dependency could either be a target, or a file on disk. Previously, if a target was not found with a matching name, the name was considered to refer to a file on disk. This can lead to confusing error messages if there is a typo in what should be a target name.
And no, there are no CMake specific convention for the naming of library targets. But since the name is taken by default as the target's output name:
I prefer to take the same name for the targets as for my source code directory
And add no lib prefix, since this is automatically added by CMake depending on the platform you are compiling your project with
From the CMake Tutorial
The most official source you could get is probably an extract from the "Mastering CMake" book written by Ken Martin and Bill Hoffman from Kitware.
The tutorials from the book all use CamelCase and no namespaces for component/target names.
References
What is the naming convention for CMake scripts?
cmake usefulness of aliases

Getting imported targets through `find_package`?

The CMake manual of Qt 5 uses find_package and says:
Imported targets are created for each Qt module. Imported target names should be preferred instead of using a variable like Qt5<Module>_LIBRARIES in CMake commands such as target_link_libraries.
Is it special for Qt or does find_package generate imported targets for all libraries? The documentation of find_package in CMake 3.0 says:
When the package is found package-specific information is provided through variables and Imported Targets documented by the package itself.
And the manual for cmake-packages says:
The result of using find_package is either a set of IMPORTED targets, or a set of variables corresponding to build-relevant information.
But I did not see another FindXXX.cmake-script where the documentation says that a imported target is created.
find_package is a two-headed beast these days:
CMake provides direct support for two forms of packages, Config-file Packages
and Find-module Packages
Source
Now, what does that actually mean?
Find-module packages are the ones you are probably most familiar with. They execute a script of CMake code (such as this one) that does a bunch of calls to functions like find_library and find_path to figure out where to locate a library.
The big advantage of this approach is that it is extremely generic. As long as there is something on the filesystem, we can find it. The big downside is that it often provides little more information than the physical location of that something. That is, the result of a find-module operation is typically just a bunch of filesystem paths. This means that modelling stuff like transitive dependencies or multiple build configurations is rather difficult.
This becomes especially painful if the thing you are trying to find has itself been built with CMake. In that case, you already have a bunch of stuff modeled in your build scripts, which you now need to painstakingly reconstruct for the find script, so that it becomes available to downstream projects.
This is where config-file packages shine. Unlike find-modules, the result of running the script is not just a bunch of paths, but it instead creates fully functional CMake targets. To the dependent project it looks like the dependencies have been built as part of that same project.
This allows to transport much more information in a very convenient way. The obvious downside is that config-file scripts are much more complex than find-scripts. Hence you do not want to write them yourself, but have CMake generate them for you. Or rather have the dependency provide a config-file as part of its deployment which you can then simply load with a find_package call. And that is exactly what Qt5 does.
This also means, if your own project is a library, consider generating a config file as part of the build process. It's not the most straightforward feature of CMake, but the results are pretty powerful.
Here is a quick comparison of how the two approaches typically look like in CMake code:
Find-module style
find_package(foo)
target_link_libraries(bar ${FOO_LIBRARIES})
target_include_directories(bar ${FOO_INCLUDE_DIR})
# [...] potentially lots of other stuff that has to be set manually
Config-file style
find_package(foo)
target_link_libraries(bar foo)
# magic!
tl;dr: Always prefer config-file packages if the dependency provides them. If not, use a find-script instead.
Actually there is no "magic" with results of find_package: this command just searches appropriate FindXXX.cmake script and executes it.
If Find script sets XXX_LIBRARY variable, then caller can use this variable.
If Find script creates imported targets, then caller can use these targets.
If Find script neither sets XXX_LIBRARY variable nor creates imported targets ... well, then usage of the script is somehow different.
Documentation for find_package describes usual usage of Find scripts. But in any case you need to consult documentation about concrete script (this documentation is normally contained in the script itself).

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.