FetchContent_Declare multiple projects cause dependency conflicts

cmake_minimum_required(VERSION 3.20)
project(test)

include(FetchContent)

# snappy
FetchContent_Declare(
    snappy
    GIT_REPOSITORY https://github.com/google/snappy.git
    GIT_TAG 1.2.1
)

# leveldb
FetchContent_Declare(
    leveldb
    GIT_REPOSITORY https://github.com/google/leveldb.git
    GIT_TAG 1.23
)
FetchContent_MakeAvailable(snappy leveldb)

add_executable(test main.cpp)
target_link_libraries(test PUBLIC leveldb)

leveldb depends on snappy, and both leveldb and snappy depend on benchmark, which causes cmake configuration failure.

[build] CMake Error at build/_deps/leveldb-src/third_party/benchmark/src/CMakeLists.txt:20 (add_library):
[build]   add_library cannot create target "benchmark" because another target with
[build]   the same name already exists.  The existing target is a static library
[build]   created in source directory
[build]   "/workspaces/build/_deps/snappy-src/third_party/benchmark/src".
[build]   See documentation for policy CMP0002 for more details.
[build] 
[build] 
[build] CMake Error at build/_deps/leveldb-src/third_party/benchmark/src/CMakeLists.txt:35 (target_link_libraries):
[build]   The keyword signature for target_link_libraries has already been used with
[build]   the target "benchmark".  All uses of target_link_libraries with a target
[build]   must be either all-keyword or all-plain.
[build] 
[build]   The uses of the keyword signature are here:
[build] 
[build]    * build/_deps/snappy-src/third_party/benchmark/src/CMakeLists.txt:38 (target_link_libraries)
[build]    * build/_deps/snappy-src/third_party/benchmark/src/CMakeLists.txt:43 (target_link_libraries)
[build] 
[build] 
[build] 
[build] CMake Error at build/_deps/leveldb-src/third_party/benchmark/src/CMakeLists.txt:57 (add_library):
[build]   add_library cannot create target "benchmark_main" because another target
[build]   with the same name already exists.  The existing target is a static library
[build]   created in source directory
[build]   "/workspaces/build/_deps/snappy-src/third_party/benchmark/src".
[build]   See documentation for policy CMP0002 for more details.
[build] 
[build] 
[build] CMake Error at build/_deps/leveldb-src/third_party/benchmark/src/CMakeLists.txt:67 (target_link_libraries):
[build]   The keyword signature for target_link_libraries has already been used with
[build]   the target "benchmark_main".  All uses of target_link_libraries with a
[build]   target must be either all-keyword or all-plain.
[build] 
[build]   The uses of the keyword signature are here:
[build] 
[build]    * build/_deps/snappy-src/third_party/benchmark/src/CMakeLists.txt:70 (target_link_libraries)
[build] 
[build] 
[build] -- Configuring incomplete, errors occurred!

How should this situation be handled?

I would disable the tests and benchmarks:

cmake_minimum_required(VERSION 3.25...3.30)
project(test)

# C++ standard can be overridden when this is used as a sub-project.
if(NOT CMAKE_CXX_STANDARD)
  set(CMAKE_CXX_STANDARD 17)
  set(CMAKE_CXX_STANDARD_REQUIRED ON)
  set(CMAKE_CXX_EXTENSIONS OFF)
endif()

include(FetchContent)

# snappy
option(SNAPPY_BUILD_TESTS "Build Snappy's own tests." OFF)
option(SNAPPY_BUILD_BENCHMARKS "Build Snappy's benchmarks" OFF)
FetchContent_Declare(
    snappy
    GIT_REPOSITORY https://github.com/google/snappy.git
    GIT_TAG 1.2.1
    GIT_SUBMODULES ""
    SYSTEM
)

# leveldb
option(LEVELDB_BUILD_TESTS "Build LevelDB's unit tests" OFF)
option(LEVELDB_BUILD_BENCHMARKS "Build LevelDB's benchmarks" OFF)
FetchContent_Declare(
    leveldb
    GIT_REPOSITORY https://github.com/google/leveldb.git
    GIT_TAG 1.23
    GIT_SUBMODULES ""
    SYSTEM
)
FetchContent_MakeAvailable(leveldb snappy)

add_executable(test main.cpp)
target_link_libraries(test PUBLIC leveldb)

NOTE: I do disable too the git submodul update!

Do we need to manually handle their dependencies in this case? Is there a better way? Similar to namespace in C++, we can isolate them

Some projects do not expect to be directly incorporated into a parent project, which is what FetchContent does. If the dependency project tries to self-manage its own dependencies using something like git submodules or some other mechanism, you would generally need to somehow disable those so that FetchContent can provide the dependencies instead.

If two dependencies share a common third dependency, FetchContent handles that case. It can only do that if it is the mechanism that tries to bring that third dependency into the build. If the two immediate dependencies both use FetchContent to pull in the third dependency, it should all just work (see the FetchContent docs for the way the dependency is resolved, but it essentially comes down to a “first to declare, wins” approach). If one or both of them use a different mechanism to bring the third dependency into the build, such as via a git submodule, FetchContent can’t see or handle that and you’ll likely end up with an error similar to what you observed.