# How to make a subproject export a link directory of a library dependency

**URL:** <https://discourse.cmake.org/t/how-to-make-a-subproject-export-a-link-directory-of-a-library-dependency/6111>\
**Category:** Code\
**Created:** [July 20, 2022, 6:53pm UTC](https://discourse.cmake.org/t/how-to-make-a-subproject-export-a-link-directory-of-a-library-dependency/6111 "2022-07-20T18:53:50Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![rturrado](https://discourse.cmake.org/user_avatar/discourse.cmake.org/rturrado/32/3499_2.png) [@rturrado](https://discourse.cmake.org/u/rturrado)\
**Post date:** [July 20, 2022, 6:53pm UTC](https://discourse.cmake.org/t/how-to-make-a-subproject-export-a-link-directory-of-a-library-dependency/6111/1 "2022-07-20T18:53:50Z")

</div>

I have this dependency graph:

> my\_project  
> **&nbsp; &nbsp;** |—\> static linking perceptualdiff  
> **&nbsp; &nbsp;**  **&nbsp; &nbsp;** |—\> (static linking with dynamic loading) FreeImage (built as a shared library)

I can correctly build `perceptualdiff` on its own. It will generate `FreeImage.dll` and `FreeImage.lib` in out/build/x64-Debug/\_deps/freeimage-build.

And I can correctly build `my_project` as well. It will generate:

- `pdiff.lib` in build/NinjaMultiConfig/test/\_deps/perceptualdiff-build/Debug.
- `FreeImage.dll` and `FreeImage.lib` in build/NinjaMultiConfig/test/\_deps/freeimage-build/Debug.

However, when I try to run `my project.exe`, it won’t be able to find `FreeImage.dll`.

I understand I’m only adding `pdiff.lib`’s folder to `my_project`’s library path. How can I tell `perceptualdiff`’s CMake to “export” `FreeImage.dll`’s folder to whoever-links-against-pdiff’s library path?

[FreeImage](https://github.com/rturrado/FreeImage)’s CMakeLists.txt:

```auto
add_definitions(-DOPJ_STATIC -DLIBRAW_NODLL)
add_library(FreeImage SHARED ${FreeImage_SRC})

```

[perceptualdiff](https://github.com/rturrado/perceptualdiff)’s CMakeLists.txt:

```auto
include(FetchContent)
FetchContent_Declare(FreeImage
    GIT_REPOSITORY https://github.com/rturrado/FreeImage.git
    GIT_TAG "eb1a2ad20a6579854b56cc73eb97b7e1aa2c5861"
)
FetchContent_MakeAvailable(FreeImage)

add_library(pdiff lpyramid.cpp rgba_image.cpp metric.cpp)
target_include_directories(pdiff PUBLIC ${FreeImage_SOURCE_DIR}/Source)
target_link_libraries(pdiff PUBLIC FreeImage)

```

My project’s CMakeLists.txt:

```auto
FetchContent_Declare(perceptualdiff
    GIT_REPOSITORY https://github.com/rturrado/perceptualdiff.git
    GIT_TAG "eb58ae5ee2d30d947c04249f7b430c4953c00d6c"
)
FetchContent_MakeAvailable(
    perceptualdiff
)

add_executable(${PROJECT_NAME}_test ${app_sources})
target_include_directories(${PROJECT_NAME}_test PUBLIC
    ${perceptualdiff_SOURCE_DIR}
)
target_compile_features(${PROJECT_NAME}_test PRIVATE cxx_std_23)
target_link_libraries(${PROJECT_NAME}_test PUBLIC
    pdiff
)

```

---

<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:** [July 21, 2022, 2:03pm UTC](https://discourse.cmake.org/t/how-to-make-a-subproject-export-a-link-directory-of-a-library-dependency/6111/2 "2022-07-21T14:03:45Z")

</div>

Windows does not have an RPATH equivalent; the `PATH` environment variable just needs to be set up properly. Instead, you might consider copying the DLL as needed using `file(GET_RUNTIME_DEPENDENCIES)` or the `$<TARGET_RUNTIME_DLLS>` generator expression.

---

<div class="post-metadata">

**Author:** ![rturrado](https://discourse.cmake.org/user_avatar/discourse.cmake.org/rturrado/32/3499_2.png) [@rturrado](https://discourse.cmake.org/u/rturrado)\
**Post date:** [July 21, 2022, 6:37pm UTC](https://discourse.cmake.org/t/how-to-make-a-subproject-export-a-link-directory-of-a-library-dependency/6111/3 "2022-07-21T18:37:05Z")

</div>

Many thanks! Using `$<TARGET_RUNTIME_DLLS>` worked like a charm. Now, what would be the solution if I wanted my project to compile in non-Windows systems as well?

In case anyone wants to know the exact details of what worked for me, I just added the following lines:

- at the CMakeLists.txt where I was creating `my_project` binary, and
- after all the `add_executable`, `target_compile_features`, `target_link_libraries`, and `target_compile_options` commands:

```auto
# Copy DLLs the target depends on
add_custom_command(
    TARGET ${PROJECT_NAME}_test POST_BUILD
    COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_RUNTIME_DLLS:${PROJECT_NAME}_test> $<TARGET_FILE_DIR:${PROJECT_NAME}_test>
    COMMAND_EXPAND_LISTS
)

```

See [`$<TARGET_RUNTIME_DLLS>`](https://cmake.org/cmake/help/v3.21/manual/cmake-generator-expressions.7.html#genex:TARGET_RUNTIME_DLLS) documentation.  
See GitHub commit diff.

---

<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:** [July 21, 2022, 7:28pm UTC](https://discourse.cmake.org/t/how-to-make-a-subproject-export-a-link-directory-of-a-library-dependency/6111/4 "2022-07-21T19:28:13Z")

</div>

> [@rturrado](#):
>
> Now, what would be the solution if I wanted my project to compile in non-Windows systems as well?

macOS and Linux (or any ELF platform) have different strategies. On macOS, the library ID dictates this (it’s complicated, but rpath only takes effect if `@rpath/` is actually used). On Linux, “rpath” is the term to look for. Basically, libraries say “I need `libA.so.4`” and the rpaths are searched for a library with that name.

---

<div class="post-metadata">

**Author:** ![rturrado](https://discourse.cmake.org/user_avatar/discourse.cmake.org/rturrado/32/3499_2.png) [@rturrado](https://discourse.cmake.org/u/rturrado)\
**Post date:** [August 15, 2022, 2:45pm UTC](https://discourse.cmake.org/t/how-to-make-a-subproject-export-a-link-directory-of-a-library-dependency/6111/5 "2022-08-15T14:45:05Z")

</div>

Many thanks for your answer.

It’s a bit of a shame that there are actually three different solutions, one for each system. But I guess it’s good to know about them at least.

---

<div class="post-metadata">

**Author:** ![rturrado](https://discourse.cmake.org/user_avatar/discourse.cmake.org/rturrado/32/3499_2.png) [@rturrado](https://discourse.cmake.org/u/rturrado)\
**Post date:** [September 26, 2022, 5:47pm UTC](https://discourse.cmake.org/t/how-to-make-a-subproject-export-a-link-directory-of-a-library-dependency/6111/6 "2022-09-26T17:47:25Z")

</div>

Updating my previous answer now that I’ve also implemented the solution to my problem for Unix-like systems (I haven’t tested this for MacOS):

```auto
# Shared libraries
if(WIN32)
    # Copy DLLs the target depends on
    add_custom_command(
        TARGET ${PROJECT_NAME}_test POST_BUILD
        COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_RUNTIME_DLLS:${PROJECT_NAME}_test> $<TARGET_FILE_DIR:${PROJECT_NAME}_test>
        COMMAND_EXPAND_LISTS
    )
elseif(UNIX)
    if(APPLE)
        set(CMAKE_MACOSX_RPATH 1)
    endif()
    set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
endif()

```

That is, for Linux, setting `CMAKE_INSTALL_RPATH_USE_LINK_PATH` to `TRUE` worked for the situation described in this post. As far as I understand the effects of enabling this variable, it appends to the runtime search path (rpath) of all the targets the directories containing the binaries of the libraries that are linked against. In this case, it appends to `my_project`’s rpath the folder where `FreeImage.so` lives.

---

<div class="post-metadata">

**Author:** ![rturrado](https://discourse.cmake.org/user_avatar/discourse.cmake.org/rturrado/32/3499_2.png) [@rturrado](https://discourse.cmake.org/u/rturrado)\
**Post date:** [October 24, 2022, 12:16pm UTC](https://discourse.cmake.org/t/how-to-make-a-subproject-export-a-link-directory-of-a-library-dependency/6111/7 "2022-10-24T12:16:49Z")

</div>

I have to update again my last answer.

I faced a problem when using `CMAKE_INSTALL_RPATH_USE_LINK_PATH`.

I was building a docker, including a binary, `the_modern_c++_challenge_test`, which made use of a shared library, `libFreeImage.so`. I was placing both in a directory in the docker. When I tried executing the binary, it would fail with an error saying it couldn’t find the shared library. `ldd the_modern_c++_challenge_test` would report `libFreeImage.so` was expected to be found at an absolute path in my machine, where I had built the project (something like `/home/rturrado/projects/the_modern_c++_challenge/out/build.../_deps/free-image-build/Release/libFreeImage.so`).

The “fix” I used was to directly add `./` to the runtime path via the linker properties. I am not completely sure this is a proper fix, but it works for this particular case, because it tells the OS to go and search for the shared library at the same directory where the binary lives (as I said in the previous answer, I haven’t tested this on MacOS).

```auto
elseif(UNIX)
    if(APPLE)
        set(CMAKE_MACOSX_RPATH 1)
    endif()
    set_target_properties(${PROJECT_NAME}_test PROPERTIES
        SKIP_BUILD_RPATH FALSE
        BUILD_WITH_INSTALL_RPATH FALSE
        INSTALL_RPATH ""
        INSTALL_RPATH_USE_LINK_PATH FALSE
        LINK_FLAGS "-Wl,-rpath,./"
    )
endif()

```

---

<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:** [October 25, 2022, 9:52pm UTC](https://discourse.cmake.org/t/how-to-make-a-subproject-export-a-link-directory-of-a-library-dependency/6111/8 "2022-10-25T21:52:20Z")

</div>

> [@rturrado](#):
>
> The “fix” I used was to directly add `./` to the runtime path via the linker properties.

You want `$ORIGIN/` or `@loader_path/` instead of `./` for Linux(-like) and macOS platforms, respectively.

---

<div class="post-metadata">

**Author:** ![rturrado](https://discourse.cmake.org/user_avatar/discourse.cmake.org/rturrado/32/3499_2.png) [@rturrado](https://discourse.cmake.org/u/rturrado)\
**Post date:** [November 2, 2022, 3:29pm UTC](https://discourse.cmake.org/t/how-to-make-a-subproject-export-a-link-directory-of-a-library-dependency/6111/9 "2022-11-02T15:29:26Z")

</div>

Hi Ben,

I don’t know if I may be doing something wrong, but using `@loader_path` is not working for me.

This is the CMake section where I set the link flags for Unix systems:

```auto
    set_target_properties(${PROJECT_NAME}_test PROPERTIES
        SKIP_BUILD_RPATH FALSE
        BUILD_WITH_INSTALL_RPATH FALSE
        INSTALL_RPATH ""
        INSTALL_RPATH_USE_LINK_PATH FALSE
        LINK_FLAGS "-Wl,-rpath,@loader_path/"
    )

```

And this is the output of `objdump -x the_modern_c++_challenge_test | grep RPATH`:

```auto
RPATH @loader_path/:/home/rturrado/projects/the_modern_cpp_challenge/out/build/unixlike-gcc-debug-github/_deps/freeimage-build/Debug

```

Best regards,  
Roberto.

---

<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:** [November 2, 2022, 4:12pm UTC](https://discourse.cmake.org/t/how-to-make-a-subproject-export-a-link-directory-of-a-library-dependency/6111/10 "2022-11-02T16:12:44Z")

</div>

`@loader_path` is for macOS; `$ORIGIN` is for Linux (and other ELF-using platforms).

---

<div class="post-metadata">

**Author:** ![rturrado](https://discourse.cmake.org/user_avatar/discourse.cmake.org/rturrado/32/3499_2.png) [@rturrado](https://discourse.cmake.org/u/rturrado)\
**Post date:** [November 2, 2022, 11:10pm UTC](https://discourse.cmake.org/t/how-to-make-a-subproject-export-a-link-directory-of-a-library-dependency/6111/11 "2022-11-02T23:10:17Z")

</div>

Sorry about that. You explained it perfectly fine in your first answer but I didn’t read it correctly.

I had tried setting $ORIGIN before:  
`LINK_FLAGS "-Wl,-rpath,$ORIGIN/"`  
The objdump was showing ( **notice the / and the blank before it** ):  
`RPATH /:<absolute path to libFreeImage.so folder>`  
And it obviously didn’t work in systems where the absolute path wasn’t valid.

I’ve retried now, after doing some research on how to correctly set $ORIGIN ( **notice the single quotes** around `$ORIGIN`):  
`LINK_FLAGS "-Wl,-rpath,'$ORIGIN/'"`  
The objdump now shows:  
`RPATH $ORIGIN/:<absolute path to libFreeImage.so folder>`  
And it works.

So the whole CMake command would be:

```auto
    set_target_properties(${PROJECT_NAME}_test PROPERTIES
        SKIP_BUILD_RPATH FALSE
        BUILD_WITH_INSTALL_RPATH FALSE
        INSTALL_RPATH ""
        INSTALL_RPATH_USE_LINK_PATH FALSE
        LINK_FLAGS "-Wl,-rpath,'$ORIGIN/'"
    )

```
