为多种配置构建:Release、Debug、静态和共享

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

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

到目前为止,我们构建了一个简单的 CMake 项目,该项目依赖于 zlib 库,并且了解了 tool_requires,这是一种用于构建工具(如 CMake)的特殊类型的 requirements。在这两种情况下,我们都没有在任何地方指定我们想要以 *Release* 或 *Debug* 模式构建应用程序,或者是否要链接到 *静态* 或 *共享* 库。这是因为 Conan 如果没有另行指示,将使用在“默认配置文件”中声明的默认配置。当我们运行 conan profile detect 命令时,在第一个示例中创建了此默认配置文件。Conan 将此文件存储在 Conan 用户主目录中的 /profiles 文件夹中。您可以通过运行 conan config home 命令获取 Conan 用户主目录的位置,然后显示 /profiles 文件夹中默认配置文件的内容来检查默认配置文件的内容

$ conan config home
Current Conan home: /Users/tutorial_user/.conan2

# output the file contents
$ cat /Users/tutorial_user/.conan2/profiles/default
[settings]
os=Macos
arch=x86_64
compiler=apple-clang
compiler.version=14.0
compiler.libcxx=libc++
compiler.cppstd=gnu11
build_type=Release
[options]
[tool_requires]
[env]

# The default profile can also be checked with the command "conan profile show"

如您所见,该配置文件有不同的部分。[settings] 部分包含有关操作系统、架构、编译器和构建配置等信息。

当您调用 Conan 命令并设置 --profile 参数时,Conan 将从配置文件中获取所有信息并将其应用于您要构建或安装的软件包。如果您不指定该参数,则等效于使用 --profile=default 调用。这两个命令的行为相同

$ conan install . --build=missing
$ conan install . --build=missing --profile=default

您可以存储不同的配置文件,并使用它们来为不同的设置构建。例如,使用 build_type=Debug,或者将 tool_requires 添加到您使用该配置文件构建的所有软件包中。我们将创建一个 *debug* 配置文件来尝试使用不同的配置进行构建

<conan home>/profiles/debug
[settings]
os=Macos
arch=x86_64
compiler=apple-clang
compiler.version=14.0
compiler.libcxx=libc++
compiler.cppstd=gnu11
build_type=Debug

修改设置:为应用程序及其依赖项使用 Debug 配置

使用配置文件不是设置要使用的配置的唯一方法。您也可以使用 --settings 参数在 Conan 命令中覆盖配置文件设置。例如,您可以以 *Debug* 配置而不是 *Release* 配置构建先前示例中的项目。

在构建之前,请检查我们是否修改了先前示例中的源代码,以显示源代码构建时使用的构建配置

#include <stdlib.h>
...

int main(void) {
    ...
    #ifdef NDEBUG
    printf("Release configuration!\n");
    #else
    printf("Debug configuration!\n");
    #endif

    return EXIT_SUCCESS;
}

现在让我们以 *Debug* 配置构建我们的项目

$ conan install . --output-folder=build --build=missing --settings=build_type=Debug

如上所述,这等效于拥有 *debug* 配置文件,并使用 --profile=debug 参数而不是 --settings=build_type=Debug 参数运行这些命令。

conan install 命令将检查我们是否在本地缓存 (Zlib) 中已具有 Debug 配置所需的库,如果没有,则获取它们。它还将更新 conan_toolchain.cmakeCMakePresets.json 文件中的构建配置,CMakeToolchain 生成器会创建这些文件,以便当我们构建应用程序时,它以 *Debug* 配置构建。现在,像在前面的示例中一样构建您的项目,并在输出中检查它是否以 *Debug* 配置构建

Windows
# assuming Visual Studio 15 2017 is your VS version and that it matches your default profile
$ cd build
$ cmake .. -G "Visual Studio 15 2017" -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake
$ cmake --build . --config Debug
$ Debug\compressor.exe
Uncompressed size is: 233
Compressed size is: 147
ZLIB VERSION: 1.2.11
Debug configuration!
Linux、macOS
$ cd build
$ cmake .. -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Debug
$ cmake --build .
$ ./compressor
Uncompressed size is: 233
Compressed size is: 147
ZLIB VERSION: 1.2.11
Debug configuration!

修改选项:将应用程序依赖项链接为共享库

到目前为止,我们一直在应用程序中静态链接 *Zlib*。这是因为在 Zlib 的 Conan 软件包中,有一个属性设置为默认以该模式构建。我们可以使用 --options 参数将 shared 选项设置为 True,从而从 static 更改为 shared 链接。为此,请运行

$ conan install . --output-folder=build --build=missing --options=zlib/1.2.11:shared=True

这样做,Conan 将安装 *Zlib* 共享库,生成用于构建它们的文件,以及在运行应用程序时定位这些动态库所需的文件。

注意

选项是按软件包定义的。在这种情况下,我们定义了我们希望将特定版本的 zlib/1.2.11 作为共享库。如果我们有其他依赖项,并且我们希望所有依赖项(尽可能)都作为共享库,我们将使用 -o *:shared=True,其中 * 模式匹配所有软件包引用。

在将其配置为将 *Zlib* 链接为共享库之后,让我们再次构建应用程序

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
Linux、macOS
$ cd build
$ cmake .. -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Release
$ cmake --build .
...
[100%] Built target compressor

现在,如果您尝试运行编译的可执行文件,您将看到一个错误,因为可执行文件找不到我们刚刚安装的 *Zlib* 的共享库。

Windows
$ Release\compressor.exe
(on a pop-up window) The code execution cannot proceed because zlib1.dll was not found. Reinstalling the program may fix this problem.
# This error depends on the console being used and may not always pop up.
# It could run correctly if the console gets the zlib dll from a different path.
Linux
$ ./compressor
./compressor: error while loading shared libraries: libz.so.1: cannot open shared object file: No such file or directory
macOS
$ ./compressor
./compressor: dyld[41259]: Library not loaded: @rpath/libz.1.dylib

这是因为共享库(在 Windows 中为 *.dll*,在 OSX 中为 *.dylib*,在 Linux 中为 *.so*)是在运行时加载的。这意味着应用程序可执行文件需要在运行时知道所需的共享库在哪里。在 Windows 上,动态链接器将在同一目录中搜索,然后在 *PATH* 目录中搜索。在 OSX 上,它将在 *DYLD_LIBRARY_PATH* 中声明的目录中搜索,就像 Linux 将使用 *LD_LIBRARY_PATH* 一样。

Conan 提供了一种机制来定义这些变量,并使可执行文件可以查找和加载这些共享库。此机制是 VirtualRunEnv 生成器。如果您检查输出文件夹,您将看到 Conan 生成了一个名为 conanrun.sh/bat 的新文件。这是当我们在执行 conan install 时激活 shared 选项时,自动调用该 VirtualRunEnv 生成器的结果。此生成的脚本将设置 PATHLD_LIBRARY_PATHDYLD_LIBRARY_PATHDYLD_FRAMEWORK_PATH 环境变量,以便可执行文件可以找到共享库。

激活虚拟环境,然后再次运行可执行文件

Windows
$ conanrun.bat
$ Release\compressor.exe
Uncompressed size is: 233
Compressed size is: 147
...
Linux、macOS
$ source conanrun.sh
$ ./compressor
Uncompressed size is: 233
Compressed size is: 147
...

就像前面的示例中使用 VirtualBuildEnv 生成器一样,当我们运行 conanrun.sh/bat 脚本时,会创建一个名为 deactivate_conanrun.sh/bat 的取消激活脚本,以恢复环境。使用 source 或运行它来执行此操作

Windows
$ deactivate_conanrun.bat
Linux、macOS
$ source deactivate_conanrun.sh

设置和选项之间的区别

您可能已经注意到,为了在 *Debug* 和 *Release* 配置之间进行更改,我们使用了 Conan setting,但是当我们为可执行文件设置 *shared* 模式时,我们使用了 Conan option。请注意 settingsoptions 之间的区别

  • settings 通常是由客户端计算机定义的项目范围内的配置。诸如操作系统、编译器或构建配置之类的东西,这些配置对于多个 Conan 软件包是通用的,并且仅为其中一个定义一个默认值是没有意义的。例如,Conan 软件包声明“Visual Studio”作为默认编译器是没有意义的,因为这是由最终消费者定义的,并且如果他们在 Linux 中工作,则不太可能具有意义。

  • 选项(options) 旨在用于软件包特定的配置,这些配置可以在配方中设置为默认值。例如,一个软件包可以定义其默认链接是静态的,如果消费者没有另行指定,则应使用此链接。

引入软件包 ID 的概念

当使用具有不同设置(settings)选项(options)的 Zlib 等软件包时,您可能想知道 Conan 如何确定要从远程检索哪个二进制文件。答案在于软件包 ID(package_id)的概念。

软件包 ID(package_id)是 Conan 用来确定软件包二进制兼容性的标识符。它是基于多个因素计算的,包括软件包的设置(settings)选项(options)和依赖项。当您修改其中任何一个因素时,Conan 都会计算一个新的软件包 ID(package_id)来引用相应的二进制文件。

以下是该过程的分解

  1. 确定设置和选项:Conan 首先检索用户的输入设置和选项。这些可以来自命令行或配置文件,例如 –settings=build_type=Debug–profile=debug

  2. 计算软件包 ID:根据当前的设置(settings)选项(options)和依赖项的值,Conan 计算一个哈希值。此哈希值用作软件包 ID(package_id),表示二进制软件包的唯一标识。

  3. 获取二进制文件:然后,Conan 检查其缓存或指定的远程仓库中是否有具有计算出的软件包 ID(package_id)的二进制软件包。如果找到匹配项,则检索该二进制文件。如果未找到,Conan 可以从源代码构建软件包或指示缺少二进制文件。

在我们的教程中,当我们使用具有不同设置(settings)选项(options)的 Zlib 时,Conan 使用软件包 ID(package_id)来确保它获取了与我们指定的配置匹配的正确二进制文件。