具有多个目录的大型项目的 CMakeLists?

Posted

技术标签:

【中文标题】具有多个目录的大型项目的 CMakeLists?【英文标题】:CMakeLists for large project with multiple directories? 【发布时间】:2018-10-24 12:34:04 【问题描述】:

我正在编写一个运行良好的游戏引擎。但是我遇到了一个问题,我的 CMakeLists.txt 太乱了,而且我对 CMake 了解不够。我的项目使用多个(CMake)库,这些库是使用 add_subdirectory 然后 target_link_libraries 添加的。我的项目由引擎(可执行文件)、编辑器(库)和一些测试/示例组成。这是我的文件结构:

C:.
|   CMakeLists.txt
|   tree.txt
|   
+---Editor
|   |   README.md
|   |   
|   \---src
|           main.cpp
|           
+---Engine
|   |   README.md
|   |   
|   +---src
|   |   |   main.cpp
|   |   |   
|   |   +---API
|   |   |       Core.h
|   |   |       
|   |   +---App
|   |   |       Application.cpp
|   |   |       
|   |   +---ExtApp
|   |   |   |   AppInterface.cpp
|   |   |   |   
|   |   |   +---Engine
|   |   |   |       ExtAppLoader.cpp
|   |   |   |       
|   |   |   \---Game
|   |   |           InfoExport.cpp
|   |   |           
|   |   +---Framework
|   |   |       Asset.cpp
|   |   |       
|   |   +---Managing
|   |   |       AssetLoader.cpp
|   |   |       
|   |   +---Rendering
|   |   |   |   Renderer.cpp
|   |   |   |   
|   |   |   \---Renderables
|   |   |           Canvas2DRenderable.cpp
|   |   |           
|   |   \---Types
|   |           Vector3f.cpp
|   |           
|   \---TestResources
|       \---Shaders
|               Canvas2DTexturedTriangle.f
|               Canvas2DTexturedTriangle.v
|               Canvas2DUntexturedTriangle.f
|               Canvas2DUntexturedTriangle.v
|               ImTest.f
|               ImTest.v
|               
+---Libraries
|   +---glfw
|   |       CMakeLists.txt
|   |       
|   \---glm
|           CMakeLists.txt
|           
\---Tests
    \---TestGame
        \---src
                main.cpp

如您所见,我一开始有一个 CMakeLists,它会加载所有项目。然后我有图书馆,其中也有一个 CMakeLists。每个目录只有一个文件来使树变小,但一个目录中有多个文件。另外,这是我当前的、凌乱的、几乎没用的 CMakeLists 文件:

    cmake_minimum_required(VERSION 3.6)

#project(3DEngine)



add_subdirectory(Libraries/glfw) #Add glfw to the project

# Make sure we're running C++17 so all features(like std::filesystem) are present.
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

option(BUILD_ENGINE_FOR_EDITOR "Build the engine as DLL/SO for the editor and add editor specific things. Otherwise build engine as game exec" OFF)
option(BUILD_ENGINE_FOR_DLL_APPS "Have the Engine load the game(and plugins) from dll's." ON)
project (Engine)#project engine
include_directories(Libraries/whereami/src)
include_directories(Engine/src/)
include_directories(Libraries/glfw/include)
include_directories(Libraries/glm)
include_directories(Libraries/glad/include)
include_directories(Libraries/stb)
file(GLOB EngineRootSOURCES  "Engine/src/*.cpp" "Engine/src/*.h")
file(GLOB EngineRenderingSOURCES  "Engine/src/Rendering/*.cpp" "Engine/src/Rendering/*.h")
file(GLOB EngineAppSOURCES  "Engine/src/App/*.cpp" "Engine/src/App/*.h")
file(GLOB EngineRenderingRenderablesSOURCES  "Engine/src/Rendering/Renderables/*.cpp" "Engine/src/Rendering/Renderables/*.h")
file(GLOB EngineManagingSOURCES  "Engine/src/Managing/*.cpp" "Engine/src/Managing/*.h")
file(GLOB EngineTypesSOURCES  "Engine/src/Types/*.cpp" "Engine/src/Types/*.h")
file(GLOB EngineGameEssentialsSOURCES  "Engine/src/GameEssentials/*.cpp" "Engine/src/GameEssentials/*.h")
file(GLOB EngineLibsSOURCES  "Libraries/whereami/src/whereami.c" "Libraries/glad/src/glad.c")
file(GLOB EngineFrameworkSOURCES  "Engine/src/Framework/*.h" "Engine/src/Framework/*.cpp")
file(GLOB APISOURCES  "Engine/src/API/*.cpp" "Engine/src/API/*.h")

file(GLOB EngineExtAppSOURCES "Engine/src/ExtApp/*.cpp" "Engine/src/ExtApp/*.h"
 "Engine/src/ExtApp/Interface/*.cpp" "Engine/src/ExtApp/Interface/*.h")

 file(GLOB EngineExtAppGameSOURCES "Engine/src/ExtApp/Game/*.cpp" "Engine/src/ExtApp/Game/*.h")
 source_group("ExtApp"  FILES $EngineExtAppGameSOURCES)

 file(GLOB EngineExtAppEngineSOURCES "Engine/src/ExtApp/Engine/*.cpp" "Engine/src/ExtApp/Engine/*.h")
 source_group("ExtApp"  FILES $EngineExtAppEngineSOURCES)

if(BUILD_ENGINE_FOR_EDITOR)
file(GLOB EngineEditorSOURCES "Engine/src/ExtApp/*.cpp" "Engine/src/ExtApp/*.h"
 "Engine/src/ExtApp/Interface/*.cpp" "Engine/src/ExtApp/Interface/*.h")


 if(BUILD_ENGINE_FOR_DLL_APPS)

 add_library(Engine SHARED $EngineRootSOURCES $EngineRenderingSOURCES $EngineTypesSOURCES $EngineRenderingRenderablesSOURCES $EngineManagingSOURCES $EngineGameEssentialsSOURCES $EngineLibsSOURCES $EngineEditorSOURCES $EngineExtAppSOURCES $EngineAppSOURCES $APISOURCES $EngineExtAppEngineSOURCES $EngineFrameworkSOURCES)
 source_group("ExtApp"  FILES $EngineExtAppSOURCES)
elseif(NOT BUILD_ENGINE_FOR_DLL_APPS)
add_library(Engine SHARED $EngineRootSOURCES $EngineRenderingSOURCES $EngineTypesSOURCES $EngineRenderingRenderablesSOURCES $EngineManagingSOURCES $EngineGameEssentialsSOURCES $EngineLibsSOURCES $EngineEditorSOURCES $EngineExtAppSOURCES $EngineAppSOURCES $APISOURCES $EngineExtAppEngineSOURCES $EngineFrameworkSOURCES)
endif()



target_link_libraries(Engine glfw)
source_group("Rendering"  FILES $EngineRenderingSOURCES)
source_group("Rendering/Renderables"  FILES $EngineRenderingRenderablesSOURCES)
source_group("Managing"  FILES $EngineManagingSOURCES)
source_group("App"  FILES $EngineAppSOURCES)
source_group("Types"  FILES $EngineTypesSOURCES)
source_group("GameEssentials"  FILES $EngineGameEssentialsSOURCES)
source_group("Libs"  FILES $EngineLibsSOURCES)
source_group("ExtApp"  FILES $EngineEditorSOURCES)
source_group("API"  FILES $APISOURCES)
source_group("Framework"  FILES $EngineFrameworkSOURCES)
elseif(NOT BUILD_ENGINE_FOR_EDITOR)


if(BUILD_ENGINE_FOR_DLL_APPS)
file(GLOB EngineExtAppSOURCES "Engine/src/ExtApp/*.cpp" "Engine/src/ExtApp/*.h"
 "Engine/src/ExtApp/Interface/*.cpp" "Engine/src/ExtApp/Interface/*.h")
 add_executable(Engine $EngineRootSOURCES $EngineRenderingSOURCES $EngineTypesSOURCES $EngineRenderingRenderablesSOURCES $EngineManagingSOURCES $EngineGameEssentialsSOURCES $EngineLibsSOURCES $EngineExtAppSOURCES $EngineAppSOURCES $APISOURCES $EngineExtAppEngineSOURCES $EngineFrameworkSOURCES)
 source_group("ExtApp"  FILES $EngineExtAppSOURCES)
elseif(NOT BUILD_ENGINE_FOR_DLL_APPS)
add_executable(Engine $EngineRootSOURCES $EngineRenderingSOURCES $EngineTypesSOURCES $EngineRenderingRenderablesSOURCES $EngineManagingSOURCES $EngineGameEssentialsSOURCES $EngineLibsSOURCES $EngineExtAppSOURCES $EngineAppSOURCES $APISOURCES $EngineExtAppEngineSOURCES $EngineFrameworkSOURCES)
endif()



target_link_libraries(Engine glfw)
source_group("Rendering"  FILES $EngineRenderingSOURCES)
source_group("Rendering/Renderables"  FILES $EngineRenderingRenderablesSOURCES)
source_group("Managing"  FILES $EngineManagingSOURCES)
source_group("App"  FILES $EngineAppSOURCES)
source_group("Types"  FILES $EngineTypesSOURCES)
source_group("GameEssentials"  FILES $EngineGameEssentialsSOURCES)
source_group("Libs"  FILES $EngineLibsSOURCES)
source_group("API"  FILES $APISOURCES)
source_group("Framework"  FILES $EngineFrameworkSOURCES)
endif()

#add_library(Engine SHARED $EngineSOURCES)
## END project engine

project (Module_OpenGL_Renderer_Input)#project module_renderer_opengl3
include_directories(Libraries/glfw/include)
include_directories(Libraries/glm)
include_directories(Libraries/glad/include)
include_directories(Libraries/stb)
file(GLOB Module_OpenGL_Renderer_InputSOURCES "Modules/Module_OpenGL_Renderer_Input/src/*.cpp" "Modules/Module_OpenGL_Renderer_Input/src/*.h" "Libraries/glad/src/glad.c")
add_library(Module_OpenGL_Renderer_Input SHARED $Module_OpenGL_Renderer_InputSOURCES)
target_link_libraries(glfw)
##END project module_renderer_opengl3

project (Test1)#project test  |  This project is used to test the engine functionality.
include_directories(Libraries/imgui)
file(GLOB Test1SOURCES "Tests/Test1/src/*.cpp" "Tests/Test1/src/*.h" "Libraries/imgui/imgui*.cpp"  $APISOURCES)
add_executable(Test1 $Test1SOURCES)
target_link_libraries(Test1 $CMAKE_DL_LIBS)

project (TestGame)#project test  |  This project is used to test the engine functionality.
include_directories(Libraries/imgui)
file(GLOB TestGameSOURCES "Tests/TestGame/src/*.cpp" "Tests/TestGame/src/*.h" "Libraries/imgui/imgui*.cpp" $EngineRenderingSOURCES $EngineTypesSOURCES $EngineRenderingRenderablesSOURCES $EngineManagingSOURCES $EngineGameEssentialsSOURCES $EngineLibsSOURCES $EngineAppSOURCES  $APISOURCES $EngineExtAppSOURCES $EngineExtAppGameSOURCES  $EngineFrameworkSOURCES)
add_library(TestGame SHARED $TestGameSOURCES)
target_link_libraries(TestGame glfw)

project (Editor)#project editor  |  This is used to make projects and build projects(using the engine)
include_directories(Libraries/imgui)
file(GLOB EditorSOURCES "Editor/src/*.cpp" "Editor/src/*.h" "Libraries/imgui/imgui*.cpp"  $APISOURCES)
add_executable(Editor $EditorSOURCES)

我包含很多完全没用或不再需要的东西。 所以这是我的问题:

    是否每个目录都需要一个 CMakeLists 文件,就像我在很多项目中看到的那样? 我需要提供包含源文件的每个目录,还是让它自动搜索目录中的源文件和头文件? 我还看到很多其他 CMake 项目分别提供每个源/头文件,为什么?每次添加文件的工作量不是很大吗? 谁有我可以用作指导的大型 CMake 项目示例? 还有什么可以改进的吗?

谢谢!

【问题讨论】:

在“大型项目”中使用 CMake 没有通用方法。但是大多数此类项目使用多个 CMakeLists.txt(在不同的目录中)。你在一个问题帖子中问了太多问题,这在 Stack Overflow 上是不受欢迎的。您可以先将CMakeLists.txt 拆分为多个目录,然后询问您将面临的具体问题。 @Tsyvarev 但是我该怎么做,它会是什么样子?很抱歉在一篇文章中问了太多问题,我想如果我把它分成单独的问题,它会被视为垃圾邮件。 CMakeLists.txt 位于子目录中通常只处理该子目录中的文件。 Editor/CMakeLists.txt 可以编译 Editor/src/main.cpp 源代码。根据第一个问题,您已经看到许多在每个目录中都使用CMakeLists.txt 的项目。为什么不简单地遵循这样的项目之一? @Tsyvarev 感谢您的回复。好吧,我知道的唯一使用 CMake 的大型库/项目是 GLFW 和 Bullet,我想不出任何其他的。你可能知道一些吗? 【参考方案1】:
    是否每个目录都需要一个 CMakeLists 文件

不,没必要。

为每个子模块和库拥有一个 CMakeLists.txt 可能是个好主意 - 并在项目本身的根目录中拥有一个。

    我是否需要提供包含源文件的每个目录,或者我可以让它自动搜索目录中的源文件和头文件?

首先,查看问题 3 的答案。

其次,我不明白您为什么要搜索头文件。只需指定包含目录。

最后,如果你想使用 glob,可以将所有源文件放在一个目录下,并使用一个 glob。

    我还看到很多其他 CMake 项目分别提供每个源...文件,为什么?

这是文档所说的:

注意

我们不建议使用 GLOB 从源代码树中收集源文件列表。如果添加或删除源时没有 CMakeLists.txt 文件更改,则生成的构建系统无法知道何时要求 CMake 重新生成。 CONFIGURE_DEPENDS 标志可能无法在所有生成器上可靠地工作,或者如果将来添加不能支持它的新生成器,使用它的项目将被卡住。即使 CONFIGURE_DEPENDS 工作可靠,每次重建时执行检查仍然需要成本。


    ...每个头文件分开,为什么?

我从来没有见过这个。

    还有什么可以改进的吗?

使用target_include_directories 代替include_directories。一般来说,始终使用target_X 指令。

【讨论】:

感谢您的回答!让我回复你说的话: 子模块的例子是什么?什么是子模块?我也想添加头文件,因为我有时使用 Visual Studio,它显示的文件与其他 IDE 不同。每当我添加文件时,我总是重新配置和重新生成我的项目,我认为这应该没问题。但是,在 CMakeLists 文件中添加每个文件不是很多工作,还是人们以某种​​方式自动化?我现在将使用 target_include_directories 表单。 @TimLeijten 模块是一个独立的单元。子模块是一个模块,它是另一个模块的一部分。可执行文件是模块的示例,库是子模块的示例。我提到它是为了以防你有一个你不会描述为库的子模块。 @TimLeijten 在我看来,每次添加/删除源文件时更改 CMakeLists.txt 文件并不是很多工作,但我同意这很烦人它冗余地复制了文件系统上的信息。但这就是 CMake 的设计,尽管它可能存在缺陷。一些 IDE 会自动编辑 CMakeLists.txt。 嗯,问题是我的项目并没有真正使用模块作为它的基础,物理之类的东西是它自己的模块,但渲染、基类、场景管理、资源管理等都在相同的项目/模块。这是否意味着我为此使用了一个 CMakeLists?您是否知道任何自动修改 CMakeLists 的 IDE?或插件。(用于 VS 或 CLion)

以上是关于具有多个目录的大型项目的 CMakeLists?的主要内容,如果未能解决你的问题,请参考以下文章

CMake项目基本模板

4. CMake 系列 - 项目添加编译选项

为大型项目创建 codedui 测试的最佳设计是啥

如何将“jni/*.c/cpp”文件添加到 CMakeLists.txt 文件中?

C - 构建大型项目期间的多个功能定义

如何构建具有 cmake 文件的 c++/c 项目?