Produce .lib of header-only library that depends on external resources - cmake

EDIT: I've read up and understood the initial issue was caused by scanning-header-only not having cpp files and thus a lib file not being generated. Edited the question to reflect that extra understanding:
My current project folder structure and relevant CMakeLists content:
leveling
├── CMakeLists.txt: add_subdirectory(deps)
└── deps
├── CMakeLists.txt: add_subdirectory(scanning-header-only)
└── scanning
├── CMakeLists.txt: add_subdirectory(deps)
│ add_library(scanning-header-only file.h)
│ target_include_directories(scanning-header-only PUBLIC ${CMAKE_CURRENT_LIST_DIR}/deps/tinyxml2)
│ target_link_libraries(scanning-header-only PUBLIC tinyxml2)
└── deps
├── CMakeLists.txt: add_subdirectory(tinyxml2)
└── tinyxml2
But a scanning-header-only library file is not being generated, and thus the root project can't target_link_libraries(leveling scanning-header-only) and has had to target_include_directories(leveling ${CMAKE_CURRENT_LIST_DIR}/deps/scanning-header-only/deps/tinyxml2)
Is it possible to target_link_library a header-only library that depends on external resources?
I see that a header-only library without external resource dependency could be add_library(.. INTERFACE), but I'm failing to do so with the dependency on tinyxml2
A dirty workaround is adding and empty cpp file to scanning-header-only so a lib file is generated, but is there a correct way to do this?
Here is minimal example v1: https://www.dropbox.com/s/r1lbajz3xoat1bg/leveling-header-only-test%20v1.zip?dl=0
leveling CMakeLists.txt:
cmake_minimum_required(VERSION 3.8)
set(LEVELING_NAME leveling)
project(${LEVELING_NAME})
#
# To put tinyxml.dll next to the executable, to workaround having to make tinyxml2.dll reachable in PATH
#
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
math(EXPR platform_bits "${CMAKE_SIZEOF_VOID_P} * 8")
set(platform_dir bin/${CMAKE_SYSTEM_NAME}-${platform_bits})
foreach(config DEBUG RELEASE RELWITHDEBINFO MINSIZEREL)
foreach(var
CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${config}
CMAKE_LIBRARY_OUTPUT_DIRECTORY_${config}
CMAKE_RUNTIME_OUTPUT_DIRECTORY_${config}
)
set(${var} "${CMAKE_BINARY_DIR}/${platform_dir}/${config}")
string(TOLOWER "${${var}}" ${var})
endforeach()
endforeach()
#
# ----------------------------------------------------------------------
#
add_subdirectory(deps)
add_executable(${LEVELING_NAME} main.cpp)
target_include_directories(${LEVELING_NAME} PUBLIC
${CMAKE_CURRENT_LIST_DIR}/deps/scanning
)
target_link_libraries(${LEVELING_NAME}
xml-reading
)
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT ${LEVELING_NAME}) # Set Startup Project in VS. Implemented in CMake v3.6
set_target_properties(${LEVELING_NAME} PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}") # Set Working Directory of project in VS. Implemented in CMake v3.8
scanning CMakeLists.txt
cmake_minimum_required(VERSION 3.8)
set(XML_NAME xml-reading)
project(${XML_NAME})
#
# To put tinyxml.dll next to the executable, to workaround having to make tinyxml2.dll reachable in PATH
#
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
math(EXPR platform_bits "${CMAKE_SIZEOF_VOID_P} * 8")
set(platform_dir bin/${CMAKE_SYSTEM_NAME}-${platform_bits})
foreach(config DEBUG RELEASE RELWITHDEBINFO MINSIZEREL)
foreach(var
CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${config}
CMAKE_LIBRARY_OUTPUT_DIRECTORY_${config}
CMAKE_RUNTIME_OUTPUT_DIRECTORY_${config}
)
set(${var} "${CMAKE_BINARY_DIR}/${platform_dir}/${config}")
string(TOLOWER "${${var}}" ${var})
endforeach()
endforeach()
#
# ----------------------------------------------------------------------
#
add_subdirectory(deps)
add_library(${XML_NAME} INTERFACE CamerasXML.h)
target_include_directories(${XML_NAME} INTERFACE
${CMAKE_CURRENT_LIST_DIR}/deps/tinyxml2
)
target_link_libraries(${XML_NAME}
INTERFACE tinyxml2
)
which yields
CMake Error at deps/scanning/CMakeLists.txt:33 (add_library):
add_library INTERFACE library requires no source arguments.

A .lib is when you create a STATIC (.lib) or SHARED (.lib and .dll) library on Windows. What you want is an INTERFACE library and it generates no files. http://mariobadr.com/creating-a-header-only-library-with-cmake.html has an example. Then you can use the following commands listed here, https://cmake.org/cmake/help/latest/command/add_library.html#interface-libraries, to populate the interface. Notice that it uses INTERFACE not PUBLIC.
target_link_libraries(INTERFACE),
target_link_options(INTERFACE),
target_include_directories(INTERFACE),
target_compile_options(INTERFACE),
target_compile_definitions(INTERFACE), and
target_sources(INTERFACE),
I've never actually used this but I assume it works as documented.

A simple add_library(${XML_NAME} INTERFACE) (not specifying any source files), while having target_include_directories(${XML_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR}/deps/tinyxml2) and target_link_libraries(${XML_NAME} INTERFACE tinyxml2) will do the trick.
The tinyxml2 includes are made available to the parent project, and the tinyxml2 library is linked in the parent project.

Related

Problem using FetchContent_Declare together with shared library

I'm having problem using FetchContent_Declare with a shared library. I'm trying to apply a modular design (instead of creating one mega-repository with 20 modules I'm allocating a dedicated repository to each module). I'm trying to use FetchContent_Declare in order to link dependant modules together (I'm not a fan of git-submodules since those require that user manually initialises them). The problem I'm having is that the DLL generated by the project fetched via the mentioned function isn't copied to the binary output of the parent project. Here is a dependency graph to make it more clear.
Repo A:
- bin
- bin-etc
- lib
- include
- project_a
.. header files ..
- src
- CMakeLists.txt : 1
.. source files ..
- CMakeLists.txt : 2
Repo B: Includes A
- bin
- bin-etc
- lib
- output
.. cmake files are built from here ..
- _deps
- project_a-src
- bin
.. here the DLL file is being generated ..
- include
- project_a
.. header files ..
- src
- CMakeLists.txt : 3
.. source files ..
- CMakeLists.txt : 4
The DLL file is being generated in ./output/_deps/project_a-src/bin instead of ./bin.
Here are my CMakeLists.txt files:
# CMakeLists.txt : 1
cmake_minimum_required(VERSION 3.16)
set(PROJECTNAME project_a)
set(PROJECTDIR "${CMAKE_CURRENT_SOURCE_DIR}")
file(GLOB_RECURSE inc "${CMAKE_CURRENT_SOURCE_DIR}/../include/*.hpp")
file(GLOB_RECURSE src "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp")
source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}/../include" FILES ${inc})
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${src})
if (MSVC)
add_compile_options(/W3) # warning level 3
add_compile_options(/MP) # Multi-processor compilation
endif()
add_library(
${PROJECTNAME}
SHARED
${inc}
${src}
)
target_include_directories(${PROJECTNAME} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/../include/")
# CMakeLists.txt : 2 and 4
cmake_minimum_required(VERSION 3.16)
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
set(CMAKE_SYSTEM_VERSION 10.0.19041.0)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/bin-etc")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/lib")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/bin")
project(project_a LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED)
add_subdirectory(src)
# CMakeLists.txt : 3
cmake_minimum_required(VERSION 3.16)
set(PROJECTNAME project_b)
set(PROJECTDIR "${CMAKE_CURRENT_SOURCE_DIR}")
file(GLOB_RECURSE inc "${CMAKE_CURRENT_SOURCE_DIR}/../include/*.hpp")
file(GLOB_RECURSE inc_src "${CMAKE_CURRENT_SOURCE_DIR}/*.hpp")
file(GLOB_RECURSE src "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp")
source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}/../include" FILES ${inc})
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${inc_src})
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${src})
if (MSVC)
add_compile_options(/W3) # warning level 3
add_compile_options(/MP) # Multi-processor compilation
endif()
add_library(
${PROJECTNAME}
SHARED
${inc}
${inc_src}
${src}
)
target_include_directories(${PROJECTNAME} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/../include/")
include(FetchContent)
FetchContent_Declare(project_a
GIT_REPOSITORY <REPO LINK>
GIT_TAG master)
FetchContent_MakeAvailable(project_a)
target_link_libraries(${PROJECTNAME} project_a)
What would be the solution to this problem? Should I use another approach for the design like this? Is adding a simple copy command to cmake the right solution?
Variables like CMAKE_RUNTIME_OUTPUT_DIRECTORY affects on the output directory for all further created libraries ... unless the variable is changed.
The project_a in CMakeLists.txt(2) does
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/bin")
unconditionally, so it changes the variable even if it is built as a subproject (with FetchContent).
Generally, any project in its root CMakeLists.txt could check, whether it is actually top-level project. And perform some settings only in case it is:
cmake_minimum_required(VERSION 3.21)
project(project_a LANGUAGES CXX)
if (PROJECT_IS_TOP_LEVEL)
# Do global settings only if we are top-level project.
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
set(CMAKE_SYSTEM_VERSION 10.0.19041.0)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/bin-etc")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/lib")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/bin")
endif()
# Other settings are treated as project-specific,
# so could be done in "subproject mode" too.
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED)
add_subdirectory(src)
Note, that variable PROJECT_IS_TOP_LEVEL appears only in CMake 3.21. For detect, whether the project is top-level in older CMake versions, consult that question and its answers: How to detect if current scope has a parent in CMake?.

How to use find_package after add_subdirectory

I am trying to import a third party package into my project. So I've been following:
https://cliutils.gitlab.io/modern-cmake/chapters/install/installing.html
But this fails with:
/tmp/top-level/bin/extern/MyLib
CMake Error at bin/extern/MyLib/MyLibConfig.cmake:12 (include):
include could not find load file:
/tmp/top-level/bin/extern/MyLib/MyLibTargets.cmake
Call Stack (most recent call first):
CMakeLists.txt:6 (find_package)
What am I missing from the documentation ? For reference, my top level cmakelists.txt is:
cmake_minimum_required(VERSION 3.18)
project(top-level)
add_subdirectory(extern)
find_package(MyLib CONFIG REQUIRED HINTS
${CMAKE_CURRENT_BINARY_DIR}/extern/MyLib)
And the cmakelists.txt file for 'MyLib' is:
cmake_minimum_required(VERSION 3.18)
project(MyLib VERSION 1.0 LANGUAGES C)
add_library(MyLib mylib.c)
add_library(MyLib::MyLib ALIAS MyLib)
install(
TARGETS MyLib
EXPORT MyLibTargets
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
RUNTIME DESTINATION bin
INCLUDES
DESTINATION include)
include(CMakePackageConfigHelpers)
write_basic_package_version_file(
MyLibConfigVersion.cmake
VERSION ${PACKAGE_VERSION}
COMPATIBILITY AnyNewerVersion)
install(
EXPORT MyLibTargets
FILE MyLibTargets.cmake
NAMESPACE MyLib::
DESTINATION lib/cmake/MyLib)
configure_file(MyLibConfig.cmake.in MyLibConfig.cmake #ONLY)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/MyLibConfig.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/MyLibConfigVersion.cmake"
DESTINATION lib/cmake/MyLib)
The error message is self-explanatory:
You use script MyLibConfig.cmake from the build directory, and this script attempts to load the script MyLibTargets.cmake created by install(EXPORT MyLibTargets).
But the latter script is intended to work only after the project will be installed, it cannot work while the project is being built.
Actually, the whole call find_package(MyLib) is not needed in that situation:
since current project builds MyLib, the target MyLib::MyLib is already accessible for you.
If you want to make your top-level project to be flexible, so it would work both in cases MyLib is already installed or just being built, then you could use find_package conditionally:
cmake_minimum_required(VERSION 3.18)
project(top-level)
# This project could be built as standalone.
# In that case 'MyLib' is assumed to be already installed.
#
# Also, this project could work as a subproject of some other project,
# which also builds `MyLib` via 'add_subdirectory(MyLib)'.
if(NOT TARGET MyLib::MyLib)
find_package(MyLib CONFIG REQUIRED)
endif()
# ... use MyLib via 'MyLib::MyLib' target.
Alternatively, you may write MyLibConfig.cmake script in a manner, which allows it to be used even if MyLib is currently being built.
if(TARGET MyLib::MyLib)
return()
endif()
# ... usual content of the config file.
In that case, CMakeLists.txt for the root project could be simplified:
cmake_minimum_required(VERSION 3.18)
project(top-level)
# Normal use case is that 'MyLib' is already installed.
# But the project could work as a subproject in other scenarios.
#
# In those scenarios, a parent project should care about
# 'find_package' to work.
find_package(MyLib CONFIG REQUIRED)
# ... use MyLib via 'MyLib::MyLib' target.
The usage of the project in case of 'MyLib' being built could be as follows:
- CMakeLists.txt (outer)
- MyLib
- CMakeLists.txt (MyLib)
- top_level
- CMakeLists.txt ("top-level")
Outer CMakeLists.txt:
cmake_minimum_required(VERSION 3.18)
project(outer)
add_subdirectory(MyLib)
# Help inner project to find config file for MyLib.
#
# Here we use *internal* knowledge of MyLib project,
# that it generates 'MyLibConfig.cmake' directly in its build directory.
#
# Note: find_package expects 'XXX_DIR' variable to be CACHE one.
set(MyLib_DIR "${CMAKE_CURRENT_BINARY_DIR}/MyLib"
CACHE INTERNAL "Directory with MyLibConfig.cmake"
)
add_subdirectory(top_level)

CMake: Can I wrap an ExternalProject in some object that I can just link to my target?

I'm including this library as an external project. Based on the documentation, with some small tweaks, I have this:
# LIEF dependency
# ===========================
set(LIEF_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/LIEF")
set(LIEF_INSTALL_DIR "${LIEF_PREFIX}")
set(LIEF_INCLUDE_DIRS "${LIEF_PREFIX}/include")
# LIEF static library
set(LIEF_LIBFILE
"${LIEF_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}LIEF${CMAKE_STATIC_LIBRARY_SUFFIX}")
# URL of the LIEF repo (Can be your fork)
set(LIEF_GIT_URL "https://github.com/lief-project/LIEF.git")
# LIEF's version to be used (can be 'master')
set(LIEF_VERSION 0.11.5)
# LIEF compilation config
set(LIEF_CMAKE_ARGS
-DCMAKE_INSTALL_PREFIX=<INSTALL_DIR>
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
-DLIEF_DOC=off
-DLIEF_PYTHON_API=off
-DLIEF_EXAMPLES=off
-DLIEF_OAT=off
-DLIEF_DEX=off
-DLIEF_VDEX=off
-DLIEF_ART=off
-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
)
# Specify MSVCRT on MSVC
if(MSVC)
list(APPEND ${LIEF_CMAKE_ARGS} -DLIEF_USE_CRT_RELEASE=MT)
list(APPEND ${LIEF_CMAKE_ARGS} -DLIEF_USE_CRT_DEBUG=MTd)
endif()
# External project
ExternalProject_Add(LIEF_extproj
PREFIX "${LIEF_PREFIX}"
GIT_REPOSITORY ${LIEF_GIT_URL}
GIT_TAG ${LIEF_VERSION}
INSTALL_DIR ${LIEF_INSTALL_DIR}
CMAKE_ARGS ${LIEF_CMAKE_ARGS}
BUILD_BYPRODUCTS ${LIEF_LIBFILE}
UPDATE_COMMAND ""
)
However, the original docs simply included the directories and linked separately. Can I somehow wrap these into a single target, where if I link to that target I get everything from that library?
EDIT:
My current attempt at setting up an imported target is this:
add_library(LIEF_depimpl STATIC IMPORTED)
set_target_properties(LIEF_depimpl PROPERTIES
IMPORTED_LOCATION ${LIEF_LIBFILE}
INTERFACE_INCLUDE_DIRECTORIES ${LIEF_INCLUDE_DIRS}
)
add_dependencies(LIEF_depimpl LIEF_extproj)
When I use target_link_libraries() to link LIEF against my project, CMake generates successfully, but then I get an error in the generated makefile.
add_executable(testapp lief-test.cpp)
...
# Link the executable with LIEF
target_link_libraries(testapp PUBLIC ${LIEF_depimpl})
The syntax for target_link_libraries() isn't what I thought it was. The dependency variable should not be expanded, like this:
target_link_libraries(testapp PUBLIC LIEF_depimpl)
In addition, CMake will throw an error if it can't find the include directories for an external project, so you should create that folder in your CMake file like so:
set(LIEF_INCLUDE_DIRS "${LIEF_PREFIX}/include")
file(MAKE_DIRECTORY ${LIEF_INCLUDE_DIRS})

Create a library by linking link external library

I am using yocto build environment for my project. There are multiple applications in the project and some of them are depending on one third party library(It contains the *.s0 files, header files). So I am planning to create one static wrapper library around the third party library and link the wrapper library for all the applications.
The structure of the project:
.
├── App1
├── App2
├── App3
└── third-party
└── inc
└── src
└── lib
└── libdvm-hash.so
└── libhash-ipc.so
└── CMakeLists.txt
CMakeLists.txt
project(hash LANGUAGES CXX VERSION "1.0.0")
set(LIBRARY_NAME hash)
set(HASH_LIBRARY_FILES ${CMAKE_CURRENT_SOURCE_DIR}/lib/libdvm-hash.so ${CMAKE_CURRENT_SOURCE_DIR}/lib/libhash-ipc.so
add_library(${LIBRARY_NAME} STATIC test.cpp)
target_include_directories(${LIBRARY_NAME}
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/inc/>
$<INSTALL_INTERFACE:include>
)
target_link_libraries(${LIBRARY_NAME} PUBLIC ${HASH_LIBRARY_FILES} ssl)
set_target_properties(${LIBRARY_NAME} PROPERTIES
OUTPUT_NAME ${LIBRARY_NAME})
install( FILES
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake
DESTINATION
${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME})
install(
TARGETS ${LIBRARY_NAME}
EXPORT ${PROJECT_NAME}-targets
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
install(EXPORT ${PROJECT_NAME}-targets DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} NAMESPACE dvm::)
install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/inc/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} COMPONENT dev)
Now the problem is in the exported hash-targets.cmake the path to the library is hardcoded.
hash-targets.cmake
set_target_properties(hash PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/include"
INTERFACE_LINK_LIBRARIES "/home/mypc/path/to/the/ibdvm-hash.so;/home/mypc/pathto/the/libhash-ipc.so;ssl"
)
Is there any way to fix the hardcode path .*so and use the installed *.so from /usr/lib?
Edit
I installed *.so files in the target using yocto(using do_install_append command in bb file).
Instead of linking with absolute paths, create an IMPORTED library for every such path and link with that libraries:
# CMakeLists.txt, building the library
# Create an IMPORTED target which points to the library shipped with the project.
add_library(dvm-hash IMPORTED)
set_target_properties(dvm-hash PROPERTIES
IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/lib/libdvm-hash.so
)
# Another IMPORTED target
add_library(hash-ipc IMPORTED)
set_target_properties(hash-ipc PROPERTIES
IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/lib/libhash-ipc.so
)
...
# Link with those IMPORTED targets
target_link_libraries(${LIBRARY_NAME} PUBLIC dvm-hash hash-ipc)
That way, when create an export file for your hash library, CMake will create its linkage with targets dvm-hash and hash-ipc, but won't create that targets.
Next step is creating those IMPORTED targets in the export file for your project.
# hash-config.cmake
# Create an IMPORTED target. That time it points to the library in the target system.
add_library(dvm-hash IMPORTED)
find_library(DVM_HASH_LIBRARY dvm-hash)
set_target_properties(dvm-hash PROPERTIES
IMPORTED_LOCATION ${DVM_HASH_LIBRARY}
)
# Another IMPORTED target.
add_library(hash-ipc IMPORTED)
find_library(HASH_IPC_LIBRARY hash-ipc)
set_target_properties(hash-ipc PROPERTIES
IMPORTED_LOCATION ${HASH_IPC_LIBRARY}
)
# Include the export file for the target `hash`
# That file uses IMPORTED targets defined above.
include("${CMAKE_CURRENT_LIST_DIR}/hash-targets.cmake")

Best way to add include directories of root project to a subdirectory in CMake

I have a project with a CMakeLists.txt at the root, which includes a project in a subdirectory test/ using add_subdirectory with the flag EXCLUDE_FROM_ALL. The tests need all of the include directories of the parent project. What would be the most elegant way to do this?
CMakeLists.txt
CMAKE_MINIMUM_REQUIRED(VERSION 3.1)
SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules")
PROJECT(autoffi CXX)
SET(CMAKE_CXX_STANDARD 11)
SET(AutoFFI_VERSION_MAJOR 0)
SET(AutoFFI_VERSION_MINOR 1)
SET(Boost_USE_STATIC_LIBS OFF)
FIND_PACKAGE(BOOST 1.50 COMPONENTS system filesystem REQUIRED)
FIND_PACKAGE(LLVM 3.9 REQUIRED CONFIG)
ADD_DEFINITIONS(${LLVM_DEFINITIONS})
FIND_PACKAGE(Clang REQUIRED)
CONFIGURE_FILE(
"${PROJECT_SOURCE_DIR}/include/env.h.in"
#"${PROJECT_BINARY_DIR}/include/env.h"
"${PROJECT_SOURCE_DIR}/include/env.h" # added to VCS
)
INCLUDE_DIRECTORIES(
#"${PROJECT_BINARY_DIR}"
"${PROJECT_SOURCE_DIR}/include"
"${CLANG_INCLUDE_DIRS}"
"${Boost_INCLUDE_DIRS}"
)
# The C++ interface
ADD_LIBRARY(autoffiCore src/Type.cpp)
# FIXME: boost package finder seems to be broken
TARGET_LINK_LIBRARIES(autoffiCore boost_filesystem boost_system)
ADD_LIBRARY(autoffiBin src/BinFormat.cpp)
TARGET_LINK_LIBRARIES(autoffiBin autoffiCore)
ADD_LIBRARY(autoffiClang src/ClangEngine.cpp)
llvm_map_components_to_libnames(llvm_libs all)
TARGET_LINK_LIBRARIES(autoffiClang autoffiCore ${llvm_libs} ${CLANG_LIBS})
# The C interface
ADD_LIBRARY(autoffi src/PrettyPrinter.cpp src/libautoffi.cpp)
TARGET_LINK_LIBRARIES(autoffi autoffiBin autoffiClang)
set_target_properties(autoffiCore autoffiBin autoffiClang autoffi PROPERTIES DEFINE_SYMBOL BUILDING_SHARED)
# CLI Tools
ADD_EXECUTABLE(afdump src/dump.cpp src/PrettyPrinter.cpp)
TARGET_LINK_LIBRARIES(afdump autoffiBin)
ADD_EXECUTABLE(afconvert src/convert.cpp)
TARGET_LINK_LIBRARIES(afconvert autoffiBin)
ADD_EXECUTABLE(afcompile src/tooling.cpp src/PrettyPrinter.cpp)
TARGET_LINK_LIBRARIES(afcompile autoffiClang ${CLANG_LIBS} autoffiBin)
# Testing
ENABLE_TESTING()
ADD_TEST(headerASTDump afcompile test/assets/basic.h)
test/CMakeLists.txt
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
project("AutoFFI Tests")
SET(CMAKE_CXX_STANDARD 11)
SET(GTEST_ROOT "${CMAKE_CURRENT_LIST_DIR}/gtest")
find_package(GTest REQUIRED)
INCLUDE_DIRECTORIES(${AUTOFFI_INCLUDE_DIRS} ${GTEST_INCLUDE_DIRS})
ADD_EXECUTABLE(autoffiTest libautoffi.cpp core.cpp formats.cpp clang.cpp)
TARGET_LINK_LIBRARIES(autoffiTest ${GTEST_BOTH_LIBRARIES})
SET_TARGET_PROPERTIES(autoffiTest PROPERTIES OUTPUT_NAME runtests)
You are keeping your main directory and your subdirectory test in completely separated projects. I suppose you are building test by invoking separately cmake and make a second time. No surprise that the include_directories of your main project as no impact on the test project.
You mention the command add_subdirectory, (that should be used to include test in from your main directory) but you are actually not using it...
If you're using include_directories() to add dirs, those should propagate down into targets in the test subdir. Beware though that relative paths are relative to the current source dir:
include_directories: Add include directories to the build.
include_directories([AFTER|BEFORE] [SYSTEM] dir1 dir2 ...)
Add the given directories to those the compiler uses to search for include files. Relative paths are interpreted as relative to the current source directory.
cmake docs