einpoklum
einpoklum

Reputation: 131808

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

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

Answers (3)

Takoda
Takoda

Reputation: 354

I would suggest using ExternalProject and share artifacts between the two.

Upvotes: 0

Alex Reinking
Alex Reinking

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:

  1. 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.
  2. 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.
  3. 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

Upvotes: 6

einpoklum
einpoklum

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:

  • #22687 Project-level namespaces (more current)
  • #16414 Namespace support for target names in nested projects (longer discussion which influenced the above, recommended reading)

Upvotes: 1

Related Questions