【问题标题】:How is CMake used? [closed]如何使用 CMake? [关闭]
【发布时间】:2014-11-18 09:29:54
【问题描述】:

众所周知,初学者很难获得有关 CMake 的任何有用信息。到目前为止,我已经看过一些关于如何设置一些非常基本的项目或其他项目的教程。但是,这些都不能解释其中所显示的任何东西背后的原因,总是留下许多漏洞来填补。

在 CMakeLists 上调用 CMake 是什么意思?每个构建树应该调用一次还是什么?如果它们都使用来自同一来源的相同 CMakeLists.txt 文件,我该如何为每个构建使用不同的设置? 为什么每个子目录都需要自己的 CMakeLists 文件?在项目根目录以外的 CMakeLists.txt 上使用 CMake 是否有意义?如果是,在什么情况下? 指定如何从他们自己的子目录中的 CMakeLists 文件构建可执行文件或库与在所有源的根目录下的 CMakeLists 文件中执行它有什么区别? 我可以为 Eclipse 创建一个项目,为 Visual Studio 创建另一个项目,只需在调用 CMake 时更改 -G 选项吗?是这样用的吗?

到目前为止,我所看到的任何教程、文档页面或问题/答案都没有对理解如何使用 CMake 提供任何有用的见解。这些例子并不详尽。不管我读什么教程,我都觉得我错过了一些重要的东西。

像我这样的 CMake 新手提出的许多问题并没有明确提出,但这很明显,作为新手,我们不知道如何处理 CMake 或如何处理它。

【问题讨论】:

  • 我很想对“太宽泛”进行近距离投票……这感觉更像是一篇关于 CMake 的个人文章,而不是适合 stackoverflow。你为什么不把所有这些问题放在单独的问题中?您的问题+答案与许多其他问题有何不同,例如stackoverflow.com/q/20884380/417197, stackoverflow.com/q/4745996/417197, stackoverflow.com/q/4506193/417197?
  • @Andre 为什么这个问题:我花了一周的时间试图理解 CMake,但没有找到完整的教程。您指出的那些链接很好,但是对于那些发现 CMake 的人来说,因为他们有一个项目扔给他们,里面有一堆 CMakeLists,他们并没有对 CMake 的工作原理有太多了解。老实说,我试图写一个答案,我希望在我开始尝试学习 CMake 时能及时发回给自己。子问题旨在表明我实际上是在尝试问我应该知道什么,以免产生这些疑问。提出正确的问题很难。
  • 不是答案,但我邀请您看看jaws.rootdirectory.de。尽管 CMake 文档(以及公认的答案...)向您展示了您 可以 做的许多简短的 sn-ps,但我写了一个(绝不是“完整”或“完美” ) 基本设置,包括贯穿始终的 cmets,恕我直言,展示了 CMake(以及 CTest 和 CPack 和 Cdash ......)可以为您做的事情。它开箱即用,您可以根据项目需求轻松调整/扩展它。
  • 现在这些问题都被关闭了,真可惜。我认为需要教程之类的答案但涉及记录不佳/模糊不清的主题(另一个例子是 Torch7 C api)和研究级问题应该保持开放。这些比困扰网站的典型“嘿,我在制作一些玩具项目时出现段错误”更有趣。
  • 刚刚找到这个。作为一名程序员超过 20 年,我从来没有遇到过像 CMAKE 这样的文档记录不佳、无法入门和糟糕透顶的东西。这是一个可怕的怪物,即使是最基本的东西也被埋在“过程”的大部头之下。简直太可怕了。

标签: cmake


【解决方案1】:

什么是 CMake?

根据维基百科:

CMake 是 [...] 用于管理软件构建过程的软件 使用与编译器无关的方法。它旨在支持 依赖于多个目录层次结构和应用程序 图书馆。它与本机构建环境结合使用 例如 make、Apple 的 Xcode 和 Microsoft Visual Studio。

使用 CMake,您不再需要维护特定于编译器/构建环境的单独设置。您有 一个 配置,并且适用于 许多 环境。

CMake 可以从 same 文件中生成 Microsoft Visual Studio 解决方案、Eclipse 项目或 Makefile 迷宫,而无需更改其中的任何内容。

给定一堆目录,其中包含代码,CMake 管理您的项目在编译之前需要完成的所有依赖项、构建顺序和其他任务。它实际上并没有编译任何东西。要使用 CMake,您必须告诉它(使用名为 CMakeLists.txt 的配置文件)您需要编译哪些可执行文件、它们链接到哪些库、项目中有哪些目录以及其中的内容,以及标志等任何详细信息或您需要的任何其他东西(CMake 非常强大)。

如果设置正确,然后您可以使用 CMake 创建您选择的“本机构建环境”完成其工作所需的所有文件。在 Linux 中,默认情况下,这意味着 Makefiles。所以一旦你运行 CMake,它会创建一堆文件供自己使用,外加一些 Makefiles。此后,您只需在每次编辑完代码时从根文件夹中的控制台中键入“make”,然后生成编译和链接的可执行文件。

CMake 是如何工作的?它有什么作用?

这是我将在整个过程中使用的示例项目设置:

simple/
  CMakeLists.txt
  src/
    tutorial.cxx
    CMakeLists.txt
  lib/
    TestLib.cxx
    TestLib.h
    CMakeLists.txt
  build/

每个文件的内容都会在后面展示和讨论。

CMake 根据您项目的 root CMakeLists.txt 设置您的项目,并在您在控制台中执行 cmake 的任何目录中进行设置。从不是项目根目录的文件夹中执行此操作会产生所谓的 out-of-source 构建,这意味着在编译期间创建的文件(obj 文件、lib 文件、可执行文件,你知道) 将被放置在所述文件夹中,与实际代码分开。它有助于减少混乱,并且出于其他原因也是首选,我不会讨论。

我不知道如果您在除根 CMakeLists.txt 之外的任何其他服务器上执行 cmake 会发生什么。

在本例中,由于我希望将其全部放在build/ 文件夹中,首先我必须导航到那里,然后将根CMakeLists.txt 所在的目录传递给 CMake。

cd build
cmake ..

默认情况下,正如我所说,这使用 Makefiles 设置所有内容。下面是构建文件夹现在的样子:

simple/build/
  CMakeCache.txt
  cmake_install.cmake
  Makefile
  CMakeFiles/
    (...)
  src/
    CMakeFiles/
      (...)
    cmake_install.cmake
    Makefile
  lib/
    CMakeFiles/
      (...)
    cmake_install.cmake
    Makefile

这些文件是什么? The only thing you have to worry about is the Makefile and the project folders

注意src/lib/ 文件夹。这些已创建,因为simple/CMakeLists.txt 使用命令add_subdirectory(<folder>) 指向它们。此命令告诉 CMake 在所述文件夹中查找另一个 CMakeLists.txt 文件并执行 that 脚本,因此以这种方式添加的每个子目录必须在其中包含一个 CMakeLists.txt 文件。在这个项目中,simple/src/CMakeLists.txt 描述了如何构建实际的可执行文件,simple/lib/CMakeLists.txt 描述了如何构建库。 CMakeLists.txt 描述的每个目标将默认放置在构建树的子目录中。所以,经过快速

make

在从build/ 完成的控制台中,添加了一些文件:

simple/build/
  (...)
  lib/
    libTestLib.a
    (...)
  src/
    Tutorial
    (...)

项目已构建,可执行文件已准备好执行。 如果您希望将可执行文件放在特定文件夹中,您会怎么做? Set the appropriate CMake variable, or change the properties of a specific target。稍后将详细介绍 CMake 变量。

我如何告诉 CMake 如何构建我的项目?

以下是源目录中每个文件的内容解释:

simple/CMakeLists.txt:

cmake_minimum_required(VERSION 2.6)

project(Tutorial)

# Add all subdirectories in this project
add_subdirectory(lib)
add_subdirectory(src)

根据 CMake 在您不这样做时抛出的警告,应始终设置最低要求的版本。使用您的 CMake 版本。

您的项目名称可以在以后使用,并暗示您可以从同一个 CMake 文件管理多个项目。不过,我不会深入研究。

如前所述,add_subdirectory() 向项目添加了一个文件夹,这意味着 CMake 期望它在其中有一个 CMakeLists.txt,然后它将在继续之前运行它。顺便说一句,如果你碰巧定义了一个 CMake 函数,你可以在子目录中的其他 CMakeLists.txts 中使用它,但是你必须在使用 add_subdirectory() 之前定义它,否则它不会找到它。不过,CMake 在库方面更聪明,因此这可能是您遇到此类问题的唯一一次。

simple/lib/CMakeLists.txt:

add_library(TestLib TestLib.cxx)

要创建您自己的库,请为其命名,然后列出构建它的所有文件。直截了当。如果它需要另一个文件foo.cxx 来编译,你可以写add_library(TestLib TestLib.cxx foo.cxx)。这也适用于其他目录中的文件,例如add_library(TestLib TestLib.cxx ${CMAKE_SOURCE_DIR}/foo.cxx)。稍后将详细介绍 CMAKE_SOURCE_DIR 变量。

你可以用它做的另一件事是指定你想要一个共享库。示例:add_library(TestLib SHARED TestLib.cxx)。不要害怕,这就是 CMake 开始让您的生活更轻松的地方。无论是否共享,现在使用以这种方式创建的库所需要处理的只是您在此处给它的名称。这个库的名称现在是 TestLib,您可以从项目中的anywhere 引用它。 CMake 会找到它。

有没有更好的方法来列出依赖项? Definitely yes。请在下方查看有关此内容的更多信息。

simple/lib/TestLib.cxx:

#include <stdio.h>

void test() {
  printf("testing...\n");
}

simple/lib/TestLib.h:

#ifndef TestLib
#define TestLib

void test();

#endif

simple/src/CMakeLists.txt:

# Name the executable and all resources it depends on directly
add_executable(Tutorial tutorial.cxx)

# Link to needed libraries
target_link_libraries(Tutorial TestLib)

# Tell CMake where to look for the .h files
target_include_directories(Tutorial PUBLIC ${CMAKE_SOURCE_DIR}/lib)

命令add_executable() 的工作方式与add_library() 完全相同,当然,它会生成一个可执行文件。这个可执行文件现在可以作为target_link_libraries() 之类的目标引用。由于 tutorial.cxx 使用在 TestLib 库中找到的代码,因此您向 CMake 指出这一点,如图所示。

类似地,add_executable() 中的任何源所包含的任何 .h 文件都必须以某种方式添加。如果不是target_include_directories() 命令,编译Tutorial 时将找不到lib/TestLib.h,因此将整个lib/ 文件夹添加到包含目录中以搜索#includes。您可能还会看到命令include_directories(),它以类似的方式运行,除了它不需要您指定目标,因为它直接为所有可执行文件全局设置它。再一次,我稍后会解释 CMAKE_SOURCE_DIR。

simple/src/tutorial.cxx:

#include <stdio.h>
#include "TestLib.h"
int main (int argc, char *argv[])
{
  test();
  fprintf(stdout, "Main\n");
  return 0;
}

注意如何包含“TestLib.h”文件。无需包含完整路径:感谢target_include_directories(),CMake 会在幕后处理所有事情。

从技术上讲,在像这样的简单源代码树中,您可以不使用 lib/src/ 下的 CMakeLists.txts,只需将 add_executable(Tutorial src/tutorial.cxx) 之类的内容添加到 simple/CMakeLists.txt。这取决于您和您项目的需求。

我还应该知道什么才能正确使用 CMake?

(与您的理解相关的又名主题)

查找和使用包The answer to this question 解释得比以往任何时候都好。

声明变量和函数,使用控制流等:查看 this tutorial,它解释了 CMake 必须提供的基础知识,并且是一个很好的一般介绍。 p>

CMake 变量:有很多,所以接下来是一个速成课程,可以让你走上正轨。 The CMake wiki is a good place to get more in-depth information on variables 以及其他表面上的东西。

您可能希望在不重新构建构建树的情况下编辑一些变量。为此使用 ccmake(它编辑 CMakeCache.txt 文件)。完成更改后记得configure,然后generate 使用更新的配置生成makefile。

阅读previously referenced tutorial 以了解如何使用变量,但长话短说: set(&lt;variable name&gt; value) 更改或创建变量。 ${&lt;variable name&gt;} 使用它。

  • CMAKE_SOURCE_DIR:源码根目录。在前面的例子中,这总是等于/simple
  • CMAKE_BINARY_DIR:构建的根目录。在前面的示例中,这等于simple/build/,但如果您从foo/bar/etc/ 等文件夹运行cmake simple/,则该构建树中对CMAKE_BINARY_DIR 的所有引用都将变为/foo/bar/etc
  • CMAKE_CURRENT_SOURCE_DIR:当前CMakeLists.txt 所在的目录。这意味着它始终在变化:从simple/CMakeLists.txt 打印会产生/simple,从simple/src/CMakeLists.txt 打印会产生/simple/src
  • CMAKE_CURRENT_BINARY_DIR:你懂的。此路径不仅取决于构建所在的文件夹,还取决于当前 CMakeLists.txt 脚本的位置。

为什么这些很重要?源文件显然不会在构建树中。如果您在前面的示例中尝试类似target_include_directories(Tutorial PUBLIC ../lib) 的内容,那么该路径将相对于构建树,也就是说,它就像写${CMAKE_BINARY_DIR}/lib,它将在simple/build/lib/ 内部查找。那里没有 .h 文件;最多你会找到libTestLib.a。你想要${CMAKE_SOURCE_DIR}/lib

  • CMAKE_CXX_FLAGS:传递给编译器的标志,在这种情况下是 C++ 编译器。另外值得注意的是CMAKE_CXX_FLAGS_DEBUG,如果CMAKE_BUILD_TYPE 设置为DEBUG,则将使用它。还有更多这样的;查看the CMake wiki
  • CMAKE_RUNTIME_OUTPUT_DIRECTORY:告诉 CMake 在构建时将所有可执行文件放在哪里。这是一个全局设置。例如,您可以将其设置为 bin/ 并将所有内容整齐地放置在那里。 EXECUTABLE_OUTPUT_PATH 类似,但已弃用,以防您偶然发现它。
  • CMAKE_LIBRARY_OUTPUT_DIRECTORY:同样,一个全局设置告诉 CMake 将所有库文件放在哪里。

目标属性:您可以设置只影响一个目标的属性,无论是可执行文件还是库(或存档......你明白了)。 Here is a good example 如何使用它(与set_target_properties().

有没有一种简单的方法可以自动将源添加到目标?Use GLOB 可以在同一变量下列出给定目录中的所有内容。示例语法为FILE(GLOB &lt;variable name&gt; &lt;directory&gt;/*.cxx)

你能指定不同的构建类型吗?可以,但我不确定它是如何工作的或它的局限性。它可能需要一些 if/then'ning,但 CMake 确实提供了一些基本支持而无需配置任何东西,例如 CMAKE_CXX_FLAGS_DEBUG 的默认值。 您可以通过set(CMAKE_BUILD_TYPE &lt;type&gt;)CMakeLists.txt 文件中设置构建类型,也可以使用适当的标志从控制台调用CMake,例如cmake -DCMAKE_BUILD_TYPE=Debug

有使用 CMake 的项目的好例子吗? 维基百科有一个使用 CMake 的开源项目列表,如果你想了解的话。到目前为止,在线教程在这方面让我失望,但是this Stack Overflow question 有一个非常酷且易于理解的 CMake 设置。值得一看。

在代码中使用 CMake 中的变量:这是一个快速而肮脏的示例(改编自 some other tutorial):

simple/CMakeLists.txt:

project (Tutorial)

# Setting variables
set (Tutorial_VERSION_MAJOR 1)
set (Tutorial_VERSION_MINOR 1)

# Configure_file(<input> <output>)
# Copies a file <input> to file <output> and substitutes variable values referenced in the file content.
# So you can pass some CMake variables to the source code (in this case version numbers)
configure_file (
  "${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
  "${PROJECT_SOURCE_DIR}/src/TutorialConfig.h"
)

simple/TutorialConfig.h.in:

// Configured options and settings
#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@

CMake 生成的结果文件,simple/src/TutorialConfig.h:

// Configured options and settings
#define Tutorial_VERSION_MAJOR 1
#define Tutorial_VERSION_MINOR 1

通过巧妙地使用这些,您可以做一些很酷的事情,例如关闭库等。我确实建议您查看that tutorial,因为有一些更高级的东西迟早会对大型项目非常有用。

对于其他一切,Stack Overflow 充满了具体的问题和简洁的答案,这对除了初学者之外的所有人来说都很棒。

【讨论】:

  • 我接受这个答案,但如果其他人写了一个更好、更短的答案,我会选择那个。我会自己做,但我已经受够了 CMake 的痛苦。
  • 感谢您的出色工作。希望这能得到更多的投票! :)
  • 这里相当低调的一点:使用 CMake,您不再需要维护特定于编译器/构建环境的单独设置。您有 一个 配置,适用于(各种版本的)Visual Studio、代码块、普通 Unix Makefile 和许多其他环境。这就是我最初关注 CMake 的原因:让 Autoconf、MSVC 2005 / 32 位和 MSVC 2010 / 64 位配置保持同步成为一大难题。一旦掌握了窍门,我就添加了更多东西,例如文档生成、测试、打包等,所有这些都以跨平台的方式进行。
  • @DevSolar 是的。我比维基百科更喜欢这条线,所以如果你不介意,我会把它添加到答案中。不过,我不适合任何关于如何正确使用 CMake 的内容。这更多是为了能够告诉正在发生的事情。
  • @RichieHH 按照示例,您首先将 CMake 配置为使用 Makefiles,然后使用 CMake 生成项目需要构建的文件,最后您不再担心 CMake,因为它的工作已经完成.因此,您“担心” Makefile,因为从现在开始您需要在 bash 中使用 make 来制作您的项目。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-23
  • 2011-12-13
  • 2016-02-09
相关资源
最近更新 更多