Skip to content

Instantly share code, notes, and snippets.

@rrbutani
Last active November 4, 2025 04:59
Show Gist options
  • Select an option

  • Save rrbutani/3ca8c01294f4bc21eee23265ec9f92fb to your computer and use it in GitHub Desktop.

Select an option

Save rrbutani/3ca8c01294f4bc21eee23265ec9f92fb to your computer and use it in GitHub Desktop.

observations:

  1. path mapping does not really1 "mask" out the repo name from non-main repo paths: source files OR generated artifacts
    • this means that whether you build a particular target (or really produce a particular action for the target) when that target is in the main (root) repo vs. when its in an "external" repo effects the paths used in the action
    • i.e. you will not get cache hits if you build //:your_target and then pull your repo into another bazel workspace as @foo and build @foo//:your_target
    • TODO: would be nice to repo names as well...
  2. path mapping silently falls back to using unmapped paths if the set of mapped paths has any conflicts (see :example_both)
    • note that ctx.actions.run(_shell)'s inputs does influence path mapping and does appear to factor into how conflicts are detected but... if you try to add a path to the command line (i.e. via arguments) that you haven't listed in inputs it isn't an error — some path mapping heuristics get applied I guess? just rewriting root? not sure
    • TODO: in cases like these, try harder to path map? * even if rewriting is still limited to repo roots it feels like there are deterministic rewriting schemes that would yield some benefit (i.e. try falling back to bazel-out/exec-cfg in cases where there are only two roots? idk)
  3. interactions with --experimental_sibling_repository_layout are wonky...
    • for paths in external repos, the external repo name actually is stripped! instead of the "config name" part of the path...
    • weirdly this addresses #1 but I'm pretty sure it's accidental...
    • TODO: fix?

Footnotes

  1. at least it doesn't seem to be designed with the intention of doing so; the --experimental_sibling_repository_layout case is accidental I think

# path mapping:
common --experimental_output_paths=strip
common --disk_cache=./disk_cache
common:sib --experimental_sibling_repository_layout
common --noshow_progress
use nix -p bazel_8 starpls
/bazel-*
/.direnv
/MODULE.bazel.lock
/disk_cache
load(":defs.bzl", "list_inputs")
package(default_visibility = ["//visibility:public"])
genrule(
name = "genrule",
outs = ["gen"],
cmd = "touch $(OUTS)",
)
list_inputs(
name = "example",
inputs = [
":BUILD.bazel",
":gen",
],
)
'''
# build //:example
BUILD.bazel
bazel-out/cfg/bin/gen
[OUT] bazel-out/cfg/bin/example
# build //:example --config=sib (same)
BUILD.bazel
bazel-out/cfg/bin/gen
[OUT] bazel-out/cfg/bin/example
# build @external_repo//:example
external/+_repo_rules+external_repo/BUILD.bazel
bazel-out/cfg/bin/external/+_repo_rules+external_repo/gen
[OUT] bazel-out/cfg/bin/external/+_repo_rules+external_repo/example
# build @external_repo//:example --config=sib
../+_repo_rules+external_repo/BUILD.bazel
bazel-out/cfg/k8-fastbuild/bin/gen
[OUT] bazel-out/cfg/k8-fastbuild/bin/example
- !!! path mapping is broken!
'''
list_inputs(
name = "example_extern_ref",
inputs = [
"@@//:BUILD.bazel",
"@@//:gen",
"@external_repo//:BUILD.bazel",
"@external_repo//:gen",
],
)
'''
# build //:example_extern_ref
BUILD.bazel
bazel-out/cfg/bin/gen
external/+_repo_rules+external_repo/BUILD.bazel
bazel-out/cfg/bin/external/+_repo_rules+external_repo/gen
[OUT] bazel-out/cfg/bin/example_extern_ref
# build //:example_extern_ref --config=sib
BUILD.bazel
bazel-out/cfg/bin/gen
../+_repo_rules+external_repo/BUILD.bazel
bazel-out/cfg/k8-fastbuild/bin/gen
[OUT] bazel-out/cfg/bin/example_extern_ref
# build @external_repo//:example_extern_ref
BUILD.bazel
bazel-out/cfg/bin/gen
external/+_repo_rules+external_repo/BUILD.bazel
bazel-out/cfg/bin/external/+_repo_rules+external_repo/gen
[OUT] bazel-out/cfg/bin/external/+_repo_rules+external_repo/example_extern_ref
# build @external_repo//:example_extern_ref --config=sib
BUILD.bazel
bazel-out/cfg/bin/gen
../+_repo_rules+external_repo/BUILD.bazel
bazel-out/cfg/k8-fastbuild/bin/gen
[OUT] bazel-out/cfg/k8-fastbuild/bin/example_extern_ref
- !!! path mapping is broken!
'''
list_inputs(
name = "example_exec",
exec_inputs = [
":genrule",
],
)
'''
# build @//:example_exec
bazel-out/cfg/bin/gen
[OUT] bazel-out/cfg/bin/example_exec
# build @//:example_exec --config=sib
bazel-out/cfg/bin/gen
[OUT] bazel-out/cfg/bin/example_exec
# build @external_repo//:example_exec
bazel-out/cfg/bin/external/+_repo_rules+external_repo/gen
[OUT] bazel-out/cfg/bin/external/+_repo_rules+external_repo/example_exec
# build @external_repo//:example_exec --config=sib
bazel-out/cfg/k8-opt-exec-ST-d57f47055a04/bin/gen
[OUT] bazel-out/cfg/k8-fastbuild/bin/example_exec
- !!! path mapping is broken!
'''
list_inputs(
name = "example_both",
inputs = [
":gen",
],
exec_inputs = [
":gen",
]
)
'''
# build @//:example_both
bazel-out/k8-fastbuild/bin/gen
bazel-out/k8-opt-exec-ST-d57f47055a04/bin/gen
[OUT] bazel-out/k8-fastbuild/bin/example_both
# build @//:example_both --config=sib
bazel-out/k8-fastbuild/bin/gen
bazel-out/k8-opt-exec-ST-d57f47055a04/bin/gen
[OUT] bazel-out/k8-fastbuild/bin/example_both
# build @external_repo//:example_both
bazel-out/k8-fastbuild/bin/external/+_repo_rules+external_repo/gen
bazel-out/k8-opt-exec-ST-d57f47055a04/bin/external/+_repo_rules+external_repo/gen
[OUT] bazel-out/k8-fastbuild/bin/external/+_repo_rules+external_repo/example_both
# build @external_repo//:example_both --config=sib
bazel-out/cfg/k8-fastbuild/bin/gen
bazel-out/cfg/k8-opt-exec-ST-d57f47055a04/bin/gen
[OUT] bazel-out/cfg/k8-fastbuild/bin/example_both
- !!! path mapping is broken! we don't fall back here since there is no
conflict... that happens because the wrong part of the path is being
stripped (not the configuration name)
'''
# NOTE: path-mapping gets silently disabled since there would be a conflict in
# the mapped paths (for `:gen`)
def _impl(ctx):
out = ctx.actions.declare_file(ctx.attr.name)
ctx.actions.run_shell(
inputs = ctx.files.inputs + ctx.files.exec_inputs,
outputs = [out],
arguments = [
# TODO: what happens if unlisted?
ctx.actions.args().add(out).add_all(ctx.files.inputs).add_all(ctx.files.exec_inputs)
],
command = 'for p in "${@:2}"; do echo "$p"; done > "$1"; cat "$1"; echo "[OUT] $1"',
execution_requirements = {
"supports-path-mapping": "1",
}
)
return [DefaultInfo(files = depset([out]))]
list_inputs = rule(
implementation = _impl,
attrs = dict(
inputs = attr.label_list(allow_files = True),
exec_inputs = attr.label_list(allow_files = True, cfg = config.exec()),
),
)
module(name = "experiment", repo_name = "foo")
local = use_repo_rule("@bazel_tools//tools/build_defs/repo:local.bzl", "local_repository")
local(
name = "external_repo",
path = ".",
)
{
"folders": [{
"path": "."
}],
"extensions": {
"recommendations": ["BazelBuild.vscode-bazel"],
},
"settings": {
"bazel.lsp.command": "starpls",
"bazel.lsp.args": ["server", "--experimental_infer_ctx_attributes"]
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment