# conan使用指南

# 使用conan 包

下载示例工程

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

上述工程会依赖zlib, 在使用conan管理包的时候,会从ConanCente中拉取需要的包,上述工程中包含conanfile.txt就是为了说明需要获取的conan包的版本信息。

[requires]
zlib/1.2.11

[generators]
CMakeDeps
CMakeToolchain

上面文件中,[generators]这一部分定义了 Conan 应该使用的生成器。生成器是 Conan 的一个组件,它负责生成构建系统所需的文件。CMakeDeps:这是一个生成器,它会生成一个 conan_cmake_find_package.cmake 文件,这个文件可以用来在 CMake 项目中查找 Conan 管理的包。CMakeToolchain:这是另一个生成器,它会生成一个 conan_toolchain.cmake 文件,这个文件包含了构建项目所需的工具链配置,比如编译器设置、CMake 版本等。

conan profile detect --force

上面的命令用来创建或更新一个 Conan 用户配置文件(profile),这个文件包含了构建环境的设置,如编译器、架构、操作系统等信息。

detect_api: Found cc=gcc- 13.2.0
detect_api: gcc>=5, using the major as version
detect_api: gcc C++ standard library: libstdc++11

Detected profile:
[settings]
arch=x86_64
build_type=Release
compiler=gcc
compiler.cppstd=gnu17
compiler.libcxx=libstdc++11
compiler.version=13
os=Linux

WARN: This profile is a guess of your environment, please check it.
WARN: The output of this command is not guaranteed to be stable and can change in future Conan versions.
WARN: Use your own profile files for stability.
Saving detected profile to /home/lx/.conan2/profiles/default

可以使用如下命令获取conan的配置文件默认的路径,

conan config home
# /home/lx/.conan2

profile文件中包含不同的部分,[settings]包含了操作系统、体系结构、编译器和构建的配置,当使用conan指令带--profile参数时,相关的配置将都会从profile文件中读取,当不指定这个参数的时候,默认的会从--profile=default中读取。可以使用不同的profile文件来配置不同的构建设置。在conan命令中使用--settings参数可以覆盖掉profile中的setting设置。可以使用如下代码来确认setting参数有没有生效,

#include <cstdio>

int main() {
#ifdef NDEBUG
    printf("Not debug\n");
#else
    prinf("debug\n");
#endif

    return 0;
}

编译的时候使用的参数如下:

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

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

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

构建仓库:

cmake .. -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Release

cmake中的CMAKE_TOOLCHAIN_FILE变量非常有用,它允许用户指定一个工具链文件,这个文件定义了构建系统的环境和编译器设置。工具链文件通常用于跨平台构建,或者当你需要自定义编译器、编译选项、链接器等设置时。

# 将构建工具也作为conan包进行管理

上面的示例中使用的是系统中已经安装的cmake,有时候需要使用conan包来指定要使用的cmake版本,本节就来介绍如何使用conan来安装指定版本的cmake来进行编译系统的构建。

以上操作其实非常简单,只需要在conanfile.txt中进行指定即可,如下:

[requires]
zlib/1.2.11

[tool_requires]
cmake/3.22.6

[generators]
CMakeDeps
CMakeToolchain

在指定cmake‵版本之前,还是需要系统先有一个cmake,因为conanfile中指定的cmake`版本只是指定在当前的工程中。

cmake文件中添加一行打印使用cmake的版本信息:

message("Building with CMake version: ${CMAKE_VERSION}")

执行上述命令后,在build文件夹下将有如下文件conanbuild.sh,这个脚本自动调用VirtualBuildEnv生成器,这个脚本会设置一些环境变量如$PATH来指定我们的软件路径,如新安装的cmake

source conanbuild.sh  # 激活当前环境
cmake --version
# cmake version 3.22.6

同样在build文件夹中还有deactivate_conanbuild.sh文件用来恢复系统环境变量的设置,

source deactivate_conanbuild.sh # 恢复环境变量设置

# 应用依赖设置

默认情况下,conan生成目标文件使用的是静态链接的方式,通过设置conan--options参数可以设置目标文件是静态链接还是动态链接的方式,

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

使用上述命令的时候会在build目录下生成‵conanrun.sh脚本设置VirtualRunEnv环境相关的变量,如LD_LIBRARY_PATHPATH`,供执行可执行文件的时候使用,如执行共享库的路径。

conan中settingsoptions的区别:

  • settings 是项目范围的配置,会影响正在构建的整个项目。例如,操作系统(os)、架构(arch)和编译器(compiler)等设置,对于依赖关系图中的所有包来说,这些设置通常是相同的。
  • options 是特定于包的配置。例如,静态或共享库不是适用于所有包的设置。有些包可能只包含头文件,而其他包可能包含数据或可执行文件。shared 是一个常见的选项,但包可以定义和使用任何它们需要的选项。

# 使用conanfile.py

简单场景使用conanfile.txt就足够了,但想更灵活的使用conan就需要使用conanfile.py了,可以在其中使用 Python 代码来执行诸如动态添加需求、根据其他选项更改选项或根据需求设置选项等操作。

将以下conanfile.txt改造成conanfile.py文件:

# conanfile.txt
[requires]
zlib/1.2.11

[tool_requires]
cmake/3.22.6

[generators]
CMakeDeps
CMakeToolchain

改造成的conanfile.py文件如下:

from conan import ConanFile

class CompressorRecipe(ConanFile):
    settings = "os", "compiler", "build_type", "arch"
    generators = "CMakeToolchain", "CMakeDeps"

    def requirements(self):
        self.requires("zlib/1.2.11")

    def build_requirements(self):
        self.tool_requires("cmake/3.22.6")

这个类CompressorRecipe继承自ConanFile类,这个类有不同的settings/gennerators配置,

  • requirements()方法中调用self.requires方法来指定zlib依赖
  • build_requirements()方法中使用self.tool_requires()来指定编译工具等的使用

其他的conan build/install指令的使用方式和使用conanfile.txt时一样不要变更。

在前面的示例中,每次执行 conan install 命令时,我们都必须使用 –output-folder 参数来定义要创建 Conan 生成的文件的位置。有一种更简洁的方法来决定我们希望 Conan 在哪里为构建系统生成文件,例如如果我们想要根据我们使用的 CMake 生成器的类型来设置不同的输出文件夹, 可以直接在conanfile.py layout()方法中定义它,并使其适用于每个平台,而无需添加更多更改。

# 使用layout方法

def layout(self):
    # We make the assumption that if the compiler is msvc the
    # CMake generator is multi-config
    multi = True if self.settings.get_safe("compiler") == "msvc" else False
    if multi:
        self.folders.generators = os.path.join("build", "generators")
        self.folders.build = "build"
    else:
        self.folders.generators = os.path.join("build", str(self.settings.build_type), "generators")
        self.folders.build = os.path.join("build", str(self.settings.build_type))
  • self.folders.generatorslayout()方法中可以通过设置这个属性来指定Conan生成的所有辅助文件(CMake 工具链和 cmake 依赖文件)放置的文件夹

# 使用validate方法

conan加载conanfile.py文件时,还会自动执行validate函数,可以进行一些配置的校验。

def validate(self):
    if self.settings.os == "Macos" and self.settings.arch == "armv8":
        raise ConanInvalidConfiguration("ARM v8 not supported in Macos")

# 使用generate()方法从pkg中复制资源

在某些场景中,conan pkg中包含有对使用pkg的人来说十分重要的文件,像配置文件/assert等,使用generate()方法可以将这些文件从conan cache中拷贝到工程文件夹中,确保所有的资源可以被正确的使用。

如下的示例代码功能是将pkg resdirs目录下的资源文件拷贝到工程asserts文件夹下,

import os
from conan import ConanFile
from conan.tools.files import copy

class MyProject(ConanFile):

    ...

    def generate(self):
        # Copy all resources from the dependency's resource directory
        # to the "assets" folder in the source directory of your project
        dep = self.dependencies["dep_name"]
        copy(self, "*", dep.cpp_info.resdirs[0], os.path.join(self.source_folder, "assets"))

执行conan install命令后,所有的资源文件将会被拷贝到本地,如此就可以在本地工程中使用配置文件来构建了。

# 交叉编译

譬如在x86 64ubuntu上编译,在Raspberry Pi上运行。conan使用两个编译配置文件高的profile,即便对于本地编译运行的conan构建,也可以看做其使用了两套profile,如下:

conan install . --build=missing --profile:host=someprofile --profile:build=default
  • profile:host:这是定义构建的二进制文件将运行的平台的配置文件。对于我们的字符串压缩器应用程序,此配置文件将应用于将在 Raspberry Pi 中运行的 Zlib 库。
  • profile:build:这是定义将构建二进制文件的平台的配置文件。对于我们的字符串压缩器应用程序,CMake 工具将使用此配置文件,该工具将在 Ubuntu Linux 计算机上对其进行编译。

仅设置--profile参数时,等价于设置profile:host--profile:build将使用默认配置。

# build.profile
[settings]
os=Linux
arch=x86_64
build_type=Release
compiler=gcc
compiler.cppstd=gnu14
compiler.libcxx=libstdc++11
compiler.version=9

# host.profile
[settings]
os=Linux
arch=armv7hf
compiler=gcc
build_type=Release
compiler.cppstd=gnu14
compiler.libcxx=libstdc++11
compiler.version=9
[buildenv]
CC=arm-linux-gnueabihf-gcc-9
CXX=arm-linux-gnueabihf-g++-9
LD=arm-linux-gnueabihf-ld

对应的conanfile.py文件内容基本上不变:

from conan import ConanFile
from conan.tools.cmake import cmake_layout

class CompressorRecipe(ConanFile):
    settings = "os", "compiler", "build_type", "arch"
    generators = "CMakeToolchain", "CMakeDeps"

    def requirements(self):
        self.requires("zlib/1.2.11")

    def build_requirements(self):
        self.tool_requires("cmake/3.22.6")

    def layout(self):
        cmake_layout(self)

编译使用的命令为:

conan install . --build missing -pr:b=default -pr:h=./profiles/raspberry

使用file命令可以查看编译生成的可执行文件的目标架构是aarch64

# reference