Skip to content

Commit

Permalink
pycross: Add patching support to py_wheel_library
Browse files Browse the repository at this point in the history
This patch adds a few arguments to `py_wheel_library` to simulate how
`http_archive` accepts patch-related arguments.

I also amended the existing test to validate the behaviour at a very
high level.

References: bazelbuild#1360
  • Loading branch information
philsc committed Sep 28, 2023
1 parent 9a8c447 commit dd84feb
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 4 deletions.
17 changes: 17 additions & 0 deletions tests/pycross/0001-Add-new-file-for-testing-patch-support.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
From b2ebe6fe67ff48edaf2ae937d24b1f0b67c16f81 Mon Sep 17 00:00:00 2001
From: Philipp Schrader <[email protected]>
Date: Thu, 28 Sep 2023 09:02:44 -0700
Subject: [PATCH] Add new file for testing patch support

---
site-packages/numpy/file_added_via_patch.txt | 1 +
1 file changed, 1 insertion(+)
create mode 100644 site-packages/numpy/file_added_via_patch.txt

diff --git a/site-packages/numpy/file_added_via_patch.txt b/site-packages/numpy/file_added_via_patch.txt
new file mode 100644
index 0000000..9d947a4
--- /dev/null
+++ b/site-packages/numpy/file_added_via_patch.txt
@@ -0,0 +1 @@
+Hello from a patch!
6 changes: 6 additions & 0 deletions tests/pycross/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ load("//third_party/rules_pycross/pycross/private:wheel_library.bzl", "py_wheel_

py_wheel_library(
name = "extracted_wheel_for_testing",
patch_args = [
"-p1",
],
patches = [
"0001-Add-new-file-for-testing-patch-support.patch",
],
wheel = "@wheel_for_testing//file",
)

Expand Down
9 changes: 6 additions & 3 deletions tests/pycross/py_wheel_library_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,7 @@
class TestPyWheelLibrary(unittest.TestCase):
def setUp(self):
self.extraction_dir = Path(
RUNFILES.Rlocation(
"rules_python/tests/pycross/extracted_wheel_for_testing"
)
RUNFILES.Rlocation("rules_python/tests/pycross/extracted_wheel_for_testing")
)
self.assertTrue(self.extraction_dir.exists(), self.extraction_dir)
self.assertTrue(self.extraction_dir.is_dir(), self.extraction_dir)
Expand All @@ -43,6 +41,11 @@ def test_file_presence(self):
(self.extraction_dir / path).exists(), f"{path} does not exist"
)

def test_patched_file_contents(self):
"""Validate that the patch got applied correctly."""
file = self.extraction_dir / "site-packages/numpy/file_added_via_patch.txt"
self.assertEqual(file.read_text(), "Hello from a patch!\n")


if __name__ == "__main__":
unittest.main()
46 changes: 46 additions & 0 deletions third_party/rules_pycross/pycross/private/tools/wheel_installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import argparse
import os
import shutil
import subprocess
import sys
import tempfile
from pathlib import Path
Expand Down Expand Up @@ -97,6 +98,24 @@ def main(args: Any) -> None:

setup_namespace_pkg_compatibility(lib_dir)

patch_args = [args.patch_tool] + args.patch_arg
patch_dir = args.patch_dir or "."
for patch in args.patch or []:
with patch.open("r") as stdin:
try:
subprocess.run(
patch_args,
stdin=stdin,
check=True,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
cwd=args.directory / patch_dir,
)
except subprocess.CalledProcessError as error:
print(f"Patch {patch} failed to apply:")
print(error.stdout.decode("utf-8"))
raise


def parse_flags(argv) -> Any:
parser = argparse.ArgumentParser(description="Extract a Python wheel.")
Expand Down Expand Up @@ -127,6 +146,33 @@ def parse_flags(argv) -> Any:
help="The output path.",
)

parser.add_argument(
"--patch",
type=Path,
action="append",
help="A patch file to apply.",
)

parser.add_argument(
"--patch-arg",
type=Path,
default=[],
action="append",
help="An argument for the patch_tool when applying the patches.",
)

parser.add_argument(
"--patch-tool",
type=str,
help="The tool to invoke when applying patches.",
)

parser.add_argument(
"--patch-dir",
type=str,
help="The directory from which to invoke patch_tool.",
)

return parser.parse_args(argv[1:])


Expand Down
28 changes: 27 additions & 1 deletion third_party/rules_pycross/pycross/private/wheel_library.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,12 @@ def _py_wheel_library_impl(ctx):
args = ctx.actions.args().use_param_file("--flagfile=%s")
args.add("--wheel", wheel_file)
args.add("--directory", out.path)
args.add_all(ctx.files.patches, format_each = "--patch=%s")
args.add_all(ctx.attr.patch_args, format_each = "--patch-arg=%s")
args.add("--patch-tool", ctx.attr.patch_tool)
args.add("--patch-dir", ctx.attr.patch_dir)

inputs = [wheel_file]
inputs = [wheel_file] + ctx.files.patches
if name_file:
inputs.append(name_file)
args.add("--wheel-name-file", name_file)
Expand Down Expand Up @@ -119,6 +123,28 @@ and py_test targets must specify either `legacy_create_init=False` or the global
This option is required to support some packages which cannot handle the conversion to pkg-util style.
""",
),
"patch_args": attr.string_list(
default = ["-p0"],
doc =
"The arguments given to the patch tool. Defaults to -p0, " +
"however -p1 will usually be needed for patches generated by " +
"git. If multiple -p arguments are specified, the last one will take effect.",
),
"patch_dir": attr.string(
default = "",
doc = "The directory from which to invoke the patch_tool.",
),
"patch_tool": attr.string(
default = "patch",
doc = "The patch(1) utility to use.",
),
"patches": attr.label_list(
allow_files = True,
default = [],
doc =
"A list of files that are to be applied as patches after " +
"extracting the archive. This will use the patch command line tool.",
),
"python_version": attr.string(
doc = "The python version required for this wheel ('PY2' or 'PY3')",
values = ["PY2", "PY3", ""],
Expand Down

0 comments on commit dd84feb

Please sign in to comment.