Beginner: Difficulty understanding CMake impact on include statements

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:

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_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

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

//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:

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?

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.

1 Like

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)?

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 directoryCMAKE_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 usingB/ prefix to make them includable for C.

1 Like

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?

1 Like