# Relationship between FetchContent and ExternalProject

**URL:** https://discourse.cmake.org/t/relationship-between-fetchcontent-and-externalproject/10162
**Category:** Usage
**Created:** [February 22, 2024, 10:31pm UTC](https://discourse.cmake.org/t/relationship-between-fetchcontent-and-externalproject/10162 "2024-02-22T22:31:56Z")
**Posts on this page:** 17
**Page:** 1

<div class="post-metadata">

### Author: ![dabrahams](https://discourse.cmake.org/user_avatar/discourse.cmake.org/dabrahams/32/4265_2.png) [@dabrahams](https://discourse.cmake.org/u/dabrahams)
#### Post date: [February 22, 2024, 10:31pm UTC](https://discourse.cmake.org/t/relationship-between-fetchcontent-and-externalproject/10162/1 "2024-02-22T22:31:57Z")

</div>

Can someone give a succinct description of the relationship / differences between `FetchContent` and `ExternalProject`? It seems like the former adds external dependencies as though they were embedded with `add_subdirectory()` and the latter builds/installs the dependencies somewhere and (presumably) points at the `_DIR` of the resulting `XXXConfig.cmake` file. But I don’t have a good sense of how to choose. Reading @craig.scott 's post [here](https://discourse.cmake.org/t/fetchcontent-or-externalproject-or-other-choices-for-non-cmake-libraries/7687/4) gives me the impression they have very different use cases, but doesn’t seem to paint the full picture. Can anyone help?

I note the the “[Using Dependencies Guide](https://cmake.org/cmake/help/latest/guide/using-dependencies/index.html)” doesn’t even mention `ExternalProject`, which gives the impression it’s an outdated approach.

TIA,  
Dave

---

<div class="post-metadata">

### Author: ![ClausKlein](https://discourse.cmake.org/user_avatar/discourse.cmake.org/clausklein/32/352_2.png) [@ClausKlein](https://discourse.cmake.org/u/ClausKlein)
#### Post date: [February 23, 2024, 6:48am UTC](https://discourse.cmake.org/t/relationship-between-fetchcontent-and-externalproject/10162/2 "2024-02-23T06:48:08Z")

</div>

> [@dabrahams](#):
>
> I note the the “[Using Dependencies Guide](https://cmake.org/cmake/help/latest/guide/using-dependencies/index.html)” doesn’t even mention `ExternalProject`, which gives the impression it’s an outdated approach.

**AFAIK** you should prefer `FetchContents`.

**Quote** : Some strengths of `ExternalProject` are also its weaknesses. It allows external project builds to be completely isolated from the main project. This means it can use a different toolchain, target a different platform, use a different build type or even an entirely different build system.

From [ProfesionalCmake](https://crascit.com/professional-cmake/)

---

<div class="post-metadata">

### Author: ![dabrahams](https://discourse.cmake.org/user_avatar/discourse.cmake.org/dabrahams/32/4265_2.png) [@dabrahams](https://discourse.cmake.org/u/dabrahams)
#### Post date: [February 23, 2024, 5:44pm UTC](https://discourse.cmake.org/t/relationship-between-fetchcontent-and-externalproject/10162/3 "2024-02-23T17:44:37Z")

</div>

> [@ClausKlein](#):
>
> **AFAIK** you should prefer `FetchContents`.
> 
> **Quote** : Some strengths of `ExternalProject` are also its weaknesses. It allows external project builds to be completely isolated from the main project. This means it can use a different toolchain, target a different platform, use a different build type or even an entirely different build system.

For a dependency on LLVM, I’m beginning to think those are all pure strengths. LLVM is not really set up to be consumed as a subdirectory, and it uses a few questionable CMake practices that otherwise would infect the dependent project. It’s very common that people want to use a Release build of LLVM with assertions enabled when they create a Debug build of a dependent project.

> From [ProfesionalCmake](https://crascit.com/professional-cmake/)

I guess I’d better get that book!

---

<div class="post-metadata">

### Author: ![ben.boeckel](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/b/ea5d25/32.png) [@ben.boeckel](https://discourse.cmake.org/u/ben.boeckel)
#### Post date: [February 27, 2024, 2:08pm UTC](https://discourse.cmake.org/t/relationship-between-fetchcontent-and-externalproject/10162/4 "2024-02-27T14:08:04Z")

</div>

Note that `ExternalProject` doesn’t mix well with `add_library`. Generally you want to make a “superbuild” (basically a focused package manager) to build everything as `ExternalProject`s (including your own). The projects themselves then just use `find_package()` to find each other and the superbuild “stitches” things together (like a package manager).

---

<div class="post-metadata">

### Author: ![dabrahams](https://discourse.cmake.org/user_avatar/discourse.cmake.org/dabrahams/32/4265_2.png) [@dabrahams](https://discourse.cmake.org/u/dabrahams)
#### Post date: [February 27, 2024, 5:29pm UTC](https://discourse.cmake.org/t/relationship-between-fetchcontent-and-externalproject/10162/5 "2024-02-27T17:29:12Z")

</div>

In what way don’t they mix well?

Maybe I don’t understand what you’re suggesting, but making a project that builds my project and all of its dependencies as external seems very inconvenient for day-to-day development work on my project, because IIUC in the super-project, rebuilds of my project would not be triggered by changes to its source files, and I couldn’t build just my project without somehow bringing in all those external dependencies.

I was thinking I would conditionally use `ExternalProject` for (some) dependencies when my project is top-level. Is there a drawback to that approach? If so, how does the arrangement you propose avoid that drawback?

Also, can you confirm my general conclusion that the top-level project always needs to be in control over satisfying all of the transitive dependencies of everything that’s built? That seems like the only way dependency diamonds can ever be resolved reliably when there are version constraints. Have I got that right?

---

<div class="post-metadata">

### Author: ![ben.boeckel](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/b/ea5d25/32.png) [@ben.boeckel](https://discourse.cmake.org/u/ben.boeckel)
#### Post date: [February 27, 2024, 7:14pm UTC](https://discourse.cmake.org/t/relationship-between-fetchcontent-and-externalproject/10162/6 "2024-02-27T19:14:10Z")

</div>

> [@dabrahams](#):
>
> In what way don’t they mix well?

The files for `find_package(dep)` when using `ExternalProject_add(dep)` won’t exist until build time. So you cannot use `find_package(dep)` to load your dependency.

> [@dabrahams](#):
>
> making a project that builds my project and all of its dependencies as external seems very inconvenient for day-to-day development work on my project, because IIUC in the super-project, rebuilds of my project would not be triggered by changes to its source files

There are techniques for actual development. Look for `DEVELOPER_MODE` in our [`common-superbuild`](https://gitlab.kitware.com/paraview/common-superbuild/). What this does is build all of the _dependencies_ of the specific project and provides a `developer-mode.cmake` script that can be used to pass as `-C developer-mode.cmake` to configure an arbitrary build tree as if it were in the superbuild (namely finding dependencies).

> [@dabrahams](#):
>
> Also, can you confirm my general conclusion that the top-level project always needs to be in control over satisfying all of the transitive dependencies of everything that’s built? That seems like the only way dependency diamonds can ever be resolved reliably when there are version constraints. Have I got that right?

Yes. Vendoring without offering controls to “unvendor” is a wonderful way to make packagers’ lives hell because somebody else is going to want that dep someday too. If a superbuild is not suitable, it might be that you can document a list of Conan or vcpkg packages that are needed and use those environments to provide dependencies.

---

<div class="post-metadata">

### Author: ![dabrahams](https://discourse.cmake.org/user_avatar/discourse.cmake.org/dabrahams/32/4265_2.png) [@dabrahams](https://discourse.cmake.org/u/dabrahams)
#### Post date: [February 27, 2024, 7:44pm UTC](https://discourse.cmake.org/t/relationship-between-fetchcontent-and-externalproject/10162/7 "2024-02-27T19:44:23Z")

</div>

Thanks, and could you answer these questions?

> [@dabrahams](#):
>
> I was thinking I would conditionally use `ExternalProject` for (some) dependencies when my project is top-level. Is there a drawback to that approach? If so, how does the arrangement you propose avoid that drawback?

The whole common-superbuild project represents a whole new layer of… something… to learn—I don’t even know what ParaView is yet and it appears to be central—so I’d like to be sure I understand why I’m using it rather than an arrangement I can understand purely in terms of the CMake documentation.

Thanks again.

---

<div class="post-metadata">

### Author: ![jonesv](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/j/ac8455/32.png) [@jonesv](https://discourse.cmake.org/u/jonesv)
#### Post date: [February 27, 2024, 9:15pm UTC](https://discourse.cmake.org/t/relationship-between-fetchcontent-and-externalproject/10162/8 "2024-02-27T21:15:55Z")

</div>

> [@ben.boeckel](#):
>
> The files for `find_package(dep)` when using `ExternalProject_add(dep)` won’t exist until build time. So you cannot use `find_package(dep)` to load your dependency.

That’s only true if you expect to build the dependencies in the configure step of CMake, right? But you don’t have to: you can build and install your dependencies (not necessarily on the system, you can install them locally) and then have your main project fetch them (using CMAKE\_PREFIX\_PATH if you installed them locally).

See for instance [here](https://www.acarg.ch/posts/cmake-deps/), where building the dependencies first and the main project next can look as simple as:

```auto
# Run the helper script, installing the dependencies in `./dependencies/install`
cmake -DCMAKE_INSTALL_PREFIX=dependencies/install -Bdependencies/build -Sdependencies
cmake --build dependencies/build

# Build the project, telling `find_package()` where to find dependencies
cmake -DCMAKE_PREFIX_PATH=$(pwd)/dependencies/install -Bbuild -S.
cmake --build build

```

---

<div class="post-metadata">

### Author: ![ben.boeckel](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/b/ea5d25/32.png) [@ben.boeckel](https://discourse.cmake.org/u/ben.boeckel)
#### Post date: [February 28, 2024, 10:41am UTC](https://discourse.cmake.org/t/relationship-between-fetchcontent-and-externalproject/10162/9 "2024-02-28T10:41:31Z")

</div>

This is essentially the “DEVELOPER\_MODE” I referred to above (in the simple case where `CMAKE_PREFIX_PATH` is sufficient to find all dependencies…sadly not a general case).

> [@jonesv](#):
>
> \<edit: I can’t post a link to my blog post explaining it because it is apparently flagged as advertisement 🤔\>

You can probably put it as `https://a.literal.url`.

> [@dabrahams](#):
>
> The whole common-superbuild project represents a whole new layer of… something… to learn—I don’t even know what ParaView is yet and it appears to be central—so I’d like to be sure I understand why I’m using it rather than an arrangement I can understand purely in terms of the CMake documentation.

I’m offering it as an example of an `ExternalProject`-using project which implements some of the use cases you seem to be looking for. I don’t think I can suggest it for general usage. Projects which end up using it generally don’t even know because they just `find_package()` their dependencies and the superbuild wires things up so that its copies get found and used. The projects don’t care whether it is vcpkg, conan, rpm, dpkg, Homebrew, or whatever providing the dependencies. A superbuild is just a focused package manager.

---

<div class="post-metadata">

### Author: ![jonesv](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/j/ac8455/32.png) [@jonesv](https://discourse.cmake.org/u/jonesv)
#### Post date: [February 28, 2024, 11:10am UTC](https://discourse.cmake.org/t/relationship-between-fetchcontent-and-externalproject/10162/10 "2024-02-28T11:10:23Z")

</div>

> [@ben.boeckel](#):
>
> in the simple case where `CMAKE_PREFIX_PATH` is sufficient to find all dependencies…sadly not a general case

Now that got me curious, because I have never seen a case where that doesn’t work (but I run mostly on Linux). Is it “not a general case” on other platforms, or did you mean on Linux too?

---

<div class="post-metadata">

### Author: ![ben.boeckel](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/b/ea5d25/32.png) [@ben.boeckel](https://discourse.cmake.org/u/ben.boeckel)
#### Post date: [February 28, 2024, 11:32am UTC](https://discourse.cmake.org/t/relationship-between-fetchcontent-and-externalproject/10162/11 "2024-02-28T11:32:05Z")

</div>

Linux too. This isn’t the thread for it. Search the common-superbuild for `superbuild_add_extra_cmake_args` or see what [helper variables VTK forwards](https://gitlab.kitware.com/vtk/vtk/-/blob/d04a5ee01bb1e7a058a6d9e2c444857028c8af06/CMake/vtkInstallCMakePackageHelpers.cmake#L35) for examples.

---

<div class="post-metadata">

### Author: ![dlrdave](https://discourse.cmake.org/user_avatar/discourse.cmake.org/dlrdave/32/2589_2.png) [@dlrdave](https://discourse.cmake.org/u/dlrdave)
#### Post date: [February 28, 2024, 2:20pm UTC](https://discourse.cmake.org/t/relationship-between-fetchcontent-and-externalproject/10162/12 "2024-02-28T14:20:19Z")

</div>

> [@dabrahams](#):
>
> The whole common-superbuild project represents a whole new layer of… something… to learn

It is worth learning, though. 🙂

In my experience, ExternalProject is best used for building dependencies that do not change frequently, and are not part of the day-to-day development workflow. Semi-static dependencies that you update every few months or more.

But for that particular use case, and the flexibility of being able to build the dependencies exactly like you want to, … ExternalProject is fantastic.

_disclaimer: as an early contributor to ExternalProject, my opinions about it are probably not completely unbiased_

---

<div class="post-metadata">

### Author: ![dabrahams](https://discourse.cmake.org/user_avatar/discourse.cmake.org/dabrahams/32/4265_2.png) [@dabrahams](https://discourse.cmake.org/u/dabrahams)
#### Post date: [March 4, 2024, 7:26pm UTC](https://discourse.cmake.org/t/relationship-between-fetchcontent-and-externalproject/10162/13 "2024-03-04T19:26:20Z")

</div>

> [@dlrdave](#):
>
> my experience, ExternalProject is best used for building dependencies that do not change frequently, and are not part of the day-to-day development workflow. Semi-static dependencies that you update every few months or more.
> 
> But for that particular use case, and the flexibility of being able to build the dependencies exactly like you want to, … ExternalProject is fantastic.
> 
> _disclaimer: as an early contributor to ExternalProject, my opinions about it are probably not com_

Well, that sounds about perfect for my dependency on LLVM.

---

<div class="post-metadata">

### Author: ![dabrahams](https://discourse.cmake.org/user_avatar/discourse.cmake.org/dabrahams/32/4265_2.png) [@dabrahams](https://discourse.cmake.org/u/dabrahams)
#### Post date: [March 14, 2024, 10:02pm UTC](https://discourse.cmake.org/t/relationship-between-fetchcontent-and-externalproject/10162/14 "2024-03-14T22:02:31Z")

</div>

So can anyone explain why ExternalProject isn’t mentioned in the “[Using Dependencies Guide](https://cmake.org/cmake/help/latest/guide/using-dependencies/index.html#introduction)?”

---

<div class="post-metadata">

### Author: ![dlrdave](https://discourse.cmake.org/user_avatar/discourse.cmake.org/dlrdave/32/2589_2.png) [@dlrdave](https://discourse.cmake.org/u/dlrdave)
#### Post date: [March 15, 2024, 4:40pm UTC](https://discourse.cmake.org/t/relationship-between-fetchcontent-and-externalproject/10162/15 "2024-03-15T16:40:28Z")

</div>

I cannot explain, as I have not been intimately involved with the CMake project for years now, but … my guess is that it is something that a smaller set of people have used, and nobody has had the time to write up something extensive. It takes lots and lots of time to refine a multi-subproject superbuild to work on many platforms, and writing up how to do that takes even more time.

Also, the focus of the Using Dependencies Guide is for sure **Using** dependencies, and the SuperBuild using ExternalProject approach is for sure about **Building/Installing** dependencies, and then following the find\_package advice of the Using Dependencies Guide to find the now-installed-via-previous-super-build libraries and tools.

Everybody needs to use dependencies, unless they are lucky enough to have a low-level pure project that has zero deps. Fewer people need to build+install their own with all the pre-packaged stuff available these days.

Having said all that, it does seem like ExternalProject should at least be **mentioned** in the using deps guide.

Happy to share more about my experience with super builds if you want to chat sometime.

---

<div class="post-metadata">

### Author: ![dabrahams](https://discourse.cmake.org/user_avatar/discourse.cmake.org/dabrahams/32/4265_2.png) [@dabrahams](https://discourse.cmake.org/u/dabrahams)
#### Post date: [March 18, 2024, 12:59am UTC](https://discourse.cmake.org/t/relationship-between-fetchcontent-and-externalproject/10162/16 "2024-03-18T00:59:49Z")

</div>

> [@ben.boeckel](#):
>
> There are techniques for actual development. Look for `DEVELOPER_MODE` in our [`common-superbuild`](https://gitlab.kitware.com/paraview/common-superbuild/). What this does is build all of the _dependencies_ of the specific project and provides a `developer-mode.cmake` script that can be used to pass as `-C developer-mode.cmake` to configure an arbitrary build tree as if it were in the superbuild (namely finding dependencies).

I looked. It seems like [the doc describing `DEVELOPER_MODE`](https://gitlab.kitware.com/paraview/common-superbuild/-/blob/master/cmake/SuperbuildMacros.cmake#L75-77) misuses the word “dependent” where it means “dependency”; the project can’t possibly know all of its dependents, right?

> [@dlrdave](#):
>
> Happy to share more about my experience with super builds if you want to chat sometime.

I appreciate the kind offer! Looking at the docs [this restriction](https://gitlab.kitware.com/paraview/common-superbuild/-/blob/master/cmake/SuperbuildMacros.cmake#L10-13) worries me. Some people really want to use IDEs and to easily access their debuggers, and I don’t blame them. It’s hard to imagine advantages of the superbuild outweighing this limitation for my project. Also I wonder if the stated primary purpose of superbuilds, “to build various projects into a single prefix as a prepatory step for packaging,” is a major concern for my project. But maybe we can clear that up in our chat.

---

<div class="post-metadata">

### Author: ![ben.boeckel](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/b/ea5d25/32.png) [@ben.boeckel](https://discourse.cmake.org/u/ben.boeckel)
#### Post date: [March 29, 2024, 11:59am UTC](https://discourse.cmake.org/t/relationship-between-fetchcontent-and-externalproject/10162/17 "2024-03-29T11:59:14Z")

</div>

> [@dabrahams](#):
>
> I looked. It seems like [the doc describing `DEVELOPER_MODE`](https://gitlab.kitware.com/paraview/common-superbuild/-/blob/master/cmake/SuperbuildMacros.cmake#L75-77) misuses the word “dependent” where it means “dependency”; the project can’t possibly know all of its dependents, right?

Thanks, clarified here:

[https://gitlab.kitware.com/paraview/common-superbuild/-/merge\_requests/643](https://gitlab.kitware.com/paraview/common-superbuild/-/merge_requests/643)

> [@dabrahams](#):
>
> I appreciate the kind offer! Looking at the docs [this restriction](https://gitlab.kitware.com/paraview/common-superbuild/-/blob/master/cmake/SuperbuildMacros.cmake#L10-13) worries me.

At least Visual Studio supports loading Ninja-generator build trees these days. Xcode is still Xcode. Note that this is really a `common-superbuild` restriction as some projects change filenames based on configuration (particularly on Windows) and various places just don’t account for it. See [this code](https://gitlab.kitware.com/paraview/common-superbuild/-/blob/de8ffcafcbe620f1dc5e3899d32e798c7d46d533/cmake/SuperbuildUtils.cmake).
