Home > Back-end >  How to set CMake to have root directory in include paths
How to set CMake to have root directory in include paths

Time:11-21

I'm a little new here, this is my first post. I have a c project in CLion that is structured like so:

project-root/
├─ cmake-build-debug/
├─ controller/
├─ model/
│  ├─ foo/
│  │  ├─ a.h *
│  │  ├─ a.cpp *
│  ├─ bar/
│  │  ├─ b.h *
│  │  ├─ b.cpp *
├─ view/
│  ├─ c.h *
│  ├─ c.cpp *
CMakeLists.txt
main.cpp *

I've starred the actual cpp and h files just for visual purposes. In a.h, suppose I'd like to include b.h. The way I'd like to do this in a.h is by doing:

#include project-root/model/bar/b.h

Similarly, I'd like to use this pattern for all includes. For example, if I wanted to include a.h in c.h, in c.h I'd do:

#include project-root/model/foo/a.h

And, just one more example, even if I was in main.cpp and I'd like to include c.h, I'd do:

#include project-root/view/c.h

I tried doing the following in my CMakeLists.txt file:

cmake_minimum_required(VERSION 3.22)
project(project-root)

set(CMAKE_CXX_STANDARD 17) // this ".." seems incorrect

include_directories(..)
add_executable(project-root main.cpp <the rest of all the .h and .cpp files>)

And actually, this works! However, I think it seems a little weird to have include_directories(..), since there's a relative path above the project root. That doesn't seem correct to me.

I originally thought include_directory(.) would work, but unfortunately, no dice. When I try to do #include project-root/model/bar/b.h in a.h, I get an error saying project-root/model/bar/b.h not found

CodePudding user response:

You can use cmake builtin variables to construct absolute paths, e.g. PROJECT_SOURCE_DIR.

include_directories(${PROJECT_SOURCE_DIR})

More fine-grained control over includes is possible using target_include_directories.

CodePudding user response:

I would also advise against .. since you are depending on name of a folder that you might not necessarily control. Well, it depends on how you plan to distribute the code, for example if you use git, then project-root folder itself should be inside the repository, not the other way around.

The include paths have little to do with CMake, it will just pass them (their absolute versions) to the compiler.

In C , you have two "types" of includes - #include "foo.h" or #include <foo.h>. Both are implementation-specific, i.e. the compiler dictates how they are interpreted. But one convention is to use <> for public headers in the include path and use "" for relative includes or private headers.

Libraries might want to distinguish between public and private headers. Public ones are meant for the user of the library, private ones are internal. But this is not universally adhered to, sometimes the are internal directories...

Personally, I like to use the following structure for my projects:

. // root of a git repository
├── include/
│   └── project-name/
│       ├── model/
│       │   └── foo/
│       │       └── a.h
│       └── view/
│           └── c.h
├── src/
│   ├── model/
│   │   ├── foo/
│   │   │   └── a.cpp
│   │   └── bar/
│   │       ├── b.h
│   │       └── b.cpp
│   ├── view/
│   │   └── c.cpp
│   └── main.cpp
└── CMakeLists.txt

with CMakeLists.txt containing something like:

//Global
cmake_minimum_required(VERSION 3.22)
project(project-name)
//Repository-wide options
set(CMAKE_CXX_STANDARD 17)

//Local
add_executable(project-root)

target_include_directories(project-name PUBLIC include)
target_include_directories(project-name PRIVATE src)

// Project-specific options
// Assumes gcc/clang toolchain as an example.
target_compile_options(project-name -Wall -Wextra -Werror -pedantic)

target_sources(project-name 
  PUBLIC 
  include/project-name/model/foo/a.h
  include/project-name/view/c.h
  PRIVATE
  src/model/foo/b.h
  src/model/foo/b.cpp
  src/view/c.cpp
  src/main.cpp
)
  • Modern way is to use target_ variants instead of global ones.

  • If the repository contained more than one project, have one //Global CMakeLists.txt with the three lines above - with global options - and bunch of add_subdirectory calls which each contain //Local CMakeLists.txt for the submodule - executable/library.

  • a.h,c.h are marked as public, one should always include them with #include <project-name/model/foo/a.h> from anywhere.

  • I marked b.h as private just for demonstration purposes, there are two ways how to include it. b.cpp could use #include "b.h" or #include "model/foo/b.cpp" (no <> although it would work). I try to use the latter always apart from b.cpp-b.h declaration-definition pairs. I never use ../ or subdir/b.h since it makes a headache to move anything. This way, each header is included exactly the same way from anywhere - easily searchable and replaceable, b.{cpp,h} move together so the relative path is OK there.

  • For an executable, I usually just go with all public headers.

  • If this were a library, it can easily be converted to a e.g. a debian package since one can cleanly install the whole include directory to /usr/include without fear of clashes or hassle to extract the headers from src since src contains only private ones.

  • Related