[CMake] INSTALL(EXPORT) does not honor LINK_INTERFACE_LIBRARIES?

Rolf Eike Beer eike at sf-mail.de
Wed Mar 30 08:02:57 EDT 2011


> On 03/29/2011 11:36 PM, Rolf Eike Beer wrote:
>> Am Dienstag, 29. März 2011, 09:41:36 schrieb Brad King:
>>> On 03/29/2011 05:19 AM, Rolf Eike Beer wrote:
>>>> The basic idea is: any symbols from those private libraries are, well,
>>>> private. The user only ever sees the symbols from the public library.
>>>> In
>>>> fact he _can't_ even link to the private libraries on Windows as we
>>>> never
>>>> install the .lib files. And that's no problem at all as we already
>>>> link
>>>> to everything we need. I'm using --as-needed and --no-undefined on
>>>> Un*x
>>>> and see no problem there either.
>>>>
>>>> So the point is I want CMake to stop telling the user that he needs to
>>>> those private libraries as that's simply not true.
>>>
>>> As Michael pointed out it is needed to set this property:
>>>
>>>
>>> http://www.cmake.org/cmake/help/cmake-2-8-docs.html#prop_tgt:IMPORTED_LINK_
>>> DEPENDENT_LIBRARIES
>>>
>>> CMake running in an outside application needs to know these private
>>> runtime
>>> dependencies.  It needs them ensure that the application link line is
>>> generated such that the linker can find the transitive dependencies
>>> (e.g.
>>> -rpath-link) of the public library.
>>
>> No, why should it? Take the attached example. Build it. Do ldd on
>> testthing.
>> It links against Qt. Run testthing. It prints your path. Delete
>> libqthing.
>> Move libcthing to libqtthing. Run testthing. It prints your path just
>> like
>> before. ldd testthing. It still links against Qt.
>
> Besides, ldd is not appropriate to check against which shared libraries
> a binary is linked because it gathers all shared libraries recursively.
> As a proof, set the LINK_INTERFACE_LIBRARIES property on qthing to the
> empty string and rebuild; "ldd testthing" still shows the Qt libraries,
> indirectly pulled in by qthing. Instead, one should apply "readelf -d"
> and look for the NEEDED fields to see which shared libraries a binary
> actually depends on explicitly, at least on an ELF platform.

Yes and no. Of course it shows the recursive things. But if you delete
libqthing.so ldd on the file still shows it is linking against Qt but that
can't come from libqthing anymore. So the executable is actually
overlinked.

>> Now when I set LINK_INTERFACE_LIBRARIES on qthing to empty it will
>> prevent
>> testthing to be linked against Qt itself so after replacing libqthing with
>> libcthing everything works fine but without the (now useless) dependency
>> on Qt.
>> So why would I import that dependency to an outside project then?
>>
>> I think this transitive linking for _shared_ libraries should actually
>> be
>> considered harmful. We provide an API and that's all a user should care
>> about.
>> We may change our internal libraries in any release at will and the user
>> should care. In fact we have done this more than one and the binaries
linked
>> against older runtime versions just happily work with newer releases.
If that
>> transitive stuff would have been used none of them would work anymore
>> because
>> we have moved, renamed, deleted, replaced and done other things to our
>> internal libraries.
>>
>> I guess you had a reason for this transitive stuff, but I don't
understand it.
>> To prevent the silly user from accidentially underlinking his
executables and
>> libs? I know where this is needed for in static libs, but for shared ones?

> Everything you're saying above is correct, and perhaps one can argue
> about such a kind of transitive linking, but you can avoid it easily
> with the LINK_INTERFACE_LIBRARIES properties which are also honored by
> INSTALL(EXPORT), i.e. with imported targets. Take the exemplary project
> "LIL" from my previous post, build/install it and look at the following:
>
> CMAKE_MINIMUM_REQUIRED(VERSION 2.8 FATAL_ERROR)
> PROJECT(LILUSR C)
> SET(CMAKE_VERBOSE_MAKEFILE ON)
> INCLUDE(lil)
> FILE(WRITE ${CMAKE_BINARY_DIR}/main.c "int main(void){return 0;}\n")
> ADD_EXECUTABLE(main1 main.c)
> TARGET_LINK_LIBRARIES(main1 f1)
> ADD_EXECUTABLE(main2 main.c)
> TARGET_LINK_LIBRARIES(main2 f2)
>
> Configure with CMAKE_MODULE_PATH set to the directory containing the
> lil.cmake file from the LIL project and look for the linker commands;
> you'll see that main1 gets linked against libf1.so and libf0.so while
> main2 gets linked against libf2.so only - no f0 in sight. Hence, the
> f2 target's IMPORTED_LINK_DEPENDENT_LIBRARIES property actually does
> what's expected: It prevents libf0.so from appearing in the linker's
> command line, so there is no direct dependency of main2 on libf0.so.
>
> However, I still don't understand the relation of that transitive
> linking and its avoidance, respectively, to your initial complaint
> about CMake's error message due to the missing library in another
> library's export set. Unless I'm mistaken, you've a shared library
> target "publiclib" which is linked explicitly against another target
> "privatelib" via TARGET_LINK_LIBRARIES().

Yes.

> This means you can't place
> publiclib in an export set without privatelib since the former could
> not be set up properly w.r.t. its diverse IMPORTED properties if the
> latter is not installed, too, i.e. INSTALL(EXPORT) could not generate
> a valid export file.

Yes, and that's basically the problem I have.

Ok, I went and put every thing in the same export set. Which itself is a
bit weird as I now have to INSTALL static libraries that were linked by
the shared ones only to be able to specify that they are in the export
set. And at the end all those libraries of course show up in the export.
And I still see IMPORTED_LINK_DEPENDENT_LIBRARIES_DEBUG of the library
that this is all about set to it's dependencies.

> If you want to remove the error message, just include privatelib in
> publiclibs's export set; here, both targets may perfectly reside in
> different directories with different CMakeLists.txt files, but they
> must be members of the same export set.

I tried that, see above.

> Or don't you want to install
> privatelib but use it only to resolve publiclib's symbols during the
> build process? If so, you might include privatelib additionally in
> your project as an imported target and take this to link publiclib
> against; an imported target does not need to be in the export set
> of a, say, regular target.

The private library is a real dependency of the public one and in fact
get's installed to the same directory. It's just that this is none of the
users business when he links against the public library.

Eike


More information about the CMake mailing list