Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding expansion.bzl for Fully Expanding Environment Variables #486

Open
wants to merge 19 commits into
base: main
Choose a base branch
from

Conversation

CauhxMilloy
Copy link

@CauhxMilloy CauhxMilloy commented Jan 23, 2024

The built-in functionality (exposed Skylark methods on ctx) for expanding environment variables leaves a lot of implementation to do in bzl files. This PR adds in that missing functionality for easy reuse with a new expansion struct. expansion has various methods for expanding environment variables with a flexible API.

The existing APIs handle only one piece at a time:

  • ctx.expand_location() only handles $(location ...) (and similar) expansion. It does not handle any "make variable" expansion (no expansion from TemplateVariableInfo or other providers via toolchains).
  • ctx.expand_make_variables() only handles make variables. If it encounters $(location ...) (or similar), it errors out with no means of recovery. This method is also marked as deprecated, with ctx.var listed as the preferred API.
  • ctx.var is a simple dictionary, which contains resolved mappings based on toolchains. However, being a simple data structure (not a function) leaves recursive functionality and/or integration with ctx.expand_location() to be implemented by hand (or in this PR).

Many internal systems make use of functionality that fully resolves both make variables and $(location ...) (and does so recursively). This is done with ruleContext.getExpander().withDataLocations() (example). However, this is never exposed to skylark. This means that built-in rules will have the env attribute fully expanded, but custom rules cannot (easily).

The above mentioned methods have their own (somewhat duplicated) implementations (ctx.expand_location(), ctx.expand_make_variables(), ctx.var). Note the identical ConfigurationMakeVariableContext initialization here and here -- identical except for the use of additionalSubstitutionsMap, which could have been delegated (similar to the impl of LocationTemplateContext). Also note the separate/duplicated parsing implementations in LocationTemplateContext and in LocationExpander.

This PR tries to avoid more duplicate implementations (which can't really happen anyway; as this is in external Skylark, not Java / Bazel runtime). There is one extra implementation to mention (in cc_helper), used by cc_binary(). This is referenced in Skylark here, and implemented here. This implementation is also not consistent with the above listed Java-based implementations and it is also not accessible/exposed to other bzl files (can't access cc_helper from @rules_cc//). This implementation is done differently, as to allow for all the listed functionality.

PR details:

  • These new methods make use of ctx.expand_location() and ctx.var to allow fully recursive variable expansion that incorporates both inputs (toolchains and location).
  • The methods avoid copies/string mutations/extra loops when necessary to ensure that the functions can run quickly.
  • The methods' API allows for manual/direct data structures (lists/dicts) as input, or for pulling values directly from ctx.
  • Supports $VAR, $(VAR), and ${VAR} for variable expansion.
  • Rechecks env dict for recursion (not only checking location/toolchains).
  • Adds validation logic to check that all (non-escaped) variables have been properly expanded.
  • tests/expansion_tests.bzl added to test all added methods.

This PR is being submitted here so that it can be reused (instead of copied in many repos). It is also preferable to add functionality here in Skylib, instead of (or in addition to) any changes in the Bazel repo so that it can be more easily pulled into projects with a pinned Bazel version.

Please feel free to leave comments or suggestion on ways to improve this PR, or let me know if you have any questions. Thanks!

The built-in functionality (exposed Skylark methods on `ctx`) for expanding environment variables leaves a lot of implementation to do in `bzl` files. This PR adds in that missing functionality for easy reuse with a new `expansion` struct. `expansion` has various methods for expanding environment variables with a flexible API.

The existing APIs handle only one piece at a time:
* `ctx.expand_location()` only handles `$(location ...)` (and similar) expansion. It does not handle any "make variable" expansion (no expansion from `TemplateVariableInfo` or other providers via toolchains).
* `ctx.expand_make_variables()` only handles make variables. If it encounters `$(location ...)` (or similar), it [errors out with no means of recovery](https://github.com/bazelbuild/bazel/blob/addf41bce1f26e8bc4bd0b09cb2fbffbb0446f25/src/main/java/com/google/devtools/build/lib/analysis/ConfigurationMakeVariableContext.java#L136). This method is also marked as deprecated, with `ctx.var` listed as the preferred API.
* `ctx.var` is a simple dictionary, which contains resolved mappings based on toolchains. However, being a simple data structure (not a function) leaves recursive functionality and/or integration with `ctx.expand_location()` to be implemented by hand (or in this PR).

Many internal systems make use of functionality that fully resolves both make variables and `$(location ...)` (and does so recursively). This is done with `ruleContext.getExpander().withDataLocations()` ([example](https://github.com/bazelbuild/bazel/blob/addf41bce1f26e8bc4bd0b09cb2fbffbb0446f25/src/main/java/com/google/devtools/build/lib/analysis/RunfilesSupport.java#L522)). However, this is never exposed to skylark. This means that built-in rules will have the `env` attribute fully expanded, but custom rules cannot (easily).

The above mentioned methods have their own (somewhat duplicated) implementations ([`ctx.expand_location()`](https://github.com/bazelbuild/bazel/blob/addf41bce1f26e8bc4bd0b09cb2fbffbb0446f25/src/main/java/com/google/devtools/build/lib/analysis/starlark/StarlarkRuleContext.java#L1011), [`ctx.expand_make_variables()`](https://github.com/bazelbuild/bazel/blob/addf41bce1f26e8bc4bd0b09cb2fbffbb0446f25/src/main/java/com/google/devtools/build/lib/analysis/starlark/StarlarkRuleContext.java#L964), [`ctx.var`](https://github.com/bazelbuild/bazel/blob/addf41bce1f26e8bc4bd0b09cb2fbffbb0446f25/src/main/java/com/google/devtools/build/lib/analysis/starlark/StarlarkRuleContext.java#L823)). Note the identical `ConfigurationMakeVariableContext` initialization [here](https://github.com/bazelbuild/bazel/blob/addf41bce1f26e8bc4bd0b09cb2fbffbb0446f25/src/main/java/com/google/devtools/build/lib/analysis/starlark/StarlarkRuleContext.java#L949) and [here](https://github.com/bazelbuild/bazel/blob/36fa60b1805faa7da2c4b5330b4b186740f5f00d/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java#L978) -- identical except for the use of `additionalSubstitutionsMap`, which could have been delegated (similar to the impl of `LocationTemplateContext`). Also note the separate/duplicated parsing implementations in [`LocationTemplateContext`](https://github.com/bazelbuild/bazel/blob/addf41bce1f26e8bc4bd0b09cb2fbffbb0446f25/src/main/java/com/google/devtools/build/lib/analysis/stringtemplate/TemplateExpander.java#L77) and in [`LocationExpander`](https://github.com/bazelbuild/bazel/blob/36fa60b1805faa7da2c4b5330b4b186740f5f00d/src/main/java/com/google/devtools/build/lib/analysis/LocationExpander.java#L179).

This PR tries to avoid more duplicate implementations (which can't really happen anyway; as this is in external Skylark, not Java / Bazel runtime). These new methods make use of `ctx.expand_location()` and `ctx.var` to allow fully recursive variable expansion that incorporates both inputs (toolchains and `location`). The methods avoid copies/string mutations/extra loops when necessary to ensure that the functions can run quickly. The methods' API allows for manual/direct data structures (lists/dicts) as input, or for pulling values directly from `ctx`. `tests/expansion_tests.bzl` added to test all added methods.

This PR is being submitted here so that it can be reused (instead of copied in many repos). It is also preferable to add functionality here in Skylib, instead of (or in addition to) any changes in the [Bazel repo](https://github.com/bazelbuild/bazel) so that it can be more easily pulled into projects with a pinned Bazel version.

Please feel free to leave comments or suggestion on ways to improve this PR, or let me know if you have any questions. Thanks!
…nd `_expand_tc_and_loc_all_keys_in_str()` into `_expand_all_keys_in_str()` with None-able arg).

* Updating handling / init of optional `additional_lookup_dict` arg.
* Cleaning up some doc comments.
* Updating tests.
  * Adding demonstration where `additional_lookup_dict` overrides value from toolchains.
  * Adding demonstration where recursive expansion involves env dict and toolchain dict back and forth.
  * Adding a few more section separator comments.
  * Adding `_validate_all_keys_expanded()` to validate there are no expandable keys in a given string.
    * Will either call `fail()` or return list of failures (depending on `fail_instead_of_return` parameter).
    * Failure messages will contain the parsed unexpanded variable and the whole string which contains it.
  * Adding optional `validate_expansion` parameter to allow automatic validation after the expansion process.
  * Adding exposed `validate_expansions` to allow the client to call the functionality directly.
  * Adding new tests for validation logic.
    * Parameterized over many different configurations.
    * All tests pass in <= 0.1s each.
* Pulling out `_CONSIDERED_KEY_FORMATS` into file-scoped constant.
* Pulling out "odd count dollar sign" logic into `_odd_count_dollar_sign_repeat()`.
* Updating some variable names and adding more comments.
CauhxMilloy added a commit to CauhxMilloy/bazel-bats that referenced this pull request Jan 29, 2024
* Adding `expansion.bzl` directly to this project.
  * Allows for better environment variable expansion than built-in skylark methods.
  * I'm also trying to submit this to Skylib (bazelbuild/bazel-skylib#486).
    * I could add this here (instead or in addition to).
    * Obviously, (also) adding this to Skylib would be more reusable for more repos.
    * Once submitted to Skylib (if accepted), this addition of `expansion.bzl` could be replaced with referencing the one in Skylib.
      * Doing this would add an extra dependency for bazel-bats (needing to pull in Skylib in `bazel_bats_dependencies()`).
      * Alternatively, could just keep this file here (for no extra dependency).
* Updating `env` attribute expansion logic in `_bats_test_impl()`.
  * Allows better environment expansion logic than previously used.
* Updating `tests/hello_world.bats` to validate many different environment variable expansion configurations.
CauhxMilloy added a commit to CauhxMilloy/bazel-bats that referenced this pull request Jan 29, 2024
This is one approach for improving `bats_test` environment variable expansion logic (to get at least parity with built-in expansion logic).
See https://github.com/CauhxMilloy/bazel-bats/tree/improvedExpansionWithShTest for `sh_test` approach.

* Adding `expansion.bzl` directly to this project.
  * Allows for better environment variable expansion than built-in skylark methods.
  * I'm also trying to submit this to Skylib (bazelbuild/bazel-skylib#486).
    * I could add this here (instead or in addition to).
    * Obviously, (also) adding this to Skylib would be more reusable for more repos.
    * Once submitted to Skylib (if accepted), this addition of `expansion.bzl` could be replaced with referencing the one in Skylib.
      * Doing this would add an extra dependency for bazel-bats (needing to pull in Skylib in `bazel_bats_dependencies()`).
      * Alternatively, could just keep this file here (for no extra dependency).
* Updating `env` attribute expansion logic in `_bats_test_impl()`.
  * Allows better environment expansion logic than previously used.
* Updating `tests/hello_world.bats` to validate many different environment variable expansion configurations.
CauhxMilloy added a commit to CauhxMilloy/bazel-bats that referenced this pull request Jan 30, 2024
* Adding `expansion.bzl` directly to this project.
  * Allows for better environment variable expansion than built-in skylark methods.
  * I'm also trying to submit this to Skylib (bazelbuild/bazel-skylib#486).
    * I could add this here (instead or in addition to).
    * Obviously, (also) adding this to Skylib would be more reusable for more repos.
    * Once submitted to Skylib (if accepted), this addition of `expansion.bzl` could be replaced with referencing the one in Skylib.
      * Doing this would add an extra dependency for bazel-bats (needing to pull in Skylib in `bazel_bats_dependencies()`).
      * Alternatively, could just keep this file here (for no extra dependency).
* Updating `env` attribute expansion logic in `_bats_test_impl()`.
  * Allows better environment expansion logic than previously used.
* Updating `tests/hello_world.bats` to validate many different environment variable expansion configurations.

This is one approach for improving `bats_test` environment variable expansion logic (to get at least parity with built-in expansion logic).
See https://github.com/CauhxMilloy/bazel-bats/tree/improvedExpansionWithShTest for `sh_test` approach.
CauhxMilloy added a commit to CauhxMilloy/bazel-bats that referenced this pull request Jan 30, 2024
* Adding `expansion.bzl` directly to this project.
  * Allows for better environment variable expansion than built-in skylark methods.
  * I'm also trying to submit this to Skylib (bazelbuild/bazel-skylib#486).
    * I could add this here (instead or in addition to).
    * Obviously, (also) adding this to Skylib would be more reusable for more repos.
    * Once submitted to Skylib (if accepted), this addition of `expansion.bzl` could be replaced with referencing the one in Skylib.
      * Doing this would add an extra dependency for bazel-bats (needing to pull in Skylib in `bazel_bats_dependencies()`).
      * Alternatively, could just keep this file here (for no extra dependency).
* Updating `env` attribute expansion logic in `_bats_test_impl()`.
  * Allows better environment expansion logic than previously used.
* Updating `tests/hello_world.bats` to validate many different environment variable expansion configurations.

This is one approach for improving `bats_test` environment variable expansion logic (to get at least parity with built-in expansion logic).
See https://github.com/CauhxMilloy/bazel-bats/tree/improvedExpansionWithShTest for `sh_test` approach.
CauhxMilloy added a commit to CauhxMilloy/bazel-bats that referenced this pull request Jan 30, 2024
* Adding `expansion.bzl` directly to this project.
  * Allows for better environment variable expansion than built-in skylark methods.
  * I'm also trying to submit this to Skylib (bazelbuild/bazel-skylib#486).
    * I could add this here (instead or in addition to).
    * Obviously, (also) adding this to Skylib would be more reusable for more repos.
    * Once submitted to Skylib (if accepted), this addition of `expansion.bzl` could be replaced with referencing the one in Skylib.
      * Doing this would add an extra dependency for bazel-bats (needing to pull in Skylib in `bazel_bats_dependencies()`).
      * Alternatively, could just keep this file here (for no extra dependency).
* Updating `env` attribute expansion logic in `_bats_test_impl()`.
  * Allows better environment expansion logic than previously used.
* Updating `tests/hello_world.bats` to validate many different environment variable expansion configurations.

This is one approach for improving `bats_test` environment variable expansion logic (to get at least parity with built-in expansion logic).
See https://github.com/CauhxMilloy/bazel-bats/tree/improvedExpansionWithShTest for `sh_test` approach.
filmil pushed a commit to filmil/bazel-bats that referenced this pull request Feb 3, 2024
* Adding `expansion.bzl` directly to this project.
  * Allows for better environment variable expansion than built-in skylark methods.
  * I'm also trying to submit this to Skylib (bazelbuild/bazel-skylib#486).
    * I could add this here (instead or in addition to).
    * Obviously, (also) adding this to Skylib would be more reusable for more repos.
    * Once submitted to Skylib (if accepted), this addition of `expansion.bzl` could be replaced with referencing the one in Skylib.
      * Doing this would add an extra dependency for bazel-bats (needing to pull in Skylib in `bazel_bats_dependencies()`).
      * Alternatively, could just keep this file here (for no extra dependency).
* Updating `env` attribute expansion logic in `_bats_test_impl()`.
  * Allows better environment expansion logic than previously used.
* Updating `tests/hello_world.bats` to validate many different environment variable expansion configurations.

This is one approach for improving `bats_test` environment variable expansion logic (to get at least parity with built-in expansion logic).
See https://github.com/CauhxMilloy/bazel-bats/tree/improvedExpansionWithShTest for `sh_test` approach.
…o use `dict(dict).update(dict)` to be backwards compatible with Bazel 5.x.
…e backwards compatible with Bazel 5.x.

  * `dict(dict).update(dict)` is not a good replacement as it returns `NoneType`.
  * Renaming all `expand_with_*` functions to `expand_dict_strings_with_*`.
    * Explicit that it acts on a dict of strings.
  * Adding `expand_list_strings_with_manual_dict` and `expand_list_strings_with_manual_dict_and_location` functions.
    * Similar to the existing `expand_dict_strings_with_manual_dict*` functions, these expand values but from a list of strings instead of dict of strings.
    * Note that these methods only perform recursion via the "manual dict" (the input datatype, `list`, is not an associative type).
    * Simpler API for just expanding a few string values (of any attr type, or not necessarily associated with any attr).
  * Renaming `expand_location` parameter(s) to `wrapped_expand_location`.
    * This makes it more clear that this is not `ctx.expand_location` directly, but rather a wrapped version of that function.
  * Adding `wrap_expand_location` method.
    * Offers a helper function for creating `wrapped_expand_location` (used by many expansion functions) from a given `ctx` and `deps`.
  * Updating doc string comments.
* Updating `tests/bzl/expansion_tests.bzl`.
  * Renaming all `expand_with_*` tests to `expand_dict_strings_with_*`.
  * Adding `expand_list_strings_with_*` tests (for new functions).
  * Adding "no recursion" "expected resolved" dicts for new tests.
  * Adding `TOOLCHAIN_INDIRECT_ENV_VAR*` values to expand, which show recursion specifically among only toolchain values.
@CauhxMilloy CauhxMilloy requested a review from hvadehra as a code owner July 1, 2024 02:11
@comius
Copy link
Collaborator

comius commented Jul 12, 2024

Wow, this is one nice PR. It's a shame it's here for 1.5 years.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants