Using target_link_libraries INTERFACE with static libraries

classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Using target_link_libraries INTERFACE with static libraries

Luis Caro Campos
I have the following scenario where I have 2 static libraries, where one uses symbols from the other, and the executable needs to link against both in order to build properly.

Library 'Bar' uses symbols from library 'Foo' internally, but 'Foo' is not part of the "interface" of Bar, i.e., the calling library/application only needs to include headers from 'Bar', and use symbols from Bar.

---
#static libraries, assuming BUILD_SHARED_LIBS is off
add_library(Foo)
add_library(Bar)
target_include_directories(Foo PRIVATE ... )
target_include_directories(Foo INTERFACE ...)
target_include_directories(Bar PRIVATE ...)
target_include_directories(Bar INTERFACE ..)

# this will expose Bar to Foo's interface include directories
target_link_libraries(Bar PUBLIC|PRIVATE Foo)


add_executable(MyApp)
target_link_libraries(MyApp PRIVATE Bar)

---

In order to for the executable "MyApp" to build successfully, which uses static library Bar, "Foo" also needs be in the link statement otherwise I'll get missing symbols. If 'Bar' doesn't include this information in the target link interface, I'll have to explicitly link against "Foo" for MyApp.

My question is, in this case, which is the correct keyword for use when calling "target_link_libraries" between Bar and Foo?

Technically, 'Foo' is not part of Bar's public interface, so it would be PRIVATE. However, this would force me to have to expressly link against Foo in MyApp.
The other alternative is PUBLIC, as that would take care of the dependency. I think strictly speaking, it is an "interface link dependency", but not an "interface" dependency. I know that there are LINK_PUBLIC and LINK_PRIVATE keywords for target_link_libraries, however the documentation says they are for compatibility only and the other ones should be favoured.

So in this scenario, I guess "PUBLIC" would be the best case, as that takes care of the linking of MyApp.

Question (1): does this not have the downside of adding Foo's interface include directories to MyApp's include path?

The second scenario would be if Foo was static and Bar was shared. In that case, it would be crystal clear, the target_link_libraries call between Foo and Bar should use the PRIVATE keyword.
However, what about if BUILD_SHARED_LIBS was set to on?
Both would be shared libraries, but MyApp doesn't need to link against "Foo" anymore, however it is needed at runtime.

Question (2): toggling the BUILD_SHARED_LIBS flag then seems to require altering the semantics of calls to target_link_libraries? What about the "needed at runtime" but "no part of the interface case?

My conclusion is that I should perhaps explicitly declare those libraries as static (foo and bar), and use the "PUBLIC" keyword when linking them. Although I see two downsides:
1) Foo's interface include path is added to MyApp's include path (and it is not needed)
2) Bar is now dependant on "Foo", so Foo needs to be built before. However, from a compiler point of view, the binaries of Foo are not needed to produce bar.

Any help would be appreciated, in case there are better ways to express these dependencies that I'm ignoring.



Thanks,
Luis



--

Powered by www.kitware.com

Please keep messages on-topic and check the CMake FAQ at: http://www.cmake.org/Wiki/CMake_FAQ

Kitware offers various services to support the CMake community. For more information on each offering, please visit:

CMake Support: http://cmake.org/cmake/help/support.html
CMake Consulting: http://cmake.org/cmake/help/consulting.html
CMake Training Courses: http://cmake.org/cmake/help/training.html

Visit other Kitware open-source projects at http://www.kitware.com/opensource/opensource.html

Follow this link to subscribe/unsubscribe:
http://public.kitware.com/mailman/listinfo/cmake
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Using target_link_libraries INTERFACE with static libraries

Mario Werner
Hi Luis,

as you correctly concluded, if 'Foo' is not part of the public interface
of 'Bar', then the PRIVATE is the correct specifier.

However, you don't have to add 'Foo' to the consumers of 'Bar' (e.g.,
'MyApp'). If everything works as expected, CMake automatically tracks
the link dependency from 'Bar' to 'Foo' and adds it to the link command
of the consuming artifact when 'Bar' is a static library.

I did a quick experiment based on the code layout you outlined and for
me everything worked out of the box. If it does not for you, it would be
great if you can provide an actual minimal working example which shows
the issue.

Maybe the very detailed explanation [1] provided by Craig Scott is also
interesting in this context.

HTH,
Mario

[1] https://cmake.org/pipermail/cmake/2016-May/063400.html

On 2017-06-01 13:11, Luis Caro Campos wrote:

> I have the following scenario where I have 2 static libraries, where one
> uses symbols from the other, and the executable needs to link against both
> in order to build properly.
>
> Library 'Bar' uses symbols from library 'Foo' internally, but 'Foo' is not
> part of the "interface" of Bar, i.e., the calling library/application only
> needs to include headers from 'Bar', and use symbols from Bar.
>
> ---
> #static libraries, assuming BUILD_SHARED_LIBS is off
> add_library(Foo)
> add_library(Bar)
> target_include_directories(Foo PRIVATE ... )
> target_include_directories(Foo INTERFACE ...)
> target_include_directories(Bar PRIVATE ...)
> target_include_directories(Bar INTERFACE ..)
>
> # this will expose Bar to Foo's interface include directories
> target_link_libraries(Bar PUBLIC|PRIVATE Foo)
>
>
> add_executable(MyApp)
> target_link_libraries(MyApp PRIVATE Bar)
>
> ---
>
> In order to for the executable "MyApp" to build successfully, which uses
> static library Bar, "Foo" also needs be in the link statement otherwise
> I'll get missing symbols. If 'Bar' doesn't include this information in the
> target link interface, I'll have to explicitly link against "Foo" for MyApp.
>
> My question is, in this case, which is the correct keyword for use when
> calling "target_link_libraries" between Bar and Foo?
>
> Technically, 'Foo' is not part of Bar's public interface, so it would be
> PRIVATE. However, this would force me to have to expressly link against Foo
> in MyApp.
> The other alternative is PUBLIC, as that would take care of the dependency.
> I think strictly speaking, it is an "interface link dependency", but not an
> "interface" dependency. I know that there are LINK_PUBLIC and LINK_PRIVATE
> keywords for target_link_libraries, however the documentation says they are
> for compatibility only and the other ones should be favoured.
>
> So in this scenario, I guess "PUBLIC" would be the best case, as that takes
> care of the linking of MyApp.
>
> Question (1): does this not have the downside of adding Foo's interface
> include directories to MyApp's include path?
>
> The second scenario would be if Foo was static and Bar was shared. In that
> case, it would be crystal clear, the target_link_libraries call between Foo
> and Bar should use the PRIVATE keyword.
> However, what about if BUILD_SHARED_LIBS was set to on?
> Both would be shared libraries, but MyApp doesn't need to link against
> "Foo" anymore, however it is needed at runtime.
>
> Question (2): toggling the BUILD_SHARED_LIBS flag then seems to require
> altering the semantics of calls to target_link_libraries? What about the
> "needed at runtime" but "no part of the interface case?
>
> My conclusion is that I should perhaps explicitly declare those libraries
> as static (foo and bar), and use the "PUBLIC" keyword when linking them.
> Although I see two downsides:
> 1) Foo's interface include path is added to MyApp's include path (and it is
> not needed)
> 2) Bar is now dependant on "Foo", so Foo needs to be built before. However,
> from a compiler point of view, the binaries of Foo are not needed to
> produce bar.
>
> Any help would be appreciated, in case there are better ways to express
> these dependencies that I'm ignoring.
>
>
>
> Thanks,
> Luis
>
>
>


--

Powered by www.kitware.com

Please keep messages on-topic and check the CMake FAQ at: http://www.cmake.org/Wiki/CMake_FAQ

Kitware offers various services to support the CMake community. For more information on each offering, please visit:

CMake Support: http://cmake.org/cmake/help/support.html
CMake Consulting: http://cmake.org/cmake/help/consulting.html
CMake Training Courses: http://cmake.org/cmake/help/training.html

Visit other Kitware open-source projects at http://www.kitware.com/opensource/opensource.html

Follow this link to subscribe/unsubscribe:
http://public.kitware.com/mailman/listinfo/cmake
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Using target_link_libraries INTERFACE with static libraries

Luis Caro Campos
In reply to this post by Luis Caro Campos
Hi Mario,

Eureka! I had glanced through that explanation in the past, however, I missed this key piece of information:


"For this reason, when A
links in B as PRIVATE and another target C links in A, CMake will still add
B to the list of libraries to be linked for C because parts of B are needed
by A, but A itself doesn't have that dependency encoded into it. So even
though B is an internal implementation detail of A, C still needs B added
to the linker command, which CMake conveniently handles for you."

So, indeed, CMake takes care of this by adding the 'static' library dependency to the link dependency,
even if it's private. Great feature!

My confusion comes from reading the documentation. For target_link_libraries, it simply states:

"
Libraries and targets following PRIVATE are linked to, but are not made part of the link interface."

Perhaps it should add another sentence "except when the library is a static library".  And perhaps
would also clarify how this "exception" indeed only applies to the link interface but nothing else
(include path or compile flags), as stated in Craig Scott's explanation.

Thanks a lot for the clarification!

--
Luis

On Thu, Jun 1, 2017 at 12:53 PM, Mario Werner <[hidden email]> wrote:
Hi Luis,

as you correctly concluded, if 'Foo' is not part of the public interface
of 'Bar', then the PRIVATE is the correct specifier.

However, you don't have to add 'Foo' to the consumers of 'Bar' (e.g.,
'MyApp'). If everything works as expected, CMake automatically tracks
the link dependency from 'Bar' to 'Foo' and adds it to the link command
of the consuming artifact when 'Bar' is a static library.

I did a quick experiment based on the code layout you outlined and for
me everything worked out of the box. If it does not for you, it would be
great if you can provide an actual minimal working example which shows
the issue.

Maybe the very detailed explanation [1] provided by Craig Scott is also
interesting in this context.

HTH,
Mario

[1] https://cmake.org/pipermail/cmake/2016-May/063400.html

On 2017-06-01 13:11, Luis Caro Campos wrote:
> I have the following scenario where I have 2 static libraries, where one
> uses symbols from the other, and the executable needs to link against both
> in order to build properly.
>
> Library 'Bar' uses symbols from library 'Foo' internally, but 'Foo' is not
> part of the "interface" of Bar, i.e., the calling library/application only
> needs to include headers from 'Bar', and use symbols from Bar.
>
> ---
> #static libraries, assuming BUILD_SHARED_LIBS is off
> add_library(Foo)
> add_library(Bar)
> target_include_directories(Foo PRIVATE ... )
> target_include_directories(Foo INTERFACE ...)
> target_include_directories(Bar PRIVATE ...)
> target_include_directories(Bar INTERFACE ..)
>
> # this will expose Bar to Foo's interface include directories
> target_link_libraries(Bar PUBLIC|PRIVATE Foo)
>
>
> add_executable(MyApp)
> target_link_libraries(MyApp PRIVATE Bar)
>
> ---
>
> In order to for the executable "MyApp" to build successfully, which uses
> static library Bar, "Foo" also needs be in the link statement otherwise
> I'll get missing symbols. If 'Bar' doesn't include this information in the
> target link interface, I'll have to explicitly link against "Foo" for MyApp.
>
> My question is, in this case, which is the correct keyword for use when
> calling "target_link_libraries" between Bar and Foo?
>
> Technically, 'Foo' is not part of Bar's public interface, so it would be
> PRIVATE. However, this would force me to have to expressly link against Foo
> in MyApp.
> The other alternative is PUBLIC, as that would take care of the dependency.
> I think strictly speaking, it is an "interface link dependency", but not an
> "interface" dependency. I know that there are LINK_PUBLIC and LINK_PRIVATE
> keywords for target_link_libraries, however the documentation says they are
> for compatibility only and the other ones should be favoured.
>
> So in this scenario, I guess "PUBLIC" would be the best case, as that takes
> care of the linking of MyApp.
>
> Question (1): does this not have the downside of adding Foo's interface
> include directories to MyApp's include path?
>
> The second scenario would be if Foo was static and Bar was shared. In that
> case, it would be crystal clear, the target_link_libraries call between Foo
> and Bar should use the PRIVATE keyword.
> However, what about if BUILD_SHARED_LIBS was set to on?
> Both would be shared libraries, but MyApp doesn't need to link against
> "Foo" anymore, however it is needed at runtime.
>
> Question (2): toggling the BUILD_SHARED_LIBS flag then seems to require
> altering the semantics of calls to target_link_libraries? What about the
> "needed at runtime" but "no part of the interface case?
>
> My conclusion is that I should perhaps explicitly declare those libraries
> as static (foo and bar), and use the "PUBLIC" keyword when linking them.
> Although I see two downsides:
> 1) Foo's interface include path is added to MyApp's include path (and it is
> not needed)
> 2) Bar is now dependant on "Foo", so Foo needs to be built before. However,
> from a compiler point of view, the binaries of Foo are not needed to
> produce bar.
>
> Any help would be appreciated, in case there are better ways to express
> these dependencies that I'm ignoring.
>
>
>
> Thanks,
> Luis
>
>
>


--

Powered by www.kitware.com

Please keep messages on-topic and check the CMake FAQ at: http://www.cmake.org/Wiki/CMake_FAQ

Kitware offers various services to support the CMake community. For more information on each offering, please visit:

CMake Support: http://cmake.org/cmake/help/support.html
CMake Consulting: http://cmake.org/cmake/help/consulting.html
CMake Training Courses: http://cmake.org/cmake/help/training.html

Visit other Kitware open-source projects at http://www.kitware.com/opensource/opensource.html

Follow this link to subscribe/unsubscribe:
http://public.kitware.com/mailman/listinfo/cmake


--

Powered by www.kitware.com

Please keep messages on-topic and check the CMake FAQ at: http://www.cmake.org/Wiki/CMake_FAQ

Kitware offers various services to support the CMake community. For more information on each offering, please visit:

CMake Support: http://cmake.org/cmake/help/support.html
CMake Consulting: http://cmake.org/cmake/help/consulting.html
CMake Training Courses: http://cmake.org/cmake/help/training.html

Visit other Kitware open-source projects at http://www.kitware.com/opensource/opensource.html

Follow this link to subscribe/unsubscribe:
http://public.kitware.com/mailman/listinfo/cmake
Loading...