diff --git a/docs/packaging.md b/docs/packaging.md index 091e1a00fc..0e8e110ef5 100755 --- a/docs/packaging.md +++ b/docs/packaging.md @@ -59,8 +59,8 @@ This also has the advantage that stamping information is included in the wheel's
py_wheel_rule(name, abi, author, author_email, classifiers, console_scripts, deps, description_content_type, description_file, distribution, entry_points, - extra_distinfo_files, extra_requires, homepage, license, platform, python_requires, - python_tag, requires, stamp, strip_path_prefixes, summary, version) + extra_distinfo_files, extra_requires, homepage, license, platform, project_urls, + python_requires, python_tag, requires, stamp, strip_path_prefixes, summary, version)Internal rule used by the [py_wheel macro](/docs/packaging.md#py_wheel). @@ -91,6 +91,7 @@ in the way they expect. | 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. 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"
|
+| project_urls | A string dict specifying additional browsable URLs for the project and corresponding labels, where label is the key and url is the value. e.g {{"Bug Tracker": "http://bitbucket.org/tarek/distribute/issues/"}}
| Dictionary: String -> 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. 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 | []
|
diff --git a/examples/wheel/BUILD.bazel b/examples/wheel/BUILD.bazel
index 72cc3d4e8d..f56a41b370 100644
--- a/examples/wheel/BUILD.bazel
+++ b/examples/wheel/BUILD.bazel
@@ -157,6 +157,10 @@ py_wheel(
},
homepage = "www.example.com",
license = "Apache 2.0",
+ project_urls = {
+ "Bug Tracker": "www.example.com/issues",
+ "Documentation": "www.example.com/docs",
+ },
python_tag = "py3",
# Requirements embedded into the wheel metadata.
requires = ["pytest"],
diff --git a/examples/wheel/wheel_test.py b/examples/wheel/wheel_test.py
index 6869e77be2..f51a0ecedc 100644
--- a/examples/wheel/wheel_test.py
+++ b/examples/wheel/wheel_test.py
@@ -99,7 +99,7 @@ def test_customized_wheel(self):
record_contents,
# The entries are guaranteed to be sorted.
b"""\
-example_customized-0.0.1.dist-info/METADATA,sha256=vRiyyV45PC5fzK_40nSTtIn3yYzDdsbBAbUvkZiRyc8,461
+example_customized-0.0.1.dist-info/METADATA,sha256=QYQcDJFQSIqan8eiXqL67bqsUfgEAwf2hoK_Lgi1S-0,559
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,,
@@ -131,6 +131,8 @@ def test_customized_wheel(self):
License: Apache 2.0
Description-Content-Type: text/markdown
Summary: A one-line summary of this test package
+Project-URL: Bug Tracker, www.example.com/issues
+Project-URL: Documentation, www.example.com/docs
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Intended Audience :: Developers
Requires-Dist: pytest
diff --git a/python/private/py_wheel.bzl b/python/private/py_wheel.bzl
index edafa3e402..d8bceabcb8 100644
--- a/python/private/py_wheel.bzl
+++ b/python/private/py_wheel.bzl
@@ -176,6 +176,11 @@ _other_attrs = {
doc = "A string specifying the license of the package.",
default = "",
),
+ "project_urls": attr.string_dict(
+ doc = ("A string dict specifying additional browsable URLs for the project and corresponding labels, " +
+ "where label is the key and url is the value. " +
+ 'e.g `{{"Bug Tracker": "http://bitbucket.org/tarek/distribute/issues/"}}`'),
+ ),
"python_requires": attr.string(
doc = (
"Python versions required by this distribution, e.g. '>=3.5,<3.7'"
@@ -191,6 +196,7 @@ _other_attrs = {
),
}
+_PROJECT_URL_LABEL_LENGTH_LIMIT = 32
_DESCRIPTION_FILE_EXTENSION_TO_TYPE = {
"md": "text/markdown",
"rst": "text/x-rst",
@@ -301,6 +307,11 @@ def _py_wheel_impl(ctx):
if ctx.attr.summary:
metadata_contents.append("Summary: %s" % ctx.attr.summary)
+ for label, url in sorted(ctx.attr.project_urls.items()):
+ if len(label) > _PROJECT_URL_LABEL_LENGTH_LIMIT:
+ fail("`label` {} in `project_urls` is too long. It is limited to {} characters.".format(len(label), _PROJECT_URL_LABEL_LENGTH_LIMIT))
+ metadata_contents.append("Project-URL: %s, %s" % (label, url))
+
for c in ctx.attr.classifiers:
metadata_contents.append("Classifier: %s" % c)
diff --git a/tools/build_defs/python/tests/py_wheel/BUILD.bazel b/tools/build_defs/python/tests/py_wheel/BUILD.bazel
new file mode 100644
index 0000000000..d925bb9801
--- /dev/null
+++ b/tools/build_defs/python/tests/py_wheel/BUILD.bazel
@@ -0,0 +1,18 @@
+# Copyright 2023 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Tests for py_wheel."""
+
+load(":py_wheel_tests.bzl", "py_wheel_test_suite")
+
+py_wheel_test_suite(name = "py_wheel_tests")
diff --git a/tools/build_defs/python/tests/py_wheel/py_wheel_tests.bzl b/tools/build_defs/python/tests/py_wheel/py_wheel_tests.bzl
new file mode 100644
index 0000000000..4408592d32
--- /dev/null
+++ b/tools/build_defs/python/tests/py_wheel/py_wheel_tests.bzl
@@ -0,0 +1,39 @@
+"""Test for py_wheel."""
+
+load("@rules_testing//lib:analysis_test.bzl", "analysis_test")
+load("@rules_testing//lib:truth.bzl", "matching")
+load("@rules_testing//lib:util.bzl", rt_util = "util")
+load("//python:packaging.bzl", "py_wheel")
+load("//tools/build_defs/python/tests:util.bzl", pt_util = "util")
+
+_tests = []
+
+def _test_too_long_project_url_label(name, config):
+ rt_util.helper_target(
+ config.rule,
+ name = name + "_wheel",
+ distribution = name + "_wheel",
+ python_tag = "py3",
+ version = "0.0.1",
+ project_urls = {"This is a label whose length is above the limit!": "www.example.com"},
+ )
+ analysis_test(
+ name = name,
+ target = name + "_wheel",
+ impl = _test_too_long_project_url_label_impl,
+ expect_failure = True,
+ )
+
+def _test_too_long_project_url_label_impl(env, target):
+ env.expect.that_target(target).failures().contains_predicate(
+ matching.str_matches("in `project_urls` is too long"),
+ )
+
+_tests.append(_test_too_long_project_url_label)
+
+def py_wheel_test_suite(name):
+ config = struct(rule = py_wheel, base_test_rule = py_wheel)
+ native.test_suite(
+ name = name,
+ tests = pt_util.create_tests(_tests, config = config),
+ )