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