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 ofadd_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 fromb.cpp
-b.h
declaration-definition pairs. I never use../
orsubdir/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 fromsrc
sincesrc
contains only private ones.