Search code examples
c++cmakeg++c++20msys2

CMake: Some include directories defined in source files, but undefined in headers for nested target


Given the following project structure:

.
└── library/
    ├── CMakeLists.txt
    ├── include/
    │   └── class1.hpp
    ├── src/
    │   └── class1.cpp
    ├── build/
    │   ├── _deps/
    │   │   └── fmt-build
    │   └── sample/
    │       └── app_using_library.exe
    └── sample/
        ├── CMakeLists.txt
        └── main.cpp

CMakeLists.txt of library:

project(library)

include(FetchContent)

FetchContent_Declare(
        fmt
        URL https://github.com/fmtlib/fmt/releases/download/9.1.0/fmt-9.1.0.zip
)
FetchContent_MakeAvailable(fmt)

add_library(library STATIC 
  "src/class1.cpp" 
  "include/class1.hpp" 
target_include_directories(library PUBLIC include)
target_link_libraries(library PRIVATE fmt::fmt)

CMakeLists.txt of app_using_library:

project(app_using_library)
add_executable(app_using_library main.cpp)
target_link_libraries(app_using_library PRIVATE library)

class1.hpp:

#include "fmt/core.h" // cannot open source file "fmt/core.h"

class1.cpp:

#include "fmt/core.h" // works fine

Is there a possible cause or explanation for why the fmt headers are includable in class1.cpp, but not in class1.hpp when building both library and app_using_library? The include works correctly in the header when just building library

The compiler couldn't find the fmt headers in "class1.hpp", but it could find them in "class1.cpp".

The full error:

  FAILED: sample/CMakeFiles/app_using_library.dir/main.cpp.obj 
  C:\msys64\mingw64\bin\g++.exe  -ID:/Programming/cpp/library/include -g -std=gnu++20 -MD -MT sample/CMakeFiles/app_using_library.dir/main.cpp.obj -MF sample\CMakeFiles\app_using_library.dir\main.cpp.obj.d -o sample/CMakeFiles/app_using_library.dir/main.cpp.obj -c D:/Programming/cpp/library/sample/main.cpp
  In file included from D:/Programming/cpp/library/sample/main.cpp:2:
  D:/Programming/cpp/library/include/class1.hpp:4:10: fatal error: fmt/core.h: No such file or directory
      4 | #include "fmt/core.h"
        |          ^~~~~~~~~~~~
  compilation terminated.

Solution

  • Based on the answer from Tsyarev in the comments:

    Such includes should only be done in private headers, i.e. headers not included in the INCLUDE_DIRECTORIES for that target. This prevents any executable target using library from also having to link to the library included in public headers.

    Tsyarev's answers:

    There is a notion about library's public header: that header is intended to be available to the consumers of the library. E.g., class1.hpp is public header for your library. For make a header available to the consumers, the header should be located in the PUBLIC include directory of your library. Any header included by a public header is public by yourself. E.g., since class1.hpp is a public header of the library and includes fmt/core.h, then fmt/core.h is a public header for your library too. And you need to make this headers accessible via PUBLIC include directory.

    If you use fmt/core.h only in the private headers of your library, then your current code should work: library is PRIVATE linked with fmt::fmt, app_using_library is linked with library.