Rez中文文档13 Building Packages

概述

rez包可以使用rez-build工具来构建和本地安装。这个工具有以下操作:

  • 遍历一个包的变体
  • 构建环境
  • 在此环境中运行构建系统

每一个构建都来自构建的目录路径。(通常是构建的子目录或构建下的变体特定子目录)
例如一个包含两个基本python变体的程序包:

1
2
3
4
5
+- package.py
+- CMakeLists.txt (or other build file)
+-build
+-python-2.6 # build dir for python-2.6 variant
+-python-2.7 # build dir for python-2.6 variant

在构建过程中,当前目录被视为构建路径。

构建环境

构建环境是一个rez解析环境,它的需求列表是这样构造的:

  • 首先使用程序包require列表
  • 然后添加程序包的build_require列表。其它包的build_requires也会被使用
  • 再然后追加程序包的private_build_requires
  • 最后,如果包有变体,则追加变体的需求

构建时会创建一系列环境变量(REZ_BUILD_开头),在这里可以看到构建时产生的环境变量。

在这个环境中会为每个变量调用构建系统。

构建时依赖

有时候一个包只需要依赖另一个包来构建它的代码,或者生成文档。
以文档为例,一个C++项目可能需要使用doxygen来构建它的文档,
而一旦文档被生成,就不再需要doxygen了。

这可以通过package.py的build_requires或private_build_requires来实现,
在这两个参数下的依赖包都只用于构建时,是过渡性的依赖列表。

一些private_build_requires使用包括:

  • 文档的生成器,如doxygen或sphinx
  • 构建工具,如一个名为pyqt_cmake_utils的包,它提供了将ui文件转换为py的cmake宏
  • 静态链接库(因为库在构建时已经链接好了,所以运行时就不需要这个包了)

包的通信

比如有两个C++包,maya_utils和著名的boost库。maya_utils如何找到boost的头文件,或库文件?
简单的回答是,这取决于你。Rez其实并不是一个构建系统,它支持各种构建系统,它可以配置构建环境,但构建本身的细节部分是留给使用者的。

话虽如此,rez支持cmake有一段时间了,而且rez自带大量的实用代码来管理cmake构建。
当配置rez环境时,每个包的命令部分都会配置环境。当发生构建时,一个特殊的变量building被设置为True。你的包会使用这个变量来向未构建的包传达构建信息。

举个例子,我们的boost包命令看起来也许是这样的:

1
2
3
4
def commands():
if building:
# there is a 'FindBoost.cmake' file in this dir..
env.CMAKE_MODULE_PATH.append("{root}/cmake")

一个简单的FindBoost.cmake文件可能是这样:

1
2
3
set(Boost_INCLUDE_DIRS $ENV{REZ_BOOST_ROOT}/include)
set(Boost_LIBRARY_DIRS $ENV{REZ_BOOST_ROOT}/lib)
set(Boost_LIBRARIES boost-python)

然后,我们的maya_utils包可能有一个CMakeLists.txt文件(cmake的构建脚本),其中包含:

1
2
3
4
find_package(Boost)
include_directories(${Boost_INCLUDE_DIRS})
link_directories(${Boost_LIBRARY_DIRS})
target_link_libraries(maya_utils ${Boost_LIBRARIES})

find_package cmake宏会搜索CMAKE_MODULE_PATH环境变量中列出的路径,并寻找一个名为FindXXX.cmake的文件,其中XXX是包的名称(在本例中为Boost),然后将其包含在内。

构建系统

Rez支持多种构建系统,新的构建系统可以作为插件添加。当调用构建时,会自动检测并选择构建系统。例如如果在程序包根目录发现来CMakeLists.txt文件,就会使用cmake构建系统。

参数传递

有两种方法可以将参数传递给构建系统。

其一,一些编译系统插件会直接向rez-build命令添加额外选项。例如在一个基于CMake的程序包中,
又运行了rez-build -h命令,你会看到cmake的特定参数列表,比如 —build-target

其二,你可以直接向编译系统传递参数,使用rez-build —build-args命令。

例如,这里在cmake中明确定义一个变量:
]$ rez-build -- -DMYVAR=YES

自定义构建

除了从构建文件中检查构建系统外,程序包还可以使用build_command属性明确指定构建命令。

在package.py文件中定义:

1
2
3
4
5
name = "nuke_utils"

version = "1.2.3"

build_command = "bash {root}/build.sh {install}"

当在这个包运行rez-build时,给定的build.sh脚本将用bash执行。

{root}字符为软件包根目录,如果又安装,{install}字符串将展开为”install”,否则为空。

构建过程中的当前工作目录被设置为构建路径,而不是包的根目录。因此通常会使用{root}字符串来引用包根目录下的构建脚本。

传递参数

你可以直接为你的编译脚本添加参数到rez-build命令中,通过在程序包根目录创建一个parse_build_args.py文件,如下:

1
2
# in parse_build_args.py
parser.add_argument("--foo", action="store_true", help="do some foo")

现在,如果你在这个软件包上运行rez-build -h会看到列出的选项:

1
2
3
4
5
6
7
8
9
10
$ rez-build -h
usage: rez build [-h] [-c] [-i] [-p PATH] [--fail-graph] [-s] [--view-pre]
[--process {remote,local}] [--foo]
[--variants INDEX [INDEX ...]] [--ba ARGS] [--cba ARGS] [-v]

Build a package from source.

optional arguments:
...
--foo do some foo

添加的参数会被存储到环境变量中,这样你的编译脚本就可以访问它们。

它们的前缀是__PARSE_ARF_ ;在上面的例子中,变量__PARSE_ARG_FOO被设置。
布尔由0/1表示,而列表则用空格分隔,必要时使用引号。

一个实例

以下是一个非常简单的C++例子,展示来如何使用自定义构建命令,通过make进行构建和安装。

1
2
3
4
5
6
7
8
9
10
11
# in package.py
build_command = "make -f {root}/Makefile {install}"

# in Makefile
hai: ${REZ_BUILD_SOURCE_PATH}/lib/main.cpp
g++ -o hai ${REZ_BUILD_SOURCE_PATH}/lib/main.cpp

.PHONY: install
install: hai
mkdir -p ${REZ_BUILD_INSTALL_PATH}/bin
p $< ${REZ_BUILD_INSTALL_PATH}/bin/hai

本地包安装

在你做了一些代码修改之后,想要测试它们。你可以通过本地安装程序包,然后用rez-env解析环境来测试程序包,大概是这样:

  • 进行代码修改
  • 运行rez-build —install 安装为本地包
  • 在一个单独的shell里运行rez-env mypackage。这将启用你的本地软件包,以及程序包需求
  • 测试软件包

本地安装会将程序包构建并安装到本地仓库中,通常是~/package目录。这个目录被列在包搜索路径的开头,所以当你解析一个环境进行测试时,本地安装的包会被首先选中。你的包通常会被安装到 ~/packages/packagename/version,例如 ~/packages/maya_utils/1.0.5。如果你有变体,它们会被安装到这个安装路径的子目录中。
(可以参考文章:变体 (Variants)的结构部分)

你不需要每次安装后都运行rez-env。如果你的程序包没有改变,你可以继续使用现有测试环境。

可以通过检查rez-env执行后的输出,来确保你已经pick了你的本地包。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
]$ rez-env sequence

You are now in a rez-configured environment.

resolved by ajohns@turtle, on Thu Mar 09 11:41:06 2017, using Rez v2.7.0

requested packages:
sequence
~platform==linux (implicit)
~arch==x86_64 (implicit)
~os==Ubuntu-16.04 (implicit)

resolved packages:
arch-x86_64 /sw/packages/arch/x86_64
os-Ubuntu-16.04 /sw/packages/os/Ubuntu-16.04
platform-linux /sw/packages/platform/linux
python-2.7.12 /sw/packages/python/2.7.12
sequence-2.1.2 /home/ajohns/packages/sequence/2.1.2 (local)

注意:这里的sequence包是本地安装,括号里local表示。