为多种配置构建:Release、Debug、静态和共享¶
请首先克隆源代码来重新创建此项目。你可以在 GitHub 的 examples2 仓库 中找到它们
$ git clone https://github.com/conan-io/examples2.git
$ cd examples2/tutorial/consuming_packages/different_configurations
到目前为止,我们构建了一个依赖于 zlib 库的简单 CMake 项目,并且了解了 tool_requires
,这是一种特殊的 requirements
,用于像 CMake 这样的构建工具。在这两种情况下,我们都没有在任何地方指定我们想要在 *Release* 或 *Debug* 模式下构建应用程序,或者我们是否想要链接到 *static* 或 *shared* 库。这是因为,如果未另行指示,Conan 将使用在“default profile”中声明的默认配置。这个默认配置文件是在我们运行 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* 配置文件来尝试使用不同的配置进行构建
[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.cmake
和 CMakePresets.json
文件中的构建配置,这些文件是由 CMakeToolchain
生成器创建的,以便当我们构建应用程序时,它以 *Debug* 配置构建。现在按照你在前一个示例中的方式构建你的项目,并在输出中检查它是如何在 *Debug* 配置中构建的
# 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!
$ 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* 链接为共享库后,让我们再次构建应用程序
$ 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
$ cd build
$ cmake .. -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Release
$ cmake --build .
...
[100%] Built target compressor
现在,如果你尝试运行已编译的可执行文件,你将看到一个错误,因为可执行文件无法找到我们刚刚安装的 *Zlib* 的共享库。
$ 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.
$ ./compressor
./compressor: error while loading shared libraries: libz.so.1: cannot open shared object file: No such file or directory
$ ./compressor
./compressor: dyld[41259]: Library not loaded: @rpath/libz.1.dylib
这是因为共享库(Windows 中的 *.dll*,macOS 中的 *.dylib* 和 Linux 中的 *.so*)是在运行时加载的。这意味着应用程序可执行文件需要知道在运行时所需的共享库的位置。在 Windows 上,动态链接器将在同一目录中搜索,然后在 *PATH* 目录中搜索。在 macOS 上,它将在 *DYLD_LIBRARY_PATH* 中声明的目录中搜索,而在 Linux 上,它将使用 *LD_LIBRARY_PATH*。
Conan 提供了一种机制来定义这些变量,并使可执行文件能够找到和加载这些共享库。这个机制就是 VirtualRunEnv
生成器。如果你检查输出文件夹,你将看到 Conan 生成了一个名为 conanrun.sh/bat
的新文件。这是当我们执行 conan install 时,通过激活 shared
选项自动调用该 VirtualRunEnv
生成器的结果。这个生成的脚本将设置 *PATH*、*LD_LIBRARY_PATH*、*DYLD_LIBRARY_PATH* 和 *DYLD_FRAMEWORK_PATH* 环境变量,以便可执行文件可以找到共享库。
激活虚拟环境,然后再次运行可执行文件
$ conanrun.bat
$ Release\compressor.exe
Uncompressed size is: 233
Compressed size is: 147
...
$ source conanrun.sh
$ ./compressor
Uncompressed size is: 233
Compressed size is: 147
...
就像前面使用 VirtualBuildEnv
生成器的示例一样,当我们运行 conanrun.sh/bat
脚本时,会创建一个名为 deactivate_conanrun.sh/bat
的停用脚本,以恢复环境。Source 或运行它来执行此操作
$ deactivate_conanrun.bat
$ source deactivate_conanrun.sh
设置和选项之间的区别¶
你可能已经注意到,对于在 *Debug* 和 *Release* 配置之间进行更改,我们使用了 Conan **设置**,但是当我们为可执行文件设置 *shared* 模式时,我们使用了 Conan **选项**。请注意 **设置** 和 **选项** 之间的区别
**设置** 通常是由客户端机器定义的项目范围内的配置。诸如操作系统、编译器或构建配置之类的事物,对于多个 Conan 包来说是通用的,并且仅为一个包定义一个默认值是没有意义的。例如,Conan 包声明“Visual Studio”作为默认编译器是没有意义的,因为这是由最终消费者定义的,并且如果他们在 Linux 中工作,则不太可能有意义。
**选项** 旨在用于特定于包的配置,可以在配方中设置为默认值。例如,一个包可以定义其默认链接是静态的,如果消费者没有另行指定,则应使用此链接。
介绍 Package ID 的概念¶
当使用具有不同 settings 和 options 的像 Zlib 这样的包时,你可能想知道 Conan 如何确定要从远程检索哪个二进制文件。答案在于 package_id 的概念。
package_id 是 Conan 用来确定包的二进制兼容性的标识符。它基于多个因素计算得出,包括包的 settings、options 和依赖项。当你修改任何这些因素时,Conan 会计算一个新的 package_id 来引用相应的二进制文件。
以下是流程的细分
**确定设置和选项**:Conan 首先检索用户的输入设置和选项。这些可以来自命令行或配置文件,如 –settings=build_type=Debug 或 –profile=debug。
**计算 Package ID**:使用 settings、options 和依赖项的当前值,Conan 计算一个哈希值。此哈希值充当 package_id,表示二进制包的唯一身份。
**获取二进制文件**:然后,Conan 会在其缓存或指定的远程位置中检查具有计算出的 package_id 的二进制包。如果找到匹配项,则检索该二进制文件。如果找不到,Conan 可以从源代码构建该包,或指示该二进制文件丢失。
在我们的教程的上下文中,当我们使用具有不同 settings 和 options 的 Zlib 时,Conan 使用 package_id 来确保它获取与我们指定的配置相匹配的正确二进制文件。