cmaketargetcmake-language

CMake target_include_directories relative to parent directory?


How do I add lib0 path to lib1 such that from header1.h I can #include <header0.h>?

|   CMakeLists.txt
+---lib0
|       CMakeLists.txt
|       header0.c
|       header0.h
+---lib1
|       CMakeLists.txt
|       header1.c
|       header1.h
\---main
        CMakeLists.txt
        main.c

CMakeLists.txt

cmake_minimum_required(VERSION 3.19)
project(c_multilib C)

foreach (sub "lib0" "lib1" "main")
    add_subdirectory("${sub}")
endforeach ()

lib0/CMakeLists.txt

add_library(lib0 "header0.h" "header0.c")

set_target_properties(lib0 PROPERTIES LINKER_LANGUAGE C)

include(GenerateExportHeader)
set(_generated_export_header "${CMAKE_CURRENT_SOURCE_DIR}/lib0_export.h")
generate_export_header(lib0 EXPORT_FILE_NAME "${_generated_export_header}")

target_include_directories(lib0
        INTERFACE
        "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>"
        "$<INSTALL_INTERFACE:include>"
)

install(FILES "header0.h" "${_generated_export_header}" DESTINATION "include")

lib0/header0.h

#ifndef C_MULTILIB_HEADER0_H
#define C_MULTILIB_HEADER0_H

#include "lib0_export.h"

int LIB0_EXPORT sum(int, int);

#endif /* C_MULTILIB_HEADER0_H */

lib0/header0.c

#include "header0.h"

int sum(int a, int b) {return a+b;}

lib1/CMakeLists.txt

add_library(lib1 "header1.h" "header1.c")

set_target_properties(lib1 PROPERTIES LINKER_LANGUAGE C)

include(GenerateExportHeader)
set(_generated_export_header "${CMAKE_CURRENT_SOURCE_DIR}/lib1_export.h")
generate_export_header(lib1 EXPORT_FILE_NAME "${_generated_export_header}")

target_include_directories(lib1
        INTERFACE
        "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>"
        "$<BUILD_INTERFACE:${${PROJECT_NAME}_SOURCE_DIR}/lib0>"
        "$<INSTALL_INTERFACE:include>"
)

install(FILES "header1.h" "${_generated_export_header}" DESTINATION "include")

lib1/header1.h

#ifndef C_MULTILIB_HEADER1_H
#define C_MULTILIB_HEADER1_H

#include <header0.h>
#include "lib1_export.h"

int LIB1_EXPORT add1(int);

#endif /* C_MULTILIB_HEADER1_H */

lib1/header1.c

#include <header1.h>

int add1(int a) {return sum(a, 1);}

main/CMakeLists.txt

add_executable("main" "main.c")

target_link_libraries("main" "lib0" "lib1")
target_include_directories(
        "main"
        INTERFACE
        "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>"
        "$<BUILD_INTERFACE:${${PROJECT_NAME}_SOURCE_DIR}/lib0>"
        "$<BUILD_INTERFACE:${${PROJECT_NAME}_SOURCE_DIR}/lib1>"
        "$<INSTALL_INTERFACE:include>"
)

main/main.c

#include <header0.h>
#include <header1.h>
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    printf("sum(1,5) =\t%d\n"
                   "add1(5) =\t%d\n", sum(1,5), add1(6));
    return EXIT_SUCCESS;
}

Attempts:

# lib1/CMakeLists.txt

target_include_directories(
    lib1
    INTERFACE
    "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>"
    #"$<BUILD_INTERFACE:lib0>"
    #"$<BUILD_INTERFACE:../../lib0>"
    #"$<BUILD_INTERFACE:${${PROJECT_NAME}_SOURCE_DIR}/src/lib0>"
    "$<INSTALL_INTERFACE:include>"
)

install(FILES "header1.h" DESTINATION "include")

Errors range from:

fatal error C1083: Cannot open include file: 'header0.h': No such file or directory

to:

Target contains relative path in its INTERFACE_INCLUDE_DIRECTORIES

Also get_target_property(LIB0_INCLUDES "lib0" INCLUDE_DIRECTORIES) gave:

get_target_property() called with non-existent target "lib0".


Solution

  • The src/lib0/CMakeLists.txt should look something like this

    add_library(lib0 INTERFACE)
    target_include_directories(lib0 INTERFACE ${CMAKE_CURRENT_LIST_DIR})
    

    The word INTERFACE tells Cmake that there is nothing to compile.

    The src/lib1/CMakeLists.txt should look something like this

    add_library(lib1 INTERFACE)
    target_include_directories(lib1 INTERFACE ${CMAKE_CURRENT_LIST_DIR})
    target_link_libraries(lib1 INTERFACE lib0)
    

    The src/CMakeLists.txt should look something like

    add_subdirectory(lib0)
    add_subdirectory(lib1)
    # Assuming that your src target is called my_proj: 
    target_link_libraries(my_proj PRIVATE lib0 lib1)
    

    EDIT:

    I can confirm that the code that you provide compiles if you change all of the includes from lib1_export... to header1 and modify the following 3 CMakeLists.txt files:

    lib0/CMakeLists.txt:

    add_library(lib0 header0.c)
    target_include_directories(lib0 PUBLIC ${CMAKE_CURRENT_LIST_DIR})
    

    lib1/CMakeLists.txt:

    add_library(lib1 header1.c)
    target_include_directories(lib1 PUBLIC ${CMAKE_CURRENT_LIST_DIR})
    target_link_libraries(lib1 PUBLIC lib0)
    

    main/CMakeLists.txt:

    add_executable(main main.c)
    target_link_libraries(main PRIVATE lib0 lib1)
    

    It is not clear to me what you are trying to achieve with the generator expressions (the angle brackets). If you explain what you are trying to accomplish, then we can suggest the proper fixes.