2

I'm trying to build a binary application with Bazel. This binary depends on an external, pre-compiled library, let's call it liba.so. In turn, liba.so depends on libb.so (this information I obtain via readelf -d liba.so | grep NEEDED.

To build this, I have the following setup in Bazel:

cc_import(
    name = "liba",
    shared_library = "liba.so",
    deps = [":libb"],
)

cc_import(
    name = "libb",
    shared_library = "libb.so",  
)

cc_binary(
    name = "my_app",
    srcs = ["main.cpp"],
    deps = [":liba"],
)

Building works fine, however when running (either via bazel run or directly) ldd fails to find libb.so.

I've been reading about this and the reason is that Bazel adds to the RUNPATH of the binary only its direct dependencies. Since libb.so is a transitive dependency, the binary cannot find it.

To solve this, I can think of the following hacks:

  • Add ugly linker flags to tell Bazel to add to the RPATH instead of RUNPATH. This is however deemed a bad idea, since RPATH is being deprecated and doesn't allow override via LD_LIBRARY_PATH.

  • Patch the third-party .so file to add to their RUNPATH. This works but it doesn't feel good to patch libraries I don't own.

  • Make the transitive dependencies direct dependencies of the binary. This is not good, each library should be responsible of its dependencies. The binary doesn't need to know what liba.so depends on.

Are there better ways to accomplish this? Other things I've tried without success:

  • Use cc_library instead of cc_import
  • Use data instead of deps.

Thanks!

Botje
  • 26,269
  • 3
  • 31
  • 41
user1011113
  • 1,114
  • 8
  • 27
  • Your 3rd point is very debatable: the fact that liba depends on libb might be caused by what you use in liba – Alex Garcia Nov 03 '20 at 10:51
  • If `liba` contains function `a()`, which in turn calls function `b()` from `libb`, I don't want to link `libb` in my application. Only direct dependencies should be linked – user1011113 Nov 03 '20 at 14:15

1 Answers1

0

I was wrestling with this problem for a long time. The first thing I would recommend you to try is to enable

copy_dynamic_libraries_to_binary

toolchain feature. I think it was primarily designed for Windows but should work for Linux as well.

Now for our purposes, I had to take a more complex, but in the end more reliable approach: I have implemented the aspect which for every binary traverses the dependency graph, collects everything which belongs to the binary output folder and generates file copy actions to place necessary files there when the binary is built. That includes transitive dependencies, but is flexible enough to copy other files we need in the output folder - C++ runtimes, data files, etc. If you are interested I can clip and share relevant code snippets.

Konstantin

EDIT: Per request I crafted a small standalone demo to show how to collect imported dependencies in executable output folder: https://github.com/Bazel-snippets/transitive_shared_libs

Konstantin Erman
  • 551
  • 6
  • 14
  • > If you are interested I can clip and share relevant code snippets. Hey Konstantin, I'm super interested indeed for the snippet, maybe you can share some pointers? – jmartinez Jul 06 '23 at 07:42