CMake如何使用?

CMake作为初学者获得任何有用的信息是非常困难的。 到目前为止,我已经看到了一些关于如何设置一些非常基本的项目的教程。 但是,这些都不能解释任何东西背后的原因,总是留下很多空缺来填补。

在CMakeLists上调用CMake 什么意思 ? 它应该被称为每个构build树或什么? 如果每个版本都使用来自同一个源的相同C​​MakeLists,那么我如何使用不同的设置? 为什么每个子目录都需要自己的CMakeLists? 在项目根目录以外的CMakeLists上使用CMake是否有意义? 如果是这样,在什么情况下? 指定如何在CMakeLists的子目录中创build可执行文件或库与在所有源的CMakeLists中执行该操作有什么区别? 我可以做一个项目的Eclipse和Visual Studio的只是改变-G选项时调用CMake? 这甚至是如何使用?

到目前为止,我所见过的教程,文档页面或问题/答案都没有提供有助于理解如何使用CMake的有用信息。 这些例子只是不彻底。 无论我读什么教程,我都觉得我错过了一些重要的东西。

像我这样的CMake新手问了很多问题,这些问题并没有明确地提出,但是这样做显而易见,作为新手,他们不知道如何处理CMake或者如何处理CMake,所以即使有一个问题这样看起来可能太宽泛了,我认为值得留下来回答。

我已经通过互联网find的废料回答了我自己的问题,我打算在这里分享答案。 如果你们中的任何一个熟悉CMake,请帮我纠正一些我可能会误解的东西。

CMake是什么?

根据维基百科:

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

使用CMake,您不再需要维护特定于您的编译器/编译环境的独立设置。 你有一个configuration,并且适用于很多环境。 Cmake可以从相同的文件中生成一个Microsoft Visual Studio解决scheme,一个Eclipse项目或一个Makefile迷宫,而不需要改变任何内容。

给定一些带有代码的目录,CMakepipe理所有的依赖关系,构build命令和项目需要完成的其他任务,然后才能编译。 它并不实际编译任何东西。 要使用CMake,必须告诉它(使用名为CMakeLists.txt的configuration文件)需要编译的可执行文件,链接的库,项目中的目录以及内部的目录,以及标记或者其他你需要的东西(CMake非常强大)。 如果设置正确,则使用CMake创build所select的“本地构build环境”所需的所有文件。 在Linux中,默认情况下,这意味着Makefiles。 所以一旦你运行CMake,它会创build一堆文件供自己使用,加上一些Makefile 。 所有你需要做的事情是每次你完成编辑你的代码的时候,从根目录下的控制台里input“make”,然后编译和链接可执行文件bam。

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

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

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

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

CMake根据项目的根目录 CMakeLists.txt设置你的项目,并且在你从控制台执行cmake任何目录中都这样做。 从不是项目根目录的文件夹产生所谓的源外构build,这意味着在编译期间创build的文件(obj文件,lib文件,可执行文件,你知道)将被放置在所述文件夹中,与实际的代码保持分开。 它有助于减less混乱,也是其他原因的首选,我不会讨论。

我不知道如果在root CMakeLists.txt之外执行cmake会发生什么情况。

在这个例子中,因为我想把它全部放在build/文件夹中,所以首先我必须在那里导航,然后把CMake根CMakeLists.txt所在的目录传递给CMakeLists.txt

 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/文件夹。 这些已经被创build,因为simple/CMakeLists.txt使用add_subdirectory(<folder>)命令指向它们。 这个命令告诉CMake在所述文件夹中查找另一个CMakeLists.txt文件并执行脚本,因此每个以这种方式添加的子目录都必须有一个CMakeLists.txt文件。 在这个项目中, simple/src/CMakeLists.txt描述了如何构build实际的可执行文件, simple/lib/CMakeLists.txt描述了如何构build库。 CMakeLists.txt描述的每个目标将默认放置在构build树内的子目录中。 所以,快点之后

 make 

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

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

该项目已build成,可执行文件已准备好执行。 如果你想将可执行文件放在特定的文件夹中,你会怎么做? 设置适当的CMakevariables,或更改特定目标的属性 。 稍后更多关于CMakevariables。

我如何告诉CMake如何build立我的项目?

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

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文件pipe理多个项目。 不过,我不会深究的。 如前所述, add_subdirectory()向项目添加一个文件夹,这意味着CMake希望它有一个CMakeLists.txt ,然后在继续之前运行它。 顺便说一下,如果你碰巧定义了一个CMake函数,你可以在子目录中使用CMakeLists.txt ,但是你必须在使用add_subdirectory()之前定义它,否则它不会find它。 但是,CMake对于图书馆更聪明,所以这可能是唯一一次遇到这种问题。

simple/lib/CMakeLists.txt

 add_library(TestLib TestLib.cxx) 

为了制作自己的图书馆,你给它一个名字,然后列出它所build立的所有文件。 直截了当。 如果需要编译另一个文件foo.cxx ,则可以编写add_library(TestLib TestLib.cxx foo.cxx) 。 这也适用于其他目录中的文件,例如add_library(TestLib TestLib.cxx ${CMAKE_SOURCE_DIR}/foo.cxx) 。 稍后更多关于CMAKE_SOURCE_DIRvariables。 你可以做的另一件事是指定你想要一个共享库。 例如: add_library(TestLib SHARED TestLib.cxx) 。 不要害怕,这是CMake开始让你的生活更轻松的地方。 无论是否共享,现在您只需要使用以这种方式创build的库即可处理您的名字。 这个库的名字现在是TestLib,你可以在项目的任何地方引用它。 CMake会find它。

有没有更好的方法来列出依赖关系? 肯定是的 ,请在下面查看更多。

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()完全一样,当然除了生成一个可执行文件。 现在可以将此可执行文件作为target_link_libraries()类的目标引用。 由于tutorial.cxx使用了TestLib库中的代码,因此您可以如图所示将它指向CMake。 同样的,包含add_executable()add_executable()文件的任何.h文件都不得与源代码位于同一个目录中。 如果不是针对target_include_directories()命令,则在编译Tutorial时将不会findlib/TestLib.h ,因此将整个lib/文件夹添加到include目录中以search#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”文件。 没有path是必要的; 由于target_include_directories() ,CMake负责所有幕后工作。

从技术上来说,在一个简单的源代码树中,你可以在lib/src/joinCMakeLists.txt ,然后添加像add_executable(Tutorial src/tutorial.cxx)simple/CMakeLists.txt 。 这取决于你和你的项目的需求。

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

(与你的理解相关的主题)

发现和使用软件包 : 这个问题的答案比我所能解释的更好。

声明variables和函数,使用控制stream程等等 :查看本教程 , 该教程解释了CMake所提供的基础知识,以及一般的良好介绍。

CMakevariables :有很多,所以接下来是一个速成课程,让你走上正轨。 CMake wiki是获取更多关于variables和表面上其他信息的深入信息的好地方 。

您可能想要编辑一些variables而不重build构build树。 使用ccmake(编辑CMakeCache.txt文件)。 请记住在完成更改后再进行configuration,然后使用更新后的configuration文件生成文件。

阅读以前参考的教程 ,了解使用variables,但长话短说: set(<variable name> value)来更改或创build一个variables。 ${<variable name>}来使用它。

  • CMAKE_SOURCE_DIR :源的根目录。 在前面的例子中,这总是等于/simple
  • CMAKE_BINARY_DIR :构build的根目录。 在前面的例子中,这与simple/build/是等价的,但是如果你从一个文件夹(例如foo/bar/etc/运行cmake simple/ foo/bar/etc/ ,那么所有对该构build树中的CMAKE_BINARY_DIR引用将变成/foo/bar/etc
  • CMAKE_CURRENT_SOURCE_DIR :当前CMakeLists.txt所在的目录。这意味着它会在整个过程中改变:从simple/CMakeLists.txt打印这个文件yield /simple ,并从simple/src/CMakeLists.txt打印出来yield /simple/src
  • CMAKE_CURRENT_BINARY_DIR :你明白了。 这个path不仅取决于构build文件夹,还取决于当前CMakeLists.txt脚本的位置。

为什么这些重要? 源文件显然不会在构build树中。 如果你在前面的例子中尝试了类似于target_include_directories(Tutorial PUBLIC ../lib)东西,那么这个path将是相对于构build树的,也就是说它会写${CMAKE_BINARY_DIR}/libsimple/build/lib/ 。 那里没有.h文件,最多可以findlibTestLib.a 。 您需要改为${CMAKE_SOURCE_DIR}/lib

  • CMAKE_CXX_FLAGS :传递给编译器的标志,本例中是c ++编译器。 另外值得注意的是CMAKE_CXX_FLAGS_DEBUG ,如果CMAKE_BUILD_TYPE被设置为DEBUG,将被使用。 还有更多这样的,请查看CMake wiki 。
  • CMAKE_RUNTIME_OUTPUT_DIRECTORY :告诉CMake何时build立所有的可执行文件。 这是一个全球性的设置。 例如,您可以将其设置为bin/并将所有内容整齐地放置在那里。 EXECUTABLE_OUTPUT_PATH类似,但不推荐使用,以免偶然发现。
  • CMAKE_LIBRARY_OUTPUT_DIRECTORY :同样,全局设置告诉CMake把所有的库文件放在哪里。

目标属性 :你可以设置只影响一个目标的属性,可以是一个可执行文件或者一个库(或者一个档案文件…)。 这里是一个很好的例子 (使用set_target_properties()

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

你能指定不同的构buildtypes吗? 是的,虽然我不确定这是如何工作或这种限制。 这可能需要一些if / thenning,但CMake提供了一些基本的支持,而不需要configuration任何东西,比如CMAKE_CXX_FLAGS_DEBUG默认值。 您可以通过set(CMAKE_BUILD_TYPE <type>)CMakeLists.txt set(CMAKE_BUILD_TYPE <type>)也可以通过从控制台调用具有相应标志的CMake来set(CMAKE_BUILD_TYPE <type>) ,例如cmake -DCMAKE_BUILD_TYPE=Debug

任何使用CMake的项目的好例子? 如果你想研究一下,Wikipedia有一个使用CMake的开源项目列表。 在线教程对我来说只是一个令人失望的事情,但是这个堆栈溢出问题有一个非常酷和容易理解的CMake安装程序。 值得一看。

在你的代码中使用来自CMake的variables :这是一个快速而肮脏的例子(改编自其他一些教程 ):

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 

聪明地使用这些,你可以做一些很酷的事情,比如关掉一个库等等。 我build议看一下这个教程,因为有些稍微高级的东西,迟早会在大型项目上非常有用。

对于其他的东西,堆栈溢出充满了特定的问题和简洁的答案,这对除了外行人员以外的每个人都很好。

这里有一些(很难find,关于CMake的一切)vids:

希望这有助于(和CMake,及其糟糕的文档, #^& !):) 🙂