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
2 changes: 1 addition & 1 deletion MODULE.bazel
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module(
name = "rules_host_cache_key",
version = "0.0.3",
version = "0.0.4",
)

bazel_dep(name = "platforms", version = "1.1.0")
15 changes: 8 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ key, so:
Add to `MODULE.bazel`:

```starlark
bazel_dep(name = "rules_host_cache_key", version = "0.0.3")
bazel_dep(name = "rules_host_cache_key", version = "0.0.4")

host_cache_key = use_repo_rule("@rules_host_cache_key//:defs.bzl", "host_cache_key")
host_cache_key(name = "host_cache_key")
Expand Down Expand Up @@ -80,12 +80,13 @@ The selected directory remains in the key because host tools can record its path

## When the key changes

The rule asks Bazel to watch everything it read, except search path entries
and listings on the macOS system volume: those change only with the OS build,
which Bazel watches instead. When any of it changes, Bazel recomputes the key at
its next command, including in a server that is already running, and every
action whose key changed runs again or comes from a cache entry made under the
same key.
The rule asks Bazel to watch everything it read, except search path entries and
listings on the macOS system volume: those change only with the OS build, which
Bazel watches instead. It also watches its own `host_key.py`, so upgrading this
module brings an existing output base onto the new release's key and watches.
When any of it changes, Bazel recomputes the key at its next command, including
in a server that is already running, and every action whose key changed runs
again or comes from a cache entry made under the same key.

Bazel reads each watched file in full when a new server first uses the
repository. Leaving the system volume out spares that check a read of every
Expand Down
8 changes: 7 additions & 1 deletion defs.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,13 @@ def _host_cache_key_impl(rctx):
interpreter = rctx.path(python) if python.startswith("/") else rctx.which(python)
if interpreter == None or not interpreter.exists:
fail("host_cache_key: no Python interpreter at %r; set the `python` attribute" % python)
command = [interpreter, rctx.path(Label("//:host_key.py"))]
script = Label("//:host_key.py")

# Bazel records nothing for a file reached through rctx.path. Unwatched, an
# upgrade that changes only host_key.py would leave an existing output base
# on the old script's key and watches until some other watched path changed.
rctx.watch(script)
command = [interpreter, rctx.path(script)]
for directory in rctx.attr.search_path:
command.extend(["--search-path", directory])
for prefix in rctx.attr.homebrew_prefixes:
Expand Down
25 changes: 23 additions & 2 deletions tests/check_platform.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@
)
"""

# Stands in for a release that changed only host_key.py: a report under the
# rule's interface naming another key.
UPGRADED_SCRIPT = """\
import json

print(json.dumps({"key": "upgraded", "fields": {}, "watch_files": [], "watch_directories": []}))
"""


def consumer_module(
module_root: Path, tools: Path, prefix: Path | None, developer_dir: str = ""
Expand Down Expand Up @@ -154,6 +162,12 @@ def expect(step: str, stderr: str, wanted: str) -> None:


def check_watched_changes(bazel: str, module_root: Path, scratch: Path) -> None:
# A copy, so a step can upgrade the module's own script. Convenience links
# into an output base would be followed and copied whole.
module = scratch / "module"
shutil.copytree(
module_root, module, ignore=shutil.ignore_patterns(".git", "bazel-*")
)
# The search path names the tools through a link to their directory.
tools = scratch / "tools"
(scratch / "tools-one").mkdir()
Expand All @@ -166,9 +180,9 @@ def check_watched_changes(bazel: str, module_root: Path, scratch: Path) -> None:
write_sbom(keg, BOTTLE)
workspace = scratch / "consumer"
workspace.mkdir()
shutil.copy(module_root / ".bazelversion", workspace / ".bazelversion")
shutil.copy(module / ".bazelversion", workspace / ".bazelversion")
(workspace / "BUILD.bazel").write_text(CONSUMER_BUILD)
(workspace / "MODULE.bazel").write_text(consumer_module(module_root, tools, prefix))
(workspace / "MODULE.bazel").write_text(consumer_module(module, tools, prefix))

consumer = Consumer(bazel, workspace, scratch / "disk-cache")
first, second = scratch / "first-output-base", scratch / "second-output-base"
Expand Down Expand Up @@ -209,6 +223,13 @@ def check_watched_changes(bazel: str, module_root: Path, scratch: Path) -> None:
consumer.build(second),
"executed",
)

# Upgrading this module can change host_key.py and nothing else; the
# key follows only because the rule watches its own script.
(module / "host_key.py").write_text(UPGRADED_SCRIPT)
expect(
"host_key.py upgraded, running server", consumer.build(second), "executed"
)
finally:
consumer.shutdown()

Expand Down
Loading