用于lib和可执行文件的c ++ CMake项目结构

问题描述

我对如何构建c ++项目以及如何使用CMake(在CLion内部)进行构建有疑问。我对c ++(仅两周前才开始学习该语言)和CMake(我从未配置CMake文件)非常陌生。但是,我确实对其他编程语言及其生态系统(如Java,JavaScript,PHP和.NET)有很多经验,也许与这些生态系统的类比可能会有所帮助?

我们正在为学校项目构建自己的(小型2d)游戏和游戏引擎。游戏引擎应可自行运输,游戏应基于我们自己的引擎构建。该引擎将具有自己的依赖项(使用SDL2构建,这是一个要求)。

我想象引擎将成为一个(静态)库,而实际的游戏将编译为依赖于引擎库的可执行文件

为了简化项目管理,我们希望将引擎和游戏代码都托管在同一个git存储库中。

鉴于我们可能想将math模块放入我们的引擎中(可能不是这种情况,但是出于这个问题的目的),我想象一个如下的文件夹结构:

our-project/
├── README.md
├── engine
│   ├── src
│   │   ├── math.cpp
│   │   └── math.h
│   └── test
│       └── ...
└── game
    ├── src
    │   └── main.cpp
    └── test
        └── ...

引擎将包含以下代码

// in math.h
namespace engine::math {
    int add(int a,int b);
}

// in math.cpp
#include "math.h"

namespace engine::math {
    int add(int a,int b) {
        return a + b;
    }
}

比方说,我们想利用我们游戏中的这个引擎代码,我会模仿以下代码

// in game/main.cpp
#include <iostream>
#include "engine/math.h"

using namespace engine;

int main() {
    std::cout << math::add(10,1) << std::endl;
}

问题

  1. 如何设置CMake,以便既可以单独构建引擎(静态库),又可以构建依赖于同一引擎的游戏(可执行文件)?
  2. 我应如何管理引擎头文件?如何使它们适用于游戏代码?头文件是否应该使用其他结构?
  3. 文件夹结构可管理吗?我应该考虑其他方法吗?

解决方法

  1. 您在engine/src/CMakeLists.txt中声明了单独的CMake目标:

    add_library(engine
        math.cpp)
    

    game/src/CMakeLists.txt期间,您拥有

    add_executable(my-game
       main.cpp)
    
    target_link_libraries(my-game
        PRIVATE
        engine)
    

    使用时make,您可以通过以下方式分别构建目标:

    make engine # build only the engine,not the executable
    make my-game # build engine if necessary,then build executable
    

    分开的测试目标也很有意义。

  2. CMake中的包含标志如下传播。

    target_include_directories(engine
       INTERFACE
       ${CMAKE_CURRENT_SOURCE_DIR})
    

    以上设置将包含目录传播到所有链接到引擎的目标。这很简单,但有一个缺点:您无法区分公共标头和实现细节。或者,您可以拥有一个目录engine/src/public/engine,其中包含游戏应使用的所有 public 标头:

    target_include_directories(engine
       INTERFACE
       ${CMAKE_CURRENT_SOURCE_DIR}/public)
    
    target_include_directories(engine
       PRIVATE
       ${CMAKE_CURRENT_SOURCE_DIR}/public/engine)
    

    通过这种方式,game中的客户端代码使用#include "engine/math.h",而在engine中,您可以使用#include "math.h"。我喜欢这样的设置,因为它很容易看到什么是库的接口以及什么是实现。但这还是有点品味问题。

  3. 再次自以为是。但是我认为这是一个很好的目录结构。坚持下去。