Install
openclaw skills install @loonghao/rez-package-definitionAuthoring Rez package.py files — standard attributes, early/late binding functions, requires/variants/build_requires, string expansion and package overrides. Use when writing, reviewing or debugging a package definition file, or when the user asks how to declare dependencies, variants, tools or build-time requirements. Covers Rez 3.4.0.
openclaw skills install @loonghao/rez-package-definitionpackage.py)One-sentence summary: every variable in
package.pybecomes an attribute of the package;requiresdeclares dependencies andcommands()configures the runtime environment.
Skill scope: the definition file itself. For the commands() body see rez-package-commands;
for requests/version syntax see rez-core-concepts; for builds see rez-cli. For the decisions behind
versions, variants, range width and the build/release loop see rez-package-authoring.
name = "sequence"
version = "2.1.2"
description = "Sequence detection library."
authors = ["ajohns"]
tools = ["lsq", "cpq"]
requires = ["python-2.6+<3", "argparse"]
def commands():
env.PATH.append("{root}/bin")
env.PYTHONPATH.append("{root}/python")
uuid = "6c43d533-92bb-4f8b-b812-7020bf54d3f1"
The file lives at the root of each package install:
/packages/inhouse/foo/1.0.0/package.py.
Every module-level variable becomes an attribute — including custom ones. These do not:
import sys does not create a sys attribute);commands, @early and @late functions);That list is exactly as wide as it reads, and no wider. A class is not a module and not a
function, so from pathlib import Path at module scope does create a Path attribute — and it is
serialized into the installed package.py as Path = <class 'pathlib.Path'>, which no longer
parses. Same for a class defined in the file and for any object instance. Values derived from a
stripped module survive too: sys_path = sys.prefix is kept, frozen on the build machine. See
rez-package-pitfalls for the errors and the correct form.
| Attribute | Type | Purpose |
|---|---|---|
name | str | package name (required) |
version | str | version (required) |
description | str | general description, no version details |
authors | list[str] | ordered, major contributor first |
requires | list[str] | runtime dependencies |
build_requires | list[str] | build-time deps, transitive |
private_build_requires | list[str] | build-time deps, not transitive |
tools | list[str] | entry points exposed into the environment |
variants | list[list[str]] | variant definitions (see rez-core-concepts) |
hashed_variants | bool | install variants under a hash |
commands() | function | runtime environment configuration |
uuid | str | stable identity across renames |
has_plugins / plugin_for | bool / str | plugin discovery via rez-plugins |
cachable | bool | allow package caching |
config | dict | override rez settings during build/release |
Arbitrary custom attributes are allowed and are often the cleanest way to pass
build-computed data to commands().
requires vs build_requires vs private_build_requiresThe build environment requirement list is assembled in this order:
requiresbuild_requires — transitive: build requirements of all packages in the env are includedprivate_build_requires — not transitiveUse private_build_requires for doc generators (doxygen, sphinx), build utilities and
statically linked libraries. Use build_requires for header-only C++ libraries, where consumers
of your headers also need the header at build time.
@early)Evaluated at build time; the returned value is baked into the installed package.py.
@early()
def authors():
import subprocess
p = subprocess.Popen("git shortlog -sn | cut -f2",
shell=True, stdout=subprocess.PIPE)
out, _ = p.communicate()
return out.strip().split("\n")
Rules and gotchas:
this object exposes package attributes only.@early or @late attributes — an error is raised.package.py.@early functions are evaluated multiple times: once pre-build and once per variant.
The pre-build value (with building == False) is what lands in the installed package.building is False.@early()
def requires():
if building:
return ["python-2"]
else:
return ["runtimeonly-1.2", "python-2"]
Objects available during early evaluation: this, building, build_variant_index,
build_variant_requires.
@late)Remain as functions in the installed package; evaluated lazily on first access and then cached.
Allowed for: requires, build_requires, private_build_requires, tools, help, and any
arbitrary attribute.
@late()
def tools():
import os # imports MUST be inside the function
result = this._tools
if os.getenv("_USER_ROLE") != "superuser":
result = set(result) - set(["delete-all", "mod-things"])
return list(result)
@early()
def _tools():
import os
return os.listdir("./bin") # relative: not installed yet at build time
@early custom attribute, then do
only the runtime-dependent part in @late. This keeps resolves cheap.in_context() returns True when the package is part of a resolved context (e.g. iterating a
ResolvedContext) and False when merely iterating packages (e.g. rez-search). Guard
context-dependent logic with it.
commands()is late bound but is never decorated with@early/@late.
{root} expands to the install location of the package; {this.root} is equivalent.
Available in commands() and in rex calls. See rez-package-commands for literal(),
expandable() and expandvars().
A package can override rez settings during its own build/release:
with scope("config") as c:
c.release_packages_path = "/software/packages/apps"
Overrides only apply while the package is being built or released, so only these are useful:
packages_path, local_packages_path, release_packages_path, the build_system /
release_hook / release_vcs plugin settings, package_definition_python_path,
and package_filter.
Useful for routing internally- and externally-developed packages to different release paths.
package.py can be preprocessed before evaluation — for example to add a version timestamp or
to generate attributes from an external source. See the "Package Preprocessing" section of the
Rez package-definition docs: the package_preprocess_function setting installs a global
preprocessor, and rez-build --view-pre prints the preprocessed result.
Prefer the simplest thing that works: an @early() function usually covers the same ground with
far less machinery.
package.pyrequires uses ranges (python-2.7+<3) rather than unpinned names when pinning matters.private_build_requires, not requires.@early() returns the correct value when building is False.@late() performs imports inside the function body.variants entries that must be discriminated are pinned in the request, not left ambiguous.commands() uses POSIX forward slashes, even on Windows.os.pathsep or hardcoded a:b path lists.rez-build --install # build + install locally
rez-env mypackage # resolve in a fresh shell
rez-search --validate mypackage
Narrow the output — read the specific resolve, not the whole build log:
rez-context --so # source order of the resolve