本文将介绍如何创建、构建和测试包。
一、开始
本节译自:https://docs.conan.io/en/latest/creating_packages/getting_started.html
为了开始学习如何创建包,我们将从现有的源码存储库中创建一个包:https://github.com/conan-io/hello。我们可以检查该项目,它是一个非常简单的 “hello world”的C++库,使用CMake作为构建系统来构建库和可执行文件。它不包含与Conan的任何关联。
我们使用类似的GitHub存储库作为示例,但是相同的过程也适用于其他源码来源,例如从网上下载zip或tarball。
注意: 对于这个具体的示例,除了C++编译器之外,还需要在路径中安装CMake和git。它们不是Conan所要求的,因此可以使用自己的构建系统和版本控制。
1、创建包配方
首先,为包配方创建一个文件夹,并使用创建工作包配方的conan new
帮助命令:
$ mkdir mypkg && cd mypkg
$ conan new hello/0.1 -t
将生成以下文件:
conanfile.py
test_package
CMakeLists.txt
conanfile.py
example.cpp
在根目录,有一个conanfile.py
,它是主要的配方文件,负责定义我们的包。此外,还有一个test_package
文件夹,其中包含一个简单的测试项目,该项目需要并与创建的包链接。用于测试包能够正常运行。
让我们看一下包配方conanfile.py
:
from conans import ConanFile, CMake, tools
class HelloConan(ConanFile):
name = "hello"
version = "0.1"
license = "<Put the package license here>"
url = "<Package recipe repository url here, for issues about the package>"
description = "<Description of hello here>"
settings = "os", "compiler", "build_type", "arch"
options = {
"shared": [True, False]}
default_options = {
"shared": False}
generators = "cmake"
def source(self):
self.run("git clone https://github.com/conan-io/hello.git")
# This small hack might be useful to guarantee proper /MT /MD linkage
# in MSVC if the packaged project doesn't have variables to set it
# properly
tools.replace_in_file("hello/CMakeLists.txt", "PROJECT(MyHello)",
'''PROJECT(MyHello)
include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup()''')
def build(self):
cmake = CMake(self)
cmake.configure(source_folder="hello")
cmake.build()
# Explicit way:
# self.run('cmake %s/hello %s'
# % (self.source_folder, cmake.command_line))
# self.run("cmake --build . %s" % cmake.build_config)
def package(self):
self.copy("*.h", dst="include", src="hello")
self.copy("*hello.lib", dst="lib", keep_path=False)
self.copy("*.dll", dst="bin", keep_path=False)
self.copy("*.so", dst="lib", keep_path=False)
self.copy("*.dylib", dst="lib", keep_path=False)
self.copy("*.a", dst="lib", keep_path=False)
def package_info(self):
self.cpp_info.libs = ["hello"]
这是一个完整的包配方。以下是基础信息:
settings
字段定义不同二进制包的配置。在此示例中,对操作系统、编译器、构建类型或架构定义的任何修改都将生成不同的二进制包。
注意,如果包是交叉编译的,那么运行的配方和正在构建的包所在的平台与运行代码的最终平台 (self.settings.os
和self.settings.arch
) 是不同的。因此,如果想在当前系统编译别的系统的库,需要检查:
def build(self):
if platform.system() == "Windows":
cmake = CMake(self)
cmake.configure(source_folder="hello")
cmake.build()
else:
env_build = AutoToolsBuildEnvironment(self)
env_build.configure()
env_build.make()
- 包配方还能够使用
shared
选项为静态和动态链接库创建不同的二进制包,该选项默认设置为False
(即默认情况下,它使用静态链接)。 source()
方法执行git clone
从Github检索源,其他的如下载zip压缩文件同样是可以的。正如所见,可以执行对代码的任何操作,例如签出任何分支或标签,或修补源码。在此示例中,将在现有CMake代码中添加两行,以确保二进制兼容性。build()
配置项目,然后使用标准CMake命令进行构建。CMake
对象仅用于将Conan设置转换为CMake命令行参数。请注意,并非一定需要CMake。还可以通过调用make、MSBuild、SCons或任何其他构建系统直接构建包。package()
方法将(头文件、libs))从构建文件夹复制到最终的包文件夹。- 最后,
package_info()
方法定义使用者在使用此包时必须与“hello”库链接。也可以定义如包含(或lib)路径的其他信息。这些信息在由生成器创建的文件中使用,如conanbuildinfo.cmake
。
2、test_package文件夹
注意:test_package
与库单元测试或集成测试不同,后者可能更全面些。 这些测试是“包”测试,它们验证程序包是否正确创建,且包使用者是否能够链接到该包并复用它。
如果查看test_package
文件夹,将发现example.cpp
和CMakeLists.txt
文件没有特别之处。test_package/conanfile.py
文件只是另一个配方,和之前的conanfile.txt
没什么区别:
from conans import ConanFile, CMake
import os
class HelloTestConan(ConanFile):
settings = "os", "compiler", "build_type", "arch"
generators = "cmake"
def build(self):
cmake = CMake(self)
cmake.configure()
cmake.build()
def imports(self):
self.copy("*.dll", dst="bin", src="bin")
self.copy("*.dylib*", dst="bin", src="lib")
def test(self):
os.chdir("bin")
self.run(".%sexample" % os.sep)
上述conanfile.py
具有以下特征:
- 它没有名称和版本,因为我们没有创建包,所以没有必要。
- 不需要
package()
和package_info()
方法,因为我们没有创建包。 test()
方法指定需要运行哪些二进制文件。imports()
方法设置为将动态链接库复制到bin文件夹。当应用动态链接时,test()
方法启动example
可执行文件,example
也将会运行。
3、创建和测试包
运行以下命令,就可以使用默认设置创建和测试包:
$ conan create . demo/testing
...
Hello world Release!
如果显示“Hello world Release!”,就表示成功。
conan create
命令执行以下操作:
- 将
conanfile.py
从用户文件夹复制到本地缓存(用Conan术语来讲是“export”)。 - 安装包,强制从源码构建它。
- 切换到
test_package
文件夹并创建一个临时build
文件夹。 - 执行
conan install ..
以安装test_package/conanfile.py
的要求。注意,它将从源代码构建“hello”。 - 构建并启动示例应用程序,分别调用
test_package/conanfile.py
的build()
和test()
方法。
使用Conan命令conan create
命令等同于:
$ conan export . demo/testing
$ conan install hello/0.1@demo/testing --build=hello
# package is created now, use test to test it
$ conan test test_package hello/0.1@demo/testing
conan create
命令接收与conan install
相同的命令行参数,因此可以将相同的设置、选项和命令行开关传递给它。如果要为不同的配置创建和测试包,可以如下:
$ conan create . demo/testing -s build_type=Debug
$ conan create . demo/testing -o hello:shared=True -s arch=x86
$ conan create . demo/testing -pr my_gcc49_debug_profile
...
$ conan create ...
4、省略user/channel
警告:这是一个实验特性,在未来的版本中会有突破性的变化。
可以创建一个省略了user/channel
的包:$ conan create .
。
要引用该包,还必须省略user/channle
。
示例
- 在配方中指定要求:
class HelloTestConan(ConanFile):
settings = "os", "compiler", "build_type", "arch"
requires = "packagename/1.0"
...
- 安装单个包。使用
conan install
我们必须使用packagename/1.0@
的语法(始终有效)来消除参数的歧义,该参数也可用于指定路径:
$ conan install packagename/1.0@
- 搜索引用的二进制包。
conan search
命令需要使用packagename/1.0@
的语法(始终有效)来消除用法歧义:
$ conan search packagename/1.0@
Existing packages for recipe packagename/1.0:
Package_ID: 9bfdcfa2bb925892ecf42e2a018a3f3529826676
[settings]
arch: x86_64
build_type: Release
compiler: gcc
compiler.libcxx: libstdc++11
compiler.version: 7
os: Linux
Outdated from recipe: False
- 移除包:
$ conan remove packagename/1.0
- 上传包:
conan upload packagename/1.0
5、Settings和Options
我们已经使用了settings
中的os
、arch
和compiler
等设置。注意上面的包配方还包含一个shared
的options
(定义为options = {“shared”: [True,False]}
)。那么settings
和options
直接有什么区别呢?
settings
是项目范围的配置,通常会影响正在构建的整个项目。例如,对于依赖关系图中的所有包,操作系统或编译环境自然是要相同的。想为Windows应用程序链接Linux库是不可能的。settings
在包配方中不能给定默认值。给定库的配方不能说其默认值为os=Windows
。os
将从环境得到,这是强制性的。settings
是可配置的。可以在settings.yml
文件中编辑、添加、删除设置或设置子集。options
是特定于包的配置。静态或共享库不是适用于所有包的设置。有些包是仅只有头文件,而其他包可能只有数据或可执行文件。包可以包含不同文件的混合。shared
是一个常见的选项,但是包可以定义和使用他们想要的任何选项。options
在包配方中定义,包括其支持的值,而其他选项可以由包配方本身默认。库的包可以很好地定义默认情况下它将是静态库(默认值)。如果未指定其他,包将是静态库。
上述情况有一些例外。例如,可以使用命令行按包定义设置:
$ conan install . -s MyPkg:compiler=gcc -s compiler=clang ..
这将对MyPkg使用gcc
,对其余依赖项使用clang
(非常罕见的情况)。
在某些情况下,许多包使用相同的选项,从而允许在使用模式后设置其值,例如:
$ conan install . -o *:shared=True
二、配方与源码在不同的位置
本节译自:https://docs.conan.io/en/latest/creating_packages/external_repo.html
在前面,我们从外部存储库中获取了库的源码。通常打包第三方库采用此流程。
从外部存储库获取源码有两种方式:
- 使用前面展示的
source()
方法:
from conans import ConanFile, CMake, tools
class HelloConan(ConanFile):
...
def source(self):
self.run("git clone https://github.com/conan-io/hello.git")
...
还可以使用tools.Git
类:
from conans import ConanFile, CMake, tools
class HelloConan(ConanFile):
...