1*1fa6dee9SAndroid Build Coastguard Worker// Copyright 2014 Google Inc. All rights reserved. 2*1fa6dee9SAndroid Build Coastguard Worker// 3*1fa6dee9SAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License"); 4*1fa6dee9SAndroid Build Coastguard Worker// you may not use this file except in compliance with the License. 5*1fa6dee9SAndroid Build Coastguard Worker// You may obtain a copy of the License at 6*1fa6dee9SAndroid Build Coastguard Worker// 7*1fa6dee9SAndroid Build Coastguard Worker// http://www.apache.org/licenses/LICENSE-2.0 8*1fa6dee9SAndroid Build Coastguard Worker// 9*1fa6dee9SAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software 10*1fa6dee9SAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS, 11*1fa6dee9SAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*1fa6dee9SAndroid Build Coastguard Worker// See the License for the specific language governing permissions and 13*1fa6dee9SAndroid Build Coastguard Worker// limitations under the License. 14*1fa6dee9SAndroid Build Coastguard Worker 15*1fa6dee9SAndroid Build Coastguard Workerpackage blueprint 16*1fa6dee9SAndroid Build Coastguard Worker 17*1fa6dee9SAndroid Build Coastguard Workerimport ( 18*1fa6dee9SAndroid Build Coastguard Worker "bytes" 19*1fa6dee9SAndroid Build Coastguard Worker "fmt" 20*1fa6dee9SAndroid Build Coastguard Worker "io" 21*1fa6dee9SAndroid Build Coastguard Worker "slices" 22*1fa6dee9SAndroid Build Coastguard Worker "strings" 23*1fa6dee9SAndroid Build Coastguard Worker) 24*1fa6dee9SAndroid Build Coastguard Worker 25*1fa6dee9SAndroid Build Coastguard Workerconst eof = -1 26*1fa6dee9SAndroid Build Coastguard Worker 27*1fa6dee9SAndroid Build Coastguard Workervar ( 28*1fa6dee9SAndroid Build Coastguard Worker defaultEscaper = strings.NewReplacer( 29*1fa6dee9SAndroid Build Coastguard Worker "\n", "$\n") 30*1fa6dee9SAndroid Build Coastguard Worker inputEscaper = strings.NewReplacer( 31*1fa6dee9SAndroid Build Coastguard Worker "\n", "$\n", 32*1fa6dee9SAndroid Build Coastguard Worker " ", "$ ") 33*1fa6dee9SAndroid Build Coastguard Worker outputEscaper = strings.NewReplacer( 34*1fa6dee9SAndroid Build Coastguard Worker "\n", "$\n", 35*1fa6dee9SAndroid Build Coastguard Worker " ", "$ ", 36*1fa6dee9SAndroid Build Coastguard Worker ":", "$:") 37*1fa6dee9SAndroid Build Coastguard Worker) 38*1fa6dee9SAndroid Build Coastguard Worker 39*1fa6dee9SAndroid Build Coastguard Worker// ninjaString contains the parsed result of a string that can contain references to variables (e.g. $cflags) that will 40*1fa6dee9SAndroid Build Coastguard Worker// be propagated to the build.ninja file. For literal strings with no variable references, the variables field will be 41*1fa6dee9SAndroid Build Coastguard Worker// nil. For strings with variable references str contains the original, unparsed string, and variables contains a 42*1fa6dee9SAndroid Build Coastguard Worker// pointer to a list of references, each with a span of bytes they should replace and a Variable interface. 43*1fa6dee9SAndroid Build Coastguard Workertype ninjaString struct { 44*1fa6dee9SAndroid Build Coastguard Worker str string 45*1fa6dee9SAndroid Build Coastguard Worker variables *[]variableReference 46*1fa6dee9SAndroid Build Coastguard Worker} 47*1fa6dee9SAndroid Build Coastguard Worker 48*1fa6dee9SAndroid Build Coastguard Worker// variableReference contains information about a single reference to a variable (e.g. $cflags) inside a parsed 49*1fa6dee9SAndroid Build Coastguard Worker// ninjaString. start and end are int32 to reduce memory usage. A nil variable is a special case of an inserted '$' 50*1fa6dee9SAndroid Build Coastguard Worker// at the beginning of the string to handle leading whitespace that must not be stripped by ninja. 51*1fa6dee9SAndroid Build Coastguard Workertype variableReference struct { 52*1fa6dee9SAndroid Build Coastguard Worker // start is the offset of the '$' character from the beginning of the unparsed string. 53*1fa6dee9SAndroid Build Coastguard Worker start int32 54*1fa6dee9SAndroid Build Coastguard Worker 55*1fa6dee9SAndroid Build Coastguard Worker // end is the offset of the character _after_ the final character of the variable name (or '}' if using the 56*1fa6dee9SAndroid Build Coastguard Worker //'${}' syntax) 57*1fa6dee9SAndroid Build Coastguard Worker end int32 58*1fa6dee9SAndroid Build Coastguard Worker 59*1fa6dee9SAndroid Build Coastguard Worker variable Variable 60*1fa6dee9SAndroid Build Coastguard Worker} 61*1fa6dee9SAndroid Build Coastguard Worker 62*1fa6dee9SAndroid Build Coastguard Workertype scope interface { 63*1fa6dee9SAndroid Build Coastguard Worker LookupVariable(name string) (Variable, error) 64*1fa6dee9SAndroid Build Coastguard Worker IsRuleVisible(rule Rule) bool 65*1fa6dee9SAndroid Build Coastguard Worker IsPoolVisible(pool Pool) bool 66*1fa6dee9SAndroid Build Coastguard Worker} 67*1fa6dee9SAndroid Build Coastguard Worker 68*1fa6dee9SAndroid Build Coastguard Workerfunc simpleNinjaString(str string) *ninjaString { 69*1fa6dee9SAndroid Build Coastguard Worker return &ninjaString{str: str} 70*1fa6dee9SAndroid Build Coastguard Worker} 71*1fa6dee9SAndroid Build Coastguard Worker 72*1fa6dee9SAndroid Build Coastguard Workertype parseState struct { 73*1fa6dee9SAndroid Build Coastguard Worker scope scope 74*1fa6dee9SAndroid Build Coastguard Worker str string 75*1fa6dee9SAndroid Build Coastguard Worker varStart int 76*1fa6dee9SAndroid Build Coastguard Worker varNameStart int 77*1fa6dee9SAndroid Build Coastguard Worker result *ninjaString 78*1fa6dee9SAndroid Build Coastguard Worker} 79*1fa6dee9SAndroid Build Coastguard Worker 80*1fa6dee9SAndroid Build Coastguard Workerfunc (ps *parseState) pushVariable(start, end int, v Variable) { 81*1fa6dee9SAndroid Build Coastguard Worker if ps.result.variables == nil { 82*1fa6dee9SAndroid Build Coastguard Worker ps.result.variables = &[]variableReference{{start: int32(start), end: int32(end), variable: v}} 83*1fa6dee9SAndroid Build Coastguard Worker } else { 84*1fa6dee9SAndroid Build Coastguard Worker *ps.result.variables = append(*ps.result.variables, variableReference{start: int32(start), end: int32(end), variable: v}) 85*1fa6dee9SAndroid Build Coastguard Worker } 86*1fa6dee9SAndroid Build Coastguard Worker} 87*1fa6dee9SAndroid Build Coastguard Worker 88*1fa6dee9SAndroid Build Coastguard Workertype stateFunc func(*parseState, int, rune) (stateFunc, error) 89*1fa6dee9SAndroid Build Coastguard Worker 90*1fa6dee9SAndroid Build Coastguard Worker// parseNinjaString parses an unescaped ninja string (i.e. all $<something> 91*1fa6dee9SAndroid Build Coastguard Worker// occurrences are expected to be variables or $$) and returns a *ninjaString 92*1fa6dee9SAndroid Build Coastguard Worker// that contains the original string and a list of the referenced variables. 93*1fa6dee9SAndroid Build Coastguard Workerfunc parseNinjaString(scope scope, str string) (*ninjaString, error) { 94*1fa6dee9SAndroid Build Coastguard Worker ninjaString, str, err := parseNinjaOrSimpleString(scope, str) 95*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 96*1fa6dee9SAndroid Build Coastguard Worker return nil, err 97*1fa6dee9SAndroid Build Coastguard Worker } 98*1fa6dee9SAndroid Build Coastguard Worker if ninjaString != nil { 99*1fa6dee9SAndroid Build Coastguard Worker return ninjaString, nil 100*1fa6dee9SAndroid Build Coastguard Worker } 101*1fa6dee9SAndroid Build Coastguard Worker return simpleNinjaString(str), nil 102*1fa6dee9SAndroid Build Coastguard Worker} 103*1fa6dee9SAndroid Build Coastguard Worker 104*1fa6dee9SAndroid Build Coastguard Worker// parseNinjaOrSimpleString parses an unescaped ninja string (i.e. all $<something> 105*1fa6dee9SAndroid Build Coastguard Worker// occurrences are expected to be variables or $$) and returns either a *ninjaString 106*1fa6dee9SAndroid Build Coastguard Worker// if the string contains ninja variable references, or the original string and nil 107*1fa6dee9SAndroid Build Coastguard Worker// for the *ninjaString if it doesn't. 108*1fa6dee9SAndroid Build Coastguard Workerfunc parseNinjaOrSimpleString(scope scope, str string) (*ninjaString, string, error) { 109*1fa6dee9SAndroid Build Coastguard Worker // naively pre-allocate slice by counting $ signs 110*1fa6dee9SAndroid Build Coastguard Worker n := strings.Count(str, "$") 111*1fa6dee9SAndroid Build Coastguard Worker if n == 0 { 112*1fa6dee9SAndroid Build Coastguard Worker if len(str) > 0 && str[0] == ' ' { 113*1fa6dee9SAndroid Build Coastguard Worker str = "$" + str 114*1fa6dee9SAndroid Build Coastguard Worker } 115*1fa6dee9SAndroid Build Coastguard Worker return nil, str, nil 116*1fa6dee9SAndroid Build Coastguard Worker } 117*1fa6dee9SAndroid Build Coastguard Worker variableReferences := make([]variableReference, 0, n) 118*1fa6dee9SAndroid Build Coastguard Worker result := &ninjaString{ 119*1fa6dee9SAndroid Build Coastguard Worker str: str, 120*1fa6dee9SAndroid Build Coastguard Worker variables: &variableReferences, 121*1fa6dee9SAndroid Build Coastguard Worker } 122*1fa6dee9SAndroid Build Coastguard Worker 123*1fa6dee9SAndroid Build Coastguard Worker parseState := &parseState{ 124*1fa6dee9SAndroid Build Coastguard Worker scope: scope, 125*1fa6dee9SAndroid Build Coastguard Worker str: str, 126*1fa6dee9SAndroid Build Coastguard Worker result: result, 127*1fa6dee9SAndroid Build Coastguard Worker } 128*1fa6dee9SAndroid Build Coastguard Worker 129*1fa6dee9SAndroid Build Coastguard Worker state := parseFirstRuneState 130*1fa6dee9SAndroid Build Coastguard Worker var err error 131*1fa6dee9SAndroid Build Coastguard Worker for i := 0; i < len(str); i++ { 132*1fa6dee9SAndroid Build Coastguard Worker r := rune(str[i]) 133*1fa6dee9SAndroid Build Coastguard Worker state, err = state(parseState, i, r) 134*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 135*1fa6dee9SAndroid Build Coastguard Worker return nil, "", fmt.Errorf("error parsing ninja string %q: %s", str, err) 136*1fa6dee9SAndroid Build Coastguard Worker } 137*1fa6dee9SAndroid Build Coastguard Worker } 138*1fa6dee9SAndroid Build Coastguard Worker 139*1fa6dee9SAndroid Build Coastguard Worker _, err = state(parseState, len(parseState.str), eof) 140*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 141*1fa6dee9SAndroid Build Coastguard Worker return nil, "", err 142*1fa6dee9SAndroid Build Coastguard Worker } 143*1fa6dee9SAndroid Build Coastguard Worker 144*1fa6dee9SAndroid Build Coastguard Worker // All the '$' characters counted initially could have been "$$" escapes, leaving no 145*1fa6dee9SAndroid Build Coastguard Worker // variable references. Deallocate the variables slice if so. 146*1fa6dee9SAndroid Build Coastguard Worker if len(*result.variables) == 0 { 147*1fa6dee9SAndroid Build Coastguard Worker result.variables = nil 148*1fa6dee9SAndroid Build Coastguard Worker } 149*1fa6dee9SAndroid Build Coastguard Worker 150*1fa6dee9SAndroid Build Coastguard Worker return result, "", nil 151*1fa6dee9SAndroid Build Coastguard Worker} 152*1fa6dee9SAndroid Build Coastguard Worker 153*1fa6dee9SAndroid Build Coastguard Workerfunc parseFirstRuneState(state *parseState, i int, r rune) (stateFunc, error) { 154*1fa6dee9SAndroid Build Coastguard Worker if r == ' ' { 155*1fa6dee9SAndroid Build Coastguard Worker state.pushVariable(0, 1, nil) 156*1fa6dee9SAndroid Build Coastguard Worker } 157*1fa6dee9SAndroid Build Coastguard Worker return parseStringState(state, i, r) 158*1fa6dee9SAndroid Build Coastguard Worker} 159*1fa6dee9SAndroid Build Coastguard Worker 160*1fa6dee9SAndroid Build Coastguard Workerfunc parseStringState(state *parseState, i int, r rune) (stateFunc, error) { 161*1fa6dee9SAndroid Build Coastguard Worker switch { 162*1fa6dee9SAndroid Build Coastguard Worker case r == '$': 163*1fa6dee9SAndroid Build Coastguard Worker state.varStart = i 164*1fa6dee9SAndroid Build Coastguard Worker return parseDollarStartState, nil 165*1fa6dee9SAndroid Build Coastguard Worker 166*1fa6dee9SAndroid Build Coastguard Worker case r == eof: 167*1fa6dee9SAndroid Build Coastguard Worker return nil, nil 168*1fa6dee9SAndroid Build Coastguard Worker 169*1fa6dee9SAndroid Build Coastguard Worker default: 170*1fa6dee9SAndroid Build Coastguard Worker return parseStringState, nil 171*1fa6dee9SAndroid Build Coastguard Worker } 172*1fa6dee9SAndroid Build Coastguard Worker} 173*1fa6dee9SAndroid Build Coastguard Worker 174*1fa6dee9SAndroid Build Coastguard Workerfunc parseDollarStartState(state *parseState, i int, r rune) (stateFunc, error) { 175*1fa6dee9SAndroid Build Coastguard Worker switch { 176*1fa6dee9SAndroid Build Coastguard Worker case r >= 'a' && r <= 'z', r >= 'A' && r <= 'Z', 177*1fa6dee9SAndroid Build Coastguard Worker r >= '0' && r <= '9', r == '_', r == '-': 178*1fa6dee9SAndroid Build Coastguard Worker // The beginning of a of the variable name. 179*1fa6dee9SAndroid Build Coastguard Worker state.varNameStart = i 180*1fa6dee9SAndroid Build Coastguard Worker return parseDollarState, nil 181*1fa6dee9SAndroid Build Coastguard Worker 182*1fa6dee9SAndroid Build Coastguard Worker case r == '$': 183*1fa6dee9SAndroid Build Coastguard Worker // Just a "$$". Go back to parseStringState. 184*1fa6dee9SAndroid Build Coastguard Worker return parseStringState, nil 185*1fa6dee9SAndroid Build Coastguard Worker 186*1fa6dee9SAndroid Build Coastguard Worker case r == '{': 187*1fa6dee9SAndroid Build Coastguard Worker // This is a bracketted variable name (e.g. "${blah.blah}"). 188*1fa6dee9SAndroid Build Coastguard Worker state.varNameStart = i + 1 189*1fa6dee9SAndroid Build Coastguard Worker return parseBracketsState, nil 190*1fa6dee9SAndroid Build Coastguard Worker 191*1fa6dee9SAndroid Build Coastguard Worker case r == eof: 192*1fa6dee9SAndroid Build Coastguard Worker return nil, fmt.Errorf("unexpected end of string after '$'") 193*1fa6dee9SAndroid Build Coastguard Worker 194*1fa6dee9SAndroid Build Coastguard Worker default: 195*1fa6dee9SAndroid Build Coastguard Worker // This was some arbitrary character following a dollar sign, 196*1fa6dee9SAndroid Build Coastguard Worker // which is not allowed. 197*1fa6dee9SAndroid Build Coastguard Worker return nil, fmt.Errorf("invalid character after '$' at byte "+ 198*1fa6dee9SAndroid Build Coastguard Worker "offset %d", i) 199*1fa6dee9SAndroid Build Coastguard Worker } 200*1fa6dee9SAndroid Build Coastguard Worker} 201*1fa6dee9SAndroid Build Coastguard Worker 202*1fa6dee9SAndroid Build Coastguard Workerfunc parseDollarState(state *parseState, i int, r rune) (stateFunc, error) { 203*1fa6dee9SAndroid Build Coastguard Worker switch { 204*1fa6dee9SAndroid Build Coastguard Worker case r >= 'a' && r <= 'z', r >= 'A' && r <= 'Z', 205*1fa6dee9SAndroid Build Coastguard Worker r >= '0' && r <= '9', r == '_', r == '-': 206*1fa6dee9SAndroid Build Coastguard Worker // A part of the variable name. Keep going. 207*1fa6dee9SAndroid Build Coastguard Worker return parseDollarState, nil 208*1fa6dee9SAndroid Build Coastguard Worker } 209*1fa6dee9SAndroid Build Coastguard Worker 210*1fa6dee9SAndroid Build Coastguard Worker // The variable name has ended, output what we have. 211*1fa6dee9SAndroid Build Coastguard Worker v, err := state.scope.LookupVariable(state.str[state.varNameStart:i]) 212*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 213*1fa6dee9SAndroid Build Coastguard Worker return nil, err 214*1fa6dee9SAndroid Build Coastguard Worker } 215*1fa6dee9SAndroid Build Coastguard Worker 216*1fa6dee9SAndroid Build Coastguard Worker state.pushVariable(state.varStart, i, v) 217*1fa6dee9SAndroid Build Coastguard Worker 218*1fa6dee9SAndroid Build Coastguard Worker switch { 219*1fa6dee9SAndroid Build Coastguard Worker case r == '$': 220*1fa6dee9SAndroid Build Coastguard Worker // A dollar after the variable name (e.g. "$blah$"). Start a new one. 221*1fa6dee9SAndroid Build Coastguard Worker state.varStart = i 222*1fa6dee9SAndroid Build Coastguard Worker return parseDollarStartState, nil 223*1fa6dee9SAndroid Build Coastguard Worker 224*1fa6dee9SAndroid Build Coastguard Worker case r == eof: 225*1fa6dee9SAndroid Build Coastguard Worker return nil, nil 226*1fa6dee9SAndroid Build Coastguard Worker 227*1fa6dee9SAndroid Build Coastguard Worker default: 228*1fa6dee9SAndroid Build Coastguard Worker return parseStringState, nil 229*1fa6dee9SAndroid Build Coastguard Worker } 230*1fa6dee9SAndroid Build Coastguard Worker} 231*1fa6dee9SAndroid Build Coastguard Worker 232*1fa6dee9SAndroid Build Coastguard Workerfunc parseBracketsState(state *parseState, i int, r rune) (stateFunc, error) { 233*1fa6dee9SAndroid Build Coastguard Worker switch { 234*1fa6dee9SAndroid Build Coastguard Worker case r >= 'a' && r <= 'z', r >= 'A' && r <= 'Z', 235*1fa6dee9SAndroid Build Coastguard Worker r >= '0' && r <= '9', r == '_', r == '-', r == '.': 236*1fa6dee9SAndroid Build Coastguard Worker // A part of the variable name. Keep going. 237*1fa6dee9SAndroid Build Coastguard Worker return parseBracketsState, nil 238*1fa6dee9SAndroid Build Coastguard Worker 239*1fa6dee9SAndroid Build Coastguard Worker case r == '}': 240*1fa6dee9SAndroid Build Coastguard Worker if state.varNameStart == i { 241*1fa6dee9SAndroid Build Coastguard Worker // The brackets were immediately closed. That's no good. 242*1fa6dee9SAndroid Build Coastguard Worker return nil, fmt.Errorf("empty variable name at byte offset %d", 243*1fa6dee9SAndroid Build Coastguard Worker i) 244*1fa6dee9SAndroid Build Coastguard Worker } 245*1fa6dee9SAndroid Build Coastguard Worker 246*1fa6dee9SAndroid Build Coastguard Worker // This is the end of the variable name. 247*1fa6dee9SAndroid Build Coastguard Worker v, err := state.scope.LookupVariable(state.str[state.varNameStart:i]) 248*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 249*1fa6dee9SAndroid Build Coastguard Worker return nil, err 250*1fa6dee9SAndroid Build Coastguard Worker } 251*1fa6dee9SAndroid Build Coastguard Worker 252*1fa6dee9SAndroid Build Coastguard Worker state.pushVariable(state.varStart, i+1, v) 253*1fa6dee9SAndroid Build Coastguard Worker return parseStringState, nil 254*1fa6dee9SAndroid Build Coastguard Worker 255*1fa6dee9SAndroid Build Coastguard Worker case r == eof: 256*1fa6dee9SAndroid Build Coastguard Worker return nil, fmt.Errorf("unexpected end of string in variable name") 257*1fa6dee9SAndroid Build Coastguard Worker 258*1fa6dee9SAndroid Build Coastguard Worker default: 259*1fa6dee9SAndroid Build Coastguard Worker // This character isn't allowed in a variable name. 260*1fa6dee9SAndroid Build Coastguard Worker return nil, fmt.Errorf("invalid character in variable name at "+ 261*1fa6dee9SAndroid Build Coastguard Worker "byte offset %d", i) 262*1fa6dee9SAndroid Build Coastguard Worker } 263*1fa6dee9SAndroid Build Coastguard Worker} 264*1fa6dee9SAndroid Build Coastguard Worker 265*1fa6dee9SAndroid Build Coastguard Worker// parseNinjaStrings converts a list of strings to *ninjaStrings by finding the references 266*1fa6dee9SAndroid Build Coastguard Worker// to ninja variables contained in the strings. 267*1fa6dee9SAndroid Build Coastguard Workerfunc parseNinjaStrings(scope scope, strs []string) ([]*ninjaString, 268*1fa6dee9SAndroid Build Coastguard Worker error) { 269*1fa6dee9SAndroid Build Coastguard Worker 270*1fa6dee9SAndroid Build Coastguard Worker if len(strs) == 0 { 271*1fa6dee9SAndroid Build Coastguard Worker return nil, nil 272*1fa6dee9SAndroid Build Coastguard Worker } 273*1fa6dee9SAndroid Build Coastguard Worker result := make([]*ninjaString, len(strs)) 274*1fa6dee9SAndroid Build Coastguard Worker for i, str := range strs { 275*1fa6dee9SAndroid Build Coastguard Worker ninjaStr, err := parseNinjaString(scope, str) 276*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 277*1fa6dee9SAndroid Build Coastguard Worker return nil, fmt.Errorf("error parsing element %d: %s", i, err) 278*1fa6dee9SAndroid Build Coastguard Worker } 279*1fa6dee9SAndroid Build Coastguard Worker result[i] = ninjaStr 280*1fa6dee9SAndroid Build Coastguard Worker } 281*1fa6dee9SAndroid Build Coastguard Worker return result, nil 282*1fa6dee9SAndroid Build Coastguard Worker} 283*1fa6dee9SAndroid Build Coastguard Worker 284*1fa6dee9SAndroid Build Coastguard Worker// parseNinjaOrSimpleStrings splits a list of strings into *ninjaStrings if they have ninja 285*1fa6dee9SAndroid Build Coastguard Worker// variable references or a list of strings if they don't. If none of the input strings contain 286*1fa6dee9SAndroid Build Coastguard Worker// ninja variable references (a very common case) then it returns the unmodified input slice as 287*1fa6dee9SAndroid Build Coastguard Worker// the output slice. 288*1fa6dee9SAndroid Build Coastguard Workerfunc parseNinjaOrSimpleStrings(scope scope, strs []string) ([]*ninjaString, []string, error) { 289*1fa6dee9SAndroid Build Coastguard Worker if len(strs) == 0 { 290*1fa6dee9SAndroid Build Coastguard Worker return nil, strs, nil 291*1fa6dee9SAndroid Build Coastguard Worker } 292*1fa6dee9SAndroid Build Coastguard Worker 293*1fa6dee9SAndroid Build Coastguard Worker // allSimpleStrings is true until the first time a string with ninja variable references is found. 294*1fa6dee9SAndroid Build Coastguard Worker allSimpleStrings := true 295*1fa6dee9SAndroid Build Coastguard Worker var simpleStrings []string 296*1fa6dee9SAndroid Build Coastguard Worker var ninjaStrings []*ninjaString 297*1fa6dee9SAndroid Build Coastguard Worker 298*1fa6dee9SAndroid Build Coastguard Worker for i, str := range strs { 299*1fa6dee9SAndroid Build Coastguard Worker ninjaStr, simpleStr, err := parseNinjaOrSimpleString(scope, str) 300*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 301*1fa6dee9SAndroid Build Coastguard Worker return nil, nil, fmt.Errorf("error parsing element %d: %s", i, err) 302*1fa6dee9SAndroid Build Coastguard Worker } else if ninjaStr != nil { 303*1fa6dee9SAndroid Build Coastguard Worker ninjaStrings = append(ninjaStrings, ninjaStr) 304*1fa6dee9SAndroid Build Coastguard Worker if allSimpleStrings && i > 0 { 305*1fa6dee9SAndroid Build Coastguard Worker // If all previous strings had no ninja variable references then they weren't copied into 306*1fa6dee9SAndroid Build Coastguard Worker // simpleStrings to avoid allocating it if the input slice is reused as the output. Allocate 307*1fa6dee9SAndroid Build Coastguard Worker // simpleStrings and copy all the previous strings into it. 308*1fa6dee9SAndroid Build Coastguard Worker simpleStrings = make([]string, i, len(strs)) 309*1fa6dee9SAndroid Build Coastguard Worker copy(simpleStrings, strs[:i]) 310*1fa6dee9SAndroid Build Coastguard Worker } 311*1fa6dee9SAndroid Build Coastguard Worker allSimpleStrings = false 312*1fa6dee9SAndroid Build Coastguard Worker } else { 313*1fa6dee9SAndroid Build Coastguard Worker if !allSimpleStrings { 314*1fa6dee9SAndroid Build Coastguard Worker // Only copy into the output slice if at least one string with ninja variable references 315*1fa6dee9SAndroid Build Coastguard Worker // was found. Skipped strings will be copied the first time a string with ninja variable 316*1fa6dee9SAndroid Build Coastguard Worker // is found. 317*1fa6dee9SAndroid Build Coastguard Worker simpleStrings = append(simpleStrings, simpleStr) 318*1fa6dee9SAndroid Build Coastguard Worker } 319*1fa6dee9SAndroid Build Coastguard Worker } 320*1fa6dee9SAndroid Build Coastguard Worker } 321*1fa6dee9SAndroid Build Coastguard Worker if allSimpleStrings { 322*1fa6dee9SAndroid Build Coastguard Worker // None of the input strings had ninja variable references, return the input slice as the output. 323*1fa6dee9SAndroid Build Coastguard Worker return nil, strs, nil 324*1fa6dee9SAndroid Build Coastguard Worker } 325*1fa6dee9SAndroid Build Coastguard Worker return ninjaStrings, simpleStrings, nil 326*1fa6dee9SAndroid Build Coastguard Worker} 327*1fa6dee9SAndroid Build Coastguard Worker 328*1fa6dee9SAndroid Build Coastguard Workerfunc (n *ninjaString) Value(nameTracker *nameTracker) string { 329*1fa6dee9SAndroid Build Coastguard Worker if n.variables == nil || len(*n.variables) == 0 { 330*1fa6dee9SAndroid Build Coastguard Worker return defaultEscaper.Replace(n.str) 331*1fa6dee9SAndroid Build Coastguard Worker } 332*1fa6dee9SAndroid Build Coastguard Worker str := &strings.Builder{} 333*1fa6dee9SAndroid Build Coastguard Worker n.ValueWithEscaper(str, nameTracker, defaultEscaper) 334*1fa6dee9SAndroid Build Coastguard Worker return str.String() 335*1fa6dee9SAndroid Build Coastguard Worker} 336*1fa6dee9SAndroid Build Coastguard Worker 337*1fa6dee9SAndroid Build Coastguard Workerfunc (n *ninjaString) ValueWithEscaper(w io.StringWriter, nameTracker *nameTracker, escaper *strings.Replacer) { 338*1fa6dee9SAndroid Build Coastguard Worker 339*1fa6dee9SAndroid Build Coastguard Worker if n.variables == nil || len(*n.variables) == 0 { 340*1fa6dee9SAndroid Build Coastguard Worker w.WriteString(escaper.Replace(n.str)) 341*1fa6dee9SAndroid Build Coastguard Worker return 342*1fa6dee9SAndroid Build Coastguard Worker } 343*1fa6dee9SAndroid Build Coastguard Worker 344*1fa6dee9SAndroid Build Coastguard Worker i := 0 345*1fa6dee9SAndroid Build Coastguard Worker for _, v := range *n.variables { 346*1fa6dee9SAndroid Build Coastguard Worker w.WriteString(escaper.Replace(n.str[i:v.start])) 347*1fa6dee9SAndroid Build Coastguard Worker if v.variable == nil { 348*1fa6dee9SAndroid Build Coastguard Worker w.WriteString("$ ") 349*1fa6dee9SAndroid Build Coastguard Worker } else { 350*1fa6dee9SAndroid Build Coastguard Worker w.WriteString("${") 351*1fa6dee9SAndroid Build Coastguard Worker w.WriteString(nameTracker.Variable(v.variable)) 352*1fa6dee9SAndroid Build Coastguard Worker w.WriteString("}") 353*1fa6dee9SAndroid Build Coastguard Worker } 354*1fa6dee9SAndroid Build Coastguard Worker i = int(v.end) 355*1fa6dee9SAndroid Build Coastguard Worker } 356*1fa6dee9SAndroid Build Coastguard Worker w.WriteString(escaper.Replace(n.str[i:len(n.str)])) 357*1fa6dee9SAndroid Build Coastguard Worker} 358*1fa6dee9SAndroid Build Coastguard Worker 359*1fa6dee9SAndroid Build Coastguard Workerfunc (n *ninjaString) Eval(variables map[Variable]*ninjaString) (string, error) { 360*1fa6dee9SAndroid Build Coastguard Worker if n.variables == nil || len(*n.variables) == 0 { 361*1fa6dee9SAndroid Build Coastguard Worker return n.str, nil 362*1fa6dee9SAndroid Build Coastguard Worker } 363*1fa6dee9SAndroid Build Coastguard Worker 364*1fa6dee9SAndroid Build Coastguard Worker w := &strings.Builder{} 365*1fa6dee9SAndroid Build Coastguard Worker i := 0 366*1fa6dee9SAndroid Build Coastguard Worker for _, v := range *n.variables { 367*1fa6dee9SAndroid Build Coastguard Worker w.WriteString(n.str[i:v.start]) 368*1fa6dee9SAndroid Build Coastguard Worker if v.variable == nil { 369*1fa6dee9SAndroid Build Coastguard Worker w.WriteString(" ") 370*1fa6dee9SAndroid Build Coastguard Worker } else { 371*1fa6dee9SAndroid Build Coastguard Worker variable, ok := variables[v.variable] 372*1fa6dee9SAndroid Build Coastguard Worker if !ok { 373*1fa6dee9SAndroid Build Coastguard Worker return "", fmt.Errorf("no such global variable: %s", v.variable) 374*1fa6dee9SAndroid Build Coastguard Worker } 375*1fa6dee9SAndroid Build Coastguard Worker value, err := variable.Eval(variables) 376*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 377*1fa6dee9SAndroid Build Coastguard Worker return "", err 378*1fa6dee9SAndroid Build Coastguard Worker } 379*1fa6dee9SAndroid Build Coastguard Worker w.WriteString(value) 380*1fa6dee9SAndroid Build Coastguard Worker } 381*1fa6dee9SAndroid Build Coastguard Worker i = int(v.end) 382*1fa6dee9SAndroid Build Coastguard Worker } 383*1fa6dee9SAndroid Build Coastguard Worker w.WriteString(n.str[i:len(n.str)]) 384*1fa6dee9SAndroid Build Coastguard Worker return w.String(), nil 385*1fa6dee9SAndroid Build Coastguard Worker} 386*1fa6dee9SAndroid Build Coastguard Worker 387*1fa6dee9SAndroid Build Coastguard Workerfunc (n *ninjaString) Variables() []Variable { 388*1fa6dee9SAndroid Build Coastguard Worker if n.variables == nil || len(*n.variables) == 0 { 389*1fa6dee9SAndroid Build Coastguard Worker return nil 390*1fa6dee9SAndroid Build Coastguard Worker } 391*1fa6dee9SAndroid Build Coastguard Worker 392*1fa6dee9SAndroid Build Coastguard Worker variables := make([]Variable, 0, len(*n.variables)) 393*1fa6dee9SAndroid Build Coastguard Worker for _, v := range *n.variables { 394*1fa6dee9SAndroid Build Coastguard Worker if v.variable != nil { 395*1fa6dee9SAndroid Build Coastguard Worker variables = append(variables, v.variable) 396*1fa6dee9SAndroid Build Coastguard Worker } 397*1fa6dee9SAndroid Build Coastguard Worker } 398*1fa6dee9SAndroid Build Coastguard Worker return variables 399*1fa6dee9SAndroid Build Coastguard Worker} 400*1fa6dee9SAndroid Build Coastguard Worker 401*1fa6dee9SAndroid Build Coastguard Workerfunc validateNinjaName(name string) error { 402*1fa6dee9SAndroid Build Coastguard Worker for i, r := range name { 403*1fa6dee9SAndroid Build Coastguard Worker valid := (r >= 'a' && r <= 'z') || 404*1fa6dee9SAndroid Build Coastguard Worker (r >= 'A' && r <= 'Z') || 405*1fa6dee9SAndroid Build Coastguard Worker (r >= '0' && r <= '9') || 406*1fa6dee9SAndroid Build Coastguard Worker (r == '_') || 407*1fa6dee9SAndroid Build Coastguard Worker (r == '-') || 408*1fa6dee9SAndroid Build Coastguard Worker (r == '.') 409*1fa6dee9SAndroid Build Coastguard Worker if !valid { 410*1fa6dee9SAndroid Build Coastguard Worker 411*1fa6dee9SAndroid Build Coastguard Worker return fmt.Errorf("%q contains an invalid Ninja name character "+ 412*1fa6dee9SAndroid Build Coastguard Worker "%q at byte offset %d", name, r, i) 413*1fa6dee9SAndroid Build Coastguard Worker } 414*1fa6dee9SAndroid Build Coastguard Worker } 415*1fa6dee9SAndroid Build Coastguard Worker return nil 416*1fa6dee9SAndroid Build Coastguard Worker} 417*1fa6dee9SAndroid Build Coastguard Worker 418*1fa6dee9SAndroid Build Coastguard Workerfunc toNinjaName(name string) string { 419*1fa6dee9SAndroid Build Coastguard Worker ret := bytes.Buffer{} 420*1fa6dee9SAndroid Build Coastguard Worker ret.Grow(len(name)) 421*1fa6dee9SAndroid Build Coastguard Worker for _, r := range name { 422*1fa6dee9SAndroid Build Coastguard Worker valid := (r >= 'a' && r <= 'z') || 423*1fa6dee9SAndroid Build Coastguard Worker (r >= 'A' && r <= 'Z') || 424*1fa6dee9SAndroid Build Coastguard Worker (r >= '0' && r <= '9') || 425*1fa6dee9SAndroid Build Coastguard Worker (r == '_') || 426*1fa6dee9SAndroid Build Coastguard Worker (r == '-') || 427*1fa6dee9SAndroid Build Coastguard Worker (r == '.') 428*1fa6dee9SAndroid Build Coastguard Worker if valid { 429*1fa6dee9SAndroid Build Coastguard Worker ret.WriteRune(r) 430*1fa6dee9SAndroid Build Coastguard Worker } else { 431*1fa6dee9SAndroid Build Coastguard Worker // TODO(jeffrygaston): do escaping so that toNinjaName won't ever output duplicate 432*1fa6dee9SAndroid Build Coastguard Worker // names for two different input names 433*1fa6dee9SAndroid Build Coastguard Worker ret.WriteRune('_') 434*1fa6dee9SAndroid Build Coastguard Worker } 435*1fa6dee9SAndroid Build Coastguard Worker } 436*1fa6dee9SAndroid Build Coastguard Worker 437*1fa6dee9SAndroid Build Coastguard Worker return ret.String() 438*1fa6dee9SAndroid Build Coastguard Worker} 439*1fa6dee9SAndroid Build Coastguard Worker 440*1fa6dee9SAndroid Build Coastguard Workervar builtinRuleArgs = []string{"out", "in"} 441*1fa6dee9SAndroid Build Coastguard Worker 442*1fa6dee9SAndroid Build Coastguard Workerfunc validateArgName(argName string) error { 443*1fa6dee9SAndroid Build Coastguard Worker err := validateNinjaName(argName) 444*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 445*1fa6dee9SAndroid Build Coastguard Worker return err 446*1fa6dee9SAndroid Build Coastguard Worker } 447*1fa6dee9SAndroid Build Coastguard Worker 448*1fa6dee9SAndroid Build Coastguard Worker // We only allow globals within the rule's package to be used as rule 449*1fa6dee9SAndroid Build Coastguard Worker // arguments. A global in another package can always be mirrored into 450*1fa6dee9SAndroid Build Coastguard Worker // the rule's package by defining a new variable, so this doesn't limit 451*1fa6dee9SAndroid Build Coastguard Worker // what's possible. This limitation prevents situations where a Build 452*1fa6dee9SAndroid Build Coastguard Worker // invocation in another package must use the rule-defining package's 453*1fa6dee9SAndroid Build Coastguard Worker // import name for a 3rd package in order to set the rule's arguments. 454*1fa6dee9SAndroid Build Coastguard Worker if strings.ContainsRune(argName, '.') { 455*1fa6dee9SAndroid Build Coastguard Worker return fmt.Errorf("%q contains a '.' character", argName) 456*1fa6dee9SAndroid Build Coastguard Worker } 457*1fa6dee9SAndroid Build Coastguard Worker 458*1fa6dee9SAndroid Build Coastguard Worker if argName == "tags" { 459*1fa6dee9SAndroid Build Coastguard Worker return fmt.Errorf("\"tags\" is a reserved argument name") 460*1fa6dee9SAndroid Build Coastguard Worker } 461*1fa6dee9SAndroid Build Coastguard Worker 462*1fa6dee9SAndroid Build Coastguard Worker for _, builtin := range builtinRuleArgs { 463*1fa6dee9SAndroid Build Coastguard Worker if argName == builtin { 464*1fa6dee9SAndroid Build Coastguard Worker return fmt.Errorf("%q conflicts with Ninja built-in", argName) 465*1fa6dee9SAndroid Build Coastguard Worker } 466*1fa6dee9SAndroid Build Coastguard Worker } 467*1fa6dee9SAndroid Build Coastguard Worker 468*1fa6dee9SAndroid Build Coastguard Worker return nil 469*1fa6dee9SAndroid Build Coastguard Worker} 470*1fa6dee9SAndroid Build Coastguard Worker 471*1fa6dee9SAndroid Build Coastguard Workerfunc validateArgNames(argNames []string) error { 472*1fa6dee9SAndroid Build Coastguard Worker for _, argName := range argNames { 473*1fa6dee9SAndroid Build Coastguard Worker err := validateArgName(argName) 474*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 475*1fa6dee9SAndroid Build Coastguard Worker return err 476*1fa6dee9SAndroid Build Coastguard Worker } 477*1fa6dee9SAndroid Build Coastguard Worker } 478*1fa6dee9SAndroid Build Coastguard Worker 479*1fa6dee9SAndroid Build Coastguard Worker return nil 480*1fa6dee9SAndroid Build Coastguard Worker} 481*1fa6dee9SAndroid Build Coastguard Worker 482*1fa6dee9SAndroid Build Coastguard Workerfunc ninjaStringsEqual(a, b *ninjaString) bool { 483*1fa6dee9SAndroid Build Coastguard Worker return a.str == b.str && 484*1fa6dee9SAndroid Build Coastguard Worker (a.variables == nil) == (b.variables == nil) && 485*1fa6dee9SAndroid Build Coastguard Worker (a.variables == nil || 486*1fa6dee9SAndroid Build Coastguard Worker slices.Equal(*a.variables, *b.variables)) 487*1fa6dee9SAndroid Build Coastguard Worker} 488