6

When I use CMake FetchContent to import OpenCV, it works fine:

include(FetchContent)

# Fetch OpenCV
FetchContent_Declare(
        opencv
        GIT_REPOSITORY https://gitee.com/aiproach/opencv.git
        GIT_TAG        4.4.0
)
FetchContent_MakeAvailable(opencv)

set(OpenCV_DIR ${CMAKE_CURRENT_BINARY_DIR})
find_package(OpenCV REQUIRED)

But after I add Eigen:

# Fetch Eigen
FetchContent_Declare(
        eigen
        GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git
        GIT_TAG        3.3.9
)
FetchContent_MakeAvailable(eigen)
find_package(eigen3 REQUIRED)

It emits errors:

CMake Error at build/_deps/eigen-src/CMakeLists.txt:620 (add_custom_target):
  add_custom_target cannot create target "uninstall" because another target
  with the same name already exists.  The existing target is a custom target
  created in source directory
  "...../build/_deps/opencv-src".
  See documentation for policy CMP0002 for more details.


CMake Error at build/_deps/eigen-build/eigen3Config.cmake:20 (include):
  The file

    ....../build/_deps/eigen-build/Eigen3Targets.cmake

  was generated by the export() command.  It may not be used as the argument
  to the include() command.  Use ALIAS targets instead to refer to targets by
  alternative names.

Call Stack (most recent call first):
  CMakeLists.txt:30 (find_package)

I was told that this is caused by namespace collision, but I don't know how to solve that issue. I searched for "FetchContent" on GitHub, but it seems everybody is using it the same way as mine. Is there a general way to fetch everything using FetchContent with just the effort to insert the project name and URL?

starball
  • 20,030
  • 7
  • 43
  • 238
Cook21
  • 61
  • 3
  • 1
    If two subprojects define a target with the same name, then you cannot include into your main project both subprojects via `FetchContent_MakeAvailable`. You could use `ExternalProject_Add` for one of those projects (or for both of them), or you could require those subprojects to be already installed before configuring your main project. – Tsyvarev Dec 16 '21 at 17:33
  • 1
    Note, that using for the same project both `FetchContent_MakeAvailable` and `find_package` is **wrong**. In case of OpenCV you have tried to overcome the error message you got by setting the variable `OpenCV_DIR` to the current binary dir, which contains `opencv-config.cmake` script, but that script is not intended to be used in such way. In case of Eigen you have the error message (the second one) pointing to the incorrect usage. – Tsyvarev Dec 16 '21 at 17:38
  • You are right, it only emits one error after I remove `find_package`, but could you show me how to use `ExternalProject_Add` with `FetchContent`? Current tutorial I found on Internet all use `FetchContent_MakeAvailable` method. – Cook21 Dec 17 '21 at 01:58
  • 1
    `ExternalProject_Add` supports project's downloading by itself. So `FetchContent` is not needed for it. In my first comment I meant that you could use FetchContent approach for OpenCV and ExternalProject_Add for Eigen. – Tsyvarev Dec 17 '21 at 08:20
  • Related: [How to avoid name clashes in cmake subprojects?](/q/30667736) – starball Feb 08 '23 at 17:18

1 Answers1

1

When the clashes are between targets from different project dependencies, there's not much you can do right now except politely ask the project maintainers to consider modifying their non-import/export target names to be namespaced like.

You could try to work around the problem by patching their CMake files after the download. If you're using the ExternalProject module or the FetchContent module (which is built on top of ExternalProject), you might be able to do this with the PATCH_COMMAND argument (see the docs), which "Specifies a custom command to patch the sources after an update.". But I've never tried this myself and it sounds like a pain to do. Another approach with ExternalProject would be to install the targets, and then add them to the project configuration as imported library targets, which would allow you to control their target names.

For example, for a library target, that might look like:

add_library(projectname_targetnamepart)
add_library("importexportnamespacename::targetnamepart" ALIAS projectname_targetnamepart)
set_target_properties(projectname_targetnamepart PROPERTIES EXPORT_NAME targetnamepart)
set_target_properties(projectname_targetnamepart PROPERTIES OUTPUT_NAME targetnamepart)
install(TARGETS projectname_targetnamepart EXPORT projectname_targets ...)
install(EXPORT projectname_targets NAMESPACE "importexportnamespacename::" ...)

There's a Kitware issue ticket by Craig Scott proposing a CMake feature for Project-level namespaces. Here's an excerpt:

A common problem when pulling in subprojects using add_subdirectory() is that target names must be unique, but different subprojects might try to use the same target name, which results in a name clash. This issue proposes to introduce the concept of a project namespace to address this and related name uniqueness problems (this is the primary goal of this issue).

Sometimes the upstream maintainer will just decline to support the add_subdirectory / FetchContent use-case. That's the case with OpenCV, as shown in this issue ticket (#16896). As for eigen, there's an open ticket that hasn't had any activity in a while (#1892).

Someone else had a similar question here: Isolating gitsubmodule projects in CMake, and indicated in the comments that they were able to resolve their issues by using the Hunter package manager, so perhaps you could give that a try.

starball
  • 20,030
  • 7
  • 43
  • 238