diff --git a/docs/packaging.md b/docs/packaging.md
index 6d063edbff..af822b07eb 100755
--- a/docs/packaging.md
+++ b/docs/packaging.md
@@ -91,7 +91,7 @@ py_wheel(
| classifiers | A list of strings describing the categories for the package. For valid classifiers see https://pypi.org/classifiers | List of strings | optional | [] |
| console_scripts | Deprecated console_script entry points, e.g. {'main': 'examples.wheel.main:main'}.
Deprecated: prefer the entry_points attribute, which supports console_scripts as well as other entry points. | Dictionary: String -> String | optional | {} |
| deps | Targets to be included in the distribution.
The targets to package are usually py_library rules or filesets (for packaging data files).
Note it's usually better to package py_library targets and use entry_points attribute to specify console_scripts than to package py_binary rules. py_binary targets would wrap a executable script that tries to locate .runfiles directory which is not packaged in the wheel. | List of labels | optional | [] |
-| description_file | A file containing text describing the package in a single line. | Label | optional | None |
+| description_file | A file containing text describing the package. | Label | optional | None |
| distribution | Name of the distribution.
This should match the project name onm PyPI. It's also the name that is used to refer to the package in other packages' dependencies. | String | required | |
| entry_points | entry_points, e.g. {'console_scripts': ['main = examples.wheel.main:main']}. | Dictionary: String -> List of strings | optional | {} |
| extra_distinfo_files | Extra files to add to distinfo directory in the archive. | Dictionary: Label -> String | optional | {} |
@@ -99,9 +99,9 @@ py_wheel(
| homepage | A string specifying the URL for the package homepage. | String | optional | "" |
| license | A string specifying the license of the package. | String | optional | "" |
| platform | Supported platform. Use 'any' for pure-Python wheel.
If you have included platform-specific data, such as a .pyd or .so extension module, you will need to specify the platform in standard pip format. If you support multiple platforms, you can define platform constraints, then use a select() to specify the appropriate specifier, eg:
platform = select({ "//platforms:windows_x86_64": "win_amd64", "//platforms:macos_x86_64": "macosx_10_7_x86_64", "//platforms:linux_x86_64": "manylinux2014_x86_64", }) | String | optional | "any" |
-| python_requires | A string specifying what other distributions need to be installed when this one is. See the section on [Declaring required dependency](https://setuptools.readthedocs.io/en/latest/userguide/dependency_management.html#declaring-dependencies) for details and examples of the format of this argument. | String | optional | "" |
+| python_requires | Python versions required by this distribution, e.g. '>=3.5,<3.7' | String | optional | "" |
| python_tag | Supported Python version(s), eg py3, cp35.cp36, etc | String | optional | "py3" |
-| requires | List of requirements for this package | List of strings | optional | [] |
+| requires | List of requirements for this package. See the section on [Declaring required dependency](https://setuptools.readthedocs.io/en/latest/userguide/dependency_management.html#declaring-dependencies) for details and examples of the format of this argument. | List of strings | optional | [] |
| stamp | Whether to encode build information into the wheel. Possible values:
- stamp = 1: Always stamp the build information into the wheel, even in [--nostamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) builds. This setting should be avoided, since it potentially kills remote caching for the target and any downstream actions that depend on it.
- stamp = 0: Always replace build information by constant values. This gives good build result caching.
- stamp = -1: Embedding of build information is controlled by the [--[no]stamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) flag.
Stamped targets are not rebuilt unless their dependencies change. | Integer | optional | -1 |
| strip_path_prefixes | path prefixes to strip from files added to the generated package | List of strings | optional | [] |
| version | Version number of the package. Note that this attribute supports stamp format strings (eg. 1.2.3-{BUILD_TIMESTAMP}) as well as 'make variables' (e.g. 1.2.3-$(VERSION)). | String | required | |
diff --git a/examples/wheel/wheel_test.py b/examples/wheel/wheel_test.py
index 0e495eb236..cbca0927c3 100644
--- a/examples/wheel/wheel_test.py
+++ b/examples/wheel/wheel_test.py
@@ -94,40 +94,11 @@ def test_customized_wheel(self):
entry_point_contents = zf.read(
"example_customized-0.0.1.dist-info/entry_points.txt"
)
- # The entries are guaranteed to be sorted.
- if platform.system() == "Windows":
- self.assertEqual(
- record_contents,
- b"""\
-example_customized-0.0.1.dist-info/METADATA,sha256=pzE96o3Sp63TDzxAZgl0F42EFevm8x15vpDLqDVp_EQ,378
-example_customized-0.0.1.dist-info/NOTICE,sha256=Xpdw-FXET1IRgZ_wTkx1YQfo1-alET0FVf6V1LXO4js,76
-example_customized-0.0.1.dist-info/README,sha256=WmOFwZ3Jga1bHG3JiGRsUheb4UbLffUxyTdHczS27-o,40
-example_customized-0.0.1.dist-info/RECORD,,
-example_customized-0.0.1.dist-info/WHEEL,sha256=sobxWSyDDkdg_rinUth-jxhXHqoNqlmNMJY3aTZn2Us,91
-example_customized-0.0.1.dist-info/entry_points.txt,sha256=pqzpbQ8MMorrJ3Jp0ntmpZcuvfByyqzMXXi2UujuXD0,137
-examples/wheel/lib/data.txt,sha256=9vJKEdfLu8bZRArKLroPZJh1XKkK3qFMXiM79MBL2Sg,12
-examples/wheel/lib/module_with_data.py,sha256=8s0Khhcqz3yVsBKv2IB5u4l4TMKh7-c_V6p65WVHPms,637
-examples/wheel/lib/simple_module.py,sha256=z2hwciab_XPNIBNH8B1Q5fYgnJvQTeYf0ZQJpY8yLLY,637
-examples/wheel/main.py,sha256=sgg5iWN_9inYBjm6_Zw27hYdmo-l24fA-2rfphT-IlY,909
-""",
- )
- else:
- # TODO: The non-ascii characters in the METADATA file are interpreted differently on the
- # ubuntu16_rbe hosts in comparison to other unix platforms. This should not be the case
- # and the code should be updated to account for this.
- rbe_expected_contents = b"""\
-example_customized-0.0.1.dist-info/METADATA,sha256=pzE96o3Sp63TDzxAZgl0F42EFevm8x15vpDLqDVp_EQ,378
-example_customized-0.0.1.dist-info/NOTICE,sha256=Xpdw-FXET1IRgZ_wTkx1YQfo1-alET0FVf6V1LXO4js,76
-example_customized-0.0.1.dist-info/README,sha256=WmOFwZ3Jga1bHG3JiGRsUheb4UbLffUxyTdHczS27-o,40
-example_customized-0.0.1.dist-info/RECORD,,
-example_customized-0.0.1.dist-info/WHEEL,sha256=sobxWSyDDkdg_rinUth-jxhXHqoNqlmNMJY3aTZn2Us,91
-example_customized-0.0.1.dist-info/entry_points.txt,sha256=pqzpbQ8MMorrJ3Jp0ntmpZcuvfByyqzMXXi2UujuXD0,137
-examples/wheel/lib/data.txt,sha256=9vJKEdfLu8bZRArKLroPZJh1XKkK3qFMXiM79MBL2Sg,12
-examples/wheel/lib/module_with_data.py,sha256=8s0Khhcqz3yVsBKv2IB5u4l4TMKh7-c_V6p65WVHPms,637
-examples/wheel/lib/simple_module.py,sha256=z2hwciab_XPNIBNH8B1Q5fYgnJvQTeYf0ZQJpY8yLLY,637
-examples/wheel/main.py,sha256=sgg5iWN_9inYBjm6_Zw27hYdmo-l24fA-2rfphT-IlY,909
-"""
- unix_expected_contents = b"""\
+
+ self.assertEqual(
+ record_contents,
+ # The entries are guaranteed to be sorted.
+ b"""\
example_customized-0.0.1.dist-info/METADATA,sha256=TeeEmokHE2NWjkaMcVJuSAq4_AXUoIad2-SLuquRmbg,372
example_customized-0.0.1.dist-info/NOTICE,sha256=Xpdw-FXET1IRgZ_wTkx1YQfo1-alET0FVf6V1LXO4js,76
example_customized-0.0.1.dist-info/README,sha256=WmOFwZ3Jga1bHG3JiGRsUheb4UbLffUxyTdHczS27-o,40
@@ -138,15 +109,7 @@ def test_customized_wheel(self):
examples/wheel/lib/module_with_data.py,sha256=8s0Khhcqz3yVsBKv2IB5u4l4TMKh7-c_V6p65WVHPms,637
examples/wheel/lib/simple_module.py,sha256=z2hwciab_XPNIBNH8B1Q5fYgnJvQTeYf0ZQJpY8yLLY,637
examples/wheel/main.py,sha256=sgg5iWN_9inYBjm6_Zw27hYdmo-l24fA-2rfphT-IlY,909
-"""
- self.assertIn(
- record_contents,
- [
- rbe_expected_contents,
- unix_expected_contents,
- ],
- )
-
+""")
self.assertEqual(
wheel_contents,
b"""\
@@ -156,44 +119,9 @@ def test_customized_wheel(self):
Tag: py3-none-any
""",
)
- if platform.system() == "Windows":
- self.assertEqual(
- metadata_contents,
- b"""\
-Metadata-Version: 2.1
-Name: example_customized
-Version: 0.0.1
-Author: Example Author with non-ascii characters: \xc3\x85\xc2\xbc\xc3\x83\xc2\xb3\xc3\x85\xc2\x82w
-Author-email: example@example.com
-Home-page: www.example.com
-License: Apache 2.0
-Classifier: License :: OSI Approved :: Apache Software License
-Classifier: Intended Audience :: Developers
-Requires-Dist: pytest
-
-This is a sample description of a wheel.
-""",
- )
- else:
- # TODO: The non-ascii characters in the METADATA file are interpreted differently on the
- # ubuntu16_rbe hosts in comparison to other unix platforms. This should not be the case
- # and the code should be updated to account for this.
- rbe_expected_contents = b"""\
-Metadata-Version: 2.1
-Name: example_customized
-Version: 0.0.1
-Author: Example Author with non-ascii characters: \xc3\x85\xc2\xbc\xc3\x83\xc2\xb3\xc3\x85\xc2\x82w
-Author-email: example@example.com
-Home-page: www.example.com
-License: Apache 2.0
-Classifier: License :: OSI Approved :: Apache Software License
-Classifier: Intended Audience :: Developers
-Requires-Dist: pytest
-
-This is a sample description of a wheel.
-"""
-
- unix_expected_contents = b"""\
+ self.assertEqual(
+ metadata_contents,
+ b"""\
Metadata-Version: 2.1
Name: example_customized
Version: 0.0.1
@@ -206,14 +134,7 @@ def test_customized_wheel(self):
Requires-Dist: pytest
This is a sample description of a wheel.
-"""
- self.assertIn(
- metadata_contents,
- [
- rbe_expected_contents,
- unix_expected_contents,
- ],
- )
+""")
self.assertEqual(
entry_point_contents,
b"""\
diff --git a/python/packaging.bzl b/python/packaging.bzl
index 22d1f5bc4d..6d7a901f53 100644
--- a/python/packaging.bzl
+++ b/python/packaging.bzl
@@ -153,7 +153,6 @@ def _py_wheel_impl(ctx):
args.add("--name", ctx.attr.distribution)
args.add("--version", version)
args.add("--python_tag", ctx.attr.python_tag)
- args.add("--python_requires", ctx.attr.python_requires)
args.add("--abi", ctx.attr.abi)
args.add("--platform", ctx.attr.platform)
args.add("--out", outfile)
@@ -168,28 +167,42 @@ def _py_wheel_impl(ctx):
args.add("--input_file_list", packageinputfile)
- extra_headers = []
+ # Note: Description file is not embedded into metadata.txt yet,
+ # it will be done later by wheelmaker script.
+ metadata_file = ctx.actions.declare_file(ctx.attr.name + ".metadata.txt")
+ metadata_contents = ["Metadata-Version: 2.1"]
+ metadata_contents.append("Name: %s" % ctx.attr.distribution)
+ metadata_contents.append("Version: %s" % version)
+
if ctx.attr.author:
- extra_headers.append("Author: %s" % ctx.attr.author)
+ metadata_contents.append("Author: %s" % ctx.attr.author)
if ctx.attr.author_email:
- extra_headers.append("Author-email: %s" % ctx.attr.author_email)
+ metadata_contents.append("Author-email: %s" % ctx.attr.author_email)
if ctx.attr.homepage:
- extra_headers.append("Home-page: %s" % ctx.attr.homepage)
+ metadata_contents.append("Home-page: %s" % ctx.attr.homepage)
if ctx.attr.license:
- extra_headers.append("License: %s" % ctx.attr.license)
-
- for h in extra_headers:
- args.add("--header", h)
+ metadata_contents.append("License: %s" % ctx.attr.license)
for c in ctx.attr.classifiers:
- args.add("--classifier", c)
-
- for r in ctx.attr.requires:
- args.add("--requires", r)
-
- for option, requirements in ctx.attr.extra_requires.items():
- for r in requirements:
- args.add("--extra_requires", r + ";" + option)
+ metadata_contents.append("Classifier: %s" % c)
+
+ if ctx.attr.python_requires:
+ metadata_contents.append("Requires-Python: %s" % ctx.attr.python_requires)
+ for requirement in ctx.attr.requires:
+ metadata_contents.append("Requires-Dist: %s" % requirement)
+
+ for option, option_requirements in sorted(ctx.attr.extra_requires.items()):
+ metadata_contents.append("Provides-Extra: %s" % option)
+ for requirement in option_requirements:
+ metadata_contents.append(
+ "Requires-Dist: %s; extra == '%s'" % (requirement, option),
+ )
+ ctx.actions.write(
+ output = metadata_file,
+ content = "\n".join(metadata_contents) + "\n",
+ )
+ other_inputs.append(metadata_file)
+ args.add("--metadata_file", metadata_file)
# Merge console_scripts into entry_points.
entrypoints = dict(ctx.attr.entry_points) # Copy so we can mutate it
@@ -334,7 +347,9 @@ _requirement_attrs = {
doc = "List of optional requirements for this package",
),
"requires": attr.string_list(
- doc = "List of requirements for this package",
+ doc = ("List of requirements for this package. See the section on " +
+ "[Declaring required dependency](https://setuptools.readthedocs.io/en/latest/userguide/dependency_management.html#declaring-dependencies) " +
+ "for details and examples of the format of this argument."),
),
}
@@ -366,7 +381,7 @@ _other_attrs = {
doc = "A list of strings describing the categories for the package. For valid classifiers see https://pypi.org/classifiers",
),
"description_file": attr.label(
- doc = "A file containing text describing the package in a single line.",
+ doc = "A file containing text describing the package.",
allow_single_file = True,
),
"extra_distinfo_files": attr.label_keyed_string_dict(
@@ -383,10 +398,7 @@ _other_attrs = {
),
"python_requires": attr.string(
doc = (
- "A string specifying what other distributions need to be installed " +
- "when this one is. See the section on " +
- "[Declaring required dependency](https://setuptools.readthedocs.io/en/latest/userguide/dependency_management.html#declaring-dependencies) " +
- "for details and examples of the format of this argument."
+ "Python versions required by this distribution, e.g. '>=3.5,<3.7'"
),
default = "",
),
diff --git a/tools/wheelmaker.py b/tools/wheelmaker.py
index 4e03680345..d5179001a6 100644
--- a/tools/wheelmaker.py
+++ b/tools/wheelmaker.py
@@ -167,39 +167,11 @@ def add_wheelfile(self):
wheel_contents += "Tag: %s\n" % tag
self.add_string(self.distinfo_path("WHEEL"), wheel_contents)
- def add_metadata(
- self,
- extra_headers,
- description,
- classifiers,
- python_requires,
- requires,
- extra_requires,
- ):
+ def add_metadata(self, metadata, description):
"""Write METADATA file to the distribution."""
# https://www.python.org/dev/peps/pep-0566/
# https://packaging.python.org/specifications/core-metadata/
- metadata = []
- metadata.append("Metadata-Version: 2.1")
- metadata.append("Name: %s" % self._name)
- metadata.append("Version: %s" % self._version)
- metadata.extend(extra_headers)
- for classifier in classifiers:
- metadata.append("Classifier: %s" % classifier)
- if python_requires:
- metadata.append("Requires-Python: %s" % python_requires)
- for requirement in requires:
- metadata.append("Requires-Dist: %s" % requirement)
-
- extra_requires = sorted(extra_requires.items())
- for option, option_requires in extra_requires:
- metadata.append("Provides-Extra: %s" % option)
- for requirement in option_requires:
- metadata.append(
- "Requires-Dist: %s; extra == '%s'" % (requirement, option)
- )
-
- metadata = "\n".join(metadata) + "\n\n"
+ metadata += "\n"
# setuptools seems to insert UNKNOWN as description when none is
# provided.
metadata += description if description else "UNKNOWN"
@@ -303,25 +275,15 @@ def parse_args() -> argparse.Namespace:
action="append",
default=[],
help="Path prefix to be stripped from input package files' path. "
- "Can be supplied multiple times. "
- "Evaluated in order.",
+ "Can be supplied multiple times. Evaluated in order.",
)
wheel_group = parser.add_argument_group("Wheel metadata")
wheel_group.add_argument(
- "--header",
- action="append",
- help="Additional headers to be embedded in the package metadata. "
- "Can be supplied multiple times.",
- )
- wheel_group.add_argument(
- "--classifier",
- action="append",
- help="Classifiers to embed in package metadata. "
- "Can be supplied multiple times",
- )
- wheel_group.add_argument(
- "--python_requires", help="Version of python that the wheel will work with"
+ "--metadata_file",
+ type=Path,
+ help="Contents of the METADATA file (before appending contents of "
+ "--description_file)",
)
wheel_group.add_argument(
"--description_file", help="Path to the file with package description"
@@ -352,21 +314,6 @@ def parse_args() -> argparse.Namespace:
"dist-info directory. Can be supplied multiple times.",
)
- requirements_group = parser.add_argument_group("Package requirements")
- requirements_group.add_argument(
- "--requires",
- type=str,
- action="append",
- help="List of package requirements. Can be supplied multiple times.",
- )
- requirements_group.add_argument(
- "--extra_requires",
- type=str,
- action="append",
- help="List of optional requirements in a 'requirement;option name'. "
- "Can be supplied multiple times.",
- )
-
build_group = parser.add_argument_group("Building requirements")
build_group.add_argument(
"--volatile_status_file",
@@ -434,32 +381,24 @@ def main() -> None:
description = None
if arguments.description_file:
if sys.version_info[0] == 2:
- with open(arguments.description_file, "rt") as description_file:
+ with open(arguments.description_file,
+ "rt") as description_file:
description = description_file.read()
else:
- with open(
- arguments.description_file, "rt", encoding="utf-8"
- ) as description_file:
+ with open(arguments.description_file, "rt",
+ encoding="utf-8") as description_file:
description = description_file.read()
- extra_requires = collections.defaultdict(list)
- if arguments.extra_requires:
- for extra in arguments.extra_requires:
- req, option = extra.rsplit(";", 1)
- extra_requires[option].append(req)
- classifiers = arguments.classifier or []
- python_requires = arguments.python_requires or ""
- requires = arguments.requires or []
- extra_headers = arguments.header or []
-
- maker.add_metadata(
- extra_headers=extra_headers,
- description=description,
- classifiers=classifiers,
- python_requires=python_requires,
- requires=requires,
- extra_requires=extra_requires,
- )
+ metadata = None
+ if sys.version_info[0] == 2:
+ with open(arguments.metadata_file, "rt") as metadata_file:
+ metadata = metadata_file.read()
+ else:
+ with open(arguments.metadata_file, "rt",
+ encoding="utf-8") as metadata_file:
+ metadata = metadata_file.read()
+
+ maker.add_metadata(metadata=metadata, description=description)
if arguments.entry_points_file:
maker.add_file(