feat: Add support for python-wheel data directory (#1801)
Fixes #1777
* Adds `data_files` attribute to `py_wheel` rule.
* Minimal validation of the data-files target directories per
[specification](https://packaging.python.org/en/latest/specifications/binary-distribution-format/#installing-a-wheel-distribution-1-0-py32-none-any-whl)
* Added two tests.
* Added example
diff --git a/python/private/py_wheel.bzl b/python/private/py_wheel.bzl
index 5919abe..2aed9b9 100644
--- a/python/private/py_wheel.bzl
+++ b/python/private/py_wheel.bzl
@@ -120,6 +120,7 @@
_feature_flags = {}
+ALLOWED_DATA_FILE_PREFIX = ("purelib", "platlib", "headers", "scripts", "data")
_requirement_attrs = {
"extra_requires": attr.string_list_dict(
doc = ("A mapping of [extras](https://peps.python.org/pep-0508/#extras) options to lists of requirements (similar to `requires`). This attribute " +
@@ -172,6 +173,11 @@
"classifiers": attr.string_list(
doc = "A list of strings describing the categories for the package. For valid classifiers see https://pypi.org/classifiers",
),
+ "data_files": attr.label_keyed_string_dict(
+ doc = ("Any file that is not normally installed inside site-packages goes into the .data directory, named " +
+ "as the .dist-info directory but with the .data/ extension. Allowed paths: {prefixes}".format(prefixes = ALLOWED_DATA_FILE_PREFIX)),
+ allow_files = True,
+ ),
"description_content_type": attr.string(
doc = ("The type of contents in description_file. " +
"If not provided, the type will be inferred from the extension of description_file. " +
@@ -473,6 +479,28 @@
filename + ";" + target_files[0].path,
)
+ for target, filename in ctx.attr.data_files.items():
+ target_files = target.files.to_list()
+ if len(target_files) != 1:
+ fail(
+ "Multi-file target listed in data_files %s",
+ filename,
+ )
+
+ if filename.partition("/")[0] not in ALLOWED_DATA_FILE_PREFIX:
+ fail(
+ "The target data file must start with one of these prefixes: '%s'. Target filepath: '%s'" %
+ (
+ ",".join(ALLOWED_DATA_FILE_PREFIX),
+ filename,
+ ),
+ )
+ other_inputs.extend(target_files)
+ args.add(
+ "--data_files",
+ filename + ";" + target_files[0].path,
+ )
+
ctx.actions.run(
mnemonic = "PyWheel",
inputs = depset(direct = other_inputs, transitive = [inputs_to_package]),
diff --git a/python/private/repack_whl.py b/python/private/repack_whl.py
index be113ef..ea9c01f 100644
--- a/python/private/repack_whl.py
+++ b/python/private/repack_whl.py
@@ -150,8 +150,9 @@
logging.debug(f"Found dist-info dir: {distinfo_dir}")
record_path = distinfo_dir / "RECORD"
record_contents = record_path.read_text() if record_path.exists() else ""
+ distribution_prefix = distinfo_dir.with_suffix("").name
- with _WhlFile(args.output, mode="w", distinfo_dir=distinfo_dir) as out:
+ with _WhlFile(args.output, mode="w", distribution_prefix=distribution_prefix) as out:
for p in _files_to_pack(patched_wheel_dir, record_contents):
rel_path = p.relative_to(patched_wheel_dir)
out.add_file(str(rel_path), p)