使用 Conan 构建简单的 CMake 项目

让我们从一个例子开始:我们将创建一个使用最流行的 C++ 库之一的字符串压缩器应用程序:Zlib

在本例中,我们将使用 CMake 作为构建系统,但请记住 Conan 适用于任何构建系统,并且不限于使用 CMake。您可以在 更多阅读部分 中查看使用其他构建系统的更多示例。

请先克隆源代码来重新创建此项目,您可以在 GitHub 上的 examples2 仓库 中找到它们。

$ git clone https://github.com/conan-io/examples2.git
$ cd examples2/tutorial/consuming_packages/simple_cmake_project

我们从一个结构非常简单的 C 语言项目开始。

.
├── CMakeLists.txt
└── src
    └── main.c

此项目包含一个基本的 CMakeLists.txt,其中包含 zlib 依赖项,以及 main.c 中用于字符串压缩程序的源代码。

让我们看一下 main.c 文件。

main.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <zlib.h>

int main(void) {
    char buffer_in [256] = {"Conan is a MIT-licensed, Open Source package manager for C and C++ development "
                            "for C and C++ development, allowing development teams to easily and efficiently "
                            "manage their packages and dependencies across platforms and build systems."};
    char buffer_out [256] = {0};

    z_stream defstream;
    defstream.zalloc = Z_NULL;
    defstream.zfree = Z_NULL;
    defstream.opaque = Z_NULL;
    defstream.avail_in = (uInt) strlen(buffer_in);
    defstream.next_in = (Bytef *) buffer_in;
    defstream.avail_out = (uInt) sizeof(buffer_out);
    defstream.next_out = (Bytef *) buffer_out;

    deflateInit(&defstream, Z_BEST_COMPRESSION);
    deflate(&defstream, Z_FINISH);
    deflateEnd(&defstream);

    printf("Uncompressed size is: %lu\n", strlen(buffer_in));
    printf("Compressed size is: %lu\n", strlen(buffer_out));

    printf("ZLIB VERSION: %s\n", zlibVersion());

    return EXIT_SUCCESS;
}

同样,CMakeLists.txt 的内容是:

CMakeLists.txt
cmake_minimum_required(VERSION 3.15)
project(compressor C)

find_package(ZLIB REQUIRED)

add_executable(${PROJECT_NAME} src/main.c)
target_link_libraries(${PROJECT_NAME} ZLIB::ZLIB)

我们的应用程序依赖于 Zlib 库。Conan 默认会尝试从一个名为 ConanCenter 的远程服务器安装库。您可以在那里搜索库,也可以查看可用的版本。在我们的例子中,在查看了 Zlib 的可用版本后,我们选择使用最新版本之一:zlib/1.3.1

使用 Conan 通过我们的项目安装 Zlib 库并找到它的最简单方法是使用 conanfile.txt 文件。让我们创建一个包含以下内容的文件:

conanfile.txt
[requires]
zlib/1.3.1

[generators]
CMakeDeps
CMakeToolchain

正如您所见,我们在该文件中有两个部分,其语法类似于 INI 文件。

  • [requires] 部分是我们声明要在项目中使用的库的地方,在本例中是 zlib/1.3.1

  • [generators] 部分告诉 Conan 生成编译器或构建系统将用于查找依赖项和构建项目的那些文件。在本例中,由于我们的项目基于 CMake,我们将使用 CMakeDeps 来生成有关 Zlib 库文件安装位置的信息,并使用 CMakeToolchain 通过 CMake 工具链文件将构建信息传递给 CMake

除了 conanfile.txt,我们还需要一个 Conan profile 来构建我们的项目。Conan profiles 允许用户为编译器、构建配置、体系结构、共享或静态库等事物定义一组配置。Conan 默认不会尝试自动检测 profile,因此我们需要创建一个。为了让 Conan 尝试基于当前操作系统和已安装的工具来猜测 profile,请运行:

conan profile detect --force

这将根据环境检测操作系统、构建体系结构和编译器设置。它还将默认将构建配置设置为 Release。生成的 profile 将存储在 Conan 主文件夹中,名为 default,并且默认情况下 Conan 会在所有命令中使用它,除非通过命令行指定了其他 profile。macOS 命令输出的示例将是:

$ conan profile detect --force
Found apple-clang 14.0
apple-clang>=13, using the major as version
Detected profile:
[settings]
arch=x86_64
build_type=Release
compiler=apple-clang
compiler.cppstd=gnu17
compiler.libcxx=libc++
compiler.version=14
os=Macos

注意

关于 Conan 检测到的 C++ 标准的说明

Conan 始终将默认 C++ 标准设置为检测到的编译器版本默认使用的标准,macOS 使用 apple-clang 的情况除外。在这种情况下,对于 apple-clang>=11,它设置为 compiler.cppstd=gnu17。如果您想使用不同的 C++ 标准,可以直接编辑默认 profile 文件。首先,使用以下命令获取默认 profile 的位置:

$ conan profile path default
/Users/user/.conan2/profiles/default

然后打开并编辑文件,将 compiler.cppstd 设置为您想要使用的 C++ 标准。

注意

使用非自动检测的编译器

如果您想更改 Conan profile 以使用与默认编译器不同的编译器,您需要更改 compiler 设置,并使用 tools.build:compiler_executables 配置 明确告诉 Conan 在哪里找到它。

我们将使用 Conan 来安装 Zlib 并生成 CMake 需要查找此库并构建我们的项目的文件。我们将在 build 文件夹中生成这些文件。为此,请运行:

$ conan install . --output-folder=build --build=missing

您将从该命令的输出中得到类似如下的内容:

$ conan install . --output-folder=build --build=missing
...
-------- Computing dependency graph ----------
zlib/1.3.1: Not found in local cache, looking in remotes...
zlib/1.3.1: Checking remote: conancenter
zlib/1.3.1: Trying with 'conancenter'...
Downloading conanmanifest.txt
Downloading conanfile.py
Downloading conan_export.tgz
Decompressing conan_export.tgz
zlib/1.3.1: Downloaded recipe revision f1fadf0d3b196dc0332750354ad8ab7b
Graph root
    conanfile.txt: /home/conan/examples2/tutorial/consuming_packages/simple_cmake_project/conanfile.txt
Requirements
    zlib/1.3.1#f1fadf0d3b196dc0332750354ad8ab7b - Downloaded (conancenter)

-------- Computing necessary packages ----------
Requirements
    zlib/1.3.1#f1fadf0d3b196dc0332750354ad8ab7b:cdc9a35e010a17fc90bb845108cf86cfcbce64bf#dd7bf2a1ab4eb5d1943598c09b616121 - Download (conancenter)

-------- Installing packages ----------

Installing (downloading, building) binaries...
zlib/1.3.1: Retrieving package cdc9a35e010a17fc90bb845108cf86cfcbce64bf from remote 'conancenter'
Downloading conanmanifest.txt
Downloading conaninfo.txt
Downloading conan_package.tgz
Decompressing conan_package.tgz
zlib/1.3.1: Package installed cdc9a35e010a17fc90bb845108cf86cfcbce64bf
zlib/1.3.1: Downloaded package revision dd7bf2a1ab4eb5d1943598c09b616121

-------- Finalizing install (deploy, generators) ----------
conanfile.txt: Generator 'CMakeToolchain' calling 'generate()'
conanfile.txt: Generator 'CMakeDeps' calling 'generate()'
conanfile.txt: Generating aggregated env files

正如您在输出中看到的,发生了几件事:

  • Conan 从远程服务器安装了 Zlib 库,如果库可用,默认情况下应该是 Conan Center 服务器。该服务器存储了定义库如何构建的 Conan recipes,以及可以重用的二进制文件,这样我们就无需每次都从源代码构建。

  • Conan 在 build 文件夹下生成了几个文件。这些文件是由我们在 conanfile.txt 中设置的 CMakeToolchainCMakeDeps 生成器生成的。 CMakeDeps 生成的文件使 CMake 能够找到我们刚刚下载的 Zlib 库。另一方面,CMakeToolchain 为 CMake 生成了一个工具链文件,以便我们可以使用与我们为默认 profile 检测到的相同的设置,透明地使用 CMake 构建我们的项目。

现在我们准备好构建和运行我们的 compressor 应用程序了。

Windows
$ cd build
# assuming Visual Studio 15 2017 is your VS version and that it matches your default profile
$ cmake .. -G "Visual Studio 15 2017" -DCMAKE_TOOLCHAIN_FILE="conan_toolchain.cmake"
$ cmake --build . --config Release
...
[100%] Built target compressor
$ Release\compressor.exe
Uncompressed size is: 233
Compressed size is: 147
ZLIB VERSION: 1.2.11
Linux, macOS
$ cd build
$ cmake .. -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Release
$ cmake --build .
...
[100%] Built target compressor
$ ./compressor
Uncompressed size is: 233
Compressed size is: 147
ZLIB VERSION: 1.2.11

请注意,CMakeToolchain 可能会生成 CMake 预设文件,这允许使用现代 CMake ( >=3.23 ) 的用户使用 cmake --preset 代替传递工具链文件参数。请参阅 使用 CMake 预设构建