Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feature] Build fails with CMake privately linking to a library with transitive dependencies #7192

Open
rddesmond opened this issue Jun 12, 2020 · 19 comments · Fixed by apache/celix#737
Assignees

Comments

@rddesmond
Copy link

rddesmond commented Jun 12, 2020

This falls somewhere between a bug, a feature request, and a comment for the various issues about RPATH handling. It's also troublesome in CMake alone, where their #19556 and #19707 are similar (though involving shared and static libraries, respectively, not imported ones).

When using the cmake generator and linking an application that privately consumes a library in its build path, the library's transitive dependencies cannot be found at link time (but it's okay at runtime). The minimal chain (where app and C are in the same project, and B and A are independent conan projects with empty RPATHs) is:

app  --(any link)-->  libraryC  --(PRIVATE link)-->  libraryB  --(any link)-->  libraryA

In the build tree, the app should be able to link and run. In the install tree, libraryB and libraryA needs to be in a known location, but that's a different story.

Environment Details (include every applicable attribute)

  • Operating System+version: Ubuntu 16.04.6 LTS
  • Compiler+version: gcc 5.4.0 / cmake 3.17.3
  • Conan version: 1.26.0
  • Python version: Python 3.5.2

Steps to reproduce

private-transitive-issue.tar.gz contains a small example. Untar and run ./test.sh.

This conan creates proj_A (libprojA), proj_B (libprojB), and proj_C (libprojC and app), where each calls a function in the one before it (app:main -> libprojC:hello_C -> libprojB:hello_B -> libprojA:hello_A). It will fail during the linking of the app. All libraries are privately linked from CMake's perspective.

[100%] Linking CXX executable bin/app
/usr/bin/ld: warning: libproj_A.so, needed by //home/rdesmond/.conan/data/proj_B/0.1/test/test/package/45b5368b05e5bb05d84f1dd43b93bf7b9dd52802/lib/libproj_B.so, not found (try using -rpath or -rpath-link)
//home/rdesmond/.conan/data/proj_B/0.1/test/test/package/45b5368b05e5bb05d84f1dd43b93bf7b9dd52802/lib/libproj_B.so: undefined reference to `hello_A()'
collect2: error: ld returned 1 exit status

If you build it with workaround 2 below, it will run in the build tree, because the RPATH of libprojC will include the paths to libprojA and libprojB.

Workarounds

  1. Link libprojC to libprojB publicly (switch the commented line in "C/src/CMakelists.txt" from lines 9 to 8).
  2. Instruct gcc's linker to ignore the unresolved symbols with -Wl,--unresolved-symbols=ignore-in-shared-libs (uncomment "C/src/CMakelists.txt" line 13)
@rddesmond rddesmond changed the title [feature] Linking fails when privately linking to an library with transitive dependencies [feature] Linking fails when privately linking to a library with transitive dependencies Jun 12, 2020
@rddesmond rddesmond changed the title [feature] Linking fails when privately linking to a library with transitive dependencies [feature] Building fails when privately linking to a library with transitive dependencies Jun 12, 2020
@rddesmond
Copy link
Author

rddesmond commented Jun 12, 2020

I think the above captures everything that of value that conan outputs, but for completeness: consolelog.txt. Of course, I'm happy to run and gather information from any experiments you think are useful.

And thank you for continuing to make conan awesome!

@rddesmond rddesmond changed the title [feature] Building fails when privately linking to a library with transitive dependencies [feature] Build fails with CMake privately linking to a library with transitive dependencies Jun 13, 2020
@jgsogo jgsogo self-assigned this Jun 18, 2020
@jgsogo
Copy link
Contributor

jgsogo commented Jun 18, 2020

Hi!

Conan, as a package manager, by default removes rpaths from the generated libraries (+info). From the point of view of a package manager that distributes already compiled artifacts, rpath are not going to work as binaries generated in one machine won't be in the same path when used in a different machine.

In your case, you are building proj_C locally, and rpaths might help you to find packages that are in your cache because you are linking to them when building that project.

The way to go would be to import those shared libraries when running you application or, much better, to use the virtualrunenv generator:

$ conan install proj_C/0.1@test/test -g virtualrunenv
$ source activate_run.sh 
$ app
$ source deactivate_run.sh  

This generator will populate the environment with the required variables (PATH, DYLD_LIBRARY_PATH,...) to find the shared libraries that are needed in runtime.


Regarding the build process of proj_C, I can't see the same error you are reporting, it works (both in the cache with conan create and locally using plain CMake). I'm running Mac, but it should be more or less the same, the information about proj_B is added to the CMake target and it should be propagated to the consumers.

./test.sh

proj_C/0.1@test/test: Calling build()

----Running------
> cd '/Users/jgsogo/.conan/data/proj_C/0.1/test/test/build/d9a252b88eb0520168e02076d2a46030a497d625' && cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE="Release" -DCMAKE_OSX_ARCHITECTURES="x86_64" -DCMAKE_OSX_SYSROOT="/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk" -DCONAN_IN_LOCAL_CACHE="ON" -DCONAN_COMPILER="apple-clang" -DCONAN_COMPILER_VERSION="11.0" -DCONAN_CXX_FLAGS="-m64" -DCONAN_SHARED_LINKER_FLAGS="-m64" -DCONAN_C_FLAGS="-m64" -DCONAN_LIBCXX="libc++" -DBUILD_SHARED_LIBS="ON" -DCMAKE_INSTALL_PREFIX="/Users/jgsogo/.conan/data/proj_C/0.1/test/test/package/d9a252b88eb0520168e02076d2a46030a497d625" -DCMAKE_INSTALL_BINDIR="bin" -DCMAKE_INSTALL_SBINDIR="bin" -DCMAKE_INSTALL_LIBEXECDIR="bin" -DCMAKE_INSTALL_LIBDIR="lib" -DCMAKE_INSTALL_INCLUDEDIR="include" -DCMAKE_INSTALL_OLDINCLUDEDIR="include" -DCMAKE_INSTALL_DATAROOTDIR="share" -DCMAKE_MODULE_PATH="/Users/jgsogo/.conan/data/proj_C/0.1/test/test/build/d9a252b88eb0520168e02076d2a46030a497d625" -DCMAKE_EXPORT_NO_PACKAGE_REGISTRY="ON" -DCONAN_EXPORTED="1" -Wno-dev '/Users/jgsogo/.conan/data/proj_C/0.1/test/test/build/d9a252b88eb0520168e02076d2a46030a497d625/src'
-----------------
-- The CXX compiler identification is AppleClang 11.0.3.11030032
-- Check for working CXX compiler: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/c++
-- Check for working CXX compiler: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Conan: called by CMake conan helper
-- Conan: called inside local cache
-- Conan: Adjusting output directories
-- Conan: Using cmake targets configuration
-- Library proj_B found /Users/jgsogo/.conan/data/proj_B/0.1/test/test/package/b156c2519ed8956fef9acdac5e396e6bf4c72901/lib/libproj_B.dylib
-- Library proj_A found /Users/jgsogo/.conan/data/proj_A/0.1/test/test/package/e992dca89c56300b2901b18c23dbae33f62b3a9e/lib/libproj_A.dylib
-- Conan: Adjusting default RPATHs Conan policies
-- Conan: Adjusting language standard
-- Conan: C++ stdlib: libc++
-- Configuring done
-- Generating done
CMake Warning:
  Manually-specified variables were not used by the project:

    CMAKE_EXPORT_NO_PACKAGE_REGISTRY
    CMAKE_INSTALL_BINDIR
    CMAKE_INSTALL_DATAROOTDIR
    CMAKE_INSTALL_INCLUDEDIR
    CMAKE_INSTALL_LIBDIR
    CMAKE_INSTALL_LIBEXECDIR
    CMAKE_INSTALL_OLDINCLUDEDIR
    CMAKE_INSTALL_SBINDIR


-- Build files have been written to: /Users/jgsogo/.conan/data/proj_C/0.1/test/test/build/d9a252b88eb0520168e02076d2a46030a497d625

----Running------
> cmake --build '/Users/jgsogo/.conan/data/proj_C/0.1/test/test/build/d9a252b88eb0520168e02076d2a46030a497d625' '--' '-j16'
-----------------
Scanning dependencies of target proj_C
[ 25%] Building CXX object CMakeFiles/proj_C.dir/hello_C.cpp.o
[ 50%] Linking CXX shared library lib/libproj_C.dylib
[ 50%] Built target proj_C
Scanning dependencies of target app
[ 75%] Building CXX object CMakeFiles/app.dir/app.cpp.o
[100%] Linking CXX executable bin/app
[100%] Built target app
proj_C/0.1@test/test: Package 'd9a252b88eb0520168e02076d2a46030a497d625' built

@rddesmond
Copy link
Author

rddesmond commented Jun 18, 2020

Good morning! Thank you for looking at this.

This is definitely an issue regarding building (and neither running in the install tree, where rpaths are allowable, nor the running after install, where we bundle things together).

I just tested this same procedure on my mac, it works for me there like it did for you. I think the environment difference is the compiler, so I tried it on a mac and all of the x86-64 docker images on https://github.com/conan-io/conan-docker-tools. I found that it worked on clang, xcode, gcc for centos6, and gcc for mingw. It didn't work on the other gcc docker images.

environment status
mac 10.15.5 with xcode 11.0.3 success
docker conan/clang_10 success
docker conan/clang_3.8 success
docker conan/clang_3.9 success
docker conan/clang_4.0 success
docker conan/clang_5.0 success
docker conan/clang_6.0 success
docker conan/clang_7 success
docker conan/clang_8 success
docker conan/clang_9 success
docker conan/gcc_10 failure (libproj_B.so: undefined reference to 'hello_A()')
docker conan/gcc_4.6 failure (libproj_B.so: undefined reference to 'hello_A()')
docker conan/gcc_4.8 failure (libproj_B.so: undefined reference to 'hello_A()')
docker conan/gcc_5 failure (libproj_B.so: undefined reference to 'hello_A()')
docker conan/gcc_5.2 failure (libproj_B.so: undefined reference to 'hello_A()')
docker conan/gcc_5.3 failure (libproj_B.so: undefined reference to 'hello_A()')
docker conan/gcc_5.4 failure (libproj_B.so: undefined reference to 'hello_A()')
docker conan/gcc_6 failure (libproj_B.so: undefined reference to 'hello_A()')
docker conan/gcc_6.2 failure (libproj_B.so: undefined reference to 'hello_A()')
docker conan/gcc_6.3 failure (libproj_B.so: undefined reference to 'hello_A()')
docker conan/gcc_6.4 failure (libproj_B.so: undefined reference to 'hello_A()')
docker conan/gcc_7 failure (libproj_B.so: undefined reference to 'hello_A()')
docker conan/gcc_7-centos6 success
docker conan/gcc_7-mingw success
docker conan/gcc_7.2 failure (libproj_B.so: undefined reference to 'hello_A()')
docker conan/gcc_8 failure (libproj_B.so: undefined reference to 'hello_A()')
docker conan/gcc_9 failure (libproj_B.so: undefined reference to 'hello_A()')

(The script for to run all the docker images, if you'd like to recreate)

wget https://github.com/conan-io/conan/files/4773393/private-transitive-issue.tar.gz
tar xvf private-transitive-issue
git clone https://github.com/conan-io/conan-docker-tools.git
cd https://github.com/conan-io/conan-docker-tools
find . -type d | grep -v .git | grep -v conan | grep -v jenkins |  grep -v x86 | grep -v arm | grep -v android | grep -v example | sort | xargs -n1 -- bash -c 'docker build -t conan/$(basename $0) $0'
docker image list | sed -n 's|\(conan/[^ ]*\).*|\1|gp' | xargs -n1 -I {}  docker run -v/home/ubuntu:/home/ubuntu {} bash -c "cd /home/ubuntu/tmp/private-transitive-issue/ && ./test.sh 2>&1 && echo '| {} | success |' || echo '| {} | failure |'" | grep -e '|' -e 'undefined reference'

@jgsogo
Copy link
Contributor

jgsogo commented Jun 24, 2020

It fails with SHARED + PRIVATE. I'm trying to figure out what happens with gcc

@jgsogo
Copy link
Contributor

jgsogo commented Jun 24, 2020

Having a look to the verbose output from make command:

Private

  • If target_link_libraries(proj_C PRIVATE proj_B::proj_B)
[ 75%] Linking CXX executable bin/app
/usr/bin/cmake -E cmake_link_script CMakeFiles/app.dir/link.txt --verbose=1
/usr/bin/g++      CMakeFiles/app.dir/app.cpp.o  -o bin/app  -Wl,-rpath,/home/ubuntu/private-transitive-issue/C/build/lib lib/libproj_C.so 
/usr/bin/ld: warning: libproj_A.so, needed by /home/conan/.conan/data/proj_B/0.1/test/test/package/f97905d0e3c50231893bade53d9d0c121662b6e4/lib/libproj_B.so, not found (try using -rpath or -rpath-link)
/usr/bin/ld: /home/conan/.conan/data/proj_B/0.1/test/test/package/f97905d0e3c50231893bade53d9d0c121662b6e4/lib/libproj_B.so: undefined reference to `hello_A()'

If we remove RPATHS (SET(CMAKE_SKIP_BUILD_RPATH TRUE)), then the error is related to proj_B instead, because the path to libproj_B.so is not encoded into C library.

[ 75%] Linking CXX executable bin/app
/usr/bin/cmake -E cmake_link_script CMakeFiles/app.dir/link.txt --verbose=1
/usr/bin/g++      CMakeFiles/app.dir/app.cpp.o  -o bin/app  lib/libproj_C.so 
/usr/bin/ld: warning: libproj_B.so, needed by lib/libproj_C.so, not found (try using -rpath or -rpath-link)
/usr/bin/ld: lib/libproj_C.so: undefined reference to `hello_B()'

Public

  • if target_link_libraries(proj_C PUBLIC proj_B::proj_B), it works, because all the transitive information from the linked targets is propagated
/usr/bin/cmake -E cmake_link_script CMakeFiles/app.dir/link.txt --verbose=1
/usr/bin/g++      CMakeFiles/app.dir/app.cpp.o  -o bin/app  -Wl,-rpath,/home/ubuntu/private-transitive-issue/C/build/lib:/home/conan/.conan/data/proj_B/0.1/test/test/package/f97905d0e3c50231893bade53d9d0c121662b6e4/lib:/home/conan/.conan/data/proj_A/0.1/test/test/package/c315fa4cdecf2499320fcabbd4fdfda4282d90d9/lib lib/libproj_C.so /home/conan/.conan/data/proj_B/0.1/test/test/package/f97905d0e3c50231893bade53d9d0c121662b6e4/lib/libproj_B.so /home/conan/.conan/data/proj_A/0.1/test/test/package/c315fa4cdecf2499320fcabbd4fdfda4282d90d9/lib/libproj_A.so 


I see the problem is that extra ld check that GCC is doing. I would expect clang to fail exactly the same. But probably this is something for CMake itself, I cannot figure out how to resolve it in Conan.

@rddesmond
Copy link
Author

rddesmond commented Jun 26, 2020

Thanks for looking at it! I'll refocus this, submit it to CMake's repo, and leave a link to the issue here.

This does seem like a compiler-specific thing that fits in their wheelhouse, but it seemed insane to describe it without conan -- with conan it's a natural use case to have.

@jgsogo
Copy link
Contributor

jgsogo commented Jun 26, 2020

Thanks! So... spread the word in the CMake realm 😄

@stefanbuettner
Copy link
Contributor

@rddesmond Nice examples and problem description. I have the same problem and can reproduce it with the example you provided with CMake 3.10.2, conan version 1.34.1, and gcc version 7.5.0. What's the state of this? Did you carry the issue to the CMake repo? Could not find it there.

@derived-coder
Copy link

derived-coder commented Oct 12, 2021

@rddesmond
Any update on this issue?

Is the error also reproducible without conan?

@derived-coder
Copy link

I have some links to share:
https://fedoraproject.org/wiki/UnderstandingDSOLinkChange
https://fedoraproject.org/wiki/Features/ChangeInImplicitDSOLinking
https://wiki.ubuntu.com/NattyNarwhal/ToolchainTransition

I think this is a "feature" of gcc
However, I do not really understand why they did this:

Under the old system, a program that links with libxml2 and uses dlopen need not link with libdl, and a program that links with libxml2 and uses gzopen need not link with libz. While these programs will work, they will break if libxml2 is ever changed to omit the dependency on libdl/libz.

You cannot just call a function of a private dependency...that doesn't work. What is hidden is hidden, you dont have the includes to get the definition.

PengZheng added a commit to apache/celix that referenced this issue Mar 19, 2022
…ound for CMake shared library private linking issue.

For the CMake private linking issue, see conan-io/conan#7192
PengZheng added a commit to apache/celix that referenced this issue Mar 25, 2022
PengZheng added a commit to apache/celix that referenced this issue Mar 25, 2022
…ssue.

See conan-io/conan#7192 for details. virtualrunenv doesn't help for cross-compilation.
@derived-coder
Copy link

One possible solution for the problem
Add this to your cmake target:

# The LD linker complains even about missing link of transitive dependencies,
# altough they are private and hidden (wrapper library)
# LD needs to see them. This would require for consumer to have find_package
# in cmake for transitive and private dependencies.
# which is not what we want. We can fix this with this workaround
target_link_options(${TARGET_NAME} PUBLIC "-Wl,--unresolved-symbols=ignore-in-shared-libs")

matkonnerth added a commit to matkonnerth/cmakeTests that referenced this issue Jul 11, 2022
@Tobulus
Copy link
Contributor

Tobulus commented Feb 28, 2023

@jgsogo This is still an issue for us, to my understanding this was fixed in CMake here:
https://gitlab.kitware.com/cmake/cmake/-/issues/19556

In our case this is happening if an executable is linked against a lib inside the same package. And this lib has private dependencies coming from another conan package.

The proposed "solution" above is not a good workaround imo.

@memsharded
Copy link
Member

Hi all,

I think this issue is outdated:

  • Conan has gone a long way, and the build system integrations reported above are deprecated in favor of new CMakeToolchain, CMakeDeps, etc.
  • There is a new graph model in Conan 2.0 that further allows to hide transitive dependencies, skipping the binaries when they are not necessary. Doesn't seem exactly what reported here, but in any case, the build information propagation has changed a lot.

There are new templates in 2.0, like conan new cmake_lib -d name=pkga -d version=1.0 -d requires=pkgb/1.0 that provide fully working modern template that compile libraries (there is also cmake_exe template). So I think it should be relatively easy to create an updated reproducible case with the modern integrations and testing it for 2.0.

@Tobulus
Copy link
Contributor

Tobulus commented Feb 28, 2023

Hi @memsharded ,

I'm using the new CMakeDeps generator combined with cmake-conan so for Conan 1.x I'm using state-of-the-art approaches.

Forwarding to Conan 2.0 isn't really helping as we will not move there until a solution is available for cmake-conan.

But I can try to setup a recent example.

@memsharded
Copy link
Member

This is the thing, the issue reported above, with the code reported above is using the previous integrations, which can have a very high effect in how things are managed. I can see that with the new integrations it is still an issue, but it can be a bit confusing mixing both in the same ticket.

Also, the fact that cmake-conan is not ready for 2.0 doesn't mean that Conan 2.0 new graph model couldn't be a solution for this issue, as it is not really related to the linking issue. In any case, I don't think that 2.0 will fix the issue, but we should probably test first in 2.0 with the new integrations, as that would be a bit easier to prioritize.

@tbsuht
Copy link

tbsuht commented Mar 2, 2023

@memsharded I've setup a small example. If this is not related to the topic of the discussion above, we can also move this into a new issue.

The relationship is as follows ("->" == "depends on"):
app -> libB
libB -> libA
libA -> libX & libY

Build via:

cd libX
conan create . -pr:h default -pr:b default -s build_type=Release

cd libY
conan create . -pr:h default -pr:b default -s build_type=Release

cd libA
conan create . -pr:h default -pr:b default -s build_type=Release

cd app
conan create . -pr:h default -pr:b default -s build_type=Release

This is failing with:

Scanning dependencies of target app
[ 80%] Building CXX object src/app/CMakeFiles/app.dir/app.cpp.o
[ 80%] Building CXX object src/app/CMakeFiles/app.dir/main.cpp.o
[100%] Linking CXX executable app
/usr/bin/ld: warning: liblibX.so, needed by /home/user/.conan/data/libA/1.0.0/_/_/package/b2bcf496ea6094c509a11d681ff126c83f01fc2f/lib/liblibA.so, not found (try using -rpath or -rpath-link)
/usr/bin/ld: warning: liblibY.so, needed by /home/user/.conan/data/libA/1.0.0/_/_/package/b2bcf496ea6094c509a11d681ff126c83f01fc2f/lib/liblibA.so, not found (try using -rpath or -rpath-link)
/usr/bin/ld: /home/user/.conan/data/libA/1.0.0/_/_/package/b2bcf496ea6094c509a11d681ff126c83f01fc2f/lib/liblibA.so: undefined reference to `libY()'
/usr/bin/ld: /home/user/.conan/data/libA/1.0.0/_/_/package/b2bcf496ea6094c509a11d681ff126c83f01fc2f/lib/liblibA.so: undefined reference to `libX()'
collect2: error: ld returned 1 exit status
make[2]: *** [src/app/CMakeFiles/app.dir/build.make:100: src/app/app] Error 1
make[1]: *** [CMakeFiles/Makefile2:160: src/app/CMakeFiles/app.dir/all] Error 2
make: *** [Makefile:130: all] Error 2

The error is gone once you link libB PUBLIC against libA.
target_link_libraries(libB PUBLIC libA::libA)

I was wondering whether this is related to this issue, but I might be wrong.

$ conan --version
Conan version 1.53.0

@memsharded
Copy link
Member

Yes, moved your comment to: #13302

@576347172
Copy link

Any updates on this issue?
cause I am facing some same problem in my work.

@memsharded
Copy link
Member

@576347172 Have you seen the comments in #13302?, specifically #13302 (comment)

Using PUBLIC would be a good enough workaround, as Conan will not really propagated it downstream, it is just for the local build.

If not, please create a new ticket with details to reproduce, it might be a different thing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants