Skip to content

Commit 0ebea4d

Browse files
committed
Optimize copy_file
* `ctx.actions.symlink` can be used on all platforms and falls back to a copy if a symlink is unsupported. It is also heavily optimized, avoiding the need to hash the output file again. * Bazel never guarantees that an input to an action is staged as a non-symlink, so whether an output is a symlink or a hard copy only matters for top-level outputs consumed outside Bazel and handled by tools that don't follow symlinks by default, which should be extremely rare and could be worked around by explicitly setting `allow_symlink` to `False` in user-controlled code. * For file copy actions that do not go through `ctx.actions.symlink`, caching is extremely cheap since the CAS entry of the input will be reused as the CAS entry of the output. Allowing remote execution and caching enables BwoB for the copy, which can avoid downloads of both the input and the output file. The same changes are not applied to `copy_directory` as source directories are not officially supported by Bazel and any kind of change could cause subtle incorrectness.
1 parent 25a8e9d commit 0ebea4d

File tree

2 files changed

+12
-12
lines changed

2 files changed

+12
-12
lines changed

docs/copy_file_doc.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ This rule uses a Bash command on Linux/macOS/non-Windows, and a cmd.exe command
3434
| <a id="copy_file-src"></a>src | A Label. The file to make a copy of. (Can also be the label of a rule that generates a file.) | none |
3535
| <a id="copy_file-out"></a>out | Path of the output file, relative to this package. | none |
3636
| <a id="copy_file-is_executable"></a>is_executable | A boolean. Whether to make the output file executable. When True, the rule's output can be executed using `bazel run` and can be in the srcs of binary and test rules that require executable sources. WARNING: If `allow_symlink` is True, `src` must also be executable. | `False` |
37-
| <a id="copy_file-allow_symlink"></a>allow_symlink | A boolean. Whether to allow symlinking instead of copying. When False, the output is always a hard copy. When True, the output *can* be a symlink, but there is no guarantee that a symlink is created (i.e., at the time of writing, we don't create symlinks on Windows). Set this to True if you need fast copying and your tools can handle symlinks (which most UNIX tools can). | `False` |
37+
| <a id="copy_file-allow_symlink"></a>allow_symlink | A boolean. Whether to allow symlinking instead of copying. When False, the output is always a hard copy, but actions consuming that output as an input may still see a symlink (e.g. when using sandboxed excution). When True, the output *can* be a symlink, but there is no guarantee that a symlink is created (i.e., at the time of writing, we don't create symlinks on Windows by default). This defaults to True if `is_executable` is False, and False otherwise. | `None` |
3838
| <a id="copy_file-kwargs"></a>kwargs | further keyword arguments, e.g. `visibility` | none |
3939

4040

rules/private/copy_file_private.bzl

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@ cmd.exe (on Windows). '_copy_xfile' marks the resulting file executable,
1919
'_copy_file' does not.
2020
"""
2121

22-
load(":copy_common.bzl", "COPY_EXECUTION_REQUIREMENTS")
23-
2422
def copy_cmd(ctx, src, dst):
2523
# Most Windows binaries built with MSVC use a certain argument quoting
2624
# scheme. Bazel uses that scheme too to quote arguments. However,
@@ -46,7 +44,6 @@ def copy_cmd(ctx, src, dst):
4644
mnemonic = "CopyFile",
4745
progress_message = "Copying files",
4846
use_default_shell_env = True,
49-
execution_requirements = COPY_EXECUTION_REQUIREMENTS,
5047
)
5148

5249
def copy_bash(ctx, src, dst):
@@ -58,7 +55,6 @@ def copy_bash(ctx, src, dst):
5855
mnemonic = "CopyFile",
5956
progress_message = "Copying files",
6057
use_default_shell_env = True,
61-
execution_requirements = COPY_EXECUTION_REQUIREMENTS,
6258
)
6359

6460
def _copy_file_impl(ctx):
@@ -104,7 +100,7 @@ _copy_xfile = rule(
104100
attrs = _ATTRS,
105101
)
106102

107-
def copy_file(name, src, out, is_executable = False, allow_symlink = False, **kwargs):
103+
def copy_file(name, src, out, is_executable = False, allow_symlink = None, **kwargs):
108104
"""Copies a file to another location.
109105
110106
`native.genrule()` is sometimes used to copy files (often wishing to rename them). The 'copy_file' rule does this with a simpler interface than genrule.
@@ -121,11 +117,12 @@ def copy_file(name, src, out, is_executable = False, allow_symlink = False, **kw
121117
in the srcs of binary and test rules that require executable sources.
122118
WARNING: If `allow_symlink` is True, `src` must also be executable.
123119
allow_symlink: A boolean. Whether to allow symlinking instead of copying.
124-
When False, the output is always a hard copy. When True, the output
125-
*can* be a symlink, but there is no guarantee that a symlink is
126-
created (i.e., at the time of writing, we don't create symlinks on
127-
Windows). Set this to True if you need fast copying and your tools can
128-
handle symlinks (which most UNIX tools can).
120+
When False, the output is always a hard copy, but actions consuming
121+
that output as an input may still see a symlink (e.g. when using
122+
sandboxed excution). When True, the output *can* be a symlink, but
123+
there is no guarantee that a symlink is created (i.e., at the time of
124+
writing, we don't create symlinks on Windows by default). This
125+
defaults to True if `is_executable` is False, and False otherwise.
129126
**kwargs: further keyword arguments, e.g. `visibility`
130127
"""
131128

@@ -142,6 +139,9 @@ def copy_file(name, src, out, is_executable = False, allow_symlink = False, **kw
142139
"//conditions:default": False,
143140
}),
144141
is_executable = is_executable,
145-
allow_symlink = allow_symlink,
142+
# Default to True if is_executable is False since symlinking avoids
143+
# running a full action to copy the file. If the output needs to be
144+
# executable, a copy may be required if the input isn't.
145+
allow_symlink = allow_symlink if allow_symlink != None else not is_executable,
146146
**kwargs
147147
)

0 commit comments

Comments
 (0)