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