4.4.5 Legacy Runfiles Split
extraNew rules should treat runtime files as one contract: build a ctx.runfiles(...) object and return it with DefaultInfo(runfiles = ...). The legacy split into data_runfiles and default_runfiles still matters when you maintain older rulesets, because a target can appear to work in one runtime position and then lose files when used through another attribute.1 The modern model is covered in 4.2.5 DefaultInfo & Runfiles. This article is the debugging note for the old shape.
The Three Slots
DefaultInfo still exposes three runfiles-related constructor parameters.2
| Parameter | What it means now |
|---|---|
runfiles | The unified runtime file descriptor for new rule implementations. |
data_runfiles | Legacy descriptor used when the target is reached through a data attribute. |
default_runfiles | Legacy descriptor used when the target is run normally or used through attributes other than data. |
The important point is not that new rules should carefully choose between data_runfiles and default_runfiles. New rules should avoid both legacy constructor parameters and use DefaultInfo(runfiles = ...) instead.3 The DefaultInfo API carries the same warning on both legacy parameters.4
Why The Split Is Confusing
The split tried to distinguish "files needed when this is runtime data" from "files needed when this is a normal dependency or tool." In practice, that makes the consuming position affect which runtime files appear. The distinction is legacy and carries a real compatibility trap: some rules put their default outputs in data_runfiles but not in default_runfiles.5
That is why a migration should not be a mechanical rename from data_runfiles to runfiles. First decide the runtime contract the target should have. Then include the rule's own runtime files, include default outputs when the executable needs them, and merge the dependency runfiles that should propagate.6
def _impl(ctx):
runfiles = ctx.runfiles(files = ctx.files.data)
transitive = []
for dep in ctx.attr.data + ctx.attr.deps:
transitive.append(dep[DefaultInfo].default_runfiles)
return [DefaultInfo(runfiles = runfiles.merge_all(transitive))]
This uses the old default_runfiles field only as a read-side compatibility surface: dependencies expose their already-computed runtime files there. The returned provider still uses the unified runfiles parameter.7
Avoid The Other Legacy Shortcut
The same cleanup applies to ctx.runfiles(collect_data = True) and ctx.runfiles(collect_default = True). Those switches implicitly collect runfiles across hardcoded srcs, data, and deps edges, and both are not recommended.8 Add direct files with files or transitive_files, or explicitly merge dependency runfiles instead.9
A common runfiles helper shows the useful shape: it collects direct data files and pulls transitive runtime files from dependency DefaultInfo.default_runfiles, then the rule returns that object through DefaultInfo(runfiles = ...).10 The exact helper can vary, but the public surface should stay boring: one runfiles object returned from one place.
Debugging Missing Runtime Files
When a legacy rule loses files at runtime, ask two questions before changing code:
- Which attribute path is the target taking:
data,deps,tools, or directbazel run? - Which
DefaultInfofield did the producer populate: unifiedrunfiles, legacydata_runfiles, or legacydefault_runfiles?
If the file appears only when the target is listed in data, suspect a producer that populated data_runfiles but not the normal runtime path. If a tool works when run directly but fails when another action uses it, check whether the consuming rule passed the executable as a tool with its files-to-run metadata rather than just passing a raw executable path. That tool-runfiles bundle is part of the DefaultInfo runtime contract from 4.2.5 DefaultInfo & Runfiles.11
Migration is safest when it is covered by an integration test from 4.5.2 Ruleset Integration Tests: build a tiny fixture where a binary depends on a library with data, then run it through the same attribute path that used to fail. A provider-only unit test can prove fields are populated, but a bazel run or bazel test fixture proves the files actually reach the runtime environment.
Use DefaultInfo(runfiles = ...) for new and migrated rules. Read DefaultInfo.default_runfiles from dependencies when you need to merge their runtime files, but do not return the legacy split unless you are preserving compatibility for an old API.
If runtime files disappear only in some consuming positions, debug the old split before blaming sandboxing or path lookup.
Check your understanding · 3 questions
1.An older rule returns DefaultInfo(data_runfiles = a, default_runfiles = b). A bug report says runtime files appear when the target is consumed via data but disappear when consumed via deps or run directly. What is the right migration step?
Select one answer
2.Which symptoms or practices point to the legacy runfiles split?
Select all that apply
3.True or false: runfiles migration rules of thumb.
Choose True or False for each sentence
dep[DefaultInfo].default_runfiles.data_runfiles to runfiles is always safe.collect_data and collect_default are the recommended modern way to build runfiles.Footnotes
-
Rules — runfiles features to avoid and legacy split guidance. ↩
-
DefaultInfo — constructor parameters for
runfiles,data_runfiles, anddefault_runfiles. ↩ -
Rules — recommendation to avoid
data_runfilesanddefault_runfiles. ↩ -
DefaultInfo — API warnings on legacy runfiles parameters. ↩
-
Rules — legacy distinction and default-output trap for
data_runfiles. ↩ -
Rules — recommended migration shape: include default outputs and merge dependency default runfiles. ↩
-
Rules — retrieve dependency runfiles from
DefaultInfo.default_runfiles, notdata_runfiles. ↩ -
ctx —
collect_dataandcollect_defaultparameters are not recommended. ↩ -
Rules — prefer explicit
files,transitive_files, and dependency runfiles merging. ↩ -
Writing Bazel rules: data and runfiles — practical helper using
DefaultInfo.default_runfilesand returning unifiedrunfiles. ↩ -
DefaultInfo —
files_to_runcontains executable and runfiles metadata. ↩