我们很高兴地宣布Conan 1.41已发布,并带来了一些重要的新功能和错误修复。现在可以使用layout()更轻松地处理本地流程和可编辑包。我们通过新的intel-cc编译器设置引入了对Intel oneAPI工具套件的支持。此外,新的环境现在支持多配置生成器。我们在conanfile的cpp_info中添加了一个新的objects属性,以提供消费者可以链接到的目标文件(.obj.o)。最后,从这个版本开始,可以在同一个配方中使用多个不同的工具链,例如AutotoolsToolchainBazelToolchainCMakeToolchain

使用layout()更轻松地处理本地流程和可编辑包

对于此版本,我们修复了一些关于Conan 1.37中引入的新layout()功能的问题。此功能使在可编辑模式下处理包以及使用本地开发流程变得更加容易。当一个包处于可编辑模式时,Conan会在本地工作文件夹中查找其内容,而不是在Conan本地缓存中查找。这样,你就可以在本地重新构建你的包,以便依赖于此可编辑包的包将链接到更新后的包,而无需为源代码中的每次更改执行conan create命令。

假设你正在使用CMake开发一个名为say的包,并且你的项目结构如下

.
├── conanfile.py
└── say_sources
    ├── CMakeLists.txt
    ├── cpp
    │   └── say.cpp
    └── hpp
        └── say.h

使用layout()功能,我们可以描述包的内容,以便此包的使用者无论我们是在“常规”模式(在Conan缓存中查找内容)还是可编辑模式(在本地工作文件夹中查找内容)下工作,都能找到它。让我们看看如何在layout()方法中设置这些值,以便在两种情况下都能找到say包。此包的conanfile可能如下所示

from conans import ConanFile
import os

class SayConan(ConanFile):
    name = "say"
    version = "0.1"
    exports_sources = "say_sources/*"
    ...
    def layout(self):
        self.folders.source = "say_sources"
        build_type = str(self.settings.build_type).lower()
        self.folders.build = "cmake-build-{}".format(build_type)
        self.folders.generators = os.path.join(self.folders.build, "conan")

        self.cpp.package.libs = ["say"]
        self.cpp.package.includedirs = ["include"] # includedirs is already set to this value by 
                                                   # default, but declared for completion

        # self.cpp.source and self.cpp.build are used for editable mode
        # this information is relative to the source and build folders
        self.cpp.source.includedirs = ["hpp"] # maps to ./say_sources/hpp
        self.cpp.build.libdirs = ["."]        # maps to ./cmake-build-<build_type>
        self.cpp.build.bindirs = ["."]        # maps to ./cmake-build-<build_type>

    def build(self):
        ...

我们可以在layout()方法中设置多个属性,例如,与源代码所在位置以及我们希望构建包的位置相关的属性。

  • 由于我们的源代码位于say_sources文件夹中,因此self.folders.source设置为"./say_sources"
  • 我们使用CMake进行构建,因此我们希望将构建文件夹设置为cmake-build-releasecmake-build-debug,具体取决于build_type
  • self.folders.generators文件夹是Conan生成的所有文件将存储的位置,因此它们不会污染其他文件夹。

请注意,以上值适用于单配置CMake生成器。要支持多配置生成器(例如Visual Studio),你需要对这个布局进行一些更改。有关支持单配置和多配置的完整布局,请查看Conan文档中的cmake_layout()

此外,我们还可以通过设置conanfile的cpp.package属性值来设置使用者需要使用的包信息。

  • layout()方法内声明self.cpp.package.libs等效于在package_info()方法中使用“经典”的self.cpp_info.libs声明。
  • 此外,如你所知,self.cpp.package.includedirs默认设置为["include"]因此,无需声明它,但为了完整起见,我们将其保留在此处。

self.cpp.package.includedirs设置为["include"]意味着,当包未处于可编辑模式时,使用者将尝试在缓存中对应于类似以下内容的文件夹中查找say.h文件

/location/of/cache/data/say/0.1/_/_/package/<package_id>/include

如你所见,我们的本地文件夹中没有这样的结构,因此我们需要一种方法来告诉say包的使用者在可编辑模式下在哪里可以找到包含文件。我们可以使用conanfile的cpp.sourcecpp.build属性(它们是cpp_info对象)来设置该信息。

  • self.cpp.source:设置源文件(如包含文件)所在的文件夹,**相对于self.folders.source**。在此示例中,我们设置self.cpp.source.includedirs = ["hpp"]self.folders.source信息将自动添加到该路径中供使用者使用,因此Conan将尝试从./say_sources/hpp文件夹获取包含文件。
  • self.cpp.build:设置已构建文件(如库或二进制文件)所在的文件夹,**相对于self.folders.build**。与之前一样,你无需添加构建文件夹(cmake-build-releasecmake-build-debug),如果我们将self.cpp.build.libdirsself.cpp.build.bindirs设置为["."],Conan将尝试在./cmake-build-<build_type>文件夹中查找库和二进制文件。

请注意,在self.cpp.sourceself.cpp.build中设置的信息将与在self.cpp.package中设置的信息合并,因此我们无需再次声明诸如self.cpp.build.libs = ["say"]之类的内容,因为无论包是否处于可编辑模式,对于使用者来说都是一样的。

声明这些后,我们可以将包置于可编辑模式,构建它,以及其他需要say的包将以完全透明的方式使用它。

有关如何使用可编辑包和布局的完整示例,请查看Conan示例存储库。

Conan 1.41中的Intel oneAPI支持

从这个版本开始,你可以将编译器设置设置为intel-cc值以使用来自Intel oneAPI工具套件的Intel编译器。此设置有一个模式子设置,用于定义要从基础或HPC工具套件中使用的实际编译器。它可以采用以下值

  • icx:Intel oneAPI C++编译器(icx/icpx)
  • dpcpp:Intel oneAPI DPC++编译器(dpcpp)
  • classic:Intel C++编译器经典版(Linux/macOS上的icc/icpc和Windows上的icl)

此外,IntelCC已作为Conan的生成器添加。请查看Conan文档中有关此功能的更多信息。

环境生成器的多配置支持

Conan 1.35开始,提供了新的工具用于管理环境。现在,VirtualRunEnvVirtualBuildEnv的生成文件会考虑build_typearch设置来命名启动器文件,从而改善与多配置生成器的集成。例如,调用conan install cmake/3.20.0@ -g VirtualBuildEnv --build-require -s build_type=Release将创建一个名为conanbuildenv-release-x86_64.sh的文件,并且不会因其他build_type值而被覆盖。

此外,现在可以将生成的env变量启动器脚本分组到不同的名称下。默认情况下,它们都聚合到Environmentbuild组和VirtualeBuildEnv以及VirtualRunEnvrun组中,从而导致conan(build/run).(sh/bat)脚本设置所有已声明的环境。现在在conanfile中声明类似以下内容

class PkgConan(ConanFile):
    ...
    def generate(self):
        tc = CMakeToolchain(self)
        tc.generate()
        env1 = Environment(self)
        env1.define("env_name", "env1")
        env1.save_script("env1_launcher", group="mygroup")
        env2 = Environment(self)
        env2.define("env_name", "env2")
        env2.save_script("env2_launcher", group="mygroup")
        env3 = Environment(self)
        env2.define("env_name", "env3")
        env2.save_script("env3_launcher")
    ...

将创建一个名为 conanmygroup.sh 的文件,用于同时设置 env1env2,以及一个默认的 conanbuild.sh 文件,它只设置 env3 变量。

在 cpp_info.objects 新属性中定义目标文件

从 Conan 1.41 开始,您可以在 cpp_infoobjects 属性中设置目标文件(.obj.o)列表。目前,此属性仅与 CMakeDeps 生成器兼容。它会将声明的目标文件添加到生成的 CMake 目标中,以便这些目标文件稍后可以被声明依赖项的包使用。

假设我们创建一个 Linux 包 pkg/1.0,它构建并打包一个 myobject.obj 文件。与您声明依赖于 pkg/1.0 的包必须使用的库(通过设置 cpp_info.libs 属性)的方式相同,您可以声明此库打包了一些使用者需要添加的目标文件。这可以通过在 conanfile 的 package_info() 方法中设置这些信息来完成。

class Pkg(ConanFile):
    name = 'pkg'
    version = '1.0'
    ...
    def package_info(self):
        self.cpp_info.objects = ['lib/myobject.o']
    ...

然后,当其他包使用 CMakeDeps 生成器使用此包时,该目标文件的路径将添加到目标中,以便使用者可以链接到该 myobject.obj 文件。

在同一个配方中使用不同的工具链

新的工具链创建一个名为 conanbuild.conf 的文件,以将某些信息传递给构建帮助程序。文件名对于所有工具链都相同,这在配方使用不同的构建系统时会导致冲突问题。Conan 1.41 为 CMakeBazelAutotools 的构建帮助程序提供了一个新的参数 namespace。此参数使得在同一个配方中使用多个工具链成为可能,例如,当同一个包的部分内容使用不同的构建系统构建时。设置此值以将命名空间追加到文件名(如 conanbuild_namespace.conf),为每个工具生成一个唯一名称。一个使用此参数的 conanfile 可能如下所示

  from conans import ConanFile
  from conan.tools.gnu import AutotoolsToolchain, Autotools
  from conan.tools.google import BazelToolchain, Bazel
  from conan.tools.cmake import CMakeToolchain, CMake
  
  class Conan(ConanFile):
      settings = "os", "arch", "compiler", "build_type"
      def generate(self):
          # generates conanbuild_autotools.conf
          autotools = AutotoolsToolchain(self, namespace='autotools')
          autotools.generate() 
          
          # generates conanbuild_bazel.conf
          bazel = BazelToolchain(self, namespace='bazel')
          bazel.generate()     
          
          # generates conanbuild_cmake.conf
          cmake = CMakeToolchain(self, namespace='cmake')
          cmake.generate()     

      def build(self):
          # reads conanbuild_autotools.conf
          autotools = Autotools(self, namespace='autotools')
          autotools.configure() 
          autotools.make()
          
          # reads conanbuild_bazel.conf
          bazel = Bazel(self, namespace='bazel')
          bazel.configure()    
          bazel.build(label="//main:hello-world")
          
          # reads conanbuild_bazel.conf
          cmake = CMake(self, namespace='cmake')
          cmake.configure()   
          cmake.build()


除了上面列出的项目之外,还有一些小的错误修复,您可能希望阅读。如果是这样,请参阅 更改日志 以获取完整列表。

我们希望您喜欢此版本,并期待 您的反馈