From 322bbc92dfd061e1e441e7aeeae9150831f6eb8c Mon Sep 17 00:00:00 2001 From: Greg Magolan Date: Mon, 11 Apr 2022 18:08:30 -0700 Subject: [PATCH] feat: add repo_utils with fork of @bazel_tools patch function that takes a working_directory argument --- docs/BUILD.bazel | 5 ++ docs/repo_utils.md | 89 ++++++++++++++++++++++ lib/BUILD.bazel | 10 +++ lib/private/BUILD.bazel | 13 ++++ lib/private/patch copy.bzl | 151 +++++++++++++++++++++++++++++++++++++ lib/private/patch.bzl | 20 ++--- lib/private/repo_utils.bzl | 19 +++++ lib/repo_utils.bzl | 9 +++ 8 files changed, 307 insertions(+), 9 deletions(-) create mode 100755 docs/repo_utils.md create mode 100644 lib/private/patch copy.bzl create mode 100644 lib/private/repo_utils.bzl create mode 100644 lib/repo_utils.bzl diff --git a/docs/BUILD.bazel b/docs/BUILD.bazel index 0e934a8..fa642a3 100644 --- a/docs/BUILD.bazel +++ b/docs/BUILD.bazel @@ -73,4 +73,9 @@ stardoc_with_diff_test( bzl_library_target = "//lib:transitions", ) +stardoc_with_diff_test( + name = "repo_utils", + bzl_library_target = "//lib:repo_utils", +) + update_docs() diff --git a/docs/repo_utils.md b/docs/repo_utils.md new file mode 100755 index 0000000..c3b1ccc --- /dev/null +++ b/docs/repo_utils.md @@ -0,0 +1,89 @@ + + +Public API + + + +## is_darwin_os + +
+is_darwin_os(rctx)
+
+ +Returns true if the host operating system is Darwin + +**PARAMETERS** + + +| Name | Description | Default Value | +| :------------- | :------------- | :------------- | +| rctx |

-

| none | + + + + +## is_linux_os + +
+is_linux_os(rctx)
+
+ +Returns true if the host operating system is Linux + +**PARAMETERS** + + +| Name | Description | Default Value | +| :------------- | :------------- | :------------- | +| rctx |

-

| none | + + + + +## is_windows_os + +
+is_windows_os(rctx)
+
+ +Returns true if the host operating system is Windows + +**PARAMETERS** + + +| Name | Description | Default Value | +| :------------- | :------------- | :------------- | +| rctx |

-

| none | + + + + +## patch + +
+patch(ctx, patches, patch_cmds, patch_cmds_win, patch_tool, patch_args, auth, patch_directory)
+
+ +Implementation of patching an already extracted repository. + +This rule is intended to be used in the implementation function of +a repository rule. If the parameters `patches`, `patch_tool`, +`patch_args`, `patch_cmds` and `patch_cmds_win` are not specified +then they are taken from `ctx.attr`. + + +**PARAMETERS** + + +| Name | Description | Default Value | +| :------------- | :------------- | :------------- | +| ctx | The repository context of the repository rule calling this utility function. | none | +| patches | The patch files to apply. List of strings, Labels, or paths. | None | +| patch_cmds | Bash commands to run for patching, passed one at a time to bash -c. List of strings | None | +| patch_cmds_win | Powershell commands to run for patching, passed one at a time to powershell /c. List of strings. If the boolean value of this parameter is false, patch_cmds will be used and this parameter will be ignored. | None | +| patch_tool | Path of the patch tool to execute for applying patches. String. | None | +| patch_args | Arguments to pass to the patch tool. List of strings. | None | +| auth | An optional dict specifying authentication information for some of the URLs. | None | +| patch_directory | Directory to apply the patches in | None | + + diff --git a/lib/BUILD.bazel b/lib/BUILD.bazel index e0fef89..daa63a8 100644 --- a/lib/BUILD.bazel +++ b/lib/BUILD.bazel @@ -127,3 +127,13 @@ bzl_library( srcs = ["windows_utils.bzl"], visibility = ["//visibility:public"], ) + +bzl_library( + name = "repo_utils", + srcs = ["repo_utils.bzl"], + visibility = ["//visibility:public"], + deps = [ + "//lib/private:patch", + "//lib/private:repo_utils", + ], +) diff --git a/lib/private/BUILD.bazel b/lib/private/BUILD.bazel index cb836fb..22dac6f 100644 --- a/lib/private/BUILD.bazel +++ b/lib/private/BUILD.bazel @@ -120,3 +120,16 @@ bzl_library( "@bazel_skylib//lib:dicts", ], ) + +bzl_library( + name = "repo_utils", + srcs = ["repo_utils.bzl"], + visibility = ["//lib:__subpackages__"], +) + +bzl_library( + name = "patch", + srcs = ["patch.bzl"], + visibility = ["//lib:__subpackages__"], + deps = [":repo_utils"], +) diff --git a/lib/private/patch copy.bzl b/lib/private/patch copy.bzl new file mode 100644 index 0000000..0ba4ca2 --- /dev/null +++ b/lib/private/patch copy.bzl @@ -0,0 +1,151 @@ +# 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. + +"""Fork of @bazel_tools//tools/build_defs/repo:utils.bzl patch function with +working_directory argument added. +""" + +load(":repo_utils.bzl", "repo_utils") + +# Temporary directory for downloading remote patch files. +_REMOTE_PATCH_DIR = ".tmp_remote_patches" + +def _use_native_patch(patch_args): + """If patch_args only contains -p options, we can use the native patch implementation.""" + for arg in patch_args: + if not arg.startswith("-p"): + return False + return True + +def _download_patch(ctx, patch_url, integrity, auth): + name = patch_url.split("/")[-1] + patch_path = ctx.path(_REMOTE_PATCH_DIR).get_child(name) + ctx.download( + patch_url, + patch_path, + canonical_id = ctx.attr.canonical_id, + auth = auth, + integrity = integrity, + ) + return patch_path + +def patch(ctx, patches = None, patch_cmds = None, patch_cmds_win = None, patch_tool = None, patch_args = None, auth = None, working_directory = None): + """Implementation of patching an already extracted repository. + + This rule is intended to be used in the implementation function of + a repository rule. If the parameters `patches`, `patch_tool`, + `patch_args`, `patch_cmds` and `patch_cmds_win` are not specified + then they are taken from `ctx.attr`. + + Args: + ctx: The repository context of the repository rule calling this utility + function. + patches: The patch files to apply. List of strings, Labels, or paths. + patch_cmds: Bash commands to run for patching, passed one at a + time to bash -c. List of strings + patch_cmds_win: Powershell commands to run for patching, passed + one at a time to powershell /c. List of strings. If the + boolean value of this parameter is false, patch_cmds will be + used and this parameter will be ignored. + patch_tool: Path of the patch tool to execute for applying + patches. String. + patch_args: Arguments to pass to the patch tool. List of strings. + auth: An optional dict specifying authentication information for some of the URLs. + working_directory: Working directory to apply the patches in + + """ + bash_exe = ctx.os.environ["BAZEL_SH"] if "BAZEL_SH" in ctx.os.environ else "bash" + powershell_exe = ctx.os.environ["BAZEL_POWERSHELL"] if "BAZEL_POWERSHELL" in ctx.os.environ else "powershell.exe" + + if patches == None: + patches = [] + if hasattr(ctx.attr, "patches") and ctx.attr.patches: + patches += ctx.attr.patches + + remote_patches = {} + remote_patch_strip = 0 + if hasattr(ctx.attr, "remote_patches") and ctx.attr.remote_patches: + if hasattr(ctx.attr, "remote_patch_strip"): + remote_patch_strip = ctx.attr.remote_patch_strip + remote_patches = ctx.attr.remote_patches + + if patch_cmds == None and hasattr(ctx.attr, "patch_cmds"): + patch_cmds = ctx.attr.patch_cmds + if patch_cmds == None: + patch_cmds = [] + + if patch_cmds_win == None and hasattr(ctx.attr, "patch_cmds_win"): + patch_cmds_win = ctx.attr.patch_cmds_win + if patch_cmds_win == None: + patch_cmds_win = [] + + if patch_tool == None and hasattr(ctx.attr, "patch_tool"): + patch_tool = ctx.attr.patch_tool + if not patch_tool: + patch_tool = "patch" + native_patch = True + else: + native_patch = False + + if patch_args == None and hasattr(ctx.attr, "patch_args"): + patch_args = ctx.attr.patch_args + if patch_args == None: + patch_args = [] + + if len(remote_patches) > 0 or len(patches) > 0 or len(patch_cmds) > 0: + ctx.report_progress("Patching repository") + + # Apply remote patches + for patch_url in remote_patches: + integrity = remote_patches[patch_url] + patchfile = _download_patch(ctx, patch_url, integrity, auth) + ctx.patch(patchfile, remote_patch_strip) + ctx.delete(patchfile) + ctx.delete(ctx.path(_REMOTE_PATCH_DIR)) + + # Apply local patches + if native_patch and _use_native_patch(patch_args) and not working_directory: + if patch_args: + strip = int(patch_args[-1][2:]) + else: + strip = 0 + for patchfile in patches: + ctx.patch(patchfile, strip) + else: + for patchfile in patches: + command = "{patchtool} {patch_args} < {patchfile}".format( + patchtool = patch_tool, + patchfile = ctx.path(patchfile), + patch_args = " ".join([ + "'%s'" % arg + for arg in patch_args + ]), + ) + st = ctx.execute([bash_exe, "-c", command], working_directory = working_directory) + if st.return_code: + fail("Error applying patch %s:\n%s%s" % + (str(patchfile), st.stderr, st.stdout)) + + if repo_utils.is_windows_os(ctx) and patch_cmds_win: + for cmd in patch_cmds_win: + st = ctx.execute([powershell_exe, "/c", cmd], working_directory = working_directory) + if st.return_code: + fail("Error applying patch command %s:\n%s%s" % + (cmd, st.stdout, st.stderr)) + else: + for cmd in patch_cmds: + st = ctx.execute([bash_exe, "-c", cmd], working_directory = working_directory) + if st.return_code: + fail("Error applying patch command %s:\n%s%s" % + (cmd, st.stdout, st.stderr)) diff --git a/lib/private/patch.bzl b/lib/private/patch.bzl index abdb97b..8e42e3e 100644 --- a/lib/private/patch.bzl +++ b/lib/private/patch.bzl @@ -19,12 +19,11 @@ Upstream code is at https://github.com/bazelbuild/bazel/blob/f4214746fcd15f0ef8c4e747ef8e3edca9f112a5/tools/build_defs/repo/utils.bzl#L87 """ +load(":repo_utils.bzl", "repo_utils") + # Temporary directory for downloading remote patch files. _REMOTE_PATCH_DIR = ".tmp_remote_patches" -def _is_windows(ctx): - return ctx.os.name.lower().find("windows") != -1 - def _use_native_patch(patch_args): """If patch_args only contains -p options, we can use the native patch implementation.""" for arg in patch_args: @@ -44,13 +43,14 @@ def _download_patch(ctx, patch_url, integrity, auth): ) return patch_path -def patch(ctx, patches = None, patch_cmds = None, patch_cmds_win = None, patch_tool = None, patch_args = None, auth = None): +def patch(ctx, patches = None, patch_cmds = None, patch_cmds_win = None, patch_tool = None, patch_args = None, auth = None, patch_directory = None): """Implementation of patching an already extracted repository. This rule is intended to be used in the implementation function of a repository rule. If the parameters `patches`, `patch_tool`, `patch_args`, `patch_cmds` and `patch_cmds_win` are not specified then they are taken from `ctx.attr`. + Args: ctx: The repository context of the repository rule calling this utility function. @@ -65,6 +65,8 @@ def patch(ctx, patches = None, patch_cmds = None, patch_cmds_win = None, patch_t patches. String. patch_args: Arguments to pass to the patch tool. List of strings. auth: An optional dict specifying authentication information for some of the URLs. + patch_directory: Directory to apply the patches in + """ bash_exe = ctx.os.environ["BAZEL_SH"] if "BAZEL_SH" in ctx.os.environ else "bash" powershell_exe = ctx.os.environ["BAZEL_POWERSHELL"] if "BAZEL_POWERSHELL" in ctx.os.environ else "powershell.exe" @@ -116,7 +118,7 @@ def patch(ctx, patches = None, patch_cmds = None, patch_cmds_win = None, patch_t ctx.delete(ctx.path(_REMOTE_PATCH_DIR)) # Apply local patches - if native_patch and _use_native_patch(patch_args): + if native_patch and _use_native_patch(patch_args) and not patch_directory: if patch_args: strip = int(patch_args[-1][2:]) else: @@ -133,20 +135,20 @@ def patch(ctx, patches = None, patch_cmds = None, patch_cmds_win = None, patch_t for arg in patch_args ]), ) - st = ctx.execute([bash_exe, "-c", command]) + st = ctx.execute([bash_exe, "-c", command], working_directory = patch_directory) if st.return_code: fail("Error applying patch %s:\n%s%s" % (str(patchfile), st.stderr, st.stdout)) - if _is_windows(ctx) and patch_cmds_win: + if repo_utils.is_windows_os(ctx) and patch_cmds_win: for cmd in patch_cmds_win: - st = ctx.execute([powershell_exe, "/c", cmd]) + st = ctx.execute([powershell_exe, "/c", cmd], working_directory = patch_directory) if st.return_code: fail("Error applying patch command %s:\n%s%s" % (cmd, st.stdout, st.stderr)) else: for cmd in patch_cmds: - st = ctx.execute([bash_exe, "-c", cmd]) + st = ctx.execute([bash_exe, "-c", cmd], working_directory = patch_directory) if st.return_code: fail("Error applying patch command %s:\n%s%s" % (cmd, st.stdout, st.stderr)) diff --git a/lib/private/repo_utils.bzl b/lib/private/repo_utils.bzl new file mode 100644 index 0000000..743779a --- /dev/null +++ b/lib/private/repo_utils.bzl @@ -0,0 +1,19 @@ +"""Utility functions for repository rules""" + +def _is_windows_os(rctx): + """Returns true if the host operating system is Windows""" + return rctx.os.name.lower().find("windows") != -1 + +def _is_darwin_os(rctx): + """Returns true if the host operating system is Darwin""" + return rctx.os.name.lower().os_name.startswith("mac os") + +def _is_linux_os(rctx): + """Returns true if the host operating system is Linux""" + return rctx.os.name.lower().startswith("linux") + +repo_utils = struct( + is_windows_os = _is_windows_os, + is_darwin_os = _is_darwin_os, + is_linux_os = _is_linux_os, +) diff --git a/lib/repo_utils.bzl b/lib/repo_utils.bzl new file mode 100644 index 0000000..159e8b5 --- /dev/null +++ b/lib/repo_utils.bzl @@ -0,0 +1,9 @@ +"Public API" + +load("//lib/private:repo_utils.bzl", "repo_utils") +load("//lib/private:patch.bzl", _patch = "patch") + +is_windows_os = repo_utils.is_windows_os +is_darwin_os = repo_utils.is_darwin_os +is_linux_os = repo_utils.is_linux_os +patch = _patch