# Installing/exporting projects that conditionally use optional dependencies

**URL:** https://discourse.cmake.org/t/installing-exporting-projects-that-conditionally-use-optional-dependencies/5194
**Category:** Code
**Created:** [March 9, 2022, 1:31pm UTC](https://discourse.cmake.org/t/installing-exporting-projects-that-conditionally-use-optional-dependencies/5194 "2022-03-09T13:31:13Z")
**Posts on this page:** 7
**Page:** 1

<div class="post-metadata">

### Author: ![benthevining](https://discourse.cmake.org/user_avatar/discourse.cmake.org/benthevining/32/1924_2.png) [@benthevining](https://discourse.cmake.org/u/benthevining)
#### Post date: [March 9, 2022, 1:31pm UTC](https://discourse.cmake.org/t/installing-exporting-projects-that-conditionally-use-optional-dependencies/5194/1 "2022-03-09T13:31:13Z")

</div>

I’m starting to get the hang of writing install and export rules for my CMake projects, but I’ve got a couple of questions about using external dependencies in projects:

First – If I use a custom find module for an external dependency, I get the error `target X needs target Y which is not in any export set`. Does this mean that if I’m writing find modules for third party packages, and I want projects that use these find modules to be installable, the find module should define an export set for its targets?

Secondly, a more specific use case: I’ve got a header-only math library that wraps various other platform-specific libraries (like Apple vDSP, Intel IPP, MIPP, etc). The idea is that ideally, at CMake configure-time, CMake should detect which underlying library to use based on the target system and what it can find locally. This works perfectly when building from source in the local project tree, but when trying to `cmake --install` this library, I again get complaints about dependency targets not being in export sets. But regardless of the answer to my first question above, this use case brings up another question: does CMake support use cases where usage requirements may differ on the machine where a package has been installed than where it was built? Is it possible to create logic that says, for this library, in all cases, if IPP can be found on the system, then link against it, but if not, use a fallback implementation?

I think the crux of question #2 is, can targets be installed with dependencies that are not resolved (or even known) until configure time on the target machine…

---

<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 16, 2022, 6:21am UTC](https://discourse.cmake.org/t/installing-exporting-projects-that-conditionally-use-optional-dependencies/5194/2 "2022-03-16T06:21:46Z")

</div>

> [@benthevining](#):
>
> Does this mean that if I’m writing find modules for third party packages, and I want projects that use these find modules to be installable, the find module should define an export set for its targets?

No, just `find_dependency` the same thing from your `-config.cmake` file. You may need to ship these module files and modify `CMAKE_MODULE_PATH` before calling it though. If you do modify the variable, I recommend resetting it afterwards.

> [@benthevining](#):
>
> does CMake support use cases where usage requirements may differ on the machine where a package has been installed than where it was built?

Yes; use generator expressions to detect these things and conditionalize everything.

> [@benthevining](#):
>
> Is it possible to create logic that says, for this library, in all cases, if IPP can be found on the system, then link against it, but if not, use a fallback implementation?

During the build, sure. But once installed, the use of IPP is kind of set in stone, is it not?

> [@benthevining](#):
>
> can targets be installed with dependencies that are not resolved (or even known) until configure time on the target machine…

Those would need to be made available before finding your package. It seems like the easiest way to do this may involve something like always installing all targets, but making them “inert” if the _platform_ says they’re not viable. There’s no real way to make variables affect these things though that I can think of off the top of my head. Setting properties on consuming targets is probably the best way.

---

<div class="post-metadata">

### Author: ![ferdnyc](https://discourse.cmake.org/user_avatar/discourse.cmake.org/ferdnyc/32/267_2.png) [@ferdnyc](https://discourse.cmake.org/u/ferdnyc)
#### Post date: [March 16, 2022, 8:18pm UTC](https://discourse.cmake.org/t/installing-exporting-projects-that-conditionally-use-optional-dependencies/5194/3 "2022-03-16T20:18:24Z")

</div>

I always `install()` all of my custom Find modules into the `EXPORTED` configuration dir, whether they’re necessary on the platform / for the build or not. Then, my `PackageNameConfig.cmake.in` template uses `find_dependency()` to discover any of those requirements.

Many won’t be used because, like Ben (er… other-Ben) said, the decision on whether a dependency is used is typically cemented at build time — either the library was linked-with / compiled-to-use the dependency, or not. So, for those dependencies, I define `NEED_WHATEVER` variables and reference them in my config template:

```cmake
@PACKAGE_INIT@
include(CMakeFindDependencyMacro)
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR})
if (@NEED_ASIO@)
  find_dependency(ASIO)
endif()
if (@NEED_ALSA@)
  find_dependency(ALSA)
endif()

```

- `NEED_ALSA` will always be true on Linux, false everywhere else. (All of the `NEED_*` variables are explicitly defined one way or the other in all cases.)
- `NEED_ASIO` may or may not be true on Windows or macOS, always false on Linux. It’s discovered by the `FindASIO.cmake` module I’ve included with the config, but whether or not it’s necessary has already been decided when the package is built.

So the actual installed `PackageNameConfig.cmake` file will get generated with literal `if(TRUE)` and `if(FALSE)` conditionals around the `find_dependency()` calls.

(And yes, I should be doing this, instead of just messing with `CMAKE_MODULE_PATH` and leaving it that way:)

> [@ben.boeckel](#):
>
> You may need to ship these module files and modify `CMAKE_MODULE_PATH` before calling it though. If you do modify the variable, I recommend resetting it afterwards.

---

<div class="post-metadata">

### Author: ![benthevining](https://discourse.cmake.org/user_avatar/discourse.cmake.org/benthevining/32/1924_2.png) [@benthevining](https://discourse.cmake.org/u/benthevining)
#### Post date: [March 16, 2022, 10:54pm UTC](https://discourse.cmake.org/t/installing-exporting-projects-that-conditionally-use-optional-dependencies/5194/4 "2022-03-16T22:54:33Z")

</div>

Thanks for the clarification!

So is it correct that any possible usage of `find_package()` within a project must be echoed by a `find_dependency()` in the -config.cmake file, and not just required dependencies? The docs for `find_dependency()` say that it “forwards the `REQUIRED` flag from the original `find_package()` call”, so it seems like there’s no way to use this macro to express an optional dependency, because this would be controlled by the caller of the `find_package()` that loaded the -config.cmake file in question, right?

> [@ben.boeckel](#):
>
> modify `CMAKE_MODULE_PATH` before calling it though. If you do modify the variable, I recommend resetting it afterwards.

Could you elaborate on this? What about packages that provide CMake modules that you _want_ to be `include`-able after calling `find_package(MyPackage)`?

> [@ben.boeckel](#):
>
> Yes; use generator expressions to detect these things and conditionalize everything.

I know about the `$<TARGET_NAME_IF_EXISTS:tgt>` generator expression, but how would you say, if this target doesn’t exist, then try this other target? Would you have to do a string comparison to see if `$<TARGET_NAME_IF_EXISTS:tgt>` evaluates to an empty string?

> [@ben.boeckel](#):
>
> During the build, sure. But once installed, the use of IPP is kind of set in stone, is it not?

Yes, I think I was confusing myself due to the fact that the library in question is header-only. You’re right though, once it’s been installed, I can/should require whatever dependencies it was built with.

@ferdnyc I like your setup with the `NEED_dependency` variables, that seems like a good solution for optionally searching for certain dependencies. I still wonder how to express in a -config.cmake file that failing to find a dependency is not an error…

---

<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 17, 2022, 5:43am UTC](https://discourse.cmake.org/t/installing-exporting-projects-that-conditionally-use-optional-dependencies/5194/5 "2022-03-17T05:43:43Z")

</div>

> [@ferdnyc](#):
>
> ```auto
> if (@NEED_ASIO@)
> find_dependency(ASIO)
> endif()
> 
> ```

I recommend doing `if (@NEED_ASIO@) # NEED_ASIO` in the code so that when reading the configured file, it has some way to trace back how it got in this state.

> [@benthevining](#):
>
> it seems like there’s no way to use this macro to express an optional dependency

Conditionally call `find_dependency` 🙂 .

> [@benthevining](#):
>
> What about packages that provide CMake modules that you _want_ to be `include`-able after calling `find_package(MyPackage)`?

I think it better to just include it for them, but this should be explicit IMO. Keep a separate directory for consumable modules and add it in a way that is preserved and only temporarily add your `Find` module directory.

> [@benthevining](#):
>
> I still wonder how to express in a -config.cmake file that failing to find a dependency is not an error…

You can also call `find_package` yourself. `find_dependency` just does some “magic” for the `REQUIRED` and `QUIET` flags for you. Other than that, it’s just a normal `find_package` call. You can see how SMTK does it [here](https://gitlab.kitware.com/cmb/smtk/-/blob/master/CMake/smtkConfig.cmake.in#L47).

---

<div class="post-metadata">

### Author: ![benthevining](https://discourse.cmake.org/user_avatar/discourse.cmake.org/benthevining/32/1924_2.png) [@benthevining](https://discourse.cmake.org/u/benthevining)
#### Post date: [March 17, 2022, 10:43pm UTC](https://discourse.cmake.org/t/installing-exporting-projects-that-conditionally-use-optional-dependencies/5194/6 "2022-03-17T22:43:13Z")

</div>

> [@ben.boeckel](#):
>
> Conditionally call `find_dependency` 🙂 .

Simple enough 😅

> [@ben.boeckel](#):
>
> Keep a separate directory for consumable modules and add it in a way that is preserved and only temporarily add your `Find` module directory.

Interesting. I suppose that I can restore the module path to its previous state after my -config.cmake file returns, but export a `MY_PACKAGE_MODULE_PATH` variable that consuming projects can then choose to append to their module path (or not).

---

<div class="post-metadata">

### Author: ![ferdnyc](https://discourse.cmake.org/user_avatar/discourse.cmake.org/ferdnyc/32/267_2.png) [@ferdnyc](https://discourse.cmake.org/u/ferdnyc)
#### Post date: [April 25, 2022, 10:12am UTC](https://discourse.cmake.org/t/installing-exporting-projects-that-conditionally-use-optional-dependencies/5194/7 "2022-04-25T10:12:31Z")

</div>

> [@benthevining](#):
>
> Interesting. I suppose that I can restore the module path to its previous state after my -config.cmake file returns, but export a `MY_PACKAGE_MODULE_PATH` variable that consuming projects can then choose to append to their module path (or not).

I mean, I guess it depends what’s in there, or what you think they might want to only-conditionally include.

- If it’s the set of Find modules you packaged with the configuration for its dependencies, that’s a moot point because the discovery will already be done by the time any path variable is defined. But you should just go ahead and always add that directory to the path (temporarily) when running `find_dependency()` / `find_package()`, anyway. CMake will pick up any system-installed config-file packages first, and only fall back to the Find module if there’s no match. So, a redundant Find module is not generally a problem.1

- If they’re `.cmake` files that define CMake macros or functions the parent project may want to call, I’m with Ben — I’d just include them from the config. If we’re talking about _dependencies_ here, there isn’t likely to be that much bundled in the form of cmake code, right? Name the functions/macros something sensible that isn’t going to conflict, prefixed with your package name or whatever, and call it a day.

- If they’re `.cmake` files that set variables or modify the build environment, consider whether that couldn’t better be expressed as targets / properties on targets that are defined by the config, instead.

- But if it’s the rare type of package that supplies optional includes in the form of CMake modules, like KDE’s [ECM (Extra CMake Modules)](https://api.kde.org/ecm/), then… yeah, they just define variables to hold their file paths, and the documentation leads off:

#### Notes

1. (Though, a redundant Find module can cause issues in other ways, as I discovered the hard way when one of our libraries had an out-of-date bundled `FindPython.cmake` that was failing to discover current Python releases. The CMake-provided Find module in the system install had no problems at all. I have no idea why that module was there, but just deleting it from the repo solved the entire problem. Lesson: Only provide Find modules for dependencies when you _really_ have to.)
