Reputation: 131808
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?
Upvotes: 4
Views: 2000
Reputation: 354
I would suggest using ExternalProject
and share artifacts between the two.
Upvotes: 0
Reputation: 19946
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:
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.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.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.
Upvotes: 6
Reputation: 131808
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:
Upvotes: 1