From 2e213c202932d9c9206c5b4aa4a34a2b4882637b Mon Sep 17 00:00:00 2001 From: Greg Magolan Date: Tue, 12 Apr 2022 03:34:51 -0700 Subject: [PATCH] feat: add copy_directory_action help to the public API (#67) --- docs/copy_directory.md | 27 ++++++++++++++- lib/copy_directory.bzl | 2 ++ lib/private/copy_directory.bzl | 61 +++++++++++++++++++++------------- 3 files changed, 66 insertions(+), 24 deletions(-) diff --git a/docs/copy_directory.md b/docs/copy_directory.md index 9056b37..a0fbc9d 100644 --- a/docs/copy_directory.md +++ b/docs/copy_directory.md @@ -31,8 +31,33 @@ for more context. | Name | Description | Default Value | | :------------- | :------------- | :------------- | | name | Name of the rule. | none | -| src | A Label. The directory to make a copy of. (Can also be the label of a rule that generates a directory.) | none | +| src | The directory to make a copy of. Can be a source directory or TreeArtifact. | none | | out | Path of the output directory, relative to this package. | none | | kwargs | further keyword arguments, e.g. visibility | none | + + +## copy_directory_action + +
+copy_directory_action(ctx, src, dst, is_windows)
+
+ +Helper function that creates an action to copy a directory from src to dst. + +This helper is used by copy_directory. It is exposed as a public API so it can be used within +other rule implementations. + + +**PARAMETERS** + + +| Name | Description | Default Value | +| :------------- | :------------- | :------------- | +| ctx | The rule context. | none | +| src | The directory to make a copy of. Can be a source directory or TreeArtifact. | none | +| dst | The directory to copy to. Must be a TreeArtifact. | none | +| is_windows | If true, an cmd.exe action is created so there is no bash dependency. | False | + + diff --git a/lib/copy_directory.bzl b/lib/copy_directory.bzl index c7460d5..5495a46 100644 --- a/lib/copy_directory.bzl +++ b/lib/copy_directory.bzl @@ -7,6 +7,8 @@ on Windows (no Bash is required). load( "//lib/private:copy_directory.bzl", _copy_directory = "copy_directory", + _copy_helper = "copy_directory_action", ) copy_directory = _copy_directory +copy_directory_action = _copy_helper diff --git a/lib/private/copy_directory.bzl b/lib/private/copy_directory.bzl index 4f60e05..32b4fb9 100644 --- a/lib/private/copy_directory.bzl +++ b/lib/private/copy_directory.bzl @@ -14,7 +14,7 @@ _execution_requirements = { } # buildifier: disable=function-docstring -def copy_cmd(ctx, src_dir, src_path, dst): +def _copy_cmd(ctx, src, dst): # Most Windows binaries built with MSVC use a certain argument quoting # scheme. Bazel uses that scheme too to quote arguments. However, # cmd.exe uses different semantics, so Bazel's quoting is wrong here. @@ -22,27 +22,27 @@ def copy_cmd(ctx, src_dir, src_path, dst): # quoting or escaping is required. # Put a hash of the file name into the name of the generated batch file to # make it unique within the package, so that users can define multiple copy_file's. - bat = ctx.actions.declare_file("%s-%s-cmd.bat" % (ctx.label.name, hash(src_path))) + bat = ctx.actions.declare_file("%s-%s-cmd.bat" % (ctx.label.name, hash(src.path))) # Flags are documented at # https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/robocopy # NB: robocopy return non-zero exit codes on success so we must exit 0 after calling it cmd_tmpl = "@robocopy \"{src}\" \"{dst}\" /E >NUL & @exit 0" mnemonic = "CopyDirectory" - progress_message = "Copying directory %s" % src_path + progress_message = "Copying directory %s" % src.path ctx.actions.write( output = bat, # Do not use lib/shell.bzl's shell.quote() method, because that uses # Bash quoting syntax, which is different from cmd.exe's syntax. content = cmd_tmpl.format( - src = src_path.replace("/", "\\"), + src = src.path.replace("/", "\\"), dst = dst.path.replace("/", "\\"), ), is_executable = True, ) ctx.actions.run( - inputs = [src_dir], + inputs = [src], tools = [bat], outputs = [dst], executable = "cmd.exe", @@ -54,33 +54,49 @@ def copy_cmd(ctx, src_dir, src_path, dst): ) # buildifier: disable=function-docstring -def copy_bash(ctx, src_dir, src_path, dst): - cmd_tmpl = "rm -rf \"$2\" && cp -fR \"$1/\" \"$2\"" +def _copy_bash(ctx, src, dst): + cmd = "rm -rf \"$2\" && cp -fR \"$1/\" \"$2\"" mnemonic = "CopyDirectory" - progress_message = "Copying directory %s" % src_path + progress_message = "Copying directory %s" % src.path ctx.actions.run_shell( - tools = [src_dir], + tools = [src], outputs = [dst], - command = cmd_tmpl, - arguments = [src_path, dst.path], + command = cmd, + arguments = [src.path, dst.path], mnemonic = mnemonic, progress_message = progress_message, use_default_shell_env = True, execution_requirements = _execution_requirements, ) -def _copy_directory_impl(ctx): - output = ctx.actions.declare_directory(ctx.attr.out) - src_dir = ctx.file.src - src_path = src_dir.path - if ctx.attr.is_windows: - copy_cmd(ctx, src_dir, src_path, output) - else: - copy_bash(ctx, src_dir, src_path, output) +def copy_directory_action(ctx, src, dst, is_windows = False): + """Helper function that creates an action to copy a directory from src to dst. - files = depset(direct = [output]) - runfiles = ctx.runfiles(files = [output]) + This helper is used by copy_directory. It is exposed as a public API so it can be used within + other rule implementations. + + Args: + ctx: The rule context. + src: The directory to make a copy of. Can be a source directory or TreeArtifact. + dst: The directory to copy to. Must be a TreeArtifact. + is_windows: If true, an cmd.exe action is created so there is no bash dependency. + """ + if not src.is_source and not dst.is_directory: + fail("src must be a source directory or TreeArtifact") + if dst.is_source or not dst.is_directory: + fail("dst must be a TreeArtifact") + if is_windows: + _copy_cmd(ctx, src, dst) + else: + _copy_bash(ctx, src, dst) + +def _copy_directory_impl(ctx): + dst = ctx.actions.declare_directory(ctx.attr.out) + copy_directory_action(ctx, ctx.file.src, dst, ctx.attr.is_windows) + + files = depset(direct = [dst]) + runfiles = ctx.runfiles(files = [dst]) return [DefaultInfo(files = files, runfiles = runfiles)] @@ -109,8 +125,7 @@ def copy_directory(name, src, out, **kwargs): Args: name: Name of the rule. - src: A Label. The directory to make a copy of. - (Can also be the label of a rule that generates a directory.) + src: The directory to make a copy of. Can be a source directory or TreeArtifact. out: Path of the output directory, relative to this package. **kwargs: further keyword arguments, e.g. `visibility` """