【问题标题】:Cmake from scratch for a multi directory projectCmake 从头开始​​用于多目录项目
【发布时间】:2019-02-01 06:13:52
【问题描述】:

我正在反复讨论这个令人困惑的 CMake 概念。我已经阅读了几篇描述如何编写 CMake 文件的文章。但我发现它的解释非常少。

第 1 点 - 我是否必须为每个文件夹制作单独的 CMakeLists.txt(是的,为简单起见,我们将其称为文件夹)?

第 2 点 - 显然 第 1 点的答案。我发现这些命令可以自动搜索*.cpp 文件

# get all *.cpp files recursively
file(GLOB_RECURSE SRC_LIST *.c* *.h*)

第 3 点 - 似乎无需提及 *.hpp 文件。 (这是含糊的点)

我尝试编译最简单的 Hello World! 程序

CMakeLists.txt
src
| - main.cpp (here I included #include "XYZ/abc.hpp")
| - XYZ
    | - abc.cpp (included #include "XYZ/abc.hpp")
    | - abc.hpp

为此的 CMake 是

cmake_minimum_required(VERSION 3.10)
project(CMAkeSample)
add_executable(hello src/main.cpp src/XYZ/abc.cpp)

这建议,添加所有.cpp 文件以及add_executable 中的路径,我们就完成了。 这是真的吗?

第 4 点 - 现在我有一个比较大的项目(出于任何原因 在 eclipse 中开发)。但我需要为它制作cmake文件,因此不使用eclipse进行构建。

下面是文件系统,我只想制作一次CMakeLists.txt(如果可能的话)

CMakeLists.txt
src
| - main.cpp (here I included #include "File0.hpp" and #include "Folder1/File1.hpp")
| - File0.cpp (here I included #include "File0.hpp")
| - File0.hpp (here I included #include "Folder1/File1.hpp" and #include "Folder2/File2.hpp")
| - Folder1
    | - File1.cpp (included #include "File1.hpp")
    | - File1.hpp
| - Folder2
    | - File2.cpp (included #include "File2.hpp")
    | - File2.hpp

那么现在如何获取所有.cpp 文件,而无需在每个文件夹中手动编写cmake。有什么建议吗?

更新 1: 我已经添加了CMakeLists.txt,如图所示

ProjectFolder
    CMakeLists.txt
    src
    | - CMakeLists.txt
    | - main.cpp (here I included #include "File0.hpp" and #include "Folder1/File1.hpp")
    | - File0.cpp (here I included #include "File0.hpp")
    | - File0.hpp (here I included #include "Folder1/File1.hpp" and #include "Folder2/File2.hpp")
    | - Folder1
        | - CMakeLists.txt
        | - File1.cpp (included #include "File1.hpp")
        | - File1.hpp
    | - Folder2
        | - CMakeLists.txt
        | - File2.cpp (included #include "File2.hpp")
        | - File2.hpp

ProjectFolder > src > CMakeLists.txt

add_library(main main.cpp File0.cpp File0.hpp)
target_link_libraries(main File1)

ProjectFolder > src > File1 > CMakeLists.txt

add_library(File1 File1.cpp File1.hpp)
target_link_libraries(File1 File2)

ProjectFolder > src > File > CMakeLists.txt

add_library(File2 File2.cpp File2.hpp)

毕竟,我得到的错误是,

-- Configuring done
-- Generating done
-- Build files have been written to: /path/to/ProjectFolder
[ 33%] Built target main
[ 44%] Building CXX object CMakeFiles/ProjectFolder.dir/src/main.cpp.o
[ 55%] Linking CXX executable ProjectFolder
/usr/lib64/gcc/x86_64-suse-linux/7/../../../../x86_64-suse-linux/bin/ld: cannot find -lFile1

更新 2:

继续前进,为了完成这项工作,这里和那里进行了一些更改(如 UPDATE 1 中所述,并在项目文件夹的 CMakeLists.txt 中手动添加链接

set(GCC_LINK_FLAGS "-lpthread -lcurl -lgobject-2.0 -lgstreamer-1.0 -lglib-2.0 -lssl -lcrypto -lboost_thread -lboost_system")
add_subdirectory(src)
add_subdirectory(src/Folder1)
add_subdirectory(src/Folder2)

add_executable(ProjectFolder src/main.cpp)
target_link_libraries(ProjectFolder main ${GCC_LINK_FLAGS})

程序完美执行。但显然这是编写链接的非常粗糙的方式。正在寻找 Point2 的解决方案。

【问题讨论】:

  • "如何获取所有.cpp文件而不需要在每个文件夹中手动编写cmake?" - 您已经展示了使用file(GLOB_RECURSE) 的解决方案。那么,您想从我们这里得到什么?
  • 这对我不起作用,而且我从未使用过 cmake,所以我不知道要问什么,所以我问了一般问题。正如@scheff 建议的那样,我正在慢慢浏览这些文件。并在每个子目录中编写 Cmakes
  • “这对我没有正常工作” - 那么您可能需要描述(在问题帖子中),在这种情况下究竟是什么不起作用,所以我们可以帮助你解决这个问题。

标签: c++ cmake compilation


【解决方案1】:

第 1 点 - 我是否必须为每个文件夹制作单独的 CMakeLists.txt(是的,为简单起见,我们将其称为文件夹)?

您应该为每个库或可执行文件创建一个单独的文件夹。 (虽然,也可以从一个文件夹中为可执行文件构建多个项目。我有时会将此选项用于具有多个小型测试应用程序的文件夹。)

对于带有子文件夹的文件夹,您可能需要一个仅包含 add_subdirectory() 命令的 CMakeLists.txt。 (请参见下面的示例。)

甚至可以制作一个 CMakeLists.txt,它会考虑来自多个子文件夹的来源。我曾经写过一个关于这个的答案。 (SO: Single CMakeLists.txt enough for my project?) 不过,看看投票——没有多少人认为这是个好主意。 ;-)

第 2 点 - 显然第 1 点的答案是否定的。我发现这些命令可以自动搜索 *.cpp 文件

我们广泛使用它,但也有人建议不要这样做。

缺点是您的构建链在添加新源时不会自动识别。如果您在 CMakeLists.txt 中明确命名所有源,则可以这样做。

来自CMake doc.

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

因此,使用 file(GLOB,您永远不要忘记在添加、移动或删除文件后明确地重新运行 CMake。

第 3 点 - 似乎不需要提及 *.hpp 文件。 (这是模糊点)

是的,因为 cpp 文件中提到了包含文件,所以没有必要。另一方面,我们使用 CMake 和 VS2013。如果您在相应的文件中找到所有标题,那就太好了。 VS 项目文件夹。因此,我建议也提及包含。 (似乎 CMake 足够聪明,可以将它们与源代码分开。因此,它不会为头文件生成构建命令。)

第 4 点 - 现在我有一个比较大的项目(无论出于何种原因在 eclipse 中开发)。但我需要为它制作cmake文件,因此不使用eclipse进行构建。

CMake 是一个构建脚本构建器。我们使用它来生成 VS 解决方案和项目。我确信它也能够为 Linux 构建 Makefile。不幸的是,我没有这方面的实际经验。 (实际上,这就是我们切换到 CMake 的原因 - 为我们的可移植书面源代码的可移植构建提供一种方法。


顺便说一句。 cmake.org上有介绍:

CMake Tutorial


示例:

想象一下应用程序myApp 的以下目录树:

└─ MyApp/
  ├─ main/
  │ ├─ CMakeLists.txt
  │ └─ myApp.cc
  ├─ gui/
  │ ├─ CMakeLists.txt
  │ ├─ guiMainWindow.cc
  │ └─ guiMainWindow.h
  ├─ model/
  │ ├─ CMakeLists.txt
  │ ├─ model.cc
  │ └─ model.h
  └─ CMakeLists.txt 

MyApp/CMakeLists.txt 不对任何源代码负责,但它会收集子文件夹的构建脚本。它可能看起来像这样:

# MyApp/CMakeLists.txt

# make a project
project(MyApp)
# This will generate a MyApp solution for VisualStudio
# containing all involved projects.

# add sub-folders which have to be considered
add_subdirectory(main)
add_subdirectory(gui)
add_subdirectory(model)

MyApp/model 可以为MyApp 的基本数据模型提供一个库,而无需进一步依赖。所以,MyApp/model/CMakeLists.txt 可能看起来像这样:

# MyApp/model/CMakeLists.txt

# build rule for library libmodel
add_libary(model
  model.cc model.h)

MyApp/gui 可以为MyApp 的GUI 提供另一个库,并依赖于libmodelMyApp/gui/CMakeLists.txt 可能如下所示:

# MyApp/gui/CMakeLists.txt

# build rule for library libgui
add_libary(gui
  guiMainWindow.cc guiMainWindow.h)

# dependencies
target_link_libraries(gui
  model)

最后,MyApp/main 提供MyAppmain() 函数的源代码并构建可执行文件。 MyApp/main/CMakeLists.txt 可能如下所示:

# MyApp/main/CMakeLists.txt

# build rule for executable myApp
add_executable(myApp
  myApp.cc)

# dependencies
target_link_libraries(myApp
  gui model)

【讨论】:

  • 嘿,你的答案写得非常好。谢谢你这么快的回复。虽然我有一些疑问。 **there are also recommendations to not to do so**你能告诉我为什么吗? **which contains only addsubdirectory()** 任何示例链接?
  • @RC0993 我编辑了一些额外的解释来回答这个问题(并修正了关于add_subdirectory()的错别字)。 ;-)
  • 我添加了子目录。在项目根文件夹的CMakeLists.txt 中,像这样add_subdirectory(src/Folder1)Folder1 现在包含一个空白 CMakeLists.txt。我想在里面写什么。我找不到显示子目录的CmakeLists.txt 的示例。
  • @RC0993 我扩展了示例(并将其移至底部)。虽然,我们在生产中使用 CMake 很长一段时间(有很多帮助脚本),但我最近刚刚开始了一个私人项目,并从“空白”开始。我使用这些脚本来防止在我的示例中出现最粗暴的错误。虽然,这个例子是未经测试的,我可能已经监督了一些事情。顺便提一句。我的真实脚本包含一些额外的东西,例如构建静态或共享库(在 Windows .lib.dll 中)以及外部依赖项的其他定义,例如Qt。我把它省略了,以保持简洁、清晰和小巧。
  • 我已经为各个文件夹创建了 Cmake,并添加了链接,如 UPDATE 1 所示
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-09
  • 2017-01-15
相关资源
最近更新 更多