Description
Description of the bug:
External repo + build in the path + custom rules in starlark with outputs + NOT specifying --experimental_sibling_repository_layout can result in dangled symlinks.
With the following repo set up:
bazel-test-repo $ tree .
.
├── bazel-tree-contents
├── build -> xxxxx
├── BUILD
├── BUILD.external_repo.bzl
├── external_repo
│ └── test.sh
├── MODULE.bazel
├── MODULE.bazel.lock
├── my-build -> xxxxx/
├── my_test_rules.bzl
├── repo.bzl
└── xxxxx
└── BUILD
5 directories, 9 files
───────┬────────────────────────────────────────────────────────────────────────
│ File: .bazelversion
───────┼────────────────────────────────────────────────────────────────────────
1 + │ 9.0.0
───────┴────────────────────────────────────────────────────────────────────────
───────┬────────────────────────────────────────────────────────────────────────
│ File: .gitignore
───────┼────────────────────────────────────────────────────────────────────────
1 + │ bazel-*
2 + │ /MODULE.bazel.lock
───────┴────────────────────────────────────────────────────────────────────────
───────┬────────────────────────────────────────────────────────────────────────
│ File: BUILD
───────┼────────────────────────────────────────────────────────────────────────
1 + │ # Keep this file so that this folder is a package
───────┴────────────────────────────────────────────────────────────────────────
───────┬────────────────────────────────────────────────────────────────────────
│ File: BUILD.external_repo.bzl
───────┼────────────────────────────────────────────────────────────────────────
1 + │ exports_files(["test.sh"])
───────┴────────────────────────────────────────────────────────────────────────
───────┬────────────────────────────────────────────────────────────────────────
│ File: MODULE.bazel
───────┼────────────────────────────────────────────────────────────────────────
1 + │ external_repos = use_extension("//:repo.bzl", "external_repos")
2 + │
3 + │ use_repo(
4 + │ external_repos,
5 + │ "external_repo",
6 + │ )
───────┴────────────────────────────────────────────────────────────────────────
───────┬────────────────────────────────────────────────────────────────────────
│ File: external_repo/test.sh
───────┼────────────────────────────────────────────────────────────────────────
1 + │ echo external hello
───────┴────────────────────────────────────────────────────────────────────────
───────┬────────────────────────────────────────────────────────────────────────
│ File: my_test_rules.bzl
───────┼────────────────────────────────────────────────────────────────────────
1 + │ def _my_test_rule_impl(ctx):
2 + │ script_file = ctx.actions.declare_file(ctx.label.name + ".sh")
3 + │ script_contents = [
4 + │ "#!/bin/bash",
5 + │ "set -e",
6 + │ "cp -rf $1 $2",
7 + │ ]
8 + │ script_contents.append("")
9 + │
10 + │ ctx.actions.write(
11 + │ output = script_file,
12 + │ content = "\n".join(script_contents),
13 + │ is_executable = True,
14 + │ )
15 + │
16 + │ out_file = ctx.outputs.test_out
17 + │
18 + │ args = ctx.actions.args()
19 + │ args.add(ctx.attr.src[DefaultInfo].files.to_list()[0])
20 + │ args.add(out_file)
21 + │
22 + │ ctx.actions.run(
23 + │ outputs = [out_file],
24 + │ inputs = ctx.attr.src[DefaultInfo].files.to_list(),
25 + │ executable = script_file,
26 + │ arguments = [args],
27 + │ mnemonic = "TestAction",
28 + │ )
29 + │
30 + │ transitive_files = [ctx.attr.src[DefaultInfo].files]
31 + │ files = depset([script_file, out_file], transitive = transitive_files)
32 + │ return [DefaultInfo(
33 + │ files = files,
34 + │ runfiles = ctx.runfiles(transitive_files = files, collect_default = True),
35 + │ )]
36 + │
37 + │ my_test_rule = rule(
38 + │ implementation = _my_test_rule_impl,
39 + │ attrs = {
40 + │ "src": attr.label(allow_files = True),
41 + │ },
42 + │ outputs = {
43 + │ "test_out": "%{name}.test",
44 + │ },
45 + │ )
───────┴────────────────────────────────────────────────────────────────────────
───────┬────────────────────────────────────────────────────────────────────────
│ File: repo.bzl
───────┼────────────────────────────────────────────────────────────────────────
1 + │ load("@bazel_tools//tools/build_defs/repo:local.bzl", "new_local_repository")
2 + │
3 + │ def _external_repos_impl(_ctx):
4 + │ new_local_repository(
5 + │ name = "external_repo",
6 + │ path = "external_repo",
7 + │ build_file = "//:BUILD.external_repo.bzl",
8 + │ )
9 + │
10 + │ external_repos = module_extension(
11 + │ implementation = _external_repos_impl,
12 + │ )
13 + │
───────┴────────────────────────────────────────────────────────────────────────
───────┬────────────────────────────────────────────────────────────────────────
│ File: xxxxx/BUILD
───────┼────────────────────────────────────────────────────────────────────────
1 + │ load("//:my_test_rules.bzl", "my_test_rule")
2 + │
3 + │ my_test_rule(
4 + │ name = "my_target",
5 + │ src = "@external_repo//:test.sh",
6 + │ )
7 + │
8 + │ my_test_rule(
9 + │ name = "my_target2",
10 + │ src = ":my_target.test",
11 + │ )
───────┴────────────────────────────────────────────────────────────────────────
When I run rm -rf MODULE.bazel.lock && bazel clean --expunge && rm -rf MODULE.bazel.lock && bazel clean --expunge && bazel build //my-build:my_target2 --sandbox_debug or rm -rf MODULE.bazel.lock && bazel clean --expunge && rm -rf MODULE.bazel.lock && bazel clean --expunge && bazel build //xxxxx:my_target2 --sandbox_debug, the build succeeds. Note that the target label doesn't have build in its path components. When I run rm -rf MODULE.bazel.lock && bazel clean --expunge && rm -rf MODULE.bazel.lock && bazel clean --expunge && bazel build //build:my_target2 --sandbox_debug, it fails with the following error log:
ERROR: $HOME/src/bazel-test-repo/build/BUILD:3:13: output 'build/my_target.test' is a dangling symbolic link
ERROR: $HOME/src/bazel-test-repo/build/BUILD:3:13: TestAction build/my_target.test failed: not all outputs were created or valid
The same error happens if I change build/BUILD file to build/xxx/BUILD file, and build the build/xxx:my_target2 target.
Which category does this issue belong to?
No response
What's the simplest, easiest way to reproduce this bug? Please provide a minimal example if possible.
It's in the description.
Which operating system are you running Bazel on?
Linux
What is the output of bazel info release?
release 9.0.0
If bazel info release returns development version or (@non-git), tell us how you built Bazel.
No response
What's the output of git remote get-url origin; git rev-parse HEAD ?
error: No such remote 'origin'
HEAD
fatal: ambiguous argument 'HEAD': unknown revision or path not in the working tree.
Use '--' to separate paths from revisions, like this:
'git <command> [<revision>...] -- [<file>...]
If this is a regression, please try to identify the Bazel commit where the bug was introduced with bazelisk --bisect.
No response
Have you found anything relevant by searching the web?
No response
Any other information, logs, or outputs that you want to share?
No response