cmake behaviour with add_subdirectory

Hi guys,
I can see a behaviour with a cmake project which I can’t explain.

I’ve got the following simplified project layout:
.
├── a
│ ├── a.cpp
│ ├── a.h
│ └── CMakeLists.txt
├── b
│ ├── b.cpp
│ ├── b.h
│ └── CMakeLists.txt
├── build
└── CMakeLists.txt

where B is a static library, A is an executable which links B

root CMakeList.txt:
cmake_minimum_required(VERSION 3.13)
project(st)

add_subdirectory(b)
add_subdirectory(a)

A CMakeList.txt:
cmake_minimum_required(VERSION 3.13)
project(a)

add_executable(a a.cpp)
add_definitions(-DBUILDA)

target_include_directories(a PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/…/b)

target_link_libraries(a LINK_PUBLIC b)

B CMakeList.txt:
cmake_minimum_required(VERSION 3.13)
project(b)

add_definitions(-DBUILDB)

add_library(b STATIC)

target_sources(b PUBLIC b.cpp)

When I run make I’ve got the following output:

/usr/bin/cmake -S/home/ollly/supertest -B/home/ollly/supertest/build --check-build-system CMakeFiles/Makefile.cmake 0
/usr/bin/cmake -E cmake_progress_start /home/ollly/supertest/build/CMakeFiles /home/ollly/supertest/build/CMakeFiles/progress.marks
make -f CMakeFiles/Makefile2 all
make[1]: Entering directory '/home/ollly/supertest/build'
make -f b/CMakeFiles/b.dir/build.make b/CMakeFiles/b.dir/depend
make[2]: Entering directory '/home/ollly/supertest/build'
cd /home/ollly/supertest/build && /usr/bin/cmake -E cmake_depends "Unix Makefiles" /home/ollly/supertest /home/ollly/supertest/b /home/ollly/supertest/build /home/ollly/supertest/build/b /home/ollly/supertest/build/b/CMakeFiles/b.dir/DependInfo.cmake --color=
Scanning dependencies of target b
make[2]: Leaving directory '/home/ollly/supertest/build'
make -f b/CMakeFiles/b.dir/build.make b/CMakeFiles/b.dir/build
make[2]: Entering directory '/home/ollly/supertest/build'
**[ 20%] Building CXX object b/CMakeFiles/b.dir/b.cpp.o**
cd /home/ollly/supertest/build/b && /usr/bin/c++  -DBUILDB   -o CMakeFiles/b.dir/b.cpp.o -c /home/ollly/supertest/b/b.cpp
[ 40%] Linking CXX static library libb.a
cd /home/ollly/supertest/build/b && /usr/bin/cmake -P CMakeFiles/b.dir/cmake_clean_target.cmake
cd /home/ollly/supertest/build/b && /usr/bin/cmake -E cmake_link_script CMakeFiles/b.dir/link.txt --verbose=1
/usr/bin/ar qc libb.a  CMakeFiles/b.dir/b.cpp.o
/usr/bin/ranlib libb.a
make[2]: Leaving directory '/home/ollly/supertest/build'
[ 40%] Built target b
make -f a/CMakeFiles/a.dir/build.make a/CMakeFiles/a.dir/depend
make[2]: Entering directory '/home/ollly/supertest/build'
cd /home/ollly/supertest/build && /usr/bin/cmake -E cmake_depends "Unix Makefiles" /home/ollly/supertest /home/ollly/supertest/a /home/ollly/supertest/build /home/ollly/supertest/build/a /home/ollly/supertest/build/a/CMakeFiles/a.dir/DependInfo.cmake --color=
Scanning dependencies of target a
make[2]: Leaving directory '/home/ollly/supertest/build'
make -f a/CMakeFiles/a.dir/build.make a/CMakeFiles/a.dir/build
make[2]: Entering directory '/home/ollly/supertest/build'
[ 60%] Building CXX object a/CMakeFiles/a.dir/a.cpp.o
cd /home/ollly/supertest/build/a && /usr/bin/c++  -DBUILDA -I/home/ollly/supertest/a/../b   -o CMakeFiles/a.dir/a.cpp.o -c /home/ollly/supertest/a/a.cpp
**[ 80%] Building CXX object a/CMakeFiles/a.dir/__/b/b.cpp.o**
cd /home/ollly/supertest/build/a && /usr/bin/c++  -DBUILDA -I/home/ollly/supertest/a/../b   -o CMakeFiles/a.dir/__/b/b.cpp.o -c /home/ollly/supertest/b/b.cpp
[100%] Linking CXX executable a
cd /home/ollly/supertest/build/a && /usr/bin/cmake -E cmake_link_script CMakeFiles/a.dir/link.txt --verbose=1
/usr/bin/c++     CMakeFiles/a.dir/a.cpp.o CMakeFiles/a.dir/__/b/b.cpp.o  -o a  ../b/libb.a 
make[2]: Leaving directory '/home/ollly/supertest/build'
[100%] Built target a
make[1]: Leaving directory '/home/ollly/supertest/build'
/usr/bin/cmake -E cmake_progress_start /home/ollly/supertest/build/CMakeFiles 0

In this output you can see that b.cpp is compiled twice and when the executable A is being built it doesn’t link B as a library (libb.a) but instead it prefers to recompilile b.cpp and link the object file

Keeping this project structure, how can I avoid make to recompile b.cpp and link directly libb.a ?

Thanks in advance.
Jack

target_sources(b PUBLIC b.cpp)
For starters, make this PRIVATE

This https://cmake.org/cmake/help/latest/command/target_link_libraries.html#libraries-for-a-target-and-or-its-dependents-legacy says that LINK_PUBLIC a) is deprtectaed; b) only sets INTERFACE properties (also does not link)

You probably want
target_link_libraries(a PRIVATE b)

HI Jakub,
thanks for suggestions but that doesn’t answer my question.

By making this PUBLIC you’re basically saying that each target thet links to b should add “b.cpp” to its own sources for compilation.

And have you read the entire response?

I’m sorry Jakub, you’re right. solved my issue. Thanks a lot.