Can you combine FetchContent and ExternalProject?

Ordinarily, I advise choosing one or the other rather than mixing FetchContent and ExternalProject. More specifically, if you can use FetchContent to not just download the dependencies but also to add them directly to your build, that is typically simpler than ExternalProject (read up on using FetchContent_MakeAvailable() specifically). It relies on the dependency projects not doing things like assuming they are the top level project or using target names that other projects also define, but if you don’t hit either of those problems, then it’s probably the simpler route.

In your situation, it may still make sense to mix the two approaches. You want the sources to be downloaded at configure time, which FetchContent can achieve for you (you can’t use FetchContent_MakeAvailable() then though). If you genuinely do need to build the dependencies as external builds, then ExternalProject is the right tool for that. You can tell ExternalProject to use the source locations that FetchContent will have already populated for you at configure time. In the call to ExternalProject_Add(), omit any download or update details and instead provide SOURCE_DIR, with a value being provided by FetchContent. For example (untested, but hopefully you get the idea):

FetchContent_Declare(someproj
    GIT_REPOSITORY https://github.com/someGroup/someproj
    ...
)
FetchContent_GetProperties(someproj)
if(NOT someproj_POPULATED)
  FetchContent_Populate(someproj)
endif()

ExternalProject_Add(someproj
    SOURCE_DIR ${someproj_SOURCE_DIR}  # Provided by FetchContent
    ...
)

Just to be clear, your approach relies on the head node being identically configured to the compute nodes (same paths, exact same tool versions, etc.).