xref: /aosp_15_r20/external/bazelbuild-rules_python/python/private/envsubst.bzl (revision 60517a1edbc8ecf509223e9af94a7adec7d736b8)
1# Copyright 2024 The Bazel Authors. All rights reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15"""Substitute environment variables in shell format strings."""
16
17def envsubst(template_string, varnames, getenv):
18    """Helper function to substitute environment variables.
19
20    Supports `$VARNAME`, `${VARNAME}` and `${VARNAME:-default}`
21    syntaxes in the `template_string`, looking up each `VARNAME`
22    listed in the `varnames` list in the environment defined by the
23    `getenv` function. Typically called with `getenv = rctx.getenv`
24    (if it is available) or `getenv = rctx.os.environ.get` (on e.g.
25    Bazel 6 or Bazel 7, which don't have `rctx.getenv` yet).
26
27    Limitations: Unlike the shell, we don't support `${VARNAME}` and
28    `${VARNAME:-default}` in the default expression for a different
29    environment variable expansion. We do support the braceless syntax
30    in the default, so an expression such as `${HOME:-/home/$USER}` is
31    valid.
32
33    Args:
34      template_string: String that may contain variables to be expanded.
35      varnames: List of variable names of variables to expand in
36        `template_string`.
37      getenv: Callable mapping variable names (in the first argument)
38        to their values, or returns the default (provided in the
39        second argument to `getenv`) if a value wasn't found.
40
41    Returns:
42      `template_string` with environment variables expanded according
43      to their values as determined by `getenv`.
44    """
45
46    if not varnames:
47        return template_string
48
49    for varname in varnames:
50        value = getenv(varname, "")
51        template_string = template_string.replace("$%s" % varname, value)
52        template_string = template_string.replace("${%s}" % varname, value)
53        segments = template_string.split("${%s:-" % varname)
54        template_string = segments.pop(0)
55        for segment in segments:
56            default_value, separator, rest = segment.partition("}")
57            if "{" in default_value:
58                fail("Environment substitution expression " +
59                     "\"${%s:-\" has an opening \"{\" " % varname +
60                     "in default value \"%s\"." % default_value)
61            if not separator:
62                fail("Environment substitution expression " +
63                     "\"${%s:-\" is missing the final \"}\"" % varname)
64            template_string += (value if value else default_value) + rest
65    return template_string
66