# Build Directory as Surrogate for installation

**URL:** <https://discourse.cmake.org/t/build-directory-as-surrogate-for-installation/11497>\
**Category:** Code\
**Tags:** os:linux\
**Created:** [August 21, 2024, 3:59pm UTC](https://discourse.cmake.org/t/build-directory-as-surrogate-for-installation/11497 "2024-08-21T15:59:53Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![Dylan\_Reedy](https://discourse.cmake.org/user_avatar/discourse.cmake.org/dylan_reedy/32/1533_2.png) [@Dylan\_Reedy](https://discourse.cmake.org/u/Dylan_Reedy)\
**Post date:** [August 21, 2024, 3:59pm UTC](https://discourse.cmake.org/t/build-directory-as-surrogate-for-installation/11497/1 "2024-08-21T15:59:53Z")

</div>

Hello,

I work in a context where we occasionally deploy source code to “customers”. We also have several plugins that link against the main software package that are actively developed. In both cases, its desirable to use the build directory for the main project as a surrogate for an “installation”, discoverable with `find_package(MyPackage CONFIG)`. Right now we’re achieving this through some pretty wonky symlink creation, but I’m wondering if there’s a canonical way to achieve this workflow with CMake?

I assume the common answer is “just install `MyPackage` after building it”, but I’m hoping there’s a way to achieve what we want with the build directory.

Thanks in advance.

---

<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:** [August 22, 2024, 5:02am UTC](https://discourse.cmake.org/t/build-directory-as-surrogate-for-installation/11497/2 "2024-08-22T05:02:55Z")

</div>

There is absolutely a canonical way to do that, and it revolves around the [`export(TARGETS)`](https://cmake.org/cmake/help/latest/command/export.html#exporting-targets) command. (Or, if you’re already using `install(EXPORT)` to create your exported target set for installation, the very similar [`export(EXPORT)`](https://cmake.org/cmake/help/latest/command/export.html#exporting-targets-matching-install-export) command.)

Basically, `export(EXPORT)` : build-tree :: `install(EXPORT)` : install-tree.

By exporting your CMake package configuration and build targets into the build tree, you can allow dependent projects to point `CMAKE_PREFIX_PATH` into your build directory and use `find_package()` to pick up your build artifacts the same way they consume your installed configuration.

So, say you have a library project:

```cmake
add_library(foo SHARED ${foo_sources})
target_include_directories(foo
  PRIVATE
    src/include
  PUBLIC
    ${CMAKE_INSTALL_INCLUDEDIR}/foo
)
set_target_properties(foo ...)
# etc...

```

And it installs exported targets:

```cmake
include(GNUInstallDirs)
install(TARGETS foo
  EXPORT FooTargets
  [<PART> DESTINATION ...]s
)
install(EXPORT FooTargets
  NAMESPACE Foo::
  FILE FooTargets.cmake
  DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Foo
)

```

Along with a generated CMake configuration, so that your clients’ code can pick up the installed package using `find_package(Foo)` and `target_link_libraries(... Foo::foo)`:

```cmake
include(CMakePackageConfigHelpers)
configure_package_config_file(
  "${CMAKE_SOURCE_DIR}/cmake/FooConfig.cmake.in"
  "${CMAKE_BINARY_DIR}/cmake/FooConfig.cmake"
  INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Foo/
)
install(FILES
  "${CMAKE_BINARY_DIR}/cmake/FooConfig.cmake"
  DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Foo/
)

```

To make that same exported configuration available _without_ installing the package, all you need to do is ensure your exported `INTERFACE_INCLUDE_DIRECTORIES` are properly mapped in both the build and install interfaces:

```cmake
target_include_directories(foo PUBLIC
  $<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/include>
  $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/foo>
)

```

And export your targets into the _build_ directory, in addition to the installed configuration:

```cmake
export(EXPORT FooTargets
  NAMESPACE Foo::
  FILE "${CMAKE_BINARY_DIR}/cmake/FooTargets.cmake"
)

```

And any client code that would normally use `find_package(Foo)` and `target_link_libraries(... Foo::foo)` to link with your installed library can continue to do exactly the same thing, but pick up your _uninstalled_ build artifacts by simply putting your build dir on the config search path:

```console
$ cmake -B _build -S . \
  -DCMAKE_PREFIX_PATH=/path/to/foo/_build

```

See the [Importing and Exporting Guide](https://cmake.org/cmake/help/latest/guide/importing-exporting/index.html) for more details.

---

<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:** [August 22, 2024, 5:28am UTC](https://discourse.cmake.org/t/build-directory-as-surrogate-for-installation/11497/3 "2024-08-22T05:28:22Z")

</div>

There _can_ be complications with this approach, of course.

One of the biggest ones is if your internal code uses your header files differently than your downstream library consumers are expected to, and the include directory they get pointed at is only created during the install process.

When installing files, it’s fine if you pull together a bunch of headers scattered all over the `src/` tree:

```plaintext
src/types/types.h
src/client/client.h
src/server/server.h
...etc...

```

…and stick them all together inside a new `${CMAKE_INSTALL_INCLUDEDIR}/foo-1.0/foo/` directory:

```plaintext
${CMAKE_INSTALL_PREFIX}/include/foo-1.0/foo/types.h
${CMAKE_INSTALL_PREFIX}/include/foo-1.0/foo/client.h
${CMAKE_INSTALL_PREFIX}/include/foo-1.0/foo/server.h

```

But if your exported `INTERFACE_INCLUDE_DIRECTORIES` is set to `${CMAKE_INSTALL_INCLUDEDIR}/foo-1.0`, and library consumers are expected to keep the installed directory name in the header path when `#include`-ing them:

```c++
#include <foo/types.h>
#include <foo/client.h>

```

…Then you’re going to have a hard time, because that `foo-1.0/foo` directory _doesn’t get created_ until install time, to be part of your `$<INSTALL_INTERFACE:>` include paths.

If you’re in that situation, you may have to add code that copies all of the installable headers into the build dir, so that they can be referenced in the include path that gets exported into `FooTargets.cmake` by `export(EXPORT)`:

```cmake
# For installed export
set(_headers
  src/types/types.h
  src/client/client.h
  src/server/server.h
)
install(FILES ${_headers}
  DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/foo-1.0/foo
)
# For build-dir exported interface
file(MAKE_DIRECTORY
  "${CMAKE_BINARY_DIR}/include/foo-1.0/foo"
)
file(COPY ${_headers} DESTINATION
  "${CMAKE_BINARY_DIR}/include/foo-1.0/foo"
)

target_include_directories(foo PUBLIC
  $<BUILD_INTERFACE:${CMAKE_BINARY_DIR}/include/foo-1.0>
  $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/foo-1.0>
)

```

---

<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:** [August 22, 2024, 5:44am UTC](https://discourse.cmake.org/t/build-directory-as-surrogate-for-installation/11497/4 "2024-08-22T05:44:27Z")

</div>

> [@ferdnyc](#):
>
> ```cmake
> # For build-dir exported interface
> file(MAKE_DIRECTORY
> "${CMAKE_BINARY_DIR}/include/foo-1.0/foo"
> )
> file(COPY ${_headers} DESTINATION
> "${CMAKE_BINARY_DIR}/include/foo-1.0/foo"
> )
> 
> ```

Actually, you should do that copying during the build, using an `add_custom_target()` scripted `COMMAND`, e.g.:

```cmake
add_custom_target(headers ALL
  COMMAND
    ${CMAKE_COMMAND) -E copy_if_different
    ${_headers}
    ${CMAKE_BINARY_DIR}/include/foo-1.0/foo/
  COMMAND_EXPAND_LISTS
)

```

…The difference being that this version will update the build-dir copy of the headers whenever you build your project, so if a header is modified after initially running `cmake` the changes will be picked up. With the `file(COPY)` version any changes made to the header sources after the initial `cmake` run will be ignored until `cmake` is manually run again.

---

<div class="post-metadata">

**Author:** ![Dylan\_Reedy](https://discourse.cmake.org/user_avatar/discourse.cmake.org/dylan_reedy/32/1533_2.png) [@Dylan\_Reedy](https://discourse.cmake.org/u/Dylan_Reedy)\
**Post date:** [August 22, 2024, 6:53am UTC](https://discourse.cmake.org/t/build-directory-as-surrogate-for-installation/11497/5 "2024-08-22T06:53:20Z")

</div>

Wow, incredible response! What a wealth of information. I’ll see what I can get working using all of this. Many thanks!
