Sandro4912
Sandro4912

Reputation: 351

CMake source files cannot find header files

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.

Upvotes: 1

Views: 3441

Answers (1)

Kevin
Kevin

Reputation: 18243

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.

Upvotes: 2

Related Questions