Problem: I am building an external project in CMake. The project has a Makefile that ultimately produces a shared object. I want to link against and install this object in my super project, just as if it were one of the libraries in the project. The problem is that the ExternalProject lib is getting linked into my applications and libraries with a relative path, not an absolute path which causes problems when running from any directory besides where CMake puts it.
I have created a sample SSCCE example project to demonstrate my overall setup. Feel free to peruse and compile if needed (git clone https://github.com/calebwherry/cmake-SO-question-main --recursive && cd cmake-SO-question-main && mkdir build && cd build && cmake .. && make && cd src/app/testApp && ldd testApp
).
Whenever I run ldd
on the executable and libs, I get output like this:
linux-vdso.so.1 => (0x00007fff8b5a2000)
libTestLib.so => /home/jwherry3/repos/cmake-superprj-main-test/build/src/lib/TestLib/libTestLib.so (0x00007f592da57000)
../../lib/libExtLib.so (0x00007f592d855000)
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f592d539000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f592d2b7000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f592d0a0000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f592cd14000)
/lib64/ld-linux-x86-64.so.2 (0x00007f592dc5a000)
I have tried all kinds of things dealing with RPATHS but can't get the ExtLib to link right. The lib that is local to the project (libTestLib.so
) links just fine.
I also tried to set the LD_LIBRARY_PATH
to override the relative path when I run the app but even when I do that, it still doesn't find the library. I suppose because it is relative it does not follow the normal linking order? The outcome is that the binary will not run unless I am in the directory where it resides.
I feel like I'm doing something really dumb when creating dependencies with the ExternalProject and that is my issue but I have beat my head for 3 days and haven't come up with anything.
System setup: Debian Wheezy 64-bit, CMake 3.0.2, g++-4.9.2.
Took awhile but I have finally arrived at an answer with help from the CMake Users listserv, specifically @brad-king.
The main issue is that I am not compiling my shared object correctly in my external project. Brad's answer to my question:
Thanks for the complete/simple example. The problem is that the libExtLib.so file built by the external project does not have DT_SONAME set by the linker. When a linker is given a path to a shared library file that has no soname then the path is copied into the DT_NEEDED field of the consumer since it has no soname to use for that.
There are two solutions:
Make sure DT_SONAME gets set properly by the external build system.
Tell CMake that the imported library file has no soname:
set_property(TARGET ExtLib PROPERTY IMPORTED_NO_SONAME 1)
CMake will then link it via -lExtLib and the linker will not store the path to the file in DT_NEEDED but only the file name.
Either of these should resolve the issue. Number 1 is cleaner.
Since I have control over the Make files in the external library, I opted for the cleaner first solution by compiling the shared object like so:
$(SHARED_TARGET): $(OBJECTS)
$(CXX) $(CXXFLAGS) $(OBJECTS) -o $@ $(LDFLAGS) -Wl,-soname,$@
I have revised my original example and made it a lot simpler: https://github.com/calebwherry/cmake-SO-question-main. I will leave it up as a reference for anyone stumbling across this post at a later date.
P.S.
There is another fix that is sub-optimal. If I hadn't done any of the above, I could have just given target_link_library the full path to the library I was linking against and not used the imported library target. I have noted this in the CMake file in the repo and commented it out. Not a great solution but does solve the issue another way.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With