Saturday, October 30, 2021

[SOLVED] How to use 'target_sources' command with INTERFACE library?

Issue

My project is organized in subdirectories (a dozen), there are some includes between subdirectories. Some of these subdirectories contains only header files (dir1 for example).

Here is a look at the organization of the project:

project
 |
 |----- src -- dir1  <-- header-only library
 |       |      |--- dir1.h
 |       |      |--- CMakeLists.txt
 |       |
 |       |
 |       | --- dir2
 |       |      |--- dir2.cpp
 |       |      |--- dir2.h
 |       |      |--- CMakeLists.txt
 |       |
 |       | --- file.h
 |       | --- CMakeLists.txt 
 |
 |
 |----- test -- dir1 -- test.cpp
 |       | ---- dir2 -- test.cpp
 |       | ---- includeFile.h
 |
 |
 |----- CMakeLists.txt

I want to have a library containing all the code from src directory and an executable to run unit tests. After some research on stackOverFlow and Cmake examples, I arrived to these CMake files. There is one makefile per src subdirectories, one for grouping all the library and one global.

dir1 CMakeLists

add_library(dir1 INTERFACE)
target_sources(dir1 INTERFACE dir1.h)
target_include_directories(dir1 INTERFACE
  "${PROJECT_SOURCE_DIR}/src/dir1"
)

dir2 CMakeLists

set(dir2_src
  dir2.cpp dir2.h
)

add_library(dir2 ${dir2_src})

src CMakeLists

add_subdirectory(dir1)
add_subdirectory(dir2)


add_library(TheProject INTERFACE)
target_sources(TheProject INTERFACE file.h)
target_include_directories(TheProject INTERFACE
  "${PROJECT_SOURCE_DIR}/src/"
)
target_link_libraries(TheProject INTERFACE dir1
                                dir2
                                ${Boost_LIBRARIES})

main CMakeLists.txt

project(TheProject)

# gtest
# boost
# compilation flags ...

####### include paths
include_directories("./src/")
include_directories(${Boost_INCLUDE_DIRS})

####### library to compile
add_subdirectory(src)


####### executable
file(GLOB SRCS utest/*/*.cpp)
add_executable(utest ${SRCS} test/includeFile.h)

target_link_libraries(utest gtest_main TheProject)

I almost used the same organization on a smaller project without header only libraries and INTERFACE and it worked. Here, I have an error with the different interface libraries:

CMake Error at CMakeLists.txt:88 (add_executable):
  Cannot find source file:

    file.h

  Tried extensions .c .C .c++ .cc .cpp .cxx .m .M .mm .h .hh .h++ .hm .hpp
  .hxx .in .txx

I think I have not well understood the use of INTERFACE. I have done some research but nothing solve my problem.

And I have also a few questions:

  • There is some dependancies between directories, (e.g. code in dir1 has an include of some dir2 files), should I specify these dependance in their cmake file (with target_link_libraries(dir2 dir1)) ?
  • In the code, include are specified with relative paths in src and utest, is it not better to use paths like "dir1/dir1.h"?

Solution

Before CMake 3.13 relative paths, passed to target_sources, are interpreted relative to further invocations of add_executable (or add_library).

Because you call add_executable() from the top directory, CMake searches test.h relative to the top directory, not relative to the src/ one.

Use absolute paths for target_sources calls:

target_sources(TheProject INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/file.h)

Since CMake 3.13 relative paths passed to target_sources are resolved immediately, relative to the current source directory. See @markhc's answer.



Answered By - Tsyvarev