1*333d2b36SAndroid Build Coastguard Worker// Copyright 2016 Google Inc. All rights reserved. 2*333d2b36SAndroid Build Coastguard Worker// 3*333d2b36SAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License"); 4*333d2b36SAndroid Build Coastguard Worker// you may not use this file except in compliance with the License. 5*333d2b36SAndroid Build Coastguard Worker// You may obtain a copy of the License at 6*333d2b36SAndroid Build Coastguard Worker// 7*333d2b36SAndroid Build Coastguard Worker// http://www.apache.org/licenses/LICENSE-2.0 8*333d2b36SAndroid Build Coastguard Worker// 9*333d2b36SAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software 10*333d2b36SAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS, 11*333d2b36SAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*333d2b36SAndroid Build Coastguard Worker// See the License for the specific language governing permissions and 13*333d2b36SAndroid Build Coastguard Worker// limitations under the License. 14*333d2b36SAndroid Build Coastguard Worker 15*333d2b36SAndroid Build Coastguard Workerpackage android 16*333d2b36SAndroid Build Coastguard Worker 17*333d2b36SAndroid Build Coastguard Workerimport ( 18*333d2b36SAndroid Build Coastguard Worker "fmt" 19*333d2b36SAndroid Build Coastguard Worker "strings" 20*333d2b36SAndroid Build Coastguard Worker "unicode" 21*333d2b36SAndroid Build Coastguard Worker 22*333d2b36SAndroid Build Coastguard Worker "github.com/google/blueprint/proptools" 23*333d2b36SAndroid Build Coastguard Worker) 24*333d2b36SAndroid Build Coastguard Worker 25*333d2b36SAndroid Build Coastguard Worker// ExpandNinjaEscaped substitutes $() variables in a string 26*333d2b36SAndroid Build Coastguard Worker// $(var) is passed to mapping(var), which should return the expanded value, a bool for whether the result should 27*333d2b36SAndroid Build Coastguard Worker// be left unescaped when using in a ninja value (generally false, true if the expanded value is a ninja variable like 28*333d2b36SAndroid Build Coastguard Worker// '${in}'), and an error. 29*333d2b36SAndroid Build Coastguard Worker// $$ is converted to $, which is escaped back to $$. 30*333d2b36SAndroid Build Coastguard Workerfunc ExpandNinjaEscaped(s string, mapping func(string) (string, bool, error)) (string, error) { 31*333d2b36SAndroid Build Coastguard Worker return expand(s, true, mapping) 32*333d2b36SAndroid Build Coastguard Worker} 33*333d2b36SAndroid Build Coastguard Worker 34*333d2b36SAndroid Build Coastguard Worker// Expand substitutes $() variables in a string 35*333d2b36SAndroid Build Coastguard Worker// $(var) is passed to mapping(var), which should return the expanded value and an error. 36*333d2b36SAndroid Build Coastguard Worker// $$ is converted to $. 37*333d2b36SAndroid Build Coastguard Workerfunc Expand(s string, mapping func(string) (string, error)) (string, error) { 38*333d2b36SAndroid Build Coastguard Worker return expand(s, false, func(s string) (string, bool, error) { 39*333d2b36SAndroid Build Coastguard Worker s, err := mapping(s) 40*333d2b36SAndroid Build Coastguard Worker return s, false, err 41*333d2b36SAndroid Build Coastguard Worker }) 42*333d2b36SAndroid Build Coastguard Worker} 43*333d2b36SAndroid Build Coastguard Worker 44*333d2b36SAndroid Build Coastguard Workerfunc expand(s string, ninjaEscape bool, mapping func(string) (string, bool, error)) (string, error) { 45*333d2b36SAndroid Build Coastguard Worker // based on os.Expand 46*333d2b36SAndroid Build Coastguard Worker buf := make([]byte, 0, 2*len(s)) 47*333d2b36SAndroid Build Coastguard Worker i := 0 48*333d2b36SAndroid Build Coastguard Worker for j := 0; j < len(s); j++ { 49*333d2b36SAndroid Build Coastguard Worker if s[j] == '$' { 50*333d2b36SAndroid Build Coastguard Worker if j+1 >= len(s) { 51*333d2b36SAndroid Build Coastguard Worker return "", fmt.Errorf("expected character after '$'") 52*333d2b36SAndroid Build Coastguard Worker } 53*333d2b36SAndroid Build Coastguard Worker buf = append(buf, s[i:j]...) 54*333d2b36SAndroid Build Coastguard Worker value, ninjaVariable, w, err := getMapping(s[j+1:], mapping) 55*333d2b36SAndroid Build Coastguard Worker if err != nil { 56*333d2b36SAndroid Build Coastguard Worker return "", err 57*333d2b36SAndroid Build Coastguard Worker } 58*333d2b36SAndroid Build Coastguard Worker if !ninjaVariable && ninjaEscape { 59*333d2b36SAndroid Build Coastguard Worker value = proptools.NinjaEscape(value) 60*333d2b36SAndroid Build Coastguard Worker } 61*333d2b36SAndroid Build Coastguard Worker buf = append(buf, value...) 62*333d2b36SAndroid Build Coastguard Worker j += w 63*333d2b36SAndroid Build Coastguard Worker i = j + 1 64*333d2b36SAndroid Build Coastguard Worker } 65*333d2b36SAndroid Build Coastguard Worker } 66*333d2b36SAndroid Build Coastguard Worker return string(buf) + s[i:], nil 67*333d2b36SAndroid Build Coastguard Worker} 68*333d2b36SAndroid Build Coastguard Worker 69*333d2b36SAndroid Build Coastguard Workerfunc getMapping(s string, mapping func(string) (string, bool, error)) (string, bool, int, error) { 70*333d2b36SAndroid Build Coastguard Worker switch s[0] { 71*333d2b36SAndroid Build Coastguard Worker case '(': 72*333d2b36SAndroid Build Coastguard Worker // Scan to closing brace 73*333d2b36SAndroid Build Coastguard Worker for i := 1; i < len(s); i++ { 74*333d2b36SAndroid Build Coastguard Worker if s[i] == ')' { 75*333d2b36SAndroid Build Coastguard Worker ret, ninjaVariable, err := mapping(strings.TrimSpace(s[1:i])) 76*333d2b36SAndroid Build Coastguard Worker return ret, ninjaVariable, i + 1, err 77*333d2b36SAndroid Build Coastguard Worker } 78*333d2b36SAndroid Build Coastguard Worker } 79*333d2b36SAndroid Build Coastguard Worker return "", false, len(s), fmt.Errorf("missing )") 80*333d2b36SAndroid Build Coastguard Worker case '$': 81*333d2b36SAndroid Build Coastguard Worker return "$", false, 1, nil 82*333d2b36SAndroid Build Coastguard Worker default: 83*333d2b36SAndroid Build Coastguard Worker i := strings.IndexFunc(s, unicode.IsSpace) 84*333d2b36SAndroid Build Coastguard Worker if i == 0 { 85*333d2b36SAndroid Build Coastguard Worker return "", false, 0, fmt.Errorf("unexpected character '%c' after '$'", s[0]) 86*333d2b36SAndroid Build Coastguard Worker } else if i == -1 { 87*333d2b36SAndroid Build Coastguard Worker i = len(s) 88*333d2b36SAndroid Build Coastguard Worker } 89*333d2b36SAndroid Build Coastguard Worker return "", false, 0, fmt.Errorf("expected '(' after '$', did you mean $(%s)?", s[:i]) 90*333d2b36SAndroid Build Coastguard Worker } 91*333d2b36SAndroid Build Coastguard Worker} 92