CMake是如何使用的? [英] How is CMake used?

查看:21
本文介绍了CMake是如何使用的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

众所周知,初学者很难获得有关 CMake 的任何有用信息.到目前为止,我已经看过一些关于如何设置一些非常基本的项目或其他项目的教程.然而,这些都没有解释任何内容背后的原因,总是留下许多漏洞需要填补.

It is notoriously difficult to get any useful information on CMake as a beginner. So far, I've seen a few tutorials on how to set up some very basic project or another. However, none of these explain the reasoning behind anything that is shown in them, always leaving many holes to fill.

在 CMakeLists 上调用 CMake 是什么意思?每个构建树应该调用一次还是什么?如果每个构建都使用来自同一来源的相同 CMakeLists.txt 文件,我该如何为每个构建使用不同的设置?为什么每个子目录都需要自己的 CMakeLists 文件?在 CMakeLists.txt 而非项目根目录的 CMakeLists.txt 上使用 CMake 是否有意义?如果是,在什么情况下?指定如何从它们自己的子目录中的 CMakeLists 文件构建可执行文件或库与在所有源的根目录下的 CMakeLists 文件中执行有什么区别?我可以为 Eclipse 制作一个项目,为 Visual Studio 制作另一个项目,只需在调用 CMake 时更改 -G 选项吗?甚至是这样使用的吗?

What does calling CMake on a CMakeLists mean? Is it supposed to be called once per build tree or what? How do I use different settings for each build if they all use the same CMakeLists.txt file from the same source? Why does each subdirectory need its own CMakeLists file? Would it make sense to use CMake on a CMakeLists.txt other than the one at the root of the project? If so, in what cases? What's the difference between specifying how to build an executable or library from the CMakeLists file in their own subdirectory versus doing it in the CMakeLists file at the root of all source? Can I make a project for Eclipse and another for Visual Studio, just changing the -G option when calling CMake? Is that even how it's used?

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

None of the tutorials, documentation pages or questions/answers I've seen so far give any useful insight towards understanding how to use CMake. The examples are just not thorough. No matter what tutorials I read, I feel like I'm missing something important.

像我这样的 CMake 新手提出的很多问题都没有明确提出这一点,但显而易见的是,作为新手,我们不知道如何处理 CMake 或如何制作它.

There are many questions asked by CMake newbies like me that don't ask this explicitly, but that make obvious the fact that, as newbs, we have no idea how to deal with CMake or what to make of it.

推荐答案

CMake 有什么用?

根据维基百科:

CMake 是 [...] 用于管理软件构建过程的软件使用与编译器无关的方法.它旨在支持依赖于多个目录层次结构和应用程序图书馆.它与本机构建环境结合使用例如 make、Apple 的 Xcode 和 Microsoft Visual Studio.

CMake is [...] software for managing the build process of software using a compiler-independent method. It is designed to support directory hierarchies and applications that depend on multiple libraries. It is used in conjunction with native build environments such as make, Apple's Xcode, and Microsoft Visual Studio.

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

With CMake, you no longer need to maintain separate settings specific to your compiler/build environment. You have one configuration, and that works for many environments.

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

CMake can generate a Microsoft Visual Studio solution, an Eclipse project or a Makefile maze from the same files without changing anything in them.

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

Given a bunch of directories with code in them, CMake manages all the dependencies, build orders and other tasks that your project needs done before it can be compiled. It does NOT actually compile anything. To use CMake, you must tell it (using configuration files called CMakeLists.txt) what executables you need compiled, what libraries they link to, what directories there are in your project and what is inside of them, as well as any details like flags or anything else you need (CMake is quite powerful).

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

If this is correctly set up, you then use CMake to create all of the files that your "native build environment" of choice needs to do its job. In Linux, by default, this means Makefiles. So once you run CMake, it will create a bunch of files for its own use plus some Makefiles. All you need to do thereafter is type "make" in the console from the root folder every time you're done editing your code, and bam, a compiled and linked executable is made.

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

Here is an example project setup that I will use throughout:

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

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

The contents of each file are shown and discussed later on.

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

CMake sets your project up according to the root CMakeLists.txt of your project, and does so in whatever directory you executed cmake from in the console. Doing this from a folder that isn't the root of your project produces what is called an out-of-source build, which means files created during compilation (obj files, lib files, executables, you know) will be placed in said folder, kept separate from the actual code. It helps reduce clutter and is preferred for other reasons as well, which I will not discuss.

我不知道如果你在除根目录 CMakeLists.txt 之外的任何地方执行 cmake 会发生什么.

I do not know what happens if you execute cmake on any other than the root CMakeLists.txt.

在这个例子中,因为我想把它全部放在 build/ 文件夹中,首先我必须导航到那里,然后通过 CMake 根目录 CMakeLists.txt驻留.

In this example, since I want it all placed inside the build/ folder, first I have to navigate there, then pass CMake the directory in which the root CMakeLists.txt resides.

cd build
cmake ..

默认情况下,这会使用我说过的 Makefile 设置所有内容.这是构建文件夹现在的样子:

By default, this sets everything up using Makefiles as I've said. Here is what the build folder should look like now:

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

Notice the src/ and lib/ folders. These have been created because simple/CMakeLists.txt points to them using the command add_subdirectory(<folder>). This command tells CMake to look in said folder for another CMakeLists.txt file and execute that script, so every subdirectory added this way must have a CMakeLists.txt file within. In this project, simple/src/CMakeLists.txt describes how to build the actual executable and simple/lib/CMakeLists.txt describes how to build the library. Every target that a CMakeLists.txt describes will be placed by default in its subdirectory within the build tree. So, after a quick

make

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

in console done from build/, some files are added:

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

项目已构建,可执行文件已准备好执行.如果要将可执行文件放在特定文件夹中,您会怎么做? 设置适当的 CMake 变量,或更改特定目标的属性.稍后会详细介绍 CMake 变量.

The project is built, and the executable is ready to be executed. What do you do if you want the executables put in a specific folder? Set the appropriate CMake variable, or change the properties of a specific target. More on CMake variables later.

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

Here are the contents, explained, of each file in the source directory:

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 版本.

The minimum required version should always be set, according to the warning CMake throws when you don't. Use whatever your version of CMake is.

您的项目名称可以在以后使用,并暗示您可以从相同的 CMake 文件管理多个项目.不过,我不会深入研究.

The name of your project can be used later on, and hints towards the fact you can manage more than one project from the same CMake files. I won't delve into that, though.

如前所述,add_subdirectory() 向项目添加了一个文件夹,这意味着 CMake 期望它在其中包含一个 CMakeLists.txt,然后它将在此之前运行继续.顺便说一句,如果你碰巧定义了一个 CMake 函数,你可以在子目录中的其他 CMakeLists.txt 中使用它,但是你必须在使用 add_subdirectory() 之前定义它代码> 否则它不会找到它.不过,CMake 在库方面更聪明,因此这可能是您唯一一次遇到此类问题.

As mentioned before, add_subdirectory() adds a folder to the project, which means CMake expects it to have a CMakeLists.txt within, which it will then run before continuing. By the way, if you happen to have a CMake function defined you can use it from other CMakeLists.txts in subdirectories, but you have to define it before you use add_subdirectory() or it won't find it. CMake is smarter about libraries, though, so this is likely the only time you will run into this kind of problem.

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 变量.

To make your very own library, you give it a name and then list all the files it's built from. Straightforward. If it needed another file, foo.cxx, to be compiled, you would instead write add_library(TestLib TestLib.cxx foo.cxx). This also works for files in other directories, for instance add_library(TestLib TestLib.cxx ${CMAKE_SOURCE_DIR}/foo.cxx). More on the CMAKE_SOURCE_DIR variable later.

你可以用它做的另一件事是指定你想要一个共享库.示例:add_library(TestLib SHARED TestLib.cxx).不要害怕,这是 CMake 开始让您的生活更轻松的地方.无论是否共享,现在使用以这种方式创建的库所需要做的就是在此处为其指定名称.该库的名称现在是 TestLib,您可以从项目中的任何地方 引用它.CMake 会找到它.

Another thing you can do with this is specify that you want a shared library. The example: add_library(TestLib SHARED TestLib.cxx). Fear not, this is where CMake begins to make your life easier. Whether it's shared or not, now all you need to handle to use a library created in this way is the name you gave it here. The name of this library is now TestLib, and you can reference it from anywhere in the project. CMake will find it.

是否有更好的方法来列出依赖项? 绝对是.查看下面的更多信息.

Is there a better way to list dependencies? Definitely yes. Check down below for more on this.

simple/lib/TestLib.cxx:

#include <stdio.h>

void test() {
  printf("testing...
");
}

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

The command add_executable() works exactly the same as add_library(), except, of course, it will generate an executable instead. This executable can now be referenced as a target for things like target_link_libraries(). Since tutorial.cxx uses code found in the TestLib library, you point this out to CMake as shown.

同样,add_executable() 中的任何源#included 的任何.h 文件不在与源位于同一目录中,都必须以某种方式添加.如果不是target_include_directories()命令,编译Tutorial时将找不到lib/TestLib.h,所以整个lib/文件夹是添加到包含目录以搜索#includes.您可能还会看到命令 include_directories() 以类似的方式起作用,不同之处在于它不需要您指定目标,因为它直接为所有可执行文件全局设置了它.CMAKE_SOURCE_DIR 稍后再解释.

Similarly, any .h files #included by any sources in add_executable() that are not in the same directory as the source have to be added somehow. If not for the target_include_directories() command, lib/TestLib.h would not be found when compiling Tutorial, so the entire lib/ folder is added to the include directories to be searched for #includes. You might also see the command include_directories() which acts in a similar fashion, except that it does not need you to specify a target since it outright sets it globally, for all executables. Once again, I'll explain CMAKE_SOURCE_DIR later.

simple/src/tutorial.cxx:

#include <stdio.h>
#include "TestLib.h"
int main (int argc, char *argv[])
{
  test();
  fprintf(stdout, "Main
");
  return 0;
}

注意TestLib.h"文件是如何包含的.无需包含完整路径:由于 target_include_directories(),CMake 会在幕后处理所有这些.

Notice how the "TestLib.h" file is included. No need to include the full path: CMake takes care of all that behind the scenes thanks to target_include_directories().

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

Technically speaking, in a simple source tree like this you can do without the CMakeLists.txts under lib/ and src/ and just adding something like add_executable(Tutorial src/tutorial.cxx) to simple/CMakeLists.txt. It's up to you and your project's needs.

查找和使用包:这个问题的答案 解释得比我以往任何时候都好.

Finding and using packages: The answer to this question explains it better than I ever could.

声明变量和函数,使用控制流等:查看本教程 解释了 CMake 必须提供的基础知识,并且总体上是一个很好的介绍.

Declaring variables and functions, using control flow, etc.: check out this tutorial that explains the basics of what CMake has to offer, as well as being a good introduction in general.

CMake 变量:有很多,所以接下来是一个速成课程,让你走上正轨.CMake wiki 是获取有关变量的更深入信息的好地方嗯.

CMake variables: there are plenty, so what follows is a crash course to get you on the right track. The CMake wiki is a good place to get more in-depth information on variables and ostensibly other things as well.

您可能希望在不重建构建树的情况下编辑一些变量.为此使用 ccmake(它编辑 CMakeCache.txt 文件).记住在完成更改后 configure,然后 g 使用更新的配置生成 makefile.

You may want to edit some variables without rebuilding the build tree. Use ccmake for this (it edits the CMakeCache.txt file). Remember to configure when done with the changes and then generate makefiles with the updated configuration.

阅读之前参考的教程,了解如何使用变量,但长话短说:set( value) 更改或创建变量.${} 使用它.

Read the previously referenced tutorial to learn about using variables, but long story short: set(<variable name> value) to change or create a variable. ${<variable name>} to use it.

  • CMAKE_SOURCE_DIR:源码根目录.在前面的例子中,这总是等于 /simple
  • CMAKE_BINARY_DIR:构建的根目录.在前面的示例中,这等于 simple/build/,但是如果您从诸如 foo/bar/etc/<的文件夹中运行 cmake simple//code>,那么该构建树中对 CMAKE_BINARY_DIR 的所有引用都将变为 /foo/bar/etc.
  • CMAKE_CURRENT_SOURCE_DIR:当前 CMakeLists.txt 所在的目录.这意味着它在整个过程中都会发生变化:从 simple/CMakeLists.txt 打印出来code> 产生 /simple,从 simple/src/CMakeLists.txt 打印它产生 /simple/src.
  • CMAKE_CURRENT_BINARY_DIR:你懂的.此路径不仅取决于构建所在的文件夹,还取决于当前 CMakeLists.txt 脚本的位置.
  • CMAKE_SOURCE_DIR: The root directory of source. In the previous example, this is always equal to /simple
  • CMAKE_BINARY_DIR: The root directory of the build. In the previous example, this is equals to simple/build/, but if you ran cmake simple/ from a folder such as foo/bar/etc/, then all references to CMAKE_BINARY_DIR in that build tree would become /foo/bar/etc.
  • CMAKE_CURRENT_SOURCE_DIR: The directory in which the current CMakeLists.txt is in. This means it changes throughout: printing this from simple/CMakeLists.txt yields /simple, and printing it from simple/src/CMakeLists.txt yields /simple/src.
  • CMAKE_CURRENT_BINARY_DIR: You get the idea. This path would depend not only on the folder the build is in, but also on the current CMakeLists.txt script's location.

为什么这些很重要?源文件显然不会在构建树中.如果你在前面的例子中尝试像 target_include_directories(Tutorial PUBLIC ../lib) 这样的东西,那么这个路径将是相对于构建树的,也就是说它就像编写 ${CMAKE_BINARY_DIR}/lib,它将查看 simple/build/lib/ 的内部.那里没有 .h 文件;最多你会找到libTestLib.a.你想要 ${CMAKE_SOURCE_DIR}/lib 代替.

Why are these important? Source files will obviously not be in the build tree. If you try something like target_include_directories(Tutorial PUBLIC ../lib) in the previous example, that path will be relative to the build tree, that is to say it will be like writing ${CMAKE_BINARY_DIR}/lib, which will look inside simple/build/lib/. There are no .h files in there; at most you will find libTestLib.a. You want ${CMAKE_SOURCE_DIR}/lib instead.

  • 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 将所有库文件放在哪里.
  • CMAKE_CXX_FLAGS: Flags to pass on to the compiler, in this case the C++ compiler. Also worth noting is CMAKE_CXX_FLAGS_DEBUG which will be used instead if CMAKE_BUILD_TYPE is set to DEBUG. There are more like these; check out the CMake wiki.
  • CMAKE_RUNTIME_OUTPUT_DIRECTORY: Tell CMake where to put all executables when built. This is a global setting. You can, for instance, set it to bin/ and have everything neatly placed there. EXECUTABLE_OUTPUT_PATH is similar, but deprecated, in case you stumble upon it.
  • CMAKE_LIBRARY_OUTPUT_DIRECTORY: Likewise, a global setting to tell CMake where to put all library files.

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

Target properties: you can set properties that affect only one target, be it an executable or a library (or an archive... you get the idea). Here is a good example of how to use it (with set_target_properties().

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

Is there an easy way to add sources to a target automatically? Use GLOB to list everything in a given directory under the same variable. Example syntax is FILE(GLOB <variable name> <directory>/*.cxx).

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

Can you specify different build types? Yes, though I'm not sure about how this works or the limitations of this. It probably requires some if/then'ning, but CMake does offer some basic support without configuring anything, like defaults for the CMAKE_CXX_FLAGS_DEBUG, for instance. You can either set your build type from within the CMakeLists.txt file via set(CMAKE_BUILD_TYPE <type>) or by calling CMake from console with the appropriate flags, for example cmake -DCMAKE_BUILD_TYPE=Debug.

有使用 CMake 的项目的好例子吗? 维基百科有一个使用 CMake 的开源项目列表,如果你想研究的话.到目前为止,在线教程在这方面一直让我失望,但是 this Stack Overflow question有一个非常酷且易于理解的 CMake 设置.值得一看.

Any good examples of projects that use CMake? Wikipedia has a list of open-source projects that use CMake, if you want to look into that. Online tutorials have been nothing but a letdown to me so far in this regard, however this Stack Overflow question has a pretty cool and easy-to-understand CMake setup. It's worth a look.

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

Using variables from CMake in your code: Here's a quick and dirty example (adapted from some other tutorial):

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:

The resulting file generated by CMake, simple/src/TutorialConfig.h:

// Configured options and settings
#define Tutorial_VERSION_MAJOR 1
#define Tutorial_VERSION_MINOR 1

巧妙地使用这些,你可以做一些很酷的事情,比如关闭图书馆等.我确实建议您查看 该教程 因为有一些更高级的东西这迟早会在大型项目中非常有用.

With clever use of these you can do cool things like turning off a library and such. I do recommend taking a look at that tutorial as there are some slightly more advanced things that are bound to be very useful on larger projects, sooner or later.

对于其他所有内容,Stack Overflow 都充满了具体的问题和简明的答案,这对除初学者外的所有人都非常有用.

For everything else, Stack Overflow is brimming with specific questions and concise answers, which is great for everyone except the uninitiated.

这篇关于CMake是如何使用的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆