156

I am new to CMake and a bit confused with the PUBLIC, PRIVATE and INTERFACE keywords related to target_link_libraries(). Documentation mentions that they can be used to specify both the link dependencies and the link interface in one command.

What does link dependencies and link interface actually mean?

Kevin
  • 16,549
  • 8
  • 60
  • 74
Sirish
  • 9,183
  • 22
  • 72
  • 107
  • 3
    Similar question: http://stackoverflow.com/questions/26243169/cmake-target-include-directories-meaning-of-scope – TManhente May 28 '16 at 21:10

5 Answers5

316

If you are creating a shared library and your source cpp files #include the headers of another library (Say, QtNetwork for example), but your header files don't include QtNetwork headers, then QtNetwork is a PRIVATE dependency.

If your source files and your headers include the headers of another library, then it is a PUBLIC dependency.

If your header files other than your source files include the headers of another library, then it is an INTERFACE dependency.

Other build properties of PUBLIC and INTERFACE dependencies are propagated to consuming libraries. http://www.cmake.org/cmake/help/latest/manual/cmake-buildsystem.7.html#transitive-usage-requirements

starball
  • 20,030
  • 7
  • 43
  • 238
steveire
  • 10,694
  • 1
  • 37
  • 48
  • 74
    This is the most clear explanation of this question I've ever heard. I wish the [cmake documentation](https://cmake.org/cmake/help/latest/command/target_link_libraries.html) was as clear! :-) – Ela782 Dec 07 '16 at 23:41
  • 1
    Thanks for the explanation I understand the meaning now but why does specifying `PUBLIC` or `PRIVATE` matters? What does it change? – user3667089 Jan 17 '17 at 18:07
  • 12
    Using `PRIVATE` does not add your dependencies to projects linking to your library. It is cleaner and it also avoids possible conflicts between your dependencies and your user's. – user2658323 Feb 23 '17 at 17:29
  • 16
    @steveire For the sake of clarity, it might be worth explicitly stating that "include" means transitive inclusion, not just direct inclusion. E.g. if the header files of another library are only directly included by your header files, but all of your header files are included by your source files, then your source files transitively include the other library's header files, and so you have a `PUBLIC` dependency, not an `INTERFACE` dependency. – Ose Nov 27 '18 at 17:34
  • 1
    The cmake documentation should write it as concise as this answer! – Bryan Jyh Herng Chong Jul 24 '20 at 07:48
  • Does it also apply to **static** library? –  Aug 25 '21 at 06:54
  • What keyword do I need to use when my executable loads library using dlopen? – T0maas Apr 29 '22 at 14:29
  • What header files does "your header files other than your source files" refer to? – Kevin May 23 '23 at 22:59
31

@steveire accepted answer is great. I just wanted to add a table to quickly see the difference:

.-----------.------------------.----------------.
|           | Linked by target | Link interface |
:-----------+------------------+----------------:
| PUBLIC    |        X         |        X       |
:-----------+------------------+----------------:
| PRIVATE   |        X         |                |
:-----------+------------------+----------------:
| INTERFACE |                  |        X       |
'-----------'------------------'----------------'
  • Linked by target: libraries included in target sources (not a dependency for projects linking the library).
  • Link interface: libraries included in target public headers (dependencies for projects linking the library).
ikaro
  • 703
  • 6
  • 10
  • Complete newbie, why would you not just pick PUBLIC for everything? – Tom McLean Feb 27 '23 at 08:46
  • 1
    I think is well explained by other answers: Using PUBLIC does add your dependencies to projects linking to your library. Using PRIVATE does NOT add your dependencies to projects linking to your library. It is cleaner and it also avoids possible conflicts between your dependencies and your user's – ikaro Mar 02 '23 at 08:03
23

Not my brainchild but this extremely useful explanation helped me understand the situation. The most important part is quoted below for reference:

  • When A links 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 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 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 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 B either 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 B as PRIVATE and another target C links 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 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.

John
  • 2,963
  • 11
  • 33
András Aszódi
  • 8,948
  • 5
  • 48
  • 51
  • what's the public interface you're referring to? `A LINKS B as PRIVATE`: B is used in A's implementation but not included in A's public interface? Are you implicitly implying the header vs source files? – xyf Nov 27 '22 at 18:47
2

Some answers only said when to use PRIVATE/PUBLIC/INTERFACE, but the affects are ignored. Refer:CMake-Public-Private-Interface

PUBLIC
All the objects following PUBLIC will be used for linking to the current target and providing the interface to the other targets that have dependencies on the current target.

PRIVATE
All the objects following PRIVATE will only be used for linking to the current target.

INTERFACE
All the objects following INTERFACE will only be used for providing the interface to the other targets that have dependencies on the current target.

WangJian
  • 21
  • 1
1

Other posts already answered what the meaning of PUBLIC/PRIVATE/INTERFACE keywords. I want to add one more to clarify the terms "link dependencies" and "link interface."

  • Link dependencies : the list of libraries to be linked by the target. The target property LINK_LIBRARIES holds this information.
  • Link interface : the list of libraries to be linked by the target's dependents. The target property INTERFACE_LINK_LIBRARIES holds this information.

Probably the term "link interface" came from the old CMake wording used around LINK_INTERFACE_LIBRARIES properties, which is deprecated in favor of INTERFACE_LINK_LIBRARIES. See the description of CMP0022, which also uses the term "link interface." https://cmake.org/cmake/help/latest/policy/CMP0022.html

INTERFACE_LINK_LIBRARIES defines the link interface.

Ryoji
  • 155
  • 7