CMake是什么?
根据维基百科:
CMake是一种软件,用于使用独立于编译器的方法管理软件的构建过程。它旨在支持目录层次结构和依赖于多个库的应用程序。它与本地构建环境(如make、苹果的Xcode和Microsoft Visual Studio)一起使用。
使用CMake,您不再需要维护特定于编译器/构建环境的单独设置。您只需拥有一个配置,即可适用于多个环境。
CMake可以从相同的文件生成Microsoft Visual Studio解决方案、Eclipse项目或Makefile迷宫,而无需更改它们中的任何内容。
给定一堆包含代码的目录,CMake管理所有依赖项、构建顺序和其他任务,以便在编译之前完成您的项目所需的工作。它实际上不会编译任何东西。要使用CMake,您必须告诉它(使用称为CMakeLists.txt的配置文件)需要编译哪些可执行文件,它们链接到哪些库,您的项目中有哪些目录以及其中的内容,以及任何您需要的详细信息,如标志或其他内容(CMake非常强大)。
如果正确设置,您可以使用CMake创建所有所选的“本机构建环境”所需的文件。在Linux中,默认情况下,这意味着Makefiles。因此,一旦运行CMake,它将为自己使用创建一堆文件以及一些
Makefile
。之后,您只需要在每次编辑代码完成后从根文件夹中的控制台键入“make”,就会生成已编译和链接的可执行文件。
CMake是如何工作的?它做了什么?
以下是我将在整个过程中使用的示例项目设置:
simple/
CMakeLists.txt
src/
tutorial.cxx
CMakeLists.txt
lib/
TestLib.cxx
TestLib.h
CMakeLists.txt
build/
每个文件的内容稍后会显示并讨论。
CMake根据项目的
根CMakeLists.txt
设置您的项目,并在您在控制台中执行
cmake
的任何目录中进行设置。从不是项目根目录的文件夹中执行此操作会产生所谓的
源外构建,这意味着在编译过程中创建的文件(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
这些文件都是什么? 你唯一需要担心的是Makefile和项目文件夹。
注意 src/
和 lib/
文件夹。它们被创建是因为 simple/CMakeLists.txt
使用命令 add_subdirectory(<folder>)
指向它们。这个命令告诉 CMake 在该文件夹中查找另一个 CMakeLists.txt
文件并执行 那个 脚本,所以每个通过这种方式添加的子目录 必须 包含一个 CMakeLists.txt
文件。在这个项目中,simple/src/CMakeLists.txt
描述了如何构建实际的可执行文件,而 simple/lib/CMakeLists.txt
则描述了如何构建库。每个由 CMakeLists.txt
描述的目标都将默认放置在其子目录中的构建树中。所以,在快速完成后。
make
控制台显示完成,来自build/
目录,一些文件已被添加:
simple/build/
(...)
lib/
libTestLib.a
(...)
src/
Tutorial
(...)
该项目已构建完成,可执行文件已经准备好运行。
如果想要将可执行文件放到特定文件夹中,该怎么办呢? 设置适当的CMake变量,或更改特定目标的属性。有关CMake变量的更多信息,请参考后续内容。
如何告诉CMake如何构建我的项目?
下面是源目录中每个文件的内容说明:
simple/CMakeLists.txt
:
cmake_minimum_required(VERSION 2.6)
project(Tutorial)
add_subdirectory(lib)
add_subdirectory(src)
根据CMake的警告,应始终设置所需的最低版本。使用您的CMake版本即可。
您的项目名称可以稍后使用,并提示您可以从相同的CMake文件管理多个项目。我不会深入探讨这一点。
如前所述,add_subdirectory()
将一个文件夹添加到项目中,这意味着CMake希望该文件夹内有一个CMakeLists.txt
文件,在继续之前运行它。顺便说一下,如果您恰好定义了一个CMake函数,则可以从子目录中的其他CMakeLists.txt
中使用它,但必须在使用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
:
add_executable(Tutorial tutorial.cxx)
target_link_libraries(Tutorial TestLib)
target_include_directories(Tutorial PUBLIC ${CMAKE_SOURCE_DIR}/lib)
add_executable()
命令与 add_library()
命令完全相同,除了它会生成一个可执行文件。这个可执行文件现在可以被用作一些像 target_link_libraries()
的目标。由于 tutorial.cxx 使用了 TestLib 库中的代码,你需要向 CMake 指出这一点,如下所示。
同样地,任何在 add_executable()
中被源文件 #include 的 .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.txt
文件,只需在
simple/CMakeLists.txt
中添加类似
add_executable(Tutorial src/tutorial.cxx)
的内容即可。这取决于您和您的项目的需求。
我还需要了解哪些内容才能正确使用 CMake?
(也就是与您理解相关的主题)
查找和使用软件包:
这个问题的答案 比我更好地解释了它。
声明变量和函数,使用控制流等:请查看
此教程,它解释了CMake提供的基础知识,并且是一个很好的入门介绍。
CMake变量:有很多种,因此下面是一个快速入门以帮助您上手。
CMake维基是获取更深入信息的好地方,也包括其他内容。
您可能想要编辑一些变量而不重新构建构建树。为此,请使用ccmake(它会编辑
CMakeCache.txt
文件)。完成更改后,请记得进行
c
onfigure,然后使用更新的配置
g
enerate makefiles。
阅读
先前提到的教程以了解如何使用变量,简而言之:
set(<variable name> value)
用于更改或创建变量。
${<variable name>}
用于使用它。
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_BUILD_TYPE
为DEBUG,则将使用CMAKE_CXX_FLAGS_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操作,但CMake提供了一些基本支持,例如默认值
CMAKE_CXX_FLAGS_DEBUG
。您可以通过在
CMakeLists.txt
文件中设置
set(CMAKE_BUILD_TYPE <type>)
或通过使用适当的标志从控制台调用CMake来设置构建类型,例如
cmake -DCMAKE_BUILD_TYPE=Debug
。
有哪些使用CMake的好项目示例?维基百科列出了使用CMake的开源项目列表,如果您想了解更多信息,可以查看。然而,在这方面,在线教程对我来说一直是一个失望,但
此Stack Overflow问题有一个非常酷且易于理解的CMake设置。值得一看。
在代码中使用CMake的变量:这里有一个快速而简单的例子(改编自某个教程):
simple/CMakeLists.txt
:
project (Tutorial)
set (Tutorial_VERSION_MAJOR 1)
set (Tutorial_VERSION_MINOR 1)
configure_file (
"${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
"${PROJECT_SOURCE_DIR}/src/TutorialConfig.h"
)
simple/TutorialConfig.h.in
:
// Configured options and settings
由CMake生成的结果文件,
simple/src/TutorialConfig.h
:
// Configured options and settings
通过巧妙地运用这些技巧,您可以做一些很酷的事情,比如关闭一个库等。我建议您查看该教程,因为其中还有一些稍微高级的东西,在大型项目中迟早会非常有用。
对于其他所有内容,Stack Overflow都充满了具体问题和简洁的答案,这对于除了未经训练的人以外的所有人来说都很好。