Why is my library consumer getting a PRIVATE library dependency

I have three build elements:

  1. bar (library) provides bar()
  2. foo (library) provides foo() and uses bar()
  3. main (exec) uses foo()

I purposely have bar as a PRIVATE library of foo, and expect for main to fail because it doesn’t get the link information for bar, but it does. Why is a PRIVATE library being shared with the consumer?

Here is my simple CMakeLists.txt file.

cmake_minimum_required (VERSION 3.18)

project(Example)

add_library(bar STATIC bar/bar.c bar/bar.h)
target_include_directories(bar PUBLIC "${CMAKE_CURRENT_LIST_DIR}/bar")


add_library(foo STATIC foo/foo.c foo/foo.h)
target_include_directories(foo PUBLIC "${CMAKE_CURRENT_LIST_DIR}/foo")
target_link_libraries(foo PRIVATE bar)


add_executable(main main/main.c)
target_link_libraries(main PRIVATE foo)

The link line I see for this is

[100%] Linking C executable main
/usr/local/bin/cmake -E cmake_link_script CMakeFiles/main.dir/link.txt --verbose=1
/usr/bin/cc CMakeFiles/main.dir/main/main.c.o -o main  libfoo.a libbar.a 

You may ask, why would you make something that would purposely fail. I did this trying to investigate this problem. We have a scenario where the bar library has information in it’s header files that foo wants to use, but it doesn’t actually use any symbols from bar.

In order for foo to compile it needs a few header files from bar but it doesn’t need bar.a. But when we use this mechanism the consumer of foo now tries to link with bar.a too.

Static libraries will forward their private dependency link requirements to consumers to ensure that all symbols are provided.

You’re interested in $<COMPILE_ONLY> which hides the target from the linker usage requirements. It’s been discussed on Discouse a few times but search isn’t finding it. It’s just a proposal at this point.

Thanks @ben.boeckel. I found the entries doing a google search

"compile_only" inurl:discourse.cmake.org

The discussion for Add only library headers during target_link_libraries() from 3/2021 is pretty much exactly what I’m wanting.

I also see a workaround in Idiomatic way to deal with huge number of include directories, although it means creating two libraries for this, still it’s a step forwards.

Thank you!