aboutsummaryrefslogtreecommitdiff
path: root/examples/wheel
diff options
context:
space:
mode:
Diffstat (limited to 'examples/wheel')
-rw-r--r--examples/wheel/BUILD.bazel287
-rw-r--r--examples/wheel/NOTICE1
-rw-r--r--examples/wheel/README.md1
-rw-r--r--examples/wheel/lib/BUILD.bazel36
-rw-r--r--examples/wheel/lib/module_with_data.py17
-rw-r--r--examples/wheel/lib/simple_module.py17
-rw-r--r--examples/wheel/main.py30
-rw-r--r--examples/wheel/private/BUILD.bazel7
-rw-r--r--examples/wheel/private/directory_writer.py58
-rw-r--r--examples/wheel/private/wheel_utils.bzl73
-rw-r--r--examples/wheel/wheel_test.py414
11 files changed, 941 insertions, 0 deletions
diff --git a/examples/wheel/BUILD.bazel b/examples/wheel/BUILD.bazel
new file mode 100644
index 0000000..f56a41b
--- /dev/null
+++ b/examples/wheel/BUILD.bazel
@@ -0,0 +1,287 @@
+# Copyright 2018 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.
+
+load("@bazel_skylib//rules:build_test.bzl", "build_test")
+load("//examples/wheel/private:wheel_utils.bzl", "directory_writer", "make_variable_tags")
+load("//python:defs.bzl", "py_library", "py_test")
+load("//python:packaging.bzl", "py_package", "py_wheel")
+load("//python:versions.bzl", "gen_python_config_settings")
+
+package(default_visibility = ["//visibility:public"])
+
+licenses(["notice"]) # Apache 2.0
+
+py_library(
+ name = "main",
+ srcs = ["main.py"],
+ deps = [
+ "//examples/wheel/lib:simple_module",
+ "//examples/wheel/lib:module_with_data",
+ # Example dependency which is not packaged in the wheel
+ # due to "packages" filter on py_package rule.
+ "//tests/load_from_macro:foo",
+ ],
+)
+
+py_library(
+ name = "main_with_gen_data",
+ srcs = ["main.py"],
+ data = [
+ ":gen_dir",
+ ],
+)
+
+directory_writer(
+ name = "gen_dir",
+ out = "someDir",
+ files = {"foo.py": ""},
+)
+
+# Package just a specific py_libraries, without their dependencies
+py_wheel(
+ name = "minimal_with_py_library",
+ testonly = True, # Set this to verify the generated .dist target doesn't break things
+ # Package data. We're building "example_minimal_library-0.0.1-py3-none-any.whl"
+ distribution = "example_minimal_library",
+ python_tag = "py3",
+ version = "0.0.1",
+ deps = [
+ "//examples/wheel/lib:module_with_data",
+ "//examples/wheel/lib:simple_module",
+ ],
+)
+
+# Populate a rule with "Make Variable" arguments for
+# abi, python_tag and version. You might want to do this
+# for the following use cases:
+# - abi, python_tag: introspect a toolchain to map to appropriate cpython tags
+# - version: populate given this or a dependent module's version
+make_variable_tags(
+ name = "make_variable_tags",
+)
+
+py_wheel(
+ name = "minimal_with_py_library_with_make_variables",
+ testonly = True,
+ abi = "$(ABI)",
+ distribution = "example_minimal_library",
+ python_tag = "$(PYTHON_TAG)",
+ toolchains = ["//examples/wheel:make_variable_tags"],
+ version = "$(VERSION)",
+ deps = [
+ "//examples/wheel/lib:module_with_data",
+ "//examples/wheel/lib:simple_module",
+ ],
+)
+
+build_test(
+ name = "dist_build_tests",
+ targets = [":minimal_with_py_library.dist"],
+)
+
+# Package just a specific py_libraries, without their dependencies
+py_wheel(
+ name = "minimal_with_py_library_with_stamp",
+ # Package data. We're building "example_minimal_library-0.0.1-py3-none-any.whl"
+ distribution = "example_minimal_library{BUILD_USER}",
+ python_tag = "py3",
+ stamp = 1,
+ version = "0.1.{BUILD_TIMESTAMP}",
+ deps = [
+ "//examples/wheel/lib:module_with_data",
+ "//examples/wheel/lib:simple_module",
+ ],
+)
+
+# Use py_package to collect all transitive dependencies of a target,
+# selecting just the files within a specific python package.
+py_package(
+ name = "example_pkg",
+ # Only include these Python packages.
+ packages = ["examples.wheel"],
+ deps = [":main"],
+)
+
+py_package(
+ name = "example_pkg_with_data",
+ packages = ["examples.wheel"],
+ deps = [":main_with_gen_data"],
+)
+
+py_wheel(
+ name = "minimal_with_py_package",
+ # Package data. We're building "example_minimal_package-0.0.1-py3-none-any.whl"
+ distribution = "example_minimal_package",
+ python_tag = "py3",
+ version = "0.0.1",
+ deps = [":example_pkg"],
+)
+
+# An example that uses all features provided by py_wheel.
+py_wheel(
+ name = "customized",
+ author = "Example Author with non-ascii characters: żółw",
+ author_email = "example@example.com",
+ classifiers = [
+ "License :: OSI Approved :: Apache Software License",
+ "Intended Audience :: Developers",
+ ],
+ console_scripts = {
+ "customized_wheel": "examples.wheel.main:main",
+ },
+ description_file = "README.md",
+ # Package data. We're building "example_customized-0.0.1-py3-none-any.whl"
+ distribution = "example_customized",
+ entry_points = {
+ "console_scripts": ["another = foo.bar:baz"],
+ "group2": [
+ "second = second.main:s",
+ "first = first.main:f",
+ ],
+ },
+ extra_distinfo_files = {
+ "//examples/wheel:NOTICE": "NOTICE",
+ # Rename the file when packaging to show we can.
+ "//examples/wheel:README.md": "README",
+ },
+ 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"],
+ summary = "A one-line summary of this test package",
+ version = "0.0.1",
+ deps = [":example_pkg"],
+)
+
+# An example of how to change the wheel package root directory using 'strip_path_prefixes'.
+py_wheel(
+ name = "custom_package_root",
+ # Package data. We're building "examples_custom_package_root-0.0.1-py3-none-any.whl"
+ distribution = "examples_custom_package_root",
+ entry_points = {
+ "console_scripts": ["main = foo.bar:baz"],
+ },
+ python_tag = "py3",
+ strip_path_prefixes = [
+ "examples",
+ ],
+ version = "0.0.1",
+ deps = [
+ ":example_pkg",
+ ],
+)
+
+py_wheel(
+ name = "custom_package_root_multi_prefix",
+ # Package data. We're building "custom_custom_package_root_multi_prefix-0.0.1-py3-none-any.whl"
+ distribution = "example_custom_package_root_multi_prefix",
+ python_tag = "py3",
+ strip_path_prefixes = [
+ "examples/wheel/lib",
+ "examples/wheel",
+ ],
+ version = "0.0.1",
+ deps = [
+ ":example_pkg",
+ ],
+)
+
+py_wheel(
+ name = "custom_package_root_multi_prefix_reverse_order",
+ # Package data. We're building "custom_custom_package_root_multi_prefix_reverse_order-0.0.1-py3-none-any.whl"
+ distribution = "example_custom_package_root_multi_prefix_reverse_order",
+ python_tag = "py3",
+ strip_path_prefixes = [
+ "examples/wheel",
+ "examples/wheel/lib", # this is not effective, because the first prefix takes priority
+ ],
+ version = "0.0.1",
+ deps = [
+ ":example_pkg",
+ ],
+)
+
+py_wheel(
+ name = "python_requires_in_a_package",
+ distribution = "example_python_requires_in_a_package",
+ python_requires = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*",
+ python_tag = "py3",
+ version = "0.0.1",
+ deps = [
+ ":example_pkg",
+ ],
+)
+
+py_wheel(
+ name = "use_rule_with_dir_in_outs",
+ distribution = "use_rule_with_dir_in_outs",
+ python_tag = "py3",
+ version = "0.0.1",
+ deps = [
+ ":example_pkg_with_data",
+ ],
+)
+
+gen_python_config_settings()
+
+py_wheel(
+ name = "python_abi3_binary_wheel",
+ abi = "abi3",
+ distribution = "example_python_abi3_binary_wheel",
+ # these platform strings must line up with test_python_abi3_binary_wheel() in wheel_test.py
+ platform = select({
+ ":aarch64-apple-darwin": "macosx_11_0_arm64",
+ ":aarch64-unknown-linux-gnu": "manylinux2014_aarch64",
+ ":x86_64-apple-darwin": "macosx_11_0_x86_64", # this is typically macosx_10_9_x86_64?
+ ":x86_64-pc-windows-msvc": "win_amd64",
+ ":x86_64-unknown-linux-gnu": "manylinux2014_x86_64",
+ }),
+ python_requires = ">=3.8",
+ python_tag = "cp38",
+ version = "0.0.1",
+)
+
+py_wheel(
+ name = "filename_escaping",
+ # Per https://www.python.org/dev/peps/pep-0427/#escaping-and-unicode
+ # runs of non-alphanumeric, non-digit symbols should be replaced with a single underscore.
+ # Unicode non-ascii letters should *not* be replaced with underscore.
+ distribution = "file~~name-escaping",
+ python_tag = "py3",
+ version = "0.0.1-r7",
+ deps = [":example_pkg"],
+)
+
+py_test(
+ name = "wheel_test",
+ srcs = ["wheel_test.py"],
+ data = [
+ ":custom_package_root",
+ ":custom_package_root_multi_prefix",
+ ":custom_package_root_multi_prefix_reverse_order",
+ ":customized",
+ ":filename_escaping",
+ ":minimal_with_py_library",
+ ":minimal_with_py_library_with_stamp",
+ ":minimal_with_py_package",
+ ":python_abi3_binary_wheel",
+ ":python_requires_in_a_package",
+ ":use_rule_with_dir_in_outs",
+ ],
+)
diff --git a/examples/wheel/NOTICE b/examples/wheel/NOTICE
new file mode 100644
index 0000000..700336b
--- /dev/null
+++ b/examples/wheel/NOTICE
@@ -0,0 +1 @@
+This is a test "NOTICE" file to be packaged into distribtion dist-info dir.
diff --git a/examples/wheel/README.md b/examples/wheel/README.md
new file mode 100644
index 0000000..1426ff4
--- /dev/null
+++ b/examples/wheel/README.md
@@ -0,0 +1 @@
+This is a sample description of a wheel. \ No newline at end of file
diff --git a/examples/wheel/lib/BUILD.bazel b/examples/wheel/lib/BUILD.bazel
new file mode 100644
index 0000000..3b59662
--- /dev/null
+++ b/examples/wheel/lib/BUILD.bazel
@@ -0,0 +1,36 @@
+# Copyright 2018 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.
+
+load("//python:defs.bzl", "py_library")
+
+package(default_visibility = ["//visibility:public"])
+
+licenses(["notice"]) # Apache 2.0
+
+py_library(
+ name = "simple_module",
+ srcs = ["simple_module.py"],
+)
+
+py_library(
+ name = "module_with_data",
+ srcs = ["module_with_data.py"],
+ data = [":data.txt"],
+)
+
+genrule(
+ name = "make_data",
+ outs = ["data.txt"],
+ cmd = "echo foo bar baz > $@",
+)
diff --git a/examples/wheel/lib/module_with_data.py b/examples/wheel/lib/module_with_data.py
new file mode 100644
index 0000000..6b661eb
--- /dev/null
+++ b/examples/wheel/lib/module_with_data.py
@@ -0,0 +1,17 @@
+# Copyright 2018 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.
+
+
+def function():
+ return "foo"
diff --git a/examples/wheel/lib/simple_module.py b/examples/wheel/lib/simple_module.py
new file mode 100644
index 0000000..b69ae2b
--- /dev/null
+++ b/examples/wheel/lib/simple_module.py
@@ -0,0 +1,17 @@
+# Copyright 2018 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.
+
+
+def function():
+ return "bar"
diff --git a/examples/wheel/main.py b/examples/wheel/main.py
new file mode 100644
index 0000000..7c4d323
--- /dev/null
+++ b/examples/wheel/main.py
@@ -0,0 +1,30 @@
+# Copyright 2018 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.
+
+import examples.wheel.lib.module_with_data as module_with_data
+import examples.wheel.lib.simple_module as simple_module
+
+
+def function():
+ return "baz"
+
+
+def main():
+ print(function())
+ print(module_with_data.function())
+ print(simple_module.function())
+
+
+if __name__ == "__main__":
+ main()
diff --git a/examples/wheel/private/BUILD.bazel b/examples/wheel/private/BUILD.bazel
new file mode 100644
index 0000000..3462d35
--- /dev/null
+++ b/examples/wheel/private/BUILD.bazel
@@ -0,0 +1,7 @@
+load("@rules_python//python:defs.bzl", "py_binary")
+
+py_binary(
+ name = "directory_writer",
+ srcs = ["directory_writer.py"],
+ visibility = ["//:__subpackages__"],
+)
diff --git a/examples/wheel/private/directory_writer.py b/examples/wheel/private/directory_writer.py
new file mode 100644
index 0000000..7d9a93e
--- /dev/null
+++ b/examples/wheel/private/directory_writer.py
@@ -0,0 +1,58 @@
+#!/usr/bin/env python3
+# 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.
+
+"""The action executable of the `@rules_python//examples/wheel/private:wheel_utils.bzl%directory_writer` rule."""
+
+import argparse
+import json
+from pathlib import Path
+from typing import Tuple
+
+
+def _file_input(value) -> Tuple[Path, str]:
+ path, content = value.split("=", maxsplit=1)
+ return (Path(path), json.loads(content))
+
+
+def parse_args() -> argparse.Namespace:
+ parser = argparse.ArgumentParser()
+
+ parser.add_argument(
+ "--output", type=Path, required=True, help="The output directory to create."
+ )
+ parser.add_argument(
+ "--file",
+ dest="files",
+ type=_file_input,
+ action="append",
+ help="Files to create within the `output` directory.",
+ )
+
+ return parser.parse_args()
+
+
+def main() -> None:
+ args = parse_args()
+
+ args.output.mkdir(parents=True, exist_ok=True)
+
+ for (path, content) in args.files:
+ new_file = args.output / path
+ new_file.parent.mkdir(parents=True, exist_ok=True)
+ new_file.write_text(content)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/examples/wheel/private/wheel_utils.bzl b/examples/wheel/private/wheel_utils.bzl
new file mode 100644
index 0000000..037fed0
--- /dev/null
+++ b/examples/wheel/private/wheel_utils.bzl
@@ -0,0 +1,73 @@
+# 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.
+
+"""Helper rules for demonstrating `py_wheel` examples"""
+
+def _directory_writer_impl(ctx):
+ output = ctx.actions.declare_directory(ctx.attr.out)
+
+ args = ctx.actions.args()
+ args.add("--output", output.path)
+
+ for path, content in ctx.attr.files.items():
+ args.add("--file={}={}".format(
+ path,
+ json.encode(content),
+ ))
+
+ ctx.actions.run(
+ outputs = [output],
+ arguments = [args],
+ executable = ctx.executable._writer,
+ )
+
+ return [DefaultInfo(
+ files = depset([output]),
+ runfiles = ctx.runfiles(files = [output]),
+ )]
+
+directory_writer = rule(
+ implementation = _directory_writer_impl,
+ doc = "A rule for generating a directory with the requested content.",
+ attrs = {
+ "files": attr.string_dict(
+ doc = "A mapping of file name to content to create relative to the generated `out` directory.",
+ ),
+ "out": attr.string(
+ doc = "The name of the directory to create",
+ ),
+ "_writer": attr.label(
+ executable = True,
+ cfg = "exec",
+ default = Label("//examples/wheel/private:directory_writer"),
+ ),
+ },
+)
+
+def _make_variable_tags_impl(ctx): # buildifier: disable=unused-variable
+ # This example is contrived. In a real usage, this rule would
+ # look at flags or dependencies to determine what values to use.
+ # If all you're doing is setting constant values, then you can simply
+ # set them in the py_wheel() call.
+ vars = {}
+ vars["ABI"] = "cp38"
+ vars["PYTHON_TAG"] = "cp38"
+ vars["VERSION"] = "0.99.0"
+ return [platform_common.TemplateVariableInfo(vars)]
+
+make_variable_tags = rule(
+ attrs = {},
+ doc = """Make variable tags to pass to a py_wheel rule.""",
+ implementation = _make_variable_tags_impl,
+)
diff --git a/examples/wheel/wheel_test.py b/examples/wheel/wheel_test.py
new file mode 100644
index 0000000..f51a0ec
--- /dev/null
+++ b/examples/wheel/wheel_test.py
@@ -0,0 +1,414 @@
+# Copyright 2018 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.
+
+import os
+import platform
+import subprocess
+import unittest
+import zipfile
+
+
+class WheelTest(unittest.TestCase):
+ maxDiff = None
+
+ def test_py_library_wheel(self):
+ filename = os.path.join(
+ os.environ["TEST_SRCDIR"],
+ "rules_python",
+ "examples",
+ "wheel",
+ "example_minimal_library-0.0.1-py3-none-any.whl",
+ )
+ with zipfile.ZipFile(filename) as zf:
+ self.assertEqual(
+ zf.namelist(),
+ [
+ "examples/wheel/lib/module_with_data.py",
+ "examples/wheel/lib/simple_module.py",
+ "example_minimal_library-0.0.1.dist-info/WHEEL",
+ "example_minimal_library-0.0.1.dist-info/METADATA",
+ "example_minimal_library-0.0.1.dist-info/RECORD",
+ ],
+ )
+
+ def test_py_package_wheel(self):
+ filename = os.path.join(
+ os.environ["TEST_SRCDIR"],
+ "rules_python",
+ "examples",
+ "wheel",
+ "example_minimal_package-0.0.1-py3-none-any.whl",
+ )
+ with zipfile.ZipFile(filename) as zf:
+ self.assertEqual(
+ zf.namelist(),
+ [
+ "examples/wheel/lib/data.txt",
+ "examples/wheel/lib/module_with_data.py",
+ "examples/wheel/lib/simple_module.py",
+ "examples/wheel/main.py",
+ "example_minimal_package-0.0.1.dist-info/WHEEL",
+ "example_minimal_package-0.0.1.dist-info/METADATA",
+ "example_minimal_package-0.0.1.dist-info/RECORD",
+ ],
+ )
+
+ def test_customized_wheel(self):
+ filename = os.path.join(
+ os.environ["TEST_SRCDIR"],
+ "rules_python",
+ "examples",
+ "wheel",
+ "example_customized-0.0.1-py3-none-any.whl",
+ )
+ with zipfile.ZipFile(filename) as zf:
+ self.assertEqual(
+ zf.namelist(),
+ [
+ "examples/wheel/lib/data.txt",
+ "examples/wheel/lib/module_with_data.py",
+ "examples/wheel/lib/simple_module.py",
+ "examples/wheel/main.py",
+ "example_customized-0.0.1.dist-info/WHEEL",
+ "example_customized-0.0.1.dist-info/METADATA",
+ "example_customized-0.0.1.dist-info/entry_points.txt",
+ "example_customized-0.0.1.dist-info/NOTICE",
+ "example_customized-0.0.1.dist-info/README",
+ "example_customized-0.0.1.dist-info/RECORD",
+ ],
+ )
+ record_contents = zf.read("example_customized-0.0.1.dist-info/RECORD")
+ wheel_contents = zf.read("example_customized-0.0.1.dist-info/WHEEL")
+ metadata_contents = zf.read("example_customized-0.0.1.dist-info/METADATA")
+ entry_point_contents = zf.read(
+ "example_customized-0.0.1.dist-info/entry_points.txt"
+ )
+
+ self.assertEqual(
+ record_contents,
+ # The entries are guaranteed to be sorted.
+ b"""\
+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,,
+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
+""",
+ )
+ self.assertEqual(
+ wheel_contents,
+ b"""\
+Wheel-Version: 1.0
+Generator: bazel-wheelmaker 1.0
+Root-Is-Purelib: true
+Tag: py3-none-any
+""",
+ )
+ self.assertEqual(
+ metadata_contents,
+ b"""\
+Metadata-Version: 2.1
+Name: example_customized
+Author: Example Author with non-ascii characters: \xc5\xbc\xc3\xb3\xc5\x82w
+Author-email: example@example.com
+Home-page: www.example.com
+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
+Version: 0.0.1
+
+This is a sample description of a wheel.
+""",
+ )
+ self.assertEqual(
+ entry_point_contents,
+ b"""\
+[console_scripts]
+another = foo.bar:baz
+customized_wheel = examples.wheel.main:main
+
+[group2]
+first = first.main:f
+second = second.main:s""",
+ )
+
+ def test_filename_escaping(self):
+ filename = os.path.join(
+ os.environ["TEST_SRCDIR"],
+ "rules_python",
+ "examples",
+ "wheel",
+ "file_name_escaping-0.0.1_r7-py3-none-any.whl",
+ )
+ with zipfile.ZipFile(filename) as zf:
+ self.assertEqual(
+ zf.namelist(),
+ [
+ "examples/wheel/lib/data.txt",
+ "examples/wheel/lib/module_with_data.py",
+ "examples/wheel/lib/simple_module.py",
+ "examples/wheel/main.py",
+ # PEP calls for replacing only in the archive filename.
+ # Alas setuptools also escapes in the dist-info directory
+ # name, so let's be compatible.
+ "file_name_escaping-0.0.1_r7.dist-info/WHEEL",
+ "file_name_escaping-0.0.1_r7.dist-info/METADATA",
+ "file_name_escaping-0.0.1_r7.dist-info/RECORD",
+ ],
+ )
+ metadata_contents = zf.read(
+ "file_name_escaping-0.0.1_r7.dist-info/METADATA"
+ )
+ self.assertEqual(
+ metadata_contents,
+ b"""\
+Metadata-Version: 2.1
+Name: file~~name-escaping
+Version: 0.0.1-r7
+
+UNKNOWN
+""",
+ )
+
+ def test_custom_package_root_wheel(self):
+ filename = os.path.join(
+ os.environ["TEST_SRCDIR"],
+ "rules_python",
+ "examples",
+ "wheel",
+ "examples_custom_package_root-0.0.1-py3-none-any.whl",
+ )
+
+ with zipfile.ZipFile(filename) as zf:
+ self.assertEqual(
+ zf.namelist(),
+ [
+ "wheel/lib/data.txt",
+ "wheel/lib/module_with_data.py",
+ "wheel/lib/simple_module.py",
+ "wheel/main.py",
+ "examples_custom_package_root-0.0.1.dist-info/WHEEL",
+ "examples_custom_package_root-0.0.1.dist-info/METADATA",
+ "examples_custom_package_root-0.0.1.dist-info/entry_points.txt",
+ "examples_custom_package_root-0.0.1.dist-info/RECORD",
+ ],
+ )
+
+ record_contents = zf.read(
+ "examples_custom_package_root-0.0.1.dist-info/RECORD"
+ ).decode("utf-8")
+
+ # Ensure RECORD files do not have leading forward slashes
+ for line in record_contents.splitlines():
+ self.assertFalse(line.startswith("/"))
+
+ def test_custom_package_root_multi_prefix_wheel(self):
+ filename = os.path.join(
+ os.environ["TEST_SRCDIR"],
+ "rules_python",
+ "examples",
+ "wheel",
+ "example_custom_package_root_multi_prefix-0.0.1-py3-none-any.whl",
+ )
+
+ with zipfile.ZipFile(filename) as zf:
+ self.assertEqual(
+ zf.namelist(),
+ [
+ "data.txt",
+ "module_with_data.py",
+ "simple_module.py",
+ "main.py",
+ "example_custom_package_root_multi_prefix-0.0.1.dist-info/WHEEL",
+ "example_custom_package_root_multi_prefix-0.0.1.dist-info/METADATA",
+ "example_custom_package_root_multi_prefix-0.0.1.dist-info/RECORD",
+ ],
+ )
+
+ record_contents = zf.read(
+ "example_custom_package_root_multi_prefix-0.0.1.dist-info/RECORD"
+ ).decode("utf-8")
+
+ # Ensure RECORD files do not have leading forward slashes
+ for line in record_contents.splitlines():
+ self.assertFalse(line.startswith("/"))
+
+ def test_custom_package_root_multi_prefix_reverse_order_wheel(self):
+ filename = os.path.join(
+ os.environ["TEST_SRCDIR"],
+ "rules_python",
+ "examples",
+ "wheel",
+ "example_custom_package_root_multi_prefix_reverse_order-0.0.1-py3-none-any.whl",
+ )
+
+ with zipfile.ZipFile(filename) as zf:
+ self.assertEqual(
+ zf.namelist(),
+ [
+ "lib/data.txt",
+ "lib/module_with_data.py",
+ "lib/simple_module.py",
+ "main.py",
+ "example_custom_package_root_multi_prefix_reverse_order-0.0.1.dist-info/WHEEL",
+ "example_custom_package_root_multi_prefix_reverse_order-0.0.1.dist-info/METADATA",
+ "example_custom_package_root_multi_prefix_reverse_order-0.0.1.dist-info/RECORD",
+ ],
+ )
+
+ record_contents = zf.read(
+ "example_custom_package_root_multi_prefix_reverse_order-0.0.1.dist-info/RECORD"
+ ).decode("utf-8")
+
+ # Ensure RECORD files do not have leading forward slashes
+ for line in record_contents.splitlines():
+ self.assertFalse(line.startswith("/"))
+
+ def test_python_requires_wheel(self):
+ filename = os.path.join(
+ os.environ["TEST_SRCDIR"],
+ "rules_python",
+ "examples",
+ "wheel",
+ "example_python_requires_in_a_package-0.0.1-py3-none-any.whl",
+ )
+ with zipfile.ZipFile(filename) as zf:
+ metadata_contents = zf.read(
+ "example_python_requires_in_a_package-0.0.1.dist-info/METADATA"
+ )
+ # The entries are guaranteed to be sorted.
+ self.assertEqual(
+ metadata_contents,
+ b"""\
+Metadata-Version: 2.1
+Name: example_python_requires_in_a_package
+Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*
+Version: 0.0.1
+
+UNKNOWN
+""",
+ )
+
+ def test_python_abi3_binary_wheel(self):
+ arch = "amd64"
+ if platform.system() != "Windows":
+ arch = subprocess.check_output(["uname", "-m"]).strip().decode()
+ # These strings match the strings from py_wheel() in BUILD
+ os_strings = {
+ "Linux": "manylinux2014",
+ "Darwin": "macosx_11_0",
+ "Windows": "win",
+ }
+ os_string = os_strings[platform.system()]
+ filename = os.path.join(
+ os.environ["TEST_SRCDIR"],
+ "rules_python",
+ "examples",
+ "wheel",
+ f"example_python_abi3_binary_wheel-0.0.1-cp38-abi3-{os_string}_{arch}.whl",
+ )
+ with zipfile.ZipFile(filename) as zf:
+ metadata_contents = zf.read(
+ "example_python_abi3_binary_wheel-0.0.1.dist-info/METADATA"
+ )
+ # The entries are guaranteed to be sorted.
+ self.assertEqual(
+ metadata_contents,
+ b"""\
+Metadata-Version: 2.1
+Name: example_python_abi3_binary_wheel
+Requires-Python: >=3.8
+Version: 0.0.1
+
+UNKNOWN
+""",
+ )
+ wheel_contents = zf.read(
+ "example_python_abi3_binary_wheel-0.0.1.dist-info/WHEEL"
+ )
+ self.assertEqual(
+ wheel_contents.decode(),
+ f"""\
+Wheel-Version: 1.0
+Generator: bazel-wheelmaker 1.0
+Root-Is-Purelib: false
+Tag: cp38-abi3-{os_string}_{arch}
+""",
+ )
+
+ def test_rule_creates_directory_and_is_included_in_wheel(self):
+ filename = os.path.join(
+ os.environ["TEST_SRCDIR"],
+ "rules_python",
+ "examples",
+ "wheel",
+ "use_rule_with_dir_in_outs-0.0.1-py3-none-any.whl",
+ )
+
+ with zipfile.ZipFile(filename) as zf:
+ self.assertEqual(
+ zf.namelist(),
+ [
+ "examples/wheel/main.py",
+ "examples/wheel/someDir/foo.py",
+ "use_rule_with_dir_in_outs-0.0.1.dist-info/WHEEL",
+ "use_rule_with_dir_in_outs-0.0.1.dist-info/METADATA",
+ "use_rule_with_dir_in_outs-0.0.1.dist-info/RECORD",
+ ],
+ )
+
+ def test_rule_expands_workspace_status_keys_in_wheel_metadata(self):
+ filename = os.path.join(
+ os.environ["TEST_SRCDIR"],
+ "rules_python",
+ "examples",
+ "wheel",
+ "example_minimal_library_BUILD_USER_-0.1._BUILD_TIMESTAMP_-py3-none-any.whl",
+ )
+
+ with zipfile.ZipFile(filename) as zf:
+ metadata_file = None
+ for f in zf.namelist():
+ self.assertNotIn("_BUILD_TIMESTAMP_", f)
+ self.assertNotIn("_BUILD_USER_", f)
+ if os.path.basename(f) == "METADATA":
+ metadata_file = f
+ self.assertIsNotNone(metadata_file)
+
+ version = None
+ name = None
+ with zf.open(metadata_file) as fp:
+ for line in fp:
+ if line.startswith(b"Version:"):
+ version = line.decode().split()[-1]
+ if line.startswith(b"Name:"):
+ name = line.decode().split()[-1]
+ self.assertIsNotNone(version)
+ self.assertIsNotNone(name)
+ self.assertNotIn("{BUILD_TIMESTAMP}", version)
+ self.assertNotIn("{BUILD_USER}", name)
+
+
+if __name__ == "__main__":
+ unittest.main()