1

I have a project were I use CMake with a structure like this:

CMakeLists.txt
├── build
├── include
│   ├── AudioDevice.h
│   ├── Game.h
│   ├── game_objects
│   │   ├── Ball.h
│   │   ├── Brick.h
│   │   ├── GameObject.h
│   │   ├── IndestructibleBrick.h
│   │   ├── MoveableGameObject.h
│   │   ├── Physics.h
│   │   ├── Platform.h
│   │   └── Wall.h
│   ├── GameParameter.h
│   ├── InputHandler.h
│   ├── Level.h
│   ├── Renderer.h
│   └── SDL_RAII.h
├── src
│   ├── AudioDevice.cpp
│   ├── Game.cpp
│   ├── game_objects
│   │   ├── Ball.cpp
│   │   ├── Brick.cpp
│   │   ├── GameObject.cpp
│   │   ├── MoveableGameObject.cpp
│   │   ├── Physics.cpp
│   │   └── Platform.cpp
│   ├── GameParameter.cpp
│   ├── InputHandler.cpp
│   ├── Level.cpp
│   ├── main.cpp
│   ├── Renderer.cpp
│   └── SDL_RAII.cpp
├── test
│   ├── game_objects
│   │   ├── Ball_test.cpp
│   │   ├── Brick_test.cpp
│   │   ├── GameObject_test.cpp
│   │   ├── MoveableGameObject_test.cpp
│   │   ├── Physics_test.cpp
│   │   └── Platform_test.cpp
│   └── Level_test.cpp

Now I want to improve how the Project is organized with CMake. Currently I only have one CMakeLists.txt in the root directory. Good practice should be to have it in each folder.

My CMakeLists.txt in the root directory currently looks like this:

cmake_minimum_required(VERSION 3.7)

add_definitions(-std=c++17)

set(CXX_FLAGS "-Wall")
set(CMAKE_CXX_FLAGS, "${CXX_FLAGS}")

set(CMAKE_CXX_CLANG_TIDY clang-tidy -checks=-*,readability-*)

project(bricks)

list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)

set(LIBRARY_OUTPUT_PATH "${CMAKE_SOURCE_DIR}/lib")

#set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/")

find_package(SDL2 REQUIRED)
find_package(SDL2_mixer REQUIRED)

include_directories(
    ${SDL2_INCLUDE_DIRS} 
    ${SDL2_mixer_INCLUDE_DIRS} 
    include 
    include/game_objects
    include/types
    include/utility 
)

add_subdirectory(thirdparty/googletest)

add_executable(bricks 
    src/game_objects/Ball.cpp
    src/game_objects/Brick.cpp
    src/game_objects/GameObject.cpp
    src/game_objects/MoveableGameObject.cpp
    src/game_objects/Physics.cpp
    src/game_objects/Platform.cpp

    src/AudioDevice.cpp
    src/Game.cpp
    src/GameParameter.cpp
    src/InputHandler.cpp
    src/Level.cpp
    src/main.cpp
    src/Renderer.cpp
    src/SDL_RAII.cpp
)

target_link_libraries(
    bricks 
    SDL2::Main
    SDL2::Mixer
)

add_executable(test 
    test/game_objects/Ball_test.cpp
    src/game_objects/Ball.cpp
    test/game_objects/Brick_test.cpp
    src/game_objects/Brick.cpp
    test/game_objects/GameObject_test.cpp
    src/game_objects/GameObject.cpp
    test/game_objects/MoveableGameObject_test.cpp
    src/game_objects/MoveableGameObject.cpp
    test/game_objects/Physics_test.cpp
    src/game_objects/Physics.cpp
    test/game_objects/Platform_test.cpp
    src/game_objects/Platform.cpp

# needs to be added so linker works with level
    src/GameParameter.cpp

    test/Level_test.cpp
    src/Level.cpp
)

target_link_libraries(test 
    gtest_main 
)

Now I thought as a first step, it would be good to put the stuff from the folder src in its own CMakeLists.txt.

So my root file became this:

cmake_minimum_required(VERSION 3.7)

add_definitions(-std=c++17)

set(CXX_FLAGS "-Wall")
set(CMAKE_CXX_FLAGS, "${CXX_FLAGS}")

set(CMAKE_CXX_CLANG_TIDY clang-tidy -checks=-*,readability-* -fix)

project(bricks)

list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)

set(LIBRARY_OUTPUT_PATH "${CMAKE_SOURCE_DIR}/lib")

#set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/")

find_package(SDL2 REQUIRED)
find_package(SDL2_mixer REQUIRED)

add_subdirectory(src)

add_subdirectory(thirdparty/googletest)

add_executable(test 
    test/game_objects/Ball_test.cpp
    src/game_objects/Ball.cpp
    test/game_objects/Brick_test.cpp
    src/game_objects/Brick.cpp
    test/game_objects/GameObject_test.cpp
    src/game_objects/GameObject.cpp
    test/game_objects/MoveableGameObject_test.cpp
    src/game_objects/MoveableGameObject.cpp
    test/game_objects/Physics_test.cpp
    src/game_objects/Physics.cpp
    test/game_objects/Platform_test.cpp
    src/game_objects/Platform.cpp

    src/GameParameter.cpp

    test/Level_test.cpp
    src/Level.cpp
)

target_link_libraries(test 
    gtest_main 
)

And I created this file in src:

include_directories(
    ${SDL2_INCLUDE_DIRS} 
    ${SDL2_mixer_INCLUDE_DIRS} 
    ../include 
    ../include/game_objects
    ../include/types
    ../include/utility 
)

add_executable(bricks 
    game_objects/Ball.cpp
    game_objects/Brick.cpp
    game_objects/GameObject.cpp
    game_objects/MoveableGameObject.cpp
    game_objects/Physics.cpp
    game_objects/Platform.cpp


    AudioDevice.cpp
    Game.cpp
    GameParameter.cpp
    InputHandler.cpp
    Level.cpp
    main.cpp
    Renderer.cpp
    SDL_RAII.cpp
)

target_link_libraries(
    bricks 
    SDL2::Main
    SDL2::Mixer
)

Now when I run CMake it does not work. I get complains in Ball.cpp and the other files that it can't find the header files. I thought I added them with include_directories.

I really cannot find out whats going on here. I already googled myself for the issue but cannot find the solution.

I would appreciate very much if someone could give me a hint how to restructure this well.

Kevin
  • 16,549
  • 8
  • 60
  • 74
Sandro4912
  • 313
  • 1
  • 9
  • 29
  • what is the exact error you get when compiling Ball.cpp and what is the full commandline used? (build with `make VERBOSE=1`) – Botje Jun 03 '20 at 14:45
  • Does this answer your question? [Cmake include\_directories()](https://stackoverflow.com/questions/19981534/cmake-include-directories) Using the `include_directories()` command applies the include directories to the *current* directory, and those below it. So, the source files in your root CMakeLists.txt file would have **no knowledge** of your header files. You should move this call to your *top-level* CMake file. Even better, you should consider ditching `include_directories()` in favor of `target_include_directories()`, which will apply include directories on a per-target basis. – Kevin Jun 03 '20 at 14:47
  • I already checked that link before but I don't understand exactly what to do. Can you give an example with my structure. Also I don't get whats better with `target_include_directories` – Sandro4912 Jun 03 '20 at 15:21

1 Answers1

2

The include_directories() command applies the listed include directories to targets in the current CMake file, and those in child directories. So, the include_directories() command won't apply the include directories to your test target in the top-level CMake file. Therefore, the sources listed for your test executable will have no knowledge of the include directories you listed. A simple fix for this is to simply move the the include_directories() call to your top-level CMake file:

... 

include_directories(
    ${SDL2_INCLUDE_DIRS} 
    ${SDL2_mixer_INCLUDE_DIRS} 
    ${CMAKE_SOURCE_DIR}/include 
    ${CMAKE_SOURCE_DIR}/include/game_objects
    ${CMAKE_SOURCE_DIR}/include/types
    ${CMAKE_SOURCE_DIR}/include/utility 
)

add_subdirectory(src)

add_subdirectory(thirdparty/googletest)

add_executable(test 
    test/game_objects/Ball_test.cpp
    src/game_objects/Ball.cpp
    test/game_objects/Brick_test.cpp
    src/game_objects/Brick.cpp
    test/game_objects/GameObject_test.cpp
    src/game_objects/GameObject.cpp
    test/game_objects/MoveableGameObject_test.cpp
    src/game_objects/MoveableGameObject.cpp
    test/game_objects/Physics_test.cpp
    src/game_objects/Physics.cpp
    test/game_objects/Platform_test.cpp
    src/game_objects/Platform.cpp
    src/GameParameter.cpp
    test/Level_test.cpp
    src/Level.cpp
)

target_link_libraries(test 
    gtest_main 
)

You should also use the variable CMAKE_SOURCE_DIR to grab the full path to the top-level CMake file, to avoid using relative paths.


FWIW, I'm not aware of any guidance that claims you should have a CMakeLists.txt file in each sub-directory of your project. The way you structure your CMakeLists.txt files in your project hierarchy is obviously dependent on how your files are laid out in your file system, how lengthy you want each CMakeLists.txt file to be, and other design decisions. For large projects, I've seen that it is common to have one CMakeLists.txt file per target.

Kevin
  • 16,549
  • 8
  • 60
  • 74
  • Thanks for the answer. I tryed to add also tests to its own file in the test folder but it fails. I added it like this: `add_subdirectory(test)` in the main file. In the test file `add_executable(test game_objects/Ball_test.cpp ../src/game_objects/Ball.cpp` and at the bottom `target_link_libraries(test gtest_main )` But it fails with `CMake Error: Cannot open file for write: /mnt/Programmierung/Cpp/Udacity/Project/Bricks/build/test/CMakeFiles/test.dir/build.make.tmp` – Sandro4912 Jun 04 '20 at 04:54