一些开发人员认为C/ c++的包管理器在默认情况下应该将调试和发布工件捆绑在同一个包中,以便开发人员在工作时可以轻松地使用它们更改配置。

但是其他开发人员可能认为这不是一个好的实践,发布包和调试包应该是不同的,默认情况下应该分别安装。Linux“-dbg”符号包为例。

事实是两者都有优点和缺点,如果我们从开发包管理器的经验中学到一些东西,那就是没有绝对的真理,C/ c++包管理器应该为开发人员提供支持他们想要实现的打包范式的方法。我们不断听取用户的反馈,最新的Conan 0.20版本包含了一些有助于支持多包范例的实用程序。

首先,回顾和理解柯南如何处理包是很有趣的:

上图中的每个块都是给定包的一个文件夹。包中的食谱存放在“导出”文件夹中,它被复制到“源”文件夹中,使食谱源()方法可以获取包源代码。然后,对于每个不同的配置(不同的设置,例如不同的编译器版本或体系结构),使用一个新的、干净的构建文件夹,即recipebuild ()方法触发,最后,构件(通常是头文件和库)由包()方法添加到最终的包文件夹中。每个包由配置值的SHA-1散列标识。

单一配置包

这是Conan中最常用的方法,在文档和Conan .io中的大多数包中都有充分的使用。使用这种方法,每个包只包含一个配置的工件。因此,如果有一个构建“hello”库的包配方,则将有一个包包含hello.lib类库和包含hello_d.lib该库的调试版本。名称后缀是可选的,库可以以相同的名称命名,没有任何问题,但这里使用它是为了更清楚。

典型的配方是这样的(不是一个完整的配方):

HelloConanConanFile):设置“操作系统”“编译器”“build_type”“拱”def构建自我):cmakeCMake自我设置cmake配置自我#调用“cmake”。-g……”cmake构建自我#调用“cmake—build”。defpackage_info自我):自我cpp_info填词“你好”

非常重要的是要注意它声明的是build_type作为一种设定。这意味着将为此类设置的每个不同值生成不同的包。

安装这些包时,为构建系统生成的文件,如conanbuildinfo.cmake档案由cmake生成器,将包含不同的信息取决于安装设置:

CONAN_LIBS_HELLO你好...CONAN_LIBS你好$ {CONAN_LIBS

如果开发人员想要切换依赖项的配置,他通常会切换:

柯南安装- sbuild_type释放……//当需要调试时柯南安装- sbuild_type调试……

这些切换将是快速的,因为所有依赖项都已经缓存在本地。

这个过程有一些优点:它很容易实现和维护。包的大小是最小的,因此磁盘空间和传输速度更快,并且源构建也保持在必要的最小值。配置的解耦可能有助于隔离与混合不同类型工件相关的问题,还可以保护有价值的信息免受部署和分发错误的影响。例如,调试工件可能包含符号或源代码,这可以帮助或直接提供逆向工程的方法。因此,按工件分发调试工件可能是一个非常危险的问题。主要的缺点是在从Debug切换到Release时必须记住安装特定的依赖项配置,反之亦然。这是IDE的重度用户,比如Visual Studio,会觉得有点不方便。

使用多个调试/发布单个配置包

即使这些包是单配置的,如果最终用户开发人员想要在多配置环境中轻松地使用它们,比如Visual Studio,他们可以通过CMake来实现cmake_multi发电机。使用生成器,安装依赖项的调试和发布配置就足够了:

柯南安装- gcmake_multi- sbuild_type释放- scompiler.runtime医学博士…柯南安装- gcmake_multi- sbuild_type调试- scompiler.runtimeMDd……

这些命令将生成3个文件:conanbuildinfo_multi.cmakeconanbuildinfo_debug.cmake而且conanbuildinfo_release.cmake.的_debug_release文件将包含它们自己的cmake变量。

然后,在消费者CMakeLists.txt中使用:

项目MyHellocmake_minimum_required版本2.8.12包括$ {CMAKE_BINARY_DIR/ conanbuildinfo_multi.cmakeconan_basic_setup()add_executablesay_hello main.cppconan_target_link_librariessay_hello

多配置包

在多配置包中,同一个包将包含不同配置的工件。在我们的例子中,同一个包可以包含库" hello "的发布版本和调试版本。

这并不意味着每个配方只能有一个包或严格意义上的一个构建文件夹,因为对于不同的体系结构,或者不同的编译器版本,仍然可以有不同的包。包创建者可以定义他们独特的打包逻辑。

要实现这种方法,包配方可以这样做:

def构建自我):cmakeCMake自我设置如果cmakeis_multi_configurationcmdcmake“%s”“%s”自我conanfile_directorycmakecommand_line自我运行cmd自我运行“cmake——构建。——配置调试”自我运行“cmake——构建。——配置发布”其他的配置“调试”“发布”):自我输出信息“% s”配置自我运行cmake“%s”%s -DCMAKE_BUILD_TYPE=%s”自我conanfile_directorycmakecommand_line配置))自我运行“cmake——建造。”shutilrmtree“CMakeFiles”操作系统删除“CMakeCache.txt”

假设a_d正在使用后缀名(其他方法是有效的,例如具有不同的文件夹),则package_info ()方法可以是:

defpackage_info自我):自我cpp_info释放填词“你好”自我cpp_info调试填词“hello_d”

这些包不需要在安装时指定构建类型,如果提供了,它将被忽略,例如,对于使用cmake生成器的消费者:

柯南安装- gcmake# no -s build_type=发布/调试

这将为消费者构建系统生成不同的变量conanbuildinfo.cmake,如:

CONAN_LIBS_HELLO_DEBUG hello_dCONAN_LIBS_HELLO_RELEASE你好...CONAN_LIBS_DEBUG hello_d$ {CONAN_LIBS_DEBUGCONAN_LIBS_RELEASE你好$ {CONAN_LIBS_RELEASE

正如上面提到的,这种方法将有调试工件分发的风险,这是一个可以促进逆向工程的重要问题。同样,包总是更大,需要更多的时间来构建、传输和安装,即使您没有使用所有的工件(如在生产中)。其主要优点是开发人员更容易在ide中的调试和发布配置之间切换,而无需做任何其他事情。

一次构建,多次打包

可能已经存在的构建脚本正在一次为不同的配置构建二进制文件,比如调试/发布,或者不同的体系结构(32/64位),或者库类型(共享/静态)。如果在前面的“单个配置包”方法中使用了这样的构建脚本,那么它肯定会毫无问题地工作,但是我们将浪费宝贵的构建时间,因为我们将为每个包重新构建整个项目,然后提取给定配置的相关工件,而留下其他的。

使用Conan 0.20,可以指定逻辑,因此可以重用相同的构建来创建不同的包,这将更有效:

这可以通过定义build_id ()方法,该方法将指定逻辑。

设置“操作系统”“编译器”“拱”“build_type”defbuild_id自我):自我info_build设置build_type“任何”def自我):如果自我设置build_type==“调试”#包调试工件其他的#软件包发布

注意build_id ()方法使用self.info_build对象来更改生成散列。如果方法没有改变它,散列将匹配包文件夹1。通过设置build_type = "任何",我们要求双方都这样做调试而且释放的值build_type,哈希将是相同的(特定的字符串是不相关的,只要它是相同的两个配置)。注意构建散列sha3两者会有所不同吗sha1而且sha2包标识符。

这并不意味着只有一个构建文件夹。每个配置(体系结构、编译器版本等)都有一个构建文件夹。因此,如果我们只有调试/发布构建类型,并且我们为N个不同的配置生成N个包,我们将有N/2个构建文件夹,节省一半的构建时间。

结论

这篇博客文章说明了柯南如何允许非常不同的包装范式。这是由我们的维护者、贡献者和用户社区所推动的一项持续的努力,他们试图提供工具来支持我们在C和c++社区中所拥有的非常不同的用例和需求。非常感谢他们所有人!