| # Path mapping |
| |
| It is not unusual for Bazel build actions for different compilation modes to be |
| substantively identical, and yet unable to share a cached result due to |
| superficial differences in output paths. For example, a `fastbuild` result |
| might land in `bazel-out/k8-fastbuild/bin` whereas an `opt` result lands in |
| `bazel-out/k8-opt/bin`. This can result in a lot of wasted work, with Bazel |
| needlessly repeating the same action for each compilation mode even when there |
| is no real difference in the work being done. |
| |
| Fortunately, Bazel supports a feature called [path |
| mapping](https://github.com/bazelbuild/bazel/discussions/22658) that addresses |
| this issue. When path mapping is active, Bazel temporarily collapses all bin |
| directories into a single one called `bazel-out/cfg/bin` during the action. |
| This way, build actions for different compilation modes or even CPU platforms |
| can share a cached result, provided that the actions are otherwise identical. |
| |
| ## Enabling path mapping |
| |
| Getting path mapping set up requires both enabling it via command-line flags at |
| the Bazel level and then ensuring that specific targets are compatible with it. |
| |
| At the Bazel level: |
| - Pass the `--experimental_output_paths=strip` flag to Bazel |
| - Use either remote caching or `--disk_cache`. Counterintuitively, Bazel's |
| default caching will otherwise not benefit from path mapping. |
| |
| The `js_run_binary` macro will automatically enable path mapping if it |
| determines it is safe to do so, but you may need to explicitly opt in (see |
| below). To set up a `js_run_binary` target for path mapping: |
| - Pass `set_legacy_environment_variables = False`. This will disable the |
| setting of environment variables such as `BAZEL_COMPILATION_MODE` that would |
| otherwise be problematic. Alternatively, you can change the default for all |
| targets by passing `--@aspect_rules_js//js:set_legacy_environment_variables=False` |
| on the command line; individual targets can still override this default by |
| setting the attribute explicitly. |
| - Avoid using [Make |
| variables](https://bazel.build/reference/be/make-variables) or expressions |
| such as `$(execpath :target)`. Such variables and expressions are not |
| path-mapping-aware and may be unsafe. |
| - If you need to use a Make variable or an expression such as `$(rootpath |
| :target)` but you know that it is safe with respect to path mapping, you can |
| explicitly opt in with `execution_requirements = {"supports-path-mapping": |
| "1"}`. This is fine as long as you know the expression will not include |
| `bazel-out/<platform dir>`, which would be incorrect when path mapping is |
| active. |
| |
| Note that opting in with `execution_requirements` cannot rescue a path passed |
| through the action environment. Bazel only rewrites paths that reach an action |
| via an [Args](https://bazel.build/rules/lib/builtins/Args) object; environment |
| variables are never rewritten. An environment variable holding an |
| `$(execpath)`-expanded path would still point at `bazel-out/<platform dir>/bin` |
| while the action runs against `bazel-out/cfg/bin`. |
| |
| For custom rules that invoke a `js_binary`: |
| - Use the helper function `js_binary_lib.run_binary_action()` from |
| `@aspect_rules_js//js:libs.bzl`. This is a thin wrapper around |
| `ctx.actions.run()` that handles some internal implementation details in a |
| path-mapping-friendly way. |
| - Set `execution_requirements = {"supports-path-mapping": "1"}` to opt into |
| path mapping. |
| - Make sure that any paths involving `bazel-out/` are mapped correctly. The |
| only way to do this is to call `args.add()` or `args.add_all()` on an |
| [Args](https://bazel.build/rules/lib/builtins/Args) object from |
| `ctx.actions.args()`. If you need to do any additional munging of the path, |
| it must be done in a `map_each` callback passed to |
| [args.add_all()](https://bazel.build/rules/lib/builtins/Args#add_all). |