| # 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. |
| |
| 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 _use_native_patch(patch_args): |
| """If patch_args only contains -p<NUM> 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, 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. |
| 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. |
| 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" |
| |
| if patches == None and hasattr(ctx.attr, "patches"): |
| patches = ctx.attr.patches |
| if patches == None: |
| 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 patch_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 = patch_directory) |
| if st.return_code: |
| msg = "Error applying patch {}:\n{}{}".format(str(patchfile), st.stderr, st.stdout) |
| fail(msg) |
| |
| if repo_utils.is_windows(ctx) and patch_cmds_win: |
| for cmd in patch_cmds_win: |
| st = ctx.execute([powershell_exe, "/c", cmd], working_directory = patch_directory) |
| if st.return_code: |
| msg = "Error applying patch command {}:\n{}{}".format(cmd, st.stdout, st.stderr) |
| fail(msg) |
| else: |
| for cmd in patch_cmds: |
| st = ctx.execute([bash_exe, "-c", cmd], working_directory = patch_directory) |
| if st.return_code: |
| msg = "Error applying patch command {}:\n{}{}".format(cmd, st.stdout, st.stderr) |
| fail(msg) |