2

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".

A T
  • 13,008
  • 21
  • 97
  • 158
  • You haven't provided quite enough information to show what is going wrong. Please provide a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) per the Stack Overflow guidelines. Specifically, please show the rest of the `lib1/CMakeLists.txt` file, as well as the contents of the other CMake files to provide context for how you set up targets lib0 and lib1, and in what order they are set up... – Kevin Jul 20 '21 at 11:49
  • @squareskittles OK I've added a full example. It's a little verbose :( – A T Jul 20 '21 at 12:46
  • 1
    Thanks, also please provide the full error you are receiving for the provided code, including surrounding logs and compilation messages to provide context for **where** this error is occurring in the build chain. – Kevin Jul 20 '21 at 13:37
  • It is actually not clear what do you want to **achieve** by your code. E.g. assuming `header0.c` **includes** the header `header0.h`, the directory with that header should be added with PRIVATE or PUBLIC keyword, but not with the INTERFACE one. (Are you aware about meaning of keywords PUBLIC/PRIVATE/INTERFACE in CMake?). With given assumption, the code for `lib0` is simply wrong, and you need to fix it before writing the code for `lib1`, which uses `lib0`. – Tsyvarev Jul 20 '21 at 18:07
  • Not sure why you would use `INTERFACE` include dirs on your executable; you cannot link an executable to another target and the whole point of `INTERFACE...` properties is to be used by linking targets. That's why my convention is to simply use `PRIVATE` visibility when setting properties of an executable; this also means you don't need to bother with using generator expressions to distinguish build/install interface; You just don't provide any headers for the executable; all is already part of your binaries. – fabian Jul 20 '21 at 18:08
  • The `target_include_directories` can be removed from `main/CMakeLists.txt` without changing anything about the includes available to `main` btw, if this wasn't expressed clearly enough in my previous comment. Also the `lib` prefix could easily confuse cmake; iirc there are be some cases where cmake automatically removes the lib prefix, but I'm not sure; I just resist my non-existent urge to prefix my library names with `lib` – fabian Jul 20 '21 at 18:12
  • I am still getting the same error. On the naming convention this is just for stackoverflow. How do I make the headers from lib0 available in lib1, and the headers from both lib0 and lib1 available to main? - All using angle brackets not relative paths? – A T Jul 21 '21 at 03:17
  • I have marked your question as a duplicate for the [question](https://stackoverflow.com/questions/49262364/inherit-include-directories-from-used-library-in-cmake), which is about general problem "How do I add `lib0` include path to `lib1`". If you think that your code follows some approach, and the problem is not in the approach but with the code itself, then describe that approach in the question post. In the current form of the question, I see only attempts to add random include directories via `target_include_directories`. – Tsyvarev Jul 21 '21 at 08:26

1 Answers1

0

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.

bremen_matt
  • 6,902
  • 7
  • 42
  • 90
  • I've updated the question. But the original question showed the same solution. This suggested solution doesn't work as I was already doing this. It doesn't work with angle brackets. – A T Jul 20 '21 at 12:50
  • So you want to have the generator expressions (the angle brackets)? Or you just inserted that to try to get it to compile? – bremen_matt Jul 20 '21 at 14:05
  • Why do you create `lib0` as INTERFACE library? The source file `header0.c` is definitely a part of that library, so the library is not a header-only one, as INTERFACE keyword suggests. – Tsyvarev Jul 20 '21 at 18:08
  • Removing all the `INTERFACE` lines and replacing it like you say leaves me with a `c-multilib\main\main.c(1): fatal error C1083: Cannot open include file: 'header0.h': No such file or directory` – A T Jul 21 '21 at 03:16
  • Actually going through with a finetoothed comb and it worked. I think it was the `add_library(lib1 "header1.c")` vs `add_library(lib1 "header1.c" "header1.h")` – A T Jul 21 '21 at 04:34
  • But in a larger project it leaves me with many `Target "{}" INTERFACE_INCLUDE_DIRECTORIES property contains path:` errors. EDIT: `"$" ` resolved it. Added `"$"` also. – A T Jul 21 '21 at 04:54
  • @Tsyvarev: this question was modified. The original version listed headers only, and was changed to include .c files. – bremen_matt Jul 21 '21 at 07:36