Issue
I have a simple foobar
project which has a directory layout as follows:
.
├── CMakeLists.txt
└── src
├── CMakeLists.txt
├── Foo
│ ├── CMakeLists.txt
│ ├── foo.cpp
│ └── foo.h
└── Bar
├── CMakeLists.txt
├── bar.cpp
└── bar.h
Where Foo
is standalone but Bar
depends on Foo
and should be able to #include <Foo/foo.h>
.
My top level CMakeLists.txt
file is:
# Specify cmake version
cmake_minimum_required(VERSION 3.10)
# Set the project name
project(foobar VERSION 0.1.0)
# Specify the C++ standard
set(CMAKE_CXX_COMPILER /usr/bin/g++)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -O3")
# Set location for .so files
set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}")
# Set location for executables
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin)
# Add libraries
add_subdirectory(src)
# Install library
install(TARGETS Foo Bar LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
my src
level is:
add_subdirectory(Foo)
add_subdirectory(Bar)
my Foo
level is:
add_library(Foo SHARED foo.cpp)
and finally my Bar
level is:
add_library(Bar SHARED bar.cpp)
add_dependencies(Bar Foo)
target_link_libraries(Bar Foo)
Invoking cmake
goes OK, but upon make
I get the following:
Scanning dependencies of target Foo
[ 25%] Building CXX object src/Foo/CMakeFiles/Foo.dir/foo.cpp.o
[ 50%] Linking CXX shared library libFoo.so
[ 50%] Built target Foo
Scanning dependencies of target Bar
[ 75%] Building CXX object src/Bar/CMakeFiles/Bar.dir/bar.cpp.o
In file included from /home/drjrm3/code/cmake_app/src/Bar/bar.cpp:1:
/home/drjrm3/code/cmake_app/src/Bar/bar.h:3:10: fatal error: Foo/foo.h: No such file or directory
3 | #include <Foo/foo.h>
| ^~~~~~~~~~~
compilation terminated.
make[2]: *** [src/Bar/CMakeFiles/Bar.dir/build.make:63: src/Bar/CMakeFiles/Bar.dir/bar.cpp.o] Error 1
make[1]: *** [CMakeFiles/Makefile2:160: src/Bar/CMakeFiles/Bar.dir/all] Error 2
make: *** [Makefile:130: all] Error 2
which shows me that I am not properly making Foo
visible to Bar
in the way that I'm hoping to.
What needs to change so that these can each be built into isolated shared object libraries but Bar
depends upon Foo
and can <Foo/foo.h>
can be visible from within that subcomponent?
Note that my choices for making these shared objects is because this is just a minimal example of a bigger project where I have multiple components and multiple apps but wanted to make a minimal example for this question. In fact I have this example up on Github is anyone is interested.
Solution
The key concept here is property visibility. There are two types in CMake:
PRIVATE
: the property affects the target being built.INTERFACE
: the property affects targets that link to this one directly, or transitively throughtarget_link_libraries(... INTERFACE ...)
.
The PUBLIC
visibility is simply a shorthand for both.
Therefore. the build for Bar should look like so:
add_library(Bar SHARED bar.cpp)
target_link_libraries(Bar PUBLIC Foo)
We use PUBLIC
visibility here because Bar's headers include Foo's headers. That is, libraries linking to Bar need to know about Foo as well.
Now we build Foo like so:
add_library(Foo SHARED foo.cpp)
target_include_directories(Foo PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/..>")
The main bit of magic here is the $<BUILD_INTERFACE:...>
generator expression. This prevents the include directory (which is an absolute path to somewhere on your build machine) from being exported when you eventually use install(EXPORT)
to create a find_package
-compatible CMake package for these libraries.
Otherwise, we're just relying on the fact that ${CMAKE_CURRENT_SOURCE_DIR}/..
will be propagated from Foo
to Bar
(and anything that links to Bar
) because this path is in the INTERFACE
of Foo
.
Answered By - Alex Reinking Answer Checked By - Pedro (WPSolving Volunteer)