# Cannot find path to transitive library during linking (find\_package()/target\_link\_libraries())

**URL:** <https://discourse.cmake.org/t/cannot-find-path-to-transitive-library-during-linking-find-package-target-link-libraries/2921>\
**Category:** Code\
**Tags:** os:windows, comp:msvc\
**Created:** [March 8, 2021, 7:26pm UTC](https://discourse.cmake.org/t/cannot-find-path-to-transitive-library-during-linking-find-package-target-link-libraries/2921 "2021-03-08T19:26:40Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![JRR](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/j/ecccb3/32.png) [@JRR](https://discourse.cmake.org/u/JRR)\
**Post date:** [March 8, 2021, 7:26pm UTC](https://discourse.cmake.org/t/cannot-find-path-to-transitive-library-during-linking-find-package-target-link-libraries/2921/1 "2021-03-08T19:26:40Z")

</div>

I am having trouble finding a _grandchild_ library in a transitive link scenario that we have. Actually, I’m not even sure what the correct terminology for a lot of this is, so I may be overly detailed in the explanation.

Here goes…

I am using:

- cmake version 3.16.4
- on a windows 10 machine
- compiling with Visual Studio 2017

I have a scenario where:

- `LIB_A` stands alone
- `LIB-B` publicly links with `LIB-A`
- `EXE-D` links with `LIB-B`

```auto
        EXE-D --> LIB-B --> LIB-A

```

Since `LIB-B` links with `LIB-A`, that means `EXE-D` will need access to both `LIB-B` and `LIB-A`.

`LIB-A`, and `LIB-B` are all exported libraries, as per the example/explanation under CMake’s online help for [Importing and Exporting Guide](https://cmake.org/cmake/help/git-stage/guide/importing-exporting/index.html)

I used the `PUBLIC` scope when linking the child library so that they would be used transitively by downstream linking. (_i.e. makes both `LIB-A & B` available to `EXE-D`_)

* * *

`LIB-A` and `LIB-B` compile fine. They’re pretty simple. However, compiling `EXE-D` fails with the message

```auto
LINK : fatal error LNK1181: cannot open input file 'Lib_A_64.lib' [C:\TEST\
CMAKE-TRANSITIVE-COMPILE\EXE-D\Build\build64\VS2017\Exe_D_64.vcxproj]

```

* * *

I tried some debugging on my own and printed values from the EXE-D `CmakeLists.txt` file and I found the following:

```auto
Exe_D_64-LINK_LIBRARIES: Lib_B_64

Lib_B_64-IMPORTED: TRUE
Lib_B_64-IMPORTED_CONFIGURATIONS: DEBUG;RELEASE;RELWITHDEBINFO
Lib_B_64-IMPORTED_LINK_INTERFACE_LANGUAGES_DEBUG: CXX
Lib_B_64-IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE: CXX
Lib_B_64-IMPORTED_LINK_INTERFACE_LANGUAGES_RELWITHDEBINFO: CXX
Lib_B_64-IMPORTED_LOCATION_DEBUG: C:/TEMP/CMAKE-TEST/LIB-B/Win-VS2017/lib/Lib_B_64D.lib
Lib_B_64-IMPORTED_LOCATION_RELEASE: C:/TEMP/CMAKE-TEST/LIB-B/Win-VS2017/lib/Lib_B_64R.lib
Lib_B_64-IMPORTED_LOCATION_RELWITHDEBINFO: C:/TEMP/CMAKE-TEST/LIB-B/Win-VS2017/lib/Lib_B_64RD.lib
Lib_B_64-INTERFACE_LINK_LIBRARIES: Lib_A_64

```

I noticed that the error is that it cannot find `Lib_A_64.lib` and Lib\_B\_64 has `Lib_A_64` as it’s INTERFACE\_LINK\_LIBRARIES. It feels like I only have half the solution. It has the Lib\_A\_64 imported library name, but isn’t expanding to it’s location for the downstream linkage.

I also looked at the Visual Studio IDE project parameters for linker input and it pretty much says the same thing.

```auto
C:\TEMP\CMAKE-TEST\LIB-B\Win-VS2017\lib\Lib_B_64R.lib
Lib_A_64.lib
kernel32.lib
user32.lib
gdi32.lib
winspool.lib
shell32.lib
ole32.lib
oleaut32.lib
uuid.lib
comdlg32.lib
advapi32.lib

```

How do I get transitive linking so that it shares the path of the grandchild library? What are the correct words for describiing this challenge?

---

<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 8, 2021, 10:04pm UTC](https://discourse.cmake.org/t/cannot-find-path-to-transitive-library-during-linking-find-package-target-link-libraries/2921/2 "2021-03-08T22:04:08Z")

</div>

> [@JRR](#):
>
> `Lib_B_64-INTERFACE_LINK_LIBRARIES: Lib_A_64`

Is there a `Lib_A_64` imported target as well? If not, finding `Lib_B_64` should find whatever package provides that target. It will then be seen as a target and its `IMPORTED_LOCATION` will then be used.

---

<div class="post-metadata">

**Author:** ![JRR](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/j/ecccb3/32.png) [@JRR](https://discourse.cmake.org/u/JRR)\
**Post date:** [March 8, 2021, 10:35pm UTC](https://discourse.cmake.org/t/cannot-find-path-to-transitive-library-during-linking-find-package-target-link-libraries/2921/3 "2021-03-08T22:35:56Z")

</div>

Hello @ben.boeckel ,

Yes. Lib\_A\_64 is an imported target too.

When creating Lib\_A\_64 and Lib\_B\_64 they use the example/explanation under CMake’s online help for [Importing and Exporting Guide](https://cmake.org/cmake/help/git-stage/guide/importing-exporting/index.html).

This creates the following CMake files that have the imported library information

```bash
# using git-bash on windows to find the files
$ find LIB-A -ipath "*/cmake*"
LIB-A/Win-VS2017/cmake
LIB-A/Win-VS2017/cmake/Lib_A_64Config.cmake
LIB-A/Win-VS2017/cmake/Lib_A_64Targets-debug.cmake
LIB-A/Win-VS2017/cmake/Lib_A_64Targets-release.cmake
LIB-A/Win-VS2017/cmake/Lib_A_64Targets-relwithdebinfo.cmake
LIB-A/Win-VS2017/cmake/Lib_A_64Targets.cmake

```

` `

When creating LIB-B the logic is:

```cmake
list(APPEND CMAKE_PREFIX_PATH "C:/TEMP/CMAKE-TEST/LIB-A/Win-VS2017")
find_package(Lib_A_64 REQUIRED)
target_link_libraries(${TARGET_NAME_LIBRARY} PUBLIC Lib_A_64)

```

Notice that LIB-A is added as PUBLIC scope so that it will transitively link.

This creates the following CMake files that have the imported library information

```auto
$ find LIB-B -ipath "*/cmake*"
LIB-B/Win-VS2017/cmake
LIB-B/Win-VS2017/cmake/Lib_B_64Config.cmake
LIB-B/Win-VS2017/cmake/Lib_B_64Targets-debug.cmake
LIB-B/Win-VS2017/cmake/Lib_B_64Targets-release.cmake
LIB-B/Win-VS2017/cmake/Lib_B_64Targets-relwithdebinfo.cmake
LIB-B/Win-VS2017/cmake/Lib_B_64Targets.cmake

```

` `

Finally incorporating LIB-B into EXE-D is similar, only the HINT path and package names are different

```cmake
list(APPEND CMAKE_PREFIX_PATH "C:/TEMP/CMAKE-TEST/LIB-B/Win-VS2017")
find_package(Lib_B_64 REQUIRED)
target_link_libraries(${TARGET_NAME_LIBRARY} PRIVATE Lib_B_64)

```

It should also be noted that these are entirely orthogonal CMakeLists.txt files for the libraries. They are not part of some larger project inclueded with ~~`add_subdiretory()`~~.

---

<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 9, 2021, 3:12pm UTC](https://discourse.cmake.org/t/cannot-find-path-to-transitive-library-during-linking-find-package-target-link-libraries/2921/4 "2021-03-09T15:12:50Z")

</div>

In this setup `Lib_B_64Config.cmake` should do a `find_package(Lib_A_64)` to get the targets A is providing in its scope. You can do something like this:

```cmake
if (helpers_wanted) # TODO: make your own condition here
  set(Lib_B_64_CMAKE_PREFIX_PATH_save "${CMAKE_PREFIX_PATH}")
  list(APPEND CMAKE_PREFIX_PATH
    "${where_Lib_B_64_found_Lib_A}") # TODO: this should be configured in
endif ()

include(CMakeFindDependencyMacro)
find_dependency(Lib_A_64)
if (NOT Lib_A_64_FOUND)
  set(Lib_B_64_FOUND 0)
  set(Lib_B_64_NOT_FOUND_MESSAGE "Lib_A_64 not found: ${Lib_A_64_NOT_FOUND_MESSAGE}")
endif ()

if (helpers_wanted) # TODO: make your own condition here
  set(CMAKE_PREFIX_PATH "${Lib_B_64_CMAKE_PREFIX_PATH_save}")
  unset(Lib_B_64_CMAKE_PREFIX_PATH_save)
endif ()

```

---

<div class="post-metadata">

**Author:** ![JRR](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/j/ecccb3/32.png) [@JRR](https://discourse.cmake.org/u/JRR)\
**Post date:** [March 9, 2021, 5:01pm UTC](https://discourse.cmake.org/t/cannot-find-path-to-transitive-library-during-linking-find-package-target-link-libraries/2921/5 "2021-03-09T17:01:08Z")

</div>

Thank you for the suggestion @ben.boeckel

I was _just_ starting to toy with an idea similar to that, but was really nervous because so many other experiments had failed.

If I understand the overall logic flow:

* * *

1. Lib\_B\_64Config.cmake is included/pulled into scope when the caller uses `find_package(Lib_B_64...)`

2. The first chunk of code saves a backup of the current `CMAKE_PREFIX_PATH`.

3. The second chunk of code looks for `Lib_A_64` and I’m guessing part of that is getting the path to the appropriate library files.

4. The final chucnk then restores the `CMAKE_PREFIX_PATH`

* * *

Is my understanding correct?

Is this really the easiest way to do this? I can see the logic on how this would work, and I don’t mean to second guess you. However, it feels like a lot of hoops and loops to get my own libraries to work transitiviely.

I’ve noticed it works without problem if it’s a system library such as `pthread` or `dl` or some other general library. Why does this break down when using my own deployed library?

Does it have something to do with the fact that in my case I’m adding an imported library name instead of an actual library?

Thanks again! I appreciate the help!

---

<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 10, 2021, 1:52am UTC](https://discourse.cmake.org/t/cannot-find-path-to-transitive-library-during-linking-find-package-target-link-libraries/2921/6 "2021-03-10T01:52:16Z")

</div>

System libraries can be assumed to exist and can just be referred to by name. They’re “special” in that way (generally, they’re always there and in the compiler’s default search paths).

Other libraries might live in other places (e.g., I regularly install things to `~/misc/root/$proj`), installed via module systems (e.g., `module load mpi/mpich-x86_64`), or generally live in “odd” locations. This is why you need to “find” them again at install time: where it was during the build might not be the same during the transitive-use time. Think of finding a Python then being used via a `venv`: the paths are not going to be the same, but are still viable, so re-finding Python at use time allows you to be flexible in this way.

And yes, this is generally the easiest way to do it. Restoring `CMAKE_PREFIX_PATH` is just a courtesy; you could just append to it, but without documentation, someone might silently start depending on that behavior. It’s easier to just not expose it so that if you ever drop `Lib_A_64`, you don’t get issue reports about “well, it worked before”[1].

[1]Sure, the imported targets already exist, but using targets directly that you don’t find yourself is messy anyways; it’s like relying on `<vector>` to come in through your external dependency’s include files for you.

---

<div class="post-metadata">

**Author:** ![JRR](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/j/ecccb3/32.png) [@JRR](https://discourse.cmake.org/u/JRR)\
**Post date:** [March 10, 2021, 4:02pm UTC](https://discourse.cmake.org/t/cannot-find-path-to-transitive-library-during-linking-find-package-target-link-libraries/2921/7 "2021-03-10T16:02:44Z")

</div>

Thank you.

I appreciate the detailed follow up.

Cheers!
