CMake如何使用?[关闭]


95

众所周知,作为初学者,很难获得有关CMake的任何有用信息。到目前为止,我已经看了一些关于如何建立一些非常基础的项目或其他项目的教程。但是,这些都无法解释任何背后的原因是他们中所示,总是留下许多孔,以填补。

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

到目前为止,我从未见过的教程,文档页面或问题/答案都无法提供任何有用的见解,以帮助您了解如何使用CMake。这些例子只是不彻底的。无论阅读什么教程,我都觉得自己缺少了一些重要的东西。

像我这样的CMake新手会问很多问题,这些问题并没有明确地询问,但显而易见,作为新手,我们不知道如何处理CMake或如何处理它。


8
也许您应该写一篇博客文章。
steveire

2
我很想结束投票“太宽泛”……这感觉更像是关于CMake的个人文章,而不是适合stackoverflow的文章。您为什么不将所有这些问题都放在单独的问题中?又是怎样从许多人,如你的问题的答案+不同stackoverflow.com/q/20884380/417197stackoverflow.com/q/4745996/417197stackoverflow.com/q/4506193/417197
安德烈(André)2014年

13
@Andre为什么这样的问题:我花了一个星期的时间来理解CMake,但是我发现没有完整的教程。您指出的那些链接是很好的,但是对于发现CMake的人来说,因为他们被一个带有大量CMakeList的项目投向了他们,所以他们对CMake的工作原理并没有太多了解。老实说,我一直试图写一个答案,希望当我开始学习CMake时能及时发回给自己。子问题旨在表明我实际上是在试图问我应该知道什么,以免产生那些疑问。提出正确的问题很困难。
leinaD_natipaC 2014年

2
这不是一个答案,但是我邀请您看看jaws.rootdirectory.de。CMake dokumentation(和可接受的答案...)向您展示了您可以做什么的一小段代码片段,而我写了一个(绝不是“完整”或“完美”的)基本设置,包括全文注释,恕我直言,向您展示CMake(以及CTest,CPack和CDash ...)可以为您做什么。它是开箱即用的,您可以轻松地将其适应/扩展为项目的需求。
DevSolar 2015年

9
如今这样的问题被关闭很可惜。我认为那些需要教程之类的广泛问题,就像答案一样,但涉及的主题记录不佳/模糊(另一个示例是Torch7 C api),研究级别的问题应该保持开放。那些使网站困扰的典型的“嘿,我在做玩具项目时遇到了段错误”,使这些变得更加有趣。
Ash Ash'3

Answers:


137

CMake有什么用?

根据维基百科:

CMake是用于使用独立于编译器的方法管理软件构建过程的软件。它旨在支持依赖于多个库的目录层次结构和应用程序。它与本机构建环境(如make,Apple的Xcode和Microsoft Visual Studio)结合使用。

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

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

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

如果设置正确,则可以使用CMake创建您选择的“本机构建环境”完成其工作所需的所有文件。在Linux中,默认情况下,这意味着Makefile。因此,一旦运行CMake,它将创建一堆文件供自己使用,并添加一些Makefile。之后,您需要做的就是每次完成代码编辑后在控制台的根文件夹中键入“ make”,然后bam将生成一个已编译并链接的可执行文件。

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

这是我将始终使用的示例项目设置:

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

每个文件的内容将在后面显示和讨论。

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

我不知道如果您执行cmake除root以外的任何操作会发生什么CMakeLists.txt

在此示例中,由于我希望将其全部放置在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

这些文件都是什么? 您唯一需要担心的是Makefile和项目文件夹

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

make

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

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

项目已构建,并且可执行文件已准备好执行。如果要将可执行文件放入特定的文件夹,该怎么办? 设置适当的CMake变量,或更改特定目标的属性。稍后更多关于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.txt子目录中的其他s中使用它,但是必须在使用前定义它,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,您可以在项目中的任何位置引用它。CMake将找到它。

有没有更好的方法列出依赖项? 绝对可以。请在下面查看有关此内容的更多信息。

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,如图所示。

同样,在任何光源执行#included任何.h文件add_executable()不是在同一个目录作为源必须以某种方式加入。如果不是该target_include_directories()命令,则lib/TestLib.h在编译Tutorial时将找不到该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”文件。无需包括完整路径:借助,CMake可以处理所有幕后工作target_include_directories()

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

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

(又称与您的理解有关的主题)

查找和使用软件包这个问题的答案比以往任何时候都可以更好地解释它。

声明变量和函数,使用控制流等:请查看本教程该教程解释了CMake必须提供的基本知识,并且作为一个很好的入门指南。

CMake变量:有很多,因此接下来的是速成班,以使您走上正确的道路。CMake Wiki是获取有关变量以及表面上其他事物的更深入信息的好地方

您可能需要编辑一些变量而不重建构建树。为此使用ccmake(它将编辑CMakeCache.txt文件)。记住c在完成更改后将g其配置为开模,然后使用更新后的配置为makefile赋能。

阅读先前参考的教程,以了解有关使用变量的知识,但总而言之: set(<variable name> value)更改或创建变量。 ${<variable name>}使用它。

  • CMAKE_SOURCE_DIR:源的根目录。在前面的示例中,这始终等于/simple
  • CMAKE_BINARY_DIR:构建的根目录。在上一个示例中,该值等于simple/build/,但是如果您cmake simple/从诸如的文件夹运行foo/bar/etc/,则CMAKE_BINARY_DIR该构建树中对的所有引用都将变为/foo/bar/etc
  • CMAKE_CURRENT_SOURCE_DIR:当前CMakeLists.txt所在的目录。这意味着它在整个过程中都发生变化:从simple/CMakeLists.txtyields /simple打印出来,从simple/src/CMakeLists.txtyields 打印出来/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 ,则将使用该选项。还有更多类似的东西。查看CMake Wiki
  • CMAKE_RUNTIME_OUTPUT_DIRECTORY:告诉CMake构建时将所有可执行文件放在何处。这是一个全局设置。例如,您可以将其设置为bin/并将所有内容整齐地放置在此处。EXECUTABLE_OUTPUT_PATH是类似的,但不推荐使用,以防您偶然发现它。
  • CMAKE_LIBRARY_OUTPUT_DIRECTORY:同样,这是一个全局设置,用于告诉CMake将所有库文件放在何处。

目标属性:您可以设置仅影响一个目标的属性,无论是可执行文件还是库(或存档,您都可以理解)。这是如何使用它的一个很好的例子(使用set_target_properties()

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

您可以指定其他构建类型吗?是的,尽管我不确定这是如何工作的或它的局限性。它可能需要一些if / then'ning,但CMake确实提供了一些基本支持,而无需进行任何配置,例如,默认为CMAKE_CXX_FLAGS_DEBUG。您可以通过在CMakeLists.txt文件中设置构建类型,set(CMAKE_BUILD_TYPE <type>)或者通过使用适当的标志从控制台调用CMake来进行设置,例如cmake -DCMAKE_BUILD_TYPE=Debug

使用CMake的项目有什么好的例子吗?Wikipedia列出了使用CMake的开源项目,如果您想研究的话。到目前为止,在线教程对我而言只是一个失望,但是,这个Stack Overflow问题具有非常酷且易于理解的CMake设置。值得一看。

在代码中使用CMake中的变量:这是一个简单又肮脏的示例(改编自其他教程):

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

巧妙地使用它们,您可以做一些很酷的事情,例如关闭图书馆等。我确实建议您看一下该教程,因为迟早会有一些稍微高级的内容在较大的项目上非常有用。

对于其他所有内容,Stack Overflow都充满了特定的问题和简洁的答案,这对除未启动者以外的每个人都非常有用。


2
我接受这个答案,但是如果有人写一个更好,更短的答案,我会选择的。我自己做,但是我已经受够了CMake。
leinaD_natipaC 2014年

6
感谢您所做的出色工作。希望这能获得更多投票!:)
LETs 2015年

4
那里相当低调的一点是:有了CMake,您不再需要维护特定于您的编译器/构建环境的单独设置。您只有一种配置,该配置适用于Visual Studio,代码块,纯Unix Makefile和其他许多环境(的各种版本)。这就是为什么我最初关注CMake的原因:保持Autoconf,MSVC 2005 / 32bit和MSVC 2010 / 64bit配置同步变得很困难。一旦掌握了这些知识,我便添加了更多内容,例如文档生成,测试,打包等,所有这些内容都是跨平台的。
DevSolar 2015年

1
@DevSolar对。我比维基百科更喜欢那条线,所以如果您不介意,我会将其添加到答案中。但是,我不适合任何有关如何正确使用CMake的事情。这更多是关于能够告诉发生了什么。
leinaD_natipaC 2015年

1
@RichieHH在此示例之后,首先将CMake配置为使用Makefile,然后使用CMake生成需要构建项目的文件,最后您不再担心CMake,因为它的工作已经完成。因此,从某种意义上说,您“担心” Makefile,从现在开始,您需要make在bash中使用它来制作您的项目。
leinaD_natipaC
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.