Skip to content

Commit 5531557

Browse files
author
ajohns
committed
-added native support for expanding 'pkg-*' requirements lists
-added rez-build --view-pre option -fix for case where @included module used both in developer pkg and installed pkg
1 parent 5712646 commit 5531557

File tree

5 files changed

+107
-71
lines changed

5 files changed

+107
-71
lines changed

src/rez/cli/build.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ def setup_parser(parser, completions=False):
5959
help="create build scripts rather than performing the full build. "
6060
"Running these scripts will place you into a build environment, where "
6161
"you can invoke the build system directly.")
62+
parser.add_argument(
63+
"--view-pre", action="store_true",
64+
help="just view the preprocessed package definition, and exit.")
6265
setup_parser_common(parser)
6366

6467

@@ -85,12 +88,17 @@ def command(opts, parser, extra_arg_groups=None):
8588
from rez.packages_ import get_developer_package
8689
from rez.build_process_ import create_build_process
8790
from rez.build_system import create_build_system
91+
from rez.serialise import FileFormat
8892
import sys
8993

9094
# load package
9195
working_dir = os.getcwd()
9296
package = get_developer_package(working_dir)
9397

98+
if opts.view_pre:
99+
package.print_info(format_=FileFormat.py, skip_attributes=["preprocess"])
100+
sys.exit(0)
101+
94102
# create build system
95103
build_args, child_build_args = get_build_args(opts, parser, extra_arg_groups)
96104
buildsys_type = opts.buildsys if ("buildsys" in opts) else None

src/rez/package_maker__.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,17 @@
77
_function_schema, late_bound
88
from rez.package_repository import create_memory_package_repository
99
from rez.packages_ import Package
10+
from rez.package_py_utils import expand_requirement
1011
from rez.vendor.schema.schema import Schema, Optional, Or, Use, And
1112
from rez.vendor.version.version import Version
1213
from contextlib import contextmanager
1314
import os
1415

1516

16-
package_request_schema = Or(basestring,
17+
# this schema will automatically harden request strings like 'python-*'; see
18+
# the 'expand_requires' function for more info.
19+
#
20+
package_request_schema = Or(And(basestring, Use(expand_requirement)),
1721
And(PackageRequest, Use(str)))
1822

1923

src/rez/package_py_utils.py

Lines changed: 38 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -4,44 +4,54 @@
44
55
- the special 'preprocess' function;
66
- early bound functions that use the @early decorator.
7+
"""
78

8-
An example of use:
9+
# these imports just forward the symbols into this module's namespace
10+
from rez.utils.sourcecode import late
11+
from rez.exceptions import InvalidPackageError
912

10-
# in package.py
11-
name = 'mypackage'
1213

13-
version = '1.2.3'
14+
def expand_requirement(request):
15+
"""Expands a requirement string like 'python-2.*'
1416
15-
@early()
16-
def requires():
17-
from rez.package_py_utils import expand_requires
17+
Only trailing wildcards are supported; they will be replaced with the
18+
latest package version found within the range. If none are found, the
19+
wildcards will just be stripped.
1820
19-
return expand_requires(
20-
'boost-1.*.*',
21-
'maya-2017.*'
22-
)
23-
"""
21+
Example:
2422
25-
# Here to allow 'from rez.package_utils import late' in package.py
26-
from rez.utils.sourcecode import late
23+
>>> print expand_requirement('python-2.*')
24+
python-2.7
2725
28-
# Here to allow 'from rez.package_utils import InvalidPackageError' in package.py
29-
from rez.exceptions import InvalidPackageError
26+
Args:
27+
request (str): Request to expand, eg 'python-2.*'
3028
29+
Returns:
30+
str: Expanded request string.
31+
"""
32+
from rez.vendor.version.requirement import VersionedObject, Requirement
33+
from rez.packages_ import get_latest_package
3134

32-
def expand_requires(*requests):
33-
"""Create an expanded requirements list.
35+
txt = request.replace('*', '_')
36+
obj = VersionedObject(txt)
37+
rank = len(obj.version)
38+
39+
request_ = request
40+
while request_.endswith('*'):
41+
request_ = request_[:-2] # strip sep + *
3442

35-
Given a list of requirements with optional trailing wildcards, expand each
36-
out to the latest package found within that range. This is useful when a
37-
package is compatible with a version range of a package at build time, but
38-
requires a stricter requirement at runtime. For example, a compiled library
39-
may build with many versions of boost (boost-1.*.*), but once compiled, must
40-
be used with the boost version that has then been linked against (1.55.0).
43+
req = Requirement(request_)
44+
package = get_latest_package(name=req.name, range_=req.range_)
4145

42-
Note:
43-
If a package is not found in the given range, it is expanded to the
44-
request as-is, with trailing wildcards removed.
46+
if package is None:
47+
return request_
48+
49+
obj.version_ = package.version.trim(rank)
50+
return str(obj)
51+
52+
53+
def expand_requires(*requests):
54+
"""Create an expanded requirements list.
4555
4656
Example:
4757
@@ -57,27 +67,4 @@ def expand_requires(*requests):
5767
Returns:
5868
List of str: Expanded requirements.
5969
"""
60-
from rez.vendor.version.requirement import VersionedObject
61-
from rez.packages_ import get_latest_package
62-
63-
result = []
64-
65-
for request in requests:
66-
txt = request.replace('*', '_')
67-
obj = VersionedObject(txt)
68-
rank = len(obj.version)
69-
70-
request_ = request
71-
while request_.endswith('*'):
72-
request_ = request_[:-2] # consume sep + *
73-
74-
package = get_latest_package(request_)
75-
76-
if package is None:
77-
result.append(request_)
78-
continue
79-
80-
obj.version_ = package.version.trim(rank)
81-
result.append(str(obj))
82-
83-
return result
70+
return [expand_requirement(x) for x in requests]

src/rez/packages_.py

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
VariantResource, package_family_schema, package_schema, variant_schema, \
44
package_release_keys, late_requires_schema
55
from rez.package_serialise import dump_package_data
6+
from rez.utils import reraise
67
from rez.utils.logging_ import print_info, print_error
78
from rez.utils.sourcecode import SourceCode
89
from rez.utils.data_utils import cached_property, _missing
@@ -234,7 +235,7 @@ def iter_variants(self):
234235
"""
235236
repo = self.resource._repository
236237
for variant in repo.iter_variants(self.resource):
237-
yield Variant(variant, context=self.context)
238+
yield Variant(variant, context=self.context, parent=self)
238239

239240
def get_variant(self, index=None):
240241
"""Get the variant with the associated index.
@@ -257,9 +258,10 @@ class Variant(PackageBaseResourceWrapper):
257258
keys = schema_keys(variant_schema)
258259
keys.update(["index", "root", "subpath"])
259260

260-
def __init__(self, resource, context=None):
261+
def __init__(self, resource, context=None, parent=None):
261262
_check_class(resource, VariantResource)
262263
super(Variant, self).__init__(resource, context)
264+
self._parent = parent
263265

264266
# arbitrary keys
265267
def __getattr__(self, name):
@@ -293,24 +295,35 @@ def parent(self):
293295
Returns:
294296
`Package`.
295297
"""
296-
repo = self.resource._repository
297-
package = repo.get_parent_package(self.resource)
298-
return Package(package, context=self.context)
298+
if self._parent is not None:
299+
return self._parent
300+
301+
try:
302+
repo = self.resource._repository
303+
package = repo.get_parent_package(self.resource)
304+
self._parent = Package(package, context=self.context)
305+
except AttributeError as e:
306+
reraise(e, ValueError)
307+
308+
return self._parent
299309

300310
@property
301311
def requires(self):
302312
"""Get variant requirements.
303313
304-
This is a concatenation of the package requirements and those if this
314+
This is a concatenation of the package requirements and those of this
305315
specific variant.
306316
"""
307-
package_requires = self.parent.requires or []
317+
try:
318+
package_requires = self.parent.requires or []
308319

309-
if self.index is None:
310-
return package_requires
311-
else:
312-
variant_requires = self.parent.variants[self.index] or []
313-
return package_requires + variant_requires
320+
if self.index is None:
321+
return package_requires
322+
else:
323+
variant_requires = self.parent.variants[self.index] or []
324+
return package_requires + variant_requires
325+
except AttributeError as e:
326+
reraise(e, ValueError)
314327

315328
def get_requires(self, build_requires=False, private_build_requires=False):
316329
"""Get the requirements of the variant.

src/rez/utils/sourcecode.py

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -293,16 +293,40 @@ def __init__(self):
293293

294294
def load_module(self, name, package):
295295
from rez.config import config # avoiding circular import
296+
from rez.developer_package import DeveloperPackage
296297

297-
path = os.path.join(package.base, self.include_modules_subpath)
298-
pathname = os.path.join(path, "%s-*.py" % name)
298+
# in rare cases, a @late bound function may get called before the
299+
# package is built. An example is 'requires' and the other requires-like
300+
# functions. These need to be evaluated before a build, but it does also
301+
# make sense to sometimes implement these as late-bound functions. We
302+
# detect this case here, and load the modules from the original (pre-
303+
# copied into package payload) location.
304+
#
305+
if isinstance(package, DeveloperPackage):
306+
from hashlib import sha1
299307

300-
pathnames = glob(pathname)
301-
if not pathnames:
302-
return None
308+
# load sourcefile from original location
309+
path = config.package_definition_python_path
310+
filepath = os.path.join(path, "%s.py" % name)
311+
312+
if not os.path.exists(filepath):
313+
return None
314+
315+
with open(filepath) as f:
316+
txt = f.read().strip()
317+
318+
hash_str = sha1(txt).hexdigest()
319+
else:
320+
# load sourcefile that's been copied into package install payload
321+
path = os.path.join(package.base, self.include_modules_subpath)
322+
pathname = os.path.join(path, "%s-*.py" % name)
323+
324+
pathnames = glob(pathname)
325+
if not pathnames:
326+
return None
303327

304-
filepath = pathnames[0]
305-
hash_str = filepath.rsplit('-', 1)[-1].split('.', 1)[0]
328+
filepath = pathnames[0]
329+
hash_str = filepath.rsplit('-', 1)[-1].split('.', 1)[0]
306330

307331
module = self.modules.get(hash_str)
308332
if module is not None:

0 commit comments

Comments
 (0)