使用像 Conan 这样的包管理器的好处,除了许多其他优点之外,还在于管理项目的依赖项。您可以在不同的计算机和不同的操作系统上构建您的项目,只需再次运行 conan install 命令。

但这并不完全正确,您通常需要一些工具,例如编译器,以及可能用于构建项目的单独构建系统。因此,如果您更换计算机,则需要使用系统包管理器(apt、yum 等)或下载安装程序等来设置构建工具。

在最新的 0.11 版本中,我们对 Conan 进行了一些小的更改,以帮助用户轻松地复制他们的环境。我们并不打算取代现有的应用程序管理器;yum、apt、brew 等都是很棒的工具,但 Conan 用户可能会受益于他们用于构建软件的应用程序的自定义和简单的安装程序。

Conan 有一个名为 virtualenv 的新生成器,灵感来自我们非常喜欢的 Python 虚拟环境。此生成器可以像任何其他生成器一样在您的 conanfile 的 [generators] 部分激活,并将生成两个文件:activate.[sh|bat]deactivate.[sh|bat]。如果我们运行 activate 脚本,Conan 将使用已安装的包声明的值设置我们当前的 shell 环境变量。最常见的用法是设置 PATH 环境变量,以便可以轻松执行工具。

让我们看一个例子。假设我们正在 Windows 计算机上工作,并且我们想要使用不同版本的 MinGW 和 CMake 构建和测试一个项目。因此,我们可以执行以下操作

在您的项目之外创建一个单独的文件夹。此文件夹将处理我们的开发环境。

$ mkdir my_cpp_environ
$ cd my_cpp_environ

现在,创建一个 conanfile.txt 文件

[requires]
mingw_installer/0.1@lasote/testing
cmake_installer/0.1@lasote/testing

[generators]
virtualenv

在这个文件中,我们要求两个包,一个是 MinGW 安装程序,另一个是 CMake 安装程序。“0.1”版本是安装程序配方的版本,而不是 MinGW 或 CMake 工具的版本。如果您想创建自己的与工具版本匹配的配方,您可以这样做,但通过这种方式,我们能够使用相同的配方处理许多 MinGW 和 CMake 版本。稍后我们将了解如何安装这些不同版本。

安装包

$ conan install

工具安装完成后,您可以在 shell 中激活虚拟环境

$ activate
(my_cpp_environ) $

检查一切是否正常并且工具位于路径中

(my_cpp_environ) $ gcc --version

> gcc (x86_64-posix-sjlj-rev1, Built by MinGW-W64 project) 4.9.2
 Copyright (C) 2014 Free Software Foundation, Inc.
 This is free software; see the source for copying conditions.  There is NO
 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
(my_cpp_environ) $ cmake --version
cmake version 3.6.0

CMake suite maintained and supported by Kitware (kitware.com/cmake).

您现在可以使用 deactivate 脚本停用虚拟环境

(my_cpp_environ) $ deactivate

在 Linux/OS X 上也可以执行相同操作(使用 CMake,因为 MinGW 仅适用于 Windows)。如果您是 Conan 用户,您会知道每个包都依赖于设置和选项。因此,我们可以更改可用选项以轻松获取不同的 MinGW 版本。

让我们看一下 MinGW 安装程序配方。转到 (https://www.conan.io/source/mingw_installer/0.1/lasote/testing) 并点击“conanfile.py”选项卡。

我们提供了以下选项

options = {"threads": ["posix", "win32"],
          "exception": ["dwarf2", "sjlj", "seh"], 
          "arch": ["x86", "x86_64"],
          "version": ["4.8", "4.9"]}
default_options = "exception=sjlj", "threads=posix", "arch=x86_64", "version=4.9"
       

默认情况下,我们安装的是带有 posix 线程支持和 sjlj 异常的 MinGW 4.9。但我们可以使用其他选项安装 MinGW

编辑您的 conanfile.txt

[requires]
mingw_installer/0.1@lasote/testing
cmake_installer/0.1@lasote/testing

[generators]
virtualenv

[options]
mingw_installer:threads=win32
mingw_installer:version=4.8

请记住停用先前的虚拟环境

(my_cpp_environ) $ deactivate

并再次安装

$ conan install

您也可以在命令行中传递选项,而不是在 conanfile.txt 文件中指定它们。

激活虚拟环境并检查工具是否已更改

$ activate
(my_cpp_environ) $ gcc --version

> gcc (rev0, Built by MinGW-W64 project) 4.8.2
Copyright (C) 2013 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

您可以与您的团队共享此 conanfile.txt,并以此方式共享开发环境!

如何创建我自己的工具包?

如果您想为任何工具创建 Conan 包,这很容易,特别是如果您已经熟悉创建 Conan 包。

让我们看看 Conan 的 CMake 配方是如何完成的

from conans import ConanFile
import os

class CMakeInstallerConan(ConanFile):
    name = "cmake_installer"
    version = "0.1"
    license = "MIT"
    url = "http://github.com/lasote/conan-cmake-installer"
    settings = {"os": ["Windows", "Linux", "Macos"], "arch": ["x86", "x86_64"]}
    options = {"version": ["3.6.0", "3.5.2", "3.4.3", "3.3.2", 
                           "3.2.3", "3.1.3", "3.0.2", "2.8.12"]}
    default_options = "version=3.6.0"
    
    def config(self):
        if self.settings.os == "Macos" and self.settings.arch == "x86":
            raise Exception("Not supported x86 for OSx")
        if self.settings.os == "Linux" and self.options.version == "2.8.12" and self.settings.arch == "x86_64":
            raise Exception("Not supported 2.8.12 for x86_64 binaries")

    def get_filename(self):
        os = {"Macos": "Darwin", "Windows": "win32"}.get(str(self.settings.os), str(self.settings.os))
        arch = {"x86": "i386"}.get(str(self.settings.arch), 
                                   str(self.settings.arch)) if self.settings.os != "Windows" else "x86"
        return "cmake-%s-%s-%s" % (self.options.version, os, arch)
    
    def build(self):
        keychain = "%s_%s_%s" % (self.settings.os,
                                 self.settings.arch,
                                 str(self.options.version))
        minor = str(self.options.version)[0:3]
        ext = "tar.gz" if not self.settings.os == "Windows" else "zip"
        url = "https://cmake.com.cn/files/v%s/%s.%s" % (minor, self.get_filename(), ext)

        dest_file = "file.tgz" if self.settings.os != "Windows" else "file.zip"
        self.output.warn("Downloading: %s" % url)
        tools.download(url, dest_file)
        tools.unzip(dest_file)
    
    def package(self):
        self.copy("*", dst="", src=self.get_filename())

    def package_info(self):
        self.env_info.path.append(os.path.join(self.package_folder, "bin"))
   

config 方法避免了一些设置/选项组合抛出异常。build 方法下载正确的 CMake 文件并解压缩它。package 方法将所有文件从 zip 复制到包文件夹。package_info 使用“self.env_info”将包的 bin 文件夹追加到环境变量“path”。

此包与常规的 Conan 库包只有 2 个不同之处

缺少 source 方法。这是因为当您编译库时,源代码对于所有生成的包始终相同,但在这种情况下,我们正在下载二进制文件,因此我们在 build 方法中执行此操作以下载每个设置/选项组合的不同 zip 文件。我们不是真正构建工具,而只是下载它们。当然,如果您想从源代码构建它,您也可以这样做,创建您自己的包配方。package_info 方法使用新的 self.env_info 对象。“self.env_info”允许包声明环境变量,这些变量将由 virtualenv 生成器设置。

如果包工具依赖于另一个工具,则 self.env_info 变量也很有用。查看 MinGW conanfile.py 配方

class MingwinstallerConan(ConanFile):
    name = "mingw_installer"
    version = "0.1"
    license = "MIT"
    url = "http://github.com/lasote/conan-mingw-installer"
    settings = {"os": ["Windows"]}
    options = {"threads": ["posix", "win32"],
               "exception": ["dwarf2", "sjlj", "seh"], 
               "arch": ["x86", "x86_64"],
               "version": ["4.8", "4.9"]}
    default_options = "exception=sjlj", "threads=posix", "arch=x86_64", "version=4.9"

    def config(self):
        self.requires.add("7z_installer/0.1@lasote/testing", private=True)
        
   
    def build(self):
        ...
        
        tools.download(files[keychain], "file.7z")
        env = ConfigureEnvironment(self)
        self.run("%s && 7z x file.7z" % env.command_line)
    
    def package(self):
        self.copy("*", dst="", src="mingw32")
        self.copy("*", dst="", src="mingw64")

    def package_info(self):
        self.env_info.path.append(os.path.join(self.package_folder, "bin"))
        self.env_info.CXX = os.path.join(self.package_folder, "bin", "g++.exe")
        self.env_info.CC = os.path.join(self.package_folder, "bin", "gcc.exe")

在 config 方法中,我们向另一个包添加了一个 require,即 7z_installer,它将用于解压缩 mingw 安装程序(使用 7z 压缩)。

在 build 方法中,我们正在下载正确的 MinGW 安装程序并使用 helper ConfigureEnvironment。此 helper 将为我们提供一个用于设置环境变量的命令字符串。这意味着 7z 可执行文件将位于路径中,因为 7z_installer 依赖项在其 package_info() 方法中声明了 bin 文件夹。

在 package_info 方法中,我们声明了 CC 和 CXX 变量,CMake、autotools 等使用它们来分别定位 C/C++ 编译器。我们还将 bin 文件夹追加到 path 变量,因此当我们执行 activate 脚本时,我们可以使用 virtualenv 生成器在命令行中调用 gcc、g++、make 和其他工具。

这些功能的可能性甚至更大。例如,它们在 CI 环境中非常有用。此外,如果您想避免将共享 (.dlls、.dylib) 库复制到项目二进制目录(可以使用 imports 功能完成),您可以创建包以将它们添加到环境 PATH 变量中,以便使用者可以轻松找到多个不同版本和设置的正确共享库。

您怎么看?您喜欢这个功能吗?您希望将哪些工具作为 Conan 包?使用我们的 愿望清单 并告诉我们您的开发环境!