Discussion:
[CMake] Difference between PRIVATE and PUBLIC with target_link_libraries
Patrick Boettcher
2016-05-10 05:59:48 UTC
Permalink
Hi list,

What is the differences between PRIVATE and PUBLIC when used with
target_link_libraries?

I read the help and understood that it works like in C++: PRIVATE will
make everything which was PUBLIC before also PRIVATE if inherited
privately.

An example:

add_library(lib1 INTERFACE)
target_include_directories(lib1 INTERFACE /tmp)

add_library(lib2 src2.c)
target_include_directories(lib2 PUBLIC /bin)
target_link_libraries(lib2 PRIVATE lib1) # PRIVATE here

add_library(lib3 src3.c)
target_include_directories(lib3 PUBLIC /bin)
target_link_libraries(lib3 PUBLIC lib1) # PUBLIC here

add_executable(exe1 exe.c)
target_link_libraries(exe1 lib2)

add_executable(exe2 exe.c)
target_link_libraries(exe2 lib3)


When building exe2 both include-dirs (from lib1 and lib3) are present:

[..] -I/bin -I/tmp [..]

as they are for exe1 - however I would have expected to not see /tmp
because lib3 and lib1 are linked privately.

Where is my mistake?

Thanks,
--
Patrick.
--
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
Attila Krasznahorkay
2016-05-10 09:39:49 UTC
Permalink
Hi Patrick,

I *think* that these public/private rules behave a bit differently for static libraries than they do for shared ones.

But I have to admit, that based on this code I also would've guessed that -I/tmp would not show up in the build of exe1...

I did manage to use public and private dependencies as expected in my own configurations, so I'm not exactly sure what's going wrong in your case. Which version of CMake did you use for the test?

Cheers,
Attila
Post by Patrick Boettcher
Hi list,
What is the differences between PRIVATE and PUBLIC when used with
target_link_libraries?
I read the help and understood that it works like in C++: PRIVATE will
make everything which was PUBLIC before also PRIVATE if inherited
privately.
add_library(lib1 INTERFACE)
target_include_directories(lib1 INTERFACE /tmp)
add_library(lib2 src2.c)
target_include_directories(lib2 PUBLIC /bin)
target_link_libraries(lib2 PRIVATE lib1) # PRIVATE here
add_library(lib3 src3.c)
target_include_directories(lib3 PUBLIC /bin)
target_link_libraries(lib3 PUBLIC lib1) # PUBLIC here
add_executable(exe1 exe.c)
target_link_libraries(exe1 lib2)
add_executable(exe2 exe.c)
target_link_libraries(exe2 lib3)
[..] -I/bin -I/tmp [..]
as they are for exe1 - however I would have expected to not see /tmp
because lib3 and lib1 are linked privately.
Where is my mistake?
Thanks,
--
Patrick.
--
Powered by www.kitware.com
Please keep messages on-topic and check the CMake FAQ at: http://www.cmake.org/Wiki/CMake_FAQ
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
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
Patrick Boettcher
2016-05-11 05:13:55 UTC
Permalink
On Tue, 10 May 2016 11:39:49 +0200
Post by Attila Krasznahorkay
Hi Patrick,
I *think* that these public/private rules behave a bit differently
for static libraries than they do for shared ones.
But I have to admit, that based on this code I also would've guessed
that -I/tmp would not show up in the build of exe1...
I did manage to use public and private dependencies as expected in my
own configurations, so I'm not exactly sure what's going wrong in
your case. Which version of CMake did you use for the test?
I'm using cmake 3.5.0.

Thank you for your feedback - I'll try to use shared-libraries, to see
whether it changes something or not

regards,
--
Patrick.
--
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
Craig Scott
2016-05-11 11:58:34 UTC
Permalink
Hopefully the explanation that follows helps clarify what PRIVATE, PUBLIC
and INTERFACE mean and do. From my understanding of things, I think there
may have been some subtle inaccuracies in some of the discussions so far,
so hopefully the following is helpful and if I've got something wrong, then
by all means please point out the inaccuracies.


- When A links in B as *PRIVATE*, it is saying that A uses B in its
implementation, but B is not used in any part of A's public API. Any code
that makes calls into A would not need to refer directly to anything from
B. An example of this could be a networking library A which can be built to
use one of a number of different SSL libraries internally (which B
represents). A presents a unified interface for client code which does not
reference any of the internal SSL data structures or functions. Client code
would have no idea what SSL implementation (B) is being used by A, nor does
that client code need to care.
- When A links in B as *INTERFACE*, it is saying that A does not use B
in its implementation, but B is used in A's public API. Code that calls
into A may need to refer to things from B in order to make such calls. One
example of this is an interface library which simply forwards calls along
to another library but doesn't actually reference the objects on the way
through other than by a pointer or reference. Another example is where A is
defined in CMake as an interface library, meaning it has no actual
implementation itself, it is effectively just a collection of other
libraries (I'm probably over-simplifying here, but you get the picture).
- When A links in B as *PUBLIC*, it is essentially a combination of
PRIVATE and INTERFACE. It says that A uses B in its implementation and B is
also used in A's public API.


Consider first what this means for include search paths. If something links
against A, it will also need any include search paths from B if B is in A's
public API. Thus, if A links in B either as PUBLIC or INTERFACE, then any
header search paths defined for target B will also apply to anything that
links to A. Any PRIVATE header search path for B will NOT be carried
through to anything that links only to A. The target_include_directories()
command handles this. The situation with compile flags is analogously
handled with target_compile_definitions() and target_compile_options().

Now consider the situation for the actual libraries involved. If A is a
shared library, then A will have encoded into it a dependency on B. This
information can be inspected with tools like ldd on Linux, otool on Mac and
something like Dependency Walker (a.k.a. depends.exe) on Windows. If other
code links directly to A, then it also will have encoded into it a
dependency on A. It will not, however, have a dependency on B unless A
links in B as PUBLIC or INTERFACE. So far, so good. If, however, A is a
static library, the situation changes. Static libraries do not carry
information about other libraries they depend on. 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.

If you were paying careful attention, you would have noticed that when A
links in B as PRIVATE, the include directories of B never propagate to
something linking to A, but if A is a static library, then the *linking* of
B behaves as though the relationship was PUBLIC. This
PRIVATE-becomes-PUBLIC behaviour for static libraries only applies to the
*linking*, not to the other dependencies (compiler options/flags and
include search paths). The upshot of all this is that if you select
PRIVATE, PUBLIC or INTERFACE based on the explanations in the dot points
above, then CMake will ensure dependencies propagate through to where they
are required, regardless of whether libraries are static or shared. This
does, of course, rely on you the developer not missing any dependencies or
specifying the wrong PRIVATE/PUBLIC/INTERFACE relationship.

As a final note, if you call target_link_libraries() and do not specify any
of PRIVATE, PUBLIC or INTERFACE, you may be tempted to believe that it will
be treated as PUBLIC. The situation is actually more complicated than that
though. It may be treated as PUBLIC or PRIVATE, depending on what other
target_link_library() calls and/or target property manipulations have been
performed. The documentation for target_link_libraries() talks a bit about
this, but you have to go digging into the documentation for the target
properties it mentions to get an understanding of what circumstances lead
to PRIVATE or PUBLIC behaviour.

Hope that helps clarify some things. Sorry if this has gone off on a
tangent from the original enquiry, I'm coming in late to this thread.


On Wed, May 11, 2016 at 8:33 PM, iosif neitzke <
Post by Attila Krasznahorkay
I *think* that these public/private rules behave a bit differently
for static libraries than they do for shared ones.
They do. Assuming main calls a() and b() defined in A_lib and B_lib
add_library(A_lib STATIC a.c)
add_library(B_lib STATIC b.c)
target_link_libraries(A_lib PRIVATE B_lib)
add_executable(main main.c)
target_link_libraries(main A_lib)
The PRIVATE in "target_link_libraries(A_lib PRIVATE B_lib)" is
useless. It is the same as writing "target_link_libraries(A_lib
PUBLIC B_lib)", only more confusing to the reader. Static libraries
always link to their dependencies publically.
https://cmake.org/cmake/help/v3.5/command/target_link_libraries.html#libraries-for-a-target-and-or-its-dependents
However, if you change A_lib to be a shared library with
"add_library(A_lib SHARED a.c)" and left the rest of the code the
same, you would now get link errors for main not able to find b(),
because A_lib now does not pass on its dependency on B, it hides it
from main. Change the last line to "target_link_libraries(main A_lib
B_lib)" and main builds again.
--
Powered by www.kitware.com
http://www.cmake.org/Wiki/CMake_FAQ
Kitware offers various services to support the CMake community. For more
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
http://public.kitware.com/mailman/listinfo/cmake
--
Craig Scott
Melbourne, Australia
http://crascit.com
Patrick Boettcher
2016-05-12 13:19:09 UTC
Permalink
On Wed, 11 May 2016 21:58:34 +1000
Craig Scott <***@crascit.com> wrote:

[..]
Post by Craig Scott
If you were paying careful attention, you would have noticed that
when A links in B as PRIVATE, the include directories of B never
propagate to something linking to A, but if A is a static library,
then the *linking* of B behaves as though the relationship was
PUBLIC. This PRIVATE-becomes-PUBLIC behaviour for static libraries
only applies to the *linking*, not to the other dependencies
(compiler options/flags and include search paths). The upshot of all
this is that if you select PRIVATE, PUBLIC or INTERFACE based on the
explanations in the dot points above, then CMake will ensure
dependencies propagate through to where they are required, regardless
of whether libraries are static or shared. This does, of course, rely
on you the developer not missing any dependencies or specifying the
wrong PRIVATE/PUBLIC/INTERFACE relationship.
Thank you for you long explanation. It was exactly my understanding.

So cmake 3.5.0 has a bug then because I don't see the behavior you
describe. Have you seen my example I sent in my first mail?

http://public.kitware.com/pipermail/cmake/2016-May/063382.html

Include-dirs from B are propagated to C which links to A which itself
linked privately to B.

Should I file a bug?

regards,
--
Patrick.
--
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
Patrick Boettcher
2016-05-12 13:59:53 UTC
Permalink
On Thu, 12 May 2016 08:47:33 -0500
exe1 gets linked to lib2, and lib2/bin is included. exe1 probably
won't link ultimately because lib2 may need symbols from lib1.
Depends on the structure of the C code between lib2 and lib1. See
John Lakos for further information on that.
It links - because it is a STATIC library - so propagated even though
it's PRIVATE.

My problem is just the include-dir of lib1 (/tmp) which should _not_ be
propagated to exe1, but it is propagated.

regards,
--
Patrick.
--
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
Patrick Boettcher
2016-05-12 14:10:58 UTC
Permalink
On Thu, 12 May 2016 09:04:10 -0500
target_include_directories(lib1 INTERFACE /tmp) means /tmp is
propagated with lib1, but not used to build lib1.
I know. Could you elaborate how this is related with lib3 PRIVATEly linking to
lib1?

regards,
--
Patrick.
--
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
Patrick Boettcher
2016-05-12 14:26:49 UTC
Permalink
On Thu, 12 May 2016 09:20:10 -0500
I'm sorry, I'm not sure I understand. In your example, there is
target_link_libraries(lib3 PUBLIC lib1). It looks like lib2 has
target_link_libraries(lib2 PRIVATE lib1).
Yes. That is correct.

When building the code for lib2 and lib3 the include-path of lib1 is
provided (as expected).

Then when building exe1 (links to lib2) and exe2 (links to lib3) the
lib1's include-path is present in both cases.

Whereas it should not be present with exe1, at least that is my
understanding.

--
Patrick.
--
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
Craig Scott
2016-05-12 21:06:32 UTC
Permalink
Patrick,

I suggest if you can reduce your problem down to a small, reproducible
example, then file a bug. I did a test just now with CMake 3.5.2 and
everything behaved as expected, including the header search path
propagation, so maybe there's something unusual in your project which a
simple case doesn't capture. Perhaps also try different generator types in
case that results in something different (unlikely, but since you see
different behaviour to me, give it a go).


On Fri, May 13, 2016 at 12:30 AM, iosif neitzke <
I guess the key is static libraries don't exactly adhere to the rules
of PUBLIC or PRIVATE, so you end up with a library that CMake passes
along with a populated INTERFACE_INCLUDE_DIRECTORIES, and so exe1 uses
it because it is there? Not sure how it is supposed to work at this point.
On Thu, May 12, 2016 at 9:26 AM, Patrick Boettcher
Post by Patrick Boettcher
On Thu, 12 May 2016 09:20:10 -0500
I'm sorry, I'm not sure I understand. In your example, there is
target_link_libraries(lib3 PUBLIC lib1). It looks like lib2 has
target_link_libraries(lib2 PRIVATE lib1).
Yes. That is correct.
When building the code for lib2 and lib3 the include-path of lib1 is
provided (as expected).
Then when building exe1 (links to lib2) and exe2 (links to lib3) the
lib1's include-path is present in both cases.
Whereas it should not be present with exe1, at least that is my
understanding.
--
Patrick.
--
Craig Scott
Melbourne, Australia
http://crascit.com
Patrick Boettcher
2016-05-13 07:06:29 UTC
Permalink
On Fri, 13 May 2016 07:06:32 +1000
Post by Attila Krasznahorkay
Patrick,
I suggest if you can reduce your problem down to a small, reproducible
example, then file a bug. I did a test just now with CMake 3.5.2 and
everything behaved as expected, including the header search path
propagation, so maybe there's something unusual in your project which
a simple case doesn't capture. Perhaps also try different generator
types in case that results in something different (unlikely, but
since you see different behaviour to me, give it a go).
I filed a bug with a test-case reduced to 7 lines of cmake. I
reproduced it with GNU Make and Ninja a generator.

https://cmake.org/Bug/view.php?id=16102

I'm also reading some cmake-code, but haven't yet found where includes
are inherited from linked targets. Currently looking at
cmGeneratorTarget::GetIncludeDirectories() ...

Thanks for your feedback.

best regards,
--
Patrick.
--
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...