0

I am completely new to CMake and never used it, always relying on VS auto-managing system like a pleb. I'm working on a school assignment where I'm making a game, and I'm using CLion since the template has been made for it.

I've had to add every new file I create to the CMake 'add_executable' keyword for it to compile and the list is getting rather long.

# project
add_executable(project
        src/project/main.cpp
        src/project/Scenes/SceneMenu/SceneMenu.cpp
        src/project/Scenes/SceneMenu/SceneMenu.h
        src/project/Objects/Object.h
        src/project/Camera/Camera.h
        src/project/Camera/CameraGame/CameraGame.cpp
        src/project/Camera/CameraGame/CameraGame.h
        src/project/Scenes/SceneGame/SceneGame.cpp
        src/project/Scenes/SceneGame/SceneGame.h
        src/project/Scenes/Scene.h
        src/project/Scenes/SceneController.h
        src/project/Objects/Menu/Ball/Ball.h
        src/project/Objects/Menu/Ball/Ball.cpp
        src/project/Objects/Game/Church/Church.h
        src/project/Objects/Game/Church/Church.cpp
        src/project/Objects/Game/Leg/Leg.h
        src/project/Objects/Game/Leg/Leg.cpp
        src/project/Objects/Game/Ground/Ground.h
        src/project/Objects/Game/Ground/Ground.cpp
        src/project/Objects/Axial/Axis.h
        src/project/Objects/Axial/Axis.cpp
        src/project/Objects/Game/Leg/LegLower.h
        src/project/Objects/Game/Leg/LegLower.cpp
        src/project/Objects/Game/Skybox/Skybox.cpp
        src/project/Objects/Game/Skybox/Skybox.h
        src/project/Objects/Game/Mountain/Mountain.cpp
        src/project/Objects/Game/Mountain/Mountain.h
        src/project/Objects/Game/Mountain/MountainSpawner.h
        src/project/resources.h
        src/project/Animations/Bezierpath.h
        src/project/Camera/CutsceneCamera/CutsceneCamera.cpp
        src/project/Camera/CutsceneCamera/CutsceneCamera.h
        src/project/Scenes/SceneCut/SceneCut.cpp
        src/project/Scenes/SceneCut/SceneCut.h)

target_link_libraries(project ppgso shaders)
install(TARGETS project DESTINATION .)

file(COPY "data/" DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
install(DIRECTORY data/ DESTINATION .)

Is there any way I can improve this?.. Perhaps a way I don't have to add every new source file I create, or is this the only way?

It's getting annoying to do it this way since it happened to me several times during refactoring or re-structuring that it wouldn't compile due to the wrong order of some files in CMakeLists.txt, or some that didn't rename automatically, or even got removed

Jack Avante
  • 1,405
  • 1
  • 15
  • 32
  • Do you want CMake to collect **all sources in a directory** (or directories)? If yes, then see that question: https://stackoverflow.com/questions/3201154/automatically-add-all-files-in-a-folder-to-a-target-using-cmake. Otherwise it is unclear what do you want to improve in your long list of source files. – Tsyvarev Dec 07 '20 at 17:23
  • That's exactly what I was looking for. GLOB_RECURSE is my savior. Thank you, I had trouble looking this up – Jack Avante Dec 07 '20 at 17:30
  • @JackAvante **DO NOT GLOB FOR SOURCE FILES.** [The documentation even tells you not to do it.](https://cmake.org/cmake/help/v3.16/command/file.html?highlight=glob_recurse#glob-recurse) – Alex Reinking Dec 08 '20 at 01:20
  • Verbatim: "We do not recommend using GLOB to collect a list of source files from your source tree. If no CMakeLists.txt file changes when a source is added or removed then the generated build system cannot know when to ask CMake to regenerate. The `CONFIGURE_DEPENDS` flag may not work reliably on all generators, or if a new generator is added in the future that cannot support it, projects using it will be stuck. Even if `CONFIGURE_DEPENDS` works reliably, there is still a cost to perform the check on every rebuild." – Alex Reinking Dec 08 '20 at 01:21

1 Answers1

1

You have to specify a list of source files one way or another.

Best way to do this in my experience is to split your single binary on smaller parts, where each part has its own namespace or directory and then combine them together.

Few tips:

  • put CMakeLists.txt in every directory with sources, then use add_subdirectory(dir) in parent CMakeLists.txt.
  • use OBJECT libraries for "parts" of your main library/binary.
  • put all h/hpp files in include dir (with arbitrary dir structure)
  • put all cpp files in src dir (with a dir structure mirroring include)
  • never add h/hpp files to add_{executable,library}, it has no effect.
  • install only include dir

Your project can look like the following:

.
├── CMakeLists.txt
├── include
│   └── projname
│       ├── myexe.hpp
│       └── mylib
│           └── mylib.hpp
└── src
    ├── CMakeLists.txt
    ├── myexe.cpp
    └── mylib
        ├── CMakeLists.txt
        └── mylib.cpp

Where root CMakeLists.txt has:

include_directories(include)
add_subdirectory(src)

src/mylib/CMakeLists.txt has:

add_library(mylib OBJECT
  mylib.cpp
  )

Then src/CMakeLists.txt has:

add_subdirectory(mylib)

add_executable(myexe 
  # this adds all cpps from mylib as if you enumerate them here
  $<TARGET_OBJECTS:mylib>
  myexe.cpp
  )

Then, to include your headers you do this:

#include <projname/mylib/mylib.hpp>
#include <projname/myexe.hpp>

P.S. I do C++/CMake professionally and this is the structure I ended up after few years. You can see repo which implements this here.

warchantua
  • 1,154
  • 1
  • 10
  • 24
  • My understanding is that adding headers to the sources property of a target does have an effect. It matters when generating Xcode/VS projects, which won't display those files in their GUIs otherwise. – Alex Reinking Dec 08 '20 at 01:17
  • Also, unless the `OBJECT` library is shared between multiple targets, I would prefer `target_sources` since it is less complicated. But OTOH, that won't work if one of the sources is generated. Ah, CMake. – Alex Reinking Dec 08 '20 at 01:51
  • @AlexReinking OBJECT libraries have an advantage - you can put dedicated compilation/linker flags, includes, etc via target_include_directories/target_compile_options/... Can be very useful in big projects. – warchantua Dec 08 '20 at 10:20
  • At that point I start working things into static libraries. – Alex Reinking Dec 08 '20 at 13:04
  • @AlexReinking you may want to avoid creating too many static libraries in case if you want to add `install` step for your main library. In this case, you'd need to add `install` step for *every* static library, and install all of them when you install main one. With object libraries it works better - you specify exactly single `install` step (for main lib) – warchantua Dec 11 '20 at 20:21