CMake如何使用?
CMake作为初学者获得任何有用的信息是非常困难的。 到目前为止,我已经看到了一些关于如何设置一些非常基本的项目的教程。 但是,这些都不能解释任何东西背后的原因,总是留下很多空缺来填补。
在CMakeLists上调用CMake 是什么意思 ? 它应该被称为每个构build树或什么? 如果每个版本都使用来自同一个源的相同CMakeLists,那么我如何使用不同的设置? 为什么每个子目录都需要自己的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}/lib
, simple/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:
- http://ftp.heanet.ie/mirrors/fosdem-video/2008/maintracks/FOSDEM2008-cmake.ogg
- http://www.kitware.com/media/protrainingwebinars.php#introcmake
希望这有助于(和CMake,及其糟糕的文档, #^& !):) 🙂