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.txt
s之下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.txt
yields /simple
打印出来,从simple/src/CMakeLists.txt
yields 打印出来/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都充满了特定的问题和简洁的答案,这对除未启动者以外的每个人都非常有用。