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
17 changes: 15 additions & 2 deletions docs/copy_directory.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion docs/copy_to_directory.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 15 additions & 13 deletions e2e/smoke/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ load("@aspect_bazel_lib//lib:expand_template.bzl", "expand_template")
load("@aspect_bazel_lib//lib:jq.bzl", "jq")
load("@aspect_bazel_lib//lib:tar.bzl", "tar")
load("@aspect_bazel_lib//lib:yq.bzl", "yq")
load("@io_bazel_rules_go//go:def.bzl", "go_test")

# Validate that JQ works and resolves its toolchain
jq(
Expand Down Expand Up @@ -105,33 +106,34 @@ copy_to_directory(
srcs = ["d"],
out = "copy_to_directory_mtime_out",
preserve_mtime = True,
tags = ["no-remote"],
)

copy_directory(
name = "copy_directory_mtime_case",
src = "d",
out = "copy_directory_mtime_out",
preserve_mtime = True,
tags = ["no-remote"],
)

sh_test(
go_test(
name = "test_preserve_mtime",
size = "small",
srcs = ["test_preserve_mtime.sh"],
srcs = ["test_preserve_mtime.go"],
deps = [
"@io_bazel_rules_go//go/tools/bazel:go_default_library",
],
data = [
"d",
":copy_directory_mtime_case",
":copy_to_directory_mtime_case",
],
# FIXME(#898)
# On macos, fails with
# stat: illegal option -- -
# usage: stat [-FLnq] [-f format | -l | -r | -s | -x] [-t timefmt] [file ...]
# On windows, fails with
# stat: cannot stat 'd/1': No such file or directory
# On Linux, it's helplessly flaky, often failing in 2/2 attempts like
# Preserve mtime test failed. Modify times do not match for d/1 and copy_to_directory_mtime_out/d/1
# Original modify time: 2024-08-14 18:23:01.413642851 +0000
# Copied modify time: 2024-08-14 18:23:31.501819589 +0000
tags = ["manual"],
# Note: This test is marked as manual as it is not reliable in the CI environment. The reason for is that changes
# to only the modify time do not invalidate the Bazel cache. These failures can be reproduced by:
# bazel clean --expunge
# bazel test :test_preserve_mtime # This test should pass
# touch d/1 # Update the mtime, in the CI environment this is done with the SCM integration.
# bazel test :test_preserve_mtime # This test now fails
tags = ["no-remote", "external", "manual"],
)
1 change: 1 addition & 0 deletions e2e/smoke/MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ local_path_override(
)

bazel_dep(name = "bazel_skylib", version = "1.7.1", dev_dependency = True)
bazel_dep(name = "rules_go", version = "0.46.0", repo_name = "io_bazel_rules_go", dev_dependency = True)
90 changes: 90 additions & 0 deletions e2e/smoke/test_preserve_mtime.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package smoketest

import (
"os"
"path/filepath"
"testing"
"time"

"github.com/bazelbuild/rules_go/go/tools/bazel"
)

type runfilePath struct {
// runfileDir is the directory that was provided as the bazel data dep.
runfileDir string
// subPaths to look for inside of the bazel tracked directory.
subPaths []string
}

func mtime(path string) (time.Time, error) {
info, err := os.Stat(path)
if err != nil {
return time.Time{}, err
}
return info.ModTime(), nil
}

func (r runfilePath) osPath() (string, error) {
dirPath, err := bazel.Runfile(r.runfileDir)
if err != nil{
return "", err
}
parts := append([]string{dirPath}, r.subPaths...)
return filepath.Join(parts...), nil
}

func TestPreserveMTime(t *testing.T) {
cases := map[string]struct{
original runfilePath
copied runfilePath
}{
"copy_directory": {
original: runfilePath{
runfileDir: "d",
subPaths: []string{"1"},
},
copied: runfilePath{
runfileDir: "copy_directory_mtime_out",
subPaths: []string{"1"},
},
},
"copy_to_directory": {
original: runfilePath{
runfileDir: "d",
subPaths: []string{"1"},
},
copied: runfilePath{
runfileDir: "copy_to_directory_mtime_out",
subPaths: []string{"d", "1"},
},
},
}

for name, test := range cases {
t.Run(name, func(t *testing.T) {
originalPath, err := test.original.osPath()
if err != nil {
t.Fatal(err.Error())
}
originalMTime, err := mtime(originalPath)
if err != nil {
t.Fatal(err.Error())
}

copiedPath, err := test.copied.osPath()
if err != nil {
t.Fatal(err.Error())
}
copiedMTime, err := mtime(copiedPath)
if err != nil {
t.Fatal(err.Error())
}

if originalMTime != copiedMTime {
t.Fatalf(`Modify times do not match for %s and %s:
Original modify time: %s
Copied modify time: %s`, originalPath, copiedPath, originalMTime, copiedMTime)
}
})
}
}
30 changes: 0 additions & 30 deletions e2e/smoke/test_preserve_mtime.sh

This file was deleted.

13 changes: 13 additions & 0 deletions lib/copy_directory.bzl
Original file line number Diff line number Diff line change
@@ -1,6 +1,19 @@
"""A rule that copies a directory to another place.

The rule uses a precompiled binary to perform the copy, so no shell is required.

## Preserving modification times

`copy_directory` and `copy_to_directory` have a `preserve_mtime` attribute, however
there are two caveats to consider when using this feature:

1. Remote Execution / Caching: These layers will reset the modify time and are
incompatible with this feature. To avoid these failures the [no-remote tag](https://bazel.build/reference/be/common-definitions)
can be added.
2. Caching: Changes to only the modified time will not re-trigger cached actions. This can
be worked around by using a clean build when these types of changes occur. For tests the
[external tag](https://bazel.build/reference/be/common-definitions) can be used but this
will result in tests never being cached.
"""

load(
Expand Down
8 changes: 5 additions & 3 deletions lib/private/copy_directory.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,11 @@ def copy_directory_bin_action(

See copy_directory rule documentation for more details.

verbose: If true, prints out verbose logs to stdout
verbose: print verbose logs to stdout

preserve_mtime: preserve the modified time from the source.
See the caveats above about interactions with remote execution and caching.

preserve_mtime: If true, preserve the modified time from the source.
"""
args = [
src.path,
Expand Down Expand Up @@ -100,7 +102,7 @@ _copy_directory = rule(
),
"verbose": attr.bool(),
"preserve_mtime": attr.bool(
doc = "If True, the last modified time of copied files is preserved.",
doc = """If True, the last modified time of copied files is preserved. Note the caveats on copy_directory.""",
default = False,
),
# use '_tool' attribute for development only; do not commit with this attribute active since it
Expand Down
4 changes: 3 additions & 1 deletion lib/private/copy_to_directory.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,9 @@ removed from sources files.
- `off`: all files are copied
- `on`: hardlinks are used for all files (not recommended)
""",
"preserve_mtime": """If True, the last modified time of copied files is preserved.""",
"preserve_mtime": """If True, the last modified time of copied files is preserved.
See the [caveats on copy_directory](/docs/copy_directory.md#preserving-modification-times)
about interactions with remote execution and caching.""",
# verbose
"verbose": """If true, prints out verbose logs to stdout""",
}
Expand Down