# Beginner: Difficulty understanding CMake impact on include statements

**URL:** https://discourse.cmake.org/t/beginner-difficulty-understanding-cmake-impact-on-include-statements/15404
**Category:** Code
**Tags:** os:linux
**Created:** [December 15, 2025, 8:59pm UTC](https://discourse.cmake.org/t/beginner-difficulty-understanding-cmake-impact-on-include-statements/15404 "2025-12-15T20:59:29Z")
**Posts on this page:** 7
**Page:** 1

<div class="post-metadata">

### Author: ![zlime](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/z/7ba0ec/32.png) [@zlime](https://discourse.cmake.org/u/zlime)
#### Post date: [December 15, 2025, 8:59pm UTC](https://discourse.cmake.org/t/beginner-difficulty-understanding-cmake-impact-on-include-statements/15404/1 "2025-12-15T20:59:29Z")

</div>

Greetings! This is my first post. I am new to writing CMake, and am currently updating a legacy build system. Assume a directory structure like this:

```auto
A
|-CMakeLists.txt
|-B
| |-CMakeLists.txt
| |-some_class_in_B.h
| |-some_class_in_B.cpp
|-C
| |-CMakeLists.txt
| |-some_class_in_C.h
| |-some_class_in_C.cpp

```

`A/CMakeLists.txt` looks something like

```cmake
cmake_minimum_required(VERSION 3.11)
project(A)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/B)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/C)

```

`B/CMakeLists.txt` looks something like

```cmake
project(B)
set(ProjectSources
B.cpp
)
set(ProjectHeaders
B.h
)
add_library(${PROJECT_NAME} SHARED ${ProjectSources} ${ProjectHeaders})
set(TargetLibraries C)
include_directories($ENV{PROJECT_INCLUDE})
target_link_libraries(${PROJECT_NAME} ${TargetLibraries})

```

B’s implementation file and header file both look something like this

```cxx
//header
#IFNDEF SOME_CLASS_IN_B_H
#DEFINE SOME_CLASS_IN_B_H

#include "C/some_class_in_C.h"

class someClassInB
{
...
}
#ENDIF

//implementation
#include "C/some_class_in_C.h"

void someClassInB::method()
{
...
}

```

The `PROJECT_INCLUDE` environment variable is set as the install target for every header throughout the project. I’m unsure if this is best practice or not, but I’d like to move the project towards using the `target_sources` command more than the `include_directories` command. I currently have `target_sources` working for some subdirectories of the project, but they erase the ability to use the `C/some_class_in_C.h` include structure. Instead I’m having to use `#include some_class_in_C.h`, which makes it more vague where the included header is located.  
An example of what the updated CMakeLists look like is like this:

```cmake
project(B)
set(ProjectSources
B.cpp
)
set(ProjectHeaders
B.h
)
add_library("${PROJECT_NAME}" SHARED)
target_sources("${PROJECT_NAME}"
PRIVATE "${ProjectSources}"
PUBLIC
    FILE_SET HEADERS
    BASE_DIRS "${PROJECT_SOURCE_DIR}"
    FILES "${ProjectHeaders}"
)
target_link_libraries("${PROJECT_NAME}" PRIVATE C)

```

But then that requires changes in the C++ from `#include C/some_class_in_C.h` to `#include some_class_in_C.h`. I’d prefer to keep the context of where the included header is coming from, but get build errors when I try building with the previous C++ include statement.

I can’t post the source code for the project itself, but I’ll do my best to clarify any questions anyone has. Essentially my question boils down to "is there a way to preserve the C++ include statements the way they were while also reaping the benefits of the more modern, simplified and robust CMake syntax?

---

<div class="post-metadata">

### Author: ![Andrej730](https://discourse.cmake.org/user_avatar/discourse.cmake.org/andrej730/32/5752_2.png) [@Andrej730](https://discourse.cmake.org/u/Andrej730)
#### Post date: [December 16, 2025, 8:33am UTC](https://discourse.cmake.org/t/beginner-difficulty-understanding-cmake-impact-on-include-statements/15404/2 "2025-12-16T08:33:51Z")

</div>

I don’t fully understand what `PROJECT_INCLUDE` env variable is for in this case, but it sounds odd that user will need to provide some kind of includes directory explictily using env variable.

If you want `#include "C/some_class_in_C.h"` includes, then why not just include `CMAKE_SOURCE_DIR`? Either globally or for specific targets.

---

<div class="post-metadata">

### Author: ![zlime](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/z/7ba0ec/32.png) [@zlime](https://discourse.cmake.org/u/zlime)
#### Post date: [December 16, 2025, 12:49pm UTC](https://discourse.cmake.org/t/beginner-difficulty-understanding-cmake-impact-on-include-statements/15404/3 "2025-12-16T12:49:34Z")

</div>

Essentially what had happened was this was a Makefile system written in a way where a bash script performed the directory recursion and forced Make to build directories in a very specific order. This ill-formed Make system was then more or less directly translated into CMake about a decade ago, with nearly a hundred environment variables being made necessary to run it. It’s at a point now where users are writing actual paragraphs of commands to build the project instead because my colleagues never stopped to think that maybe there was something wrong with how the were using the tools at their disposal.

Essentially the PROJECT\_INCLUDE becomes a search directory for each target built before the next, so any file including the headers of another will search that. I don’t think it’s a great idea either. I’m interested in the CMAKE\_SOURCE\_DIR though, but am confused. Does the CMAKE\_SOURCE\_DIR then require me to specify the directory for every include (Even within headers and implementations in B/ would I have to specify B/some\_class\_in\_B.h)?

---

<div class="post-metadata">

### Author: ![Andrej730](https://discourse.cmake.org/user_avatar/discourse.cmake.org/andrej730/32/5752_2.png) [@Andrej730](https://discourse.cmake.org/u/Andrej730)
#### Post date: [December 16, 2025, 3:02pm UTC](https://discourse.cmake.org/t/beginner-difficulty-understanding-cmake-impact-on-include-statements/15404/4 "2025-12-16T15:02:11Z")

</div>

> [@zlime](#):
>
> Essentially the PROJECT\_INCLUDE becomes a search directory for each target built before the next, so any file including the headers of another will search that. I don’t think it’s a great idea either. I’m interested in the CMAKE\_SOURCE\_DIR though, but am confused. Does the CMAKE\_SOURCE\_DIR then require me to specify the directory for every include (Even within headers and implementations in B/ would I have to specify B/some\_class\_in\_B.h)?

If you just include it as-is - yes, it would require to use `B/some_class_in_B.h` even for .cpp files inside `B` folder.

If you want files inside `B` to refer to `some_class_in_B.h` without `B` prefix, you can also add as a private include directory`CMAKE_CURRENT_SOURCE_DIR` - it’s the same as `CMAKE_SOURCE_DIR`, but for the current subdirectory, e.g. for `B/CMakeLists.txt` it’s `B/`, for `C/CMakelists.txt` it’s `C`, etc.

Though it may get confusing - e.g. if `C` includes `B/some_class_in_B.h`, then `some_class_in_B.h` has to refer to other headers in `B` by using`B/` prefix to make them includable for `C`.

---

<div class="post-metadata">

### Author: ![zlime](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/z/7ba0ec/32.png) [@zlime](https://discourse.cmake.org/u/zlime)
#### Post date: [December 16, 2025, 5:15pm UTC](https://discourse.cmake.org/t/beginner-difficulty-understanding-cmake-impact-on-include-statements/15404/5 "2025-12-16T17:15:10Z")

</div>

I guess that makes sense. Seems like more hassle than it’s worth. Is there a best practice regarding the project directory structure that addresses this issue?

---

<div class="post-metadata">

### Author: ![Andrej730](https://discourse.cmake.org/user_avatar/discourse.cmake.org/andrej730/32/5752_2.png) [@Andrej730](https://discourse.cmake.org/u/Andrej730)
#### Post date: [December 17, 2025, 5:44am UTC](https://discourse.cmake.org/t/beginner-difficulty-understanding-cmake-impact-on-include-statements/15404/6 "2025-12-17T05:44:22Z")

</div>

There’s probably not a single solution for this - it depends on what those subproejcts are, but as a clue - for consistency maybe it’s a good idea to keep public headers include statements similar to their installation layout.

E.g. if you plan to install them directly to the root folder as `install/include/header.h` - then include them using `#include "header.h` in the main code too, or if installation will have subfolders (e.g. `install/include/A/header.h`) then to use `#include "A/header.h"`.

But it’s more of a matter of a code style, I guess.

---

<div class="post-metadata">

### Author: ![Andrew](https://discourse.cmake.org/user_avatar/discourse.cmake.org/andrew/32/2478_2.png) [@Andrew](https://discourse.cmake.org/u/Andrew)
#### Post date: [April 29, 2026, 2:29am UTC](https://discourse.cmake.org/t/beginner-difficulty-understanding-cmake-impact-on-include-statements/15404/7 "2026-04-29T02:29:05Z")

</div>

I’d counsel against using `CMAKE_CURRENT_SOURCE_DIR` unless you really need to. It’s really only useful where the CMake code you are processing is in a location unrelated to the `CMakeLists.txt` that you are working with.

For example, the `CMakeLists.txt` in A doesn’t need to `add_subdirectory( ${CMAKE_CURRENT_SOURCE_DIR}/B )`; it can just `add_subdirectory( B )`.

I’m going to assume that:

1. `some_class_in_B.h` and `some_class_in_C.h` are headers you want to share with other libraries.
2. you want B and C to be treated as independent code chunks, not just part of A.

The cleanest way I have found to do this is to structure the internals of B and C as distinct projects:

```
B
|- CMakeLists.txt
|- include/B/some_class_in_B.h
|- src/some_class_in_B.cpp
|- src/internal_B_header.h

```

Then your B/CMakeLists.txt is:

```
project( B )
  # This "B" is distinct from all the other "B" references in this example,
  # unless you explicitly choose to make it the same,
  # such as by using `${PROJECT_NAME}` rather than "B" below.
add_library( B )
target_sources( B
  PRIVATE
    src/some_class_in_B.cpp
    src/internal_B_header.h
      # only matters for IDEs
  PUBLIC
    FILE_SET HEADERS
      # only matters for IDEs and installing library
    BASE_DIRS include
    FILES
      include/B/some_class_in_B.h
)
target_include_directories( B PUBLIC include )
target_include_directories( B PRIVATE src )
  # ensure local files can find local headers
target_link_libraries( B PRIVATE C )

```

This keeps everything cleanly separated.

Other options:

If you move `B/some_class_in_B.h` to `B/B/some_class_in_B.h` you can then just set `target_include_directories( B PUBLIC . )`. This works but means your search path now includes the entire B directory not just the intended files. If another file in B matches someone else’s private search path this could cause problems.

Finally, you can drop all the prefixes and just `target_include_directories( B PUBLIC . )`.

You could even leave the files as-is `target_include_directories( B PUBLIC .. )`. This will let you refer to `B/header.h` and `C/header.h` but is ugly and will break if you move / rename / install the B or C source directories.

The important idea is that `target_include_directories` sets search path roots relative to the current CMakeLists.txt. These paths are transitively added to anything that depends on the given library.

This remains true regardless of where `B/CMakeLists.txt` is located. For example, consider:

```
add_subdirectory( /home/me/other/stuff/B B )
target_link_library( my_lib PUBLIC B )

```

and

```
add_library( B )
target_include_directories( B PUBLIC include )

```

The search paths for `my_lib` will include `-I /home/me/other/stuff/B/include`, as will any code that depends on `my_lib`.
