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