Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions diff/defs.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,20 @@ def diff(name, srcs, args = ["--unified"], patch = None, **kwargs):
partial.call(srcs[i], name = target, out = target + ".in")
srcs[i] = target

# Determine whether patchable inputs are all source directories.
# Bazel rules cannot detect source directories. This information is
# needed to produce the correct patch command.
source_directories = False
if len(srcs) == 2: # TODO: support multiple directory inputs with --to-file
all_sources = set(native.glob([srcs[0]], exclude_directories = 0, allow_empty = True))
file_sources = set(native.glob([srcs[0]], exclude_directories = 1, allow_empty = True))
source_directories = len(all_sources.difference(file_sources)) > 0

diff_rule(
name = name,
args = args,
srcs = srcs,
patch = patch or name + ".patch",
source_directories = source_directories,
**kwargs
)
25 changes: 22 additions & 3 deletions diff/private/diff.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,21 @@ def _determine_patch_type(args):

return "normal"

def _patch_cmd(type, source_file, patch_file):
def _is_recursive(args):
for arg in args:
arg = arg.lstrip(" ")
if arg.startswith("-r") or arg.startswith("--recursive"):
return True
return False

def _patch_cmd(type, source_file, patch_file, recursive, source_directories):
if type == "normal":
return "(cd \\$(bazel info workspace); patch -p0 {} < {})".format(source_file, patch_file)
elif type == "context" or type == "unified":
return "(cd \\$(bazel info workspace); patch -p0 < {})".format(patch_file)
if recursive and source_directories:
return "(cd \\$(bazel info workspace); patch --directory {} -p{} < {})".format(source_file, source_file.count("/") + 1, patch_file)
else:
return "(cd \\$(bazel info workspace); patch -p0 < {})".format(patch_file)
return None

def _detect_multifile(args):
Expand Down Expand Up @@ -137,6 +147,7 @@ def _diff_rule_impl(ctx):
fail("error: srcs attr of diff rule must contain exactly two targets unless --from-file or --to-file are specified")

type = _determine_patch_type(ctx.attr.args)
recursive = _is_recursive(ctx.attr.args)

command = _build_command(
ctx.bin_dir.path,
Expand Down Expand Up @@ -182,7 +193,7 @@ def _diff_rule_impl(ctx):
if patchable:
# Show a command to patch the source file if it's a (bazel) source file.
# NB: the error message we print here allows the user to be in any working directory.
patch_cmd = _patch_cmd(type, ctx.files.srcs[0].path, ctx.outputs.patch.path)
patch_cmd = _patch_cmd(type, ctx.files.srcs[0].path, ctx.outputs.patch.path, recursive, ctx.attr.source_directories)
if patch_cmd != None:
patch_msg = """
To accept the diff, run:
Expand Down Expand Up @@ -211,6 +222,14 @@ diff_rule = rule(
""",
default = [],
),
"source_directories": attr.bool(
doc = """\
Whether all of the patchable source inputs in `srcs` are directories. This is expected
to be passed in by a wrapper macro as File.is_directory does not detect source directory
inputs.
""",
default = False,
),
"srcs": attr.label_list(allow_files = True),
"patch": attr.output(
doc = """\
Expand Down
Loading