# Forcing libraries to be linked in order to expose header-only dependencies.

**URL:** <https://discourse.cmake.org/t/forcing-libraries-to-be-linked-in-order-to-expose-header-only-dependencies/8778>\
**Category:** Code\
**Created:** [August 19, 2023, 11:48pm UTC](https://discourse.cmake.org/t/forcing-libraries-to-be-linked-in-order-to-expose-header-only-dependencies/8778 "2023-08-19T23:48:23Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![Noah\_Watkins](https://discourse.cmake.org/user_avatar/discourse.cmake.org/noah_watkins/32/3740_2.png) [@Noah\_Watkins](https://discourse.cmake.org/u/Noah_Watkins)\
**Post date:** [August 19, 2023, 11:48pm UTC](https://discourse.cmake.org/t/forcing-libraries-to-be-linked-in-order-to-expose-header-only-dependencies/8778/1 "2023-08-19T23:48:23Z")

</div>

Like many projects we many different libraries, executables, and tests that depend on different combinations of dependencies.

```bash
project/src/
- libA/*.{h,c}
- libB/*.{h,c}
- exeA depends_on libA
- testA depends_on lib{A,B}

```

At the top-level of our project source directory we use:

```cmake
include_directories(${CMAKE_CURRENT_SOURCE_DIR})

```

So that targets in the tree can include headers easily like this

```cpp
#include "libA/interface.h"

```

This has generally worked very well. But our project has grown now to the point where it is becoming too easy for unintentional header-only dependencies to be created. For example `libA` may depend on `libB`, but each library may include headers from the other–nothing prevents this mistake and now an unintentional circular dependency exists.

```auto
target_link_libraries(libA PUBLIC libB)

src/libA/header.h
  >> #include "libB/header.h"

src/libB/header.h
  >> #include "libA/header.h"

```

We could of course remove `include_directories(${CMAKE_CURRENT_SOURCE_DIR})` and then re-organize the headers so that we have something like:

```auto
src/libA/include/libA/header.h
target_include_directories(libA libA/include)

```

So that the dependency has to be expressed explicitly in order to expose the headers from the dependent library.

However, we very much prefer to keep all of our source and headers in the same directory.

So I’ve been trying to come up with a way that we can do this dynamically by arrange for these headers to be staged/symlinked at configure-time into the binary directory and setup the target’s include directory in the same way.

I came up with this helper, which for the most part seems to work just fine. Here we do something like

```auto
add_cc_library(libA HDRS header.h SRCS src.cc)

```

And header.h will be includable as `libA/header.h` but only when the target is linked explicitly as a dependency.

My question is: Is there a better way to accomplish this? Are there big foot guns I’m not thinking about when considering this approach?

```cmake
function(add_cc_library NAME)
    cmake_parse_arguments(add_cc_library "" "" "HDRS;SRCS;DEPS" ${ARGN})
    set(_NAME "add_${NAME}")

    cmake_path(GET CMAKE_CURRENT_LIST_DIR STEM include_prefix)
    cmake_path(APPEND include_path ${CMAKE_CURRENT_BINARY_DIR} ${NAME} ${include_prefix})

    set(manifest "${CMAKE_CURRENT_BINARY_DIR}/${NAME}.manifest")
    configure_file(${CMAKE_SOURCE_DIR}/cmake/manifest.in ${manifest} @ONLY)

    set(stamp "${manifest}.stamp")
    add_custom_command(
        OUTPUT ${stamp}
        COMMAND ${CMAKE_COMMAND} -E remove_directory ${include_path}
        COMMAND ${CMAKE_COMMAND} -E make_directory ${include_path}
        COMMAND ${CMAKE_COMMAND} -E touch ${stamp}
        DEPENDS ${manifest})

    set(staged_headers)
    foreach(header ${add_cc_library_HDRS})
        set(source_header ${CMAKE_CURRENT_LIST_DIR}/${header})
        set(staged_header ${include_path}/${header})
        list(APPEND staged_headers ${staged_header})
        add_custom_command(
            OUTPUT ${staged_header}
            COMMAND ${CMAKE_COMMAND} -E create_symlink ${source_header} ${staged_header}
            DEPENDS_EXPLICIT_ONLY
            DEPENDS ${stamp})
    endforeach()

    add_library(${_NAME} ${add_cc_library_SRCS} ${staged_headers})
    target_link_libraries(${_NAME} PUBLIC ${add_cc_library_DEPS})
    target_include_directories(${_NAME} PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/${NAME})

    add_library("v::${NAME}" ALIAS ${_NAME})
endfunction()

```

---

<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:** [August 20, 2023, 2:26am UTC](https://discourse.cmake.org/t/forcing-libraries-to-be-linked-in-order-to-expose-header-only-dependencies/8778/2 "2023-08-20T02:26:45Z")

</div>

> [@Noah\_Watkins](#):
>
> Is there a better way to accomplish this?

Not while sharing source and header locations on disk where a single `-I` makes everything reachable.

> [@Noah\_Watkins](#):
>
> Are there big foot guns I’m not thinking about when considering this approach?

The compiler will report warnings in the symlink files. While that is fine for symlinks in practice, note that Windows won’t support this well (this, of course, depends on how important Windows-hosted builds are for you). I also don’t know how much this messes with debuginfo being able to find things once installed.

---

<div class="post-metadata">

**Author:** ![Noah\_Watkins](https://discourse.cmake.org/user_avatar/discourse.cmake.org/noah_watkins/32/3740_2.png) [@Noah\_Watkins](https://discourse.cmake.org/u/Noah_Watkins)\
**Post date:** [August 20, 2023, 3:09pm UTC](https://discourse.cmake.org/t/forcing-libraries-to-be-linked-in-order-to-expose-header-only-dependencies/8778/3 "2023-08-20T15:09:29Z")

</div>

> The compiler will report warnings in the symlink files.

Got it. I was (perhaps wrongly) under the impression that Bazel was playing this same symlink trick to accomplish the same goal. Perhaps it is simply ignoring this warning some how. FWIW when I was playing around with the approach I posted above I don’t recall observing any warnings.

---

<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:** [August 21, 2023, 2:12am UTC](https://discourse.cmake.org/t/forcing-libraries-to-be-linked-in-order-to-expose-header-only-dependencies/8778/4 "2023-08-21T02:12:04Z")

</div>

I mean that if the header triggers a warning, it will report the symlink’s path, not the original file’s path.
