github.com/maruel/nin@v0.0.0-20220112143044-f35891e3ce7e/dyndep_parser.go (about) 1 // Copyright 2015 Google Inc. 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 package nin 16 17 import "fmt" 18 19 // dyndepParser parses dyndep files. 20 type dyndepParser struct { 21 // Mutable. 22 lexer lexer 23 state *State 24 dyndepFile DyndepFile 25 env *BindingEnv 26 } 27 28 // ParseDyndep parses a dyndep file provided as an input with null terminated 29 // string. 30 // 31 // It updates state and dyndepFile. 32 func ParseDyndep(state *State, dyndepFile DyndepFile, filename string, input []byte) error { 33 d := dyndepParser{ 34 state: state, 35 dyndepFile: dyndepFile, 36 } 37 return d.parse(filename, input) 38 } 39 40 // If the next token is not \a expected, produce an error string 41 // saying "expected foo, got bar". 42 func (d *dyndepParser) expectToken(expected Token) error { 43 if token := d.lexer.ReadToken(); token != expected { 44 return d.lexer.Error("expected " + expected.String() + ", got " + token.String() + expected.errorHint()) 45 } 46 return nil 47 } 48 49 // Parse a file, given its contents as a string. 50 func (d *dyndepParser) parse(filename string, input []byte) error { 51 defer metricRecord(".ninja parse")() 52 if err := d.lexer.Start(filename, input); err != nil { 53 return err 54 } 55 56 // Require a supported ninjaDyndepVersion value immediately so 57 // we can exit before encountering any syntactic surprises. 58 haveDyndepVersion := false 59 60 for { 61 token := d.lexer.ReadToken() 62 switch token { 63 case BUILD: 64 if !haveDyndepVersion { 65 return d.lexer.Error("expected 'ninja_dyndep_version = ...'") 66 } 67 if err := d.parseEdge(); err != nil { 68 return err 69 } 70 case IDENT: 71 d.lexer.UnreadToken() 72 if haveDyndepVersion { 73 return d.lexer.Error("unexpected " + token.String()) 74 } 75 if err := d.parseDyndepVersion(); err != nil { 76 return err 77 } 78 haveDyndepVersion = true 79 case ERROR: 80 return d.lexer.Error(d.lexer.DescribeLastError()) 81 case TEOF: 82 if !haveDyndepVersion { 83 return d.lexer.Error("expected 'ninja_dyndep_version = ...'") 84 } 85 return nil 86 case NEWLINE: 87 default: 88 return d.lexer.Error("unexpected " + token.String()) 89 } 90 } 91 } 92 93 func (d *dyndepParser) parseDyndepVersion() error { 94 name, letValue, err := d.parseLet() 95 if err != nil { 96 return err 97 } 98 if name != "ninja_dyndep_version" { 99 return d.lexer.Error("expected 'ninja_dyndep_version = ...'") 100 } 101 version := letValue.Evaluate(d.env) 102 major, minor := parseVersion(version) 103 if major != 1 || minor != 0 { 104 return d.lexer.Error("unsupported 'ninja_dyndep_version = " + version + "'") 105 } 106 return nil 107 } 108 109 func (d *dyndepParser) parseLet() (string, EvalString, error) { 110 key := d.lexer.readIdent() 111 eval := EvalString{} 112 var err error 113 if key == "" { 114 err = d.lexer.Error("expected variable name") 115 } else if err = d.expectToken(EQUALS); err == nil { 116 eval, err = d.lexer.readEvalString(false) 117 } 118 return key, eval, err 119 } 120 121 func (d *dyndepParser) parseEdge() error { 122 // Parse one explicit output. We expect it to already have an edge. 123 // We will record its dynamically-discovered dependency information. 124 var dyndeps *Dyndeps 125 eval, err := d.lexer.readEvalString(true) 126 if err != nil { 127 return err 128 } else if len(eval.Parsed) == 0 { 129 return d.lexer.Error("expected path") 130 } 131 132 path := eval.Evaluate(d.env) 133 if len(path) == 0 { 134 return d.lexer.Error("empty path") 135 } 136 path = CanonicalizePath(path) 137 node := d.state.Paths[path] 138 if node == nil || node.InEdge == nil { 139 // TODO(maruel): Use %q for real quoting. 140 return d.lexer.Error(fmt.Sprintf("no build statement exists for '%s'", path)) 141 } 142 edge := node.InEdge 143 if _, ok := d.dyndepFile[edge]; ok { 144 // TODO(maruel): Use %q for real quoting. 145 return d.lexer.Error(fmt.Sprintf("multiple statements for '%s'", path)) 146 } 147 dyndeps = &Dyndeps{} 148 d.dyndepFile[edge] = dyndeps 149 150 // Disallow explicit outputs. 151 eval, err = d.lexer.readEvalString(true) 152 if err != nil { 153 return err 154 } else if len(eval.Parsed) != 0 { 155 return d.lexer.Error("explicit outputs not supported") 156 } 157 158 // Parse implicit outputs, if any. 159 var outs []EvalString 160 if d.lexer.PeekToken(PIPE) { 161 for { 162 eval, err = d.lexer.readEvalString(true) 163 if err != nil { 164 // TODO(maruel): Bug upstream. 165 return err 166 } 167 if len(eval.Parsed) == 0 { 168 break 169 } 170 outs = append(outs, eval) 171 } 172 } 173 174 if err = d.expectToken(COLON); err != nil { 175 return err 176 } 177 178 if ruleName := d.lexer.readIdent(); ruleName == "" || ruleName != "dyndep" { 179 return d.lexer.Error("expected build command name 'dyndep'") 180 } 181 182 // Disallow explicit inputs. 183 eval, err = d.lexer.readEvalString(true) 184 if err != nil { 185 return err 186 } else if len(eval.Parsed) != 0 { 187 return d.lexer.Error("explicit inputs not supported") 188 } 189 190 // Parse implicit inputs, if any. 191 var ins []EvalString 192 if d.lexer.PeekToken(PIPE) { 193 for { 194 eval, err = d.lexer.readEvalString(true) 195 if err != nil { 196 // TODO(maruel): Bug upstream. 197 return err 198 } 199 if len(eval.Parsed) == 0 { 200 break 201 } 202 ins = append(ins, eval) 203 } 204 } 205 206 // Disallow order-only inputs. 207 if d.lexer.PeekToken(PIPE2) { 208 return d.lexer.Error("order-only inputs not supported") 209 } 210 211 if err = d.expectToken(NEWLINE); err != nil { 212 return err 213 } 214 215 if d.lexer.PeekToken(INDENT) { 216 key, val, err := d.parseLet() 217 if err != nil { 218 return err 219 } 220 if key != "restat" { 221 return d.lexer.Error("binding is not 'restat'") 222 } 223 value := val.Evaluate(d.env) 224 dyndeps.restat = value != "" 225 } 226 227 dyndeps.implicitInputs = make([]*Node, 0, len(ins)) 228 for _, i := range ins { 229 path := i.Evaluate(d.env) 230 if len(path) == 0 { 231 return d.lexer.Error("empty path") 232 } 233 n := d.state.GetNode(CanonicalizePathBits(path)) 234 dyndeps.implicitInputs = append(dyndeps.implicitInputs, n) 235 } 236 237 dyndeps.implicitOutputs = make([]*Node, 0, len(outs)) 238 for _, i := range outs { 239 path := i.Evaluate(d.env) 240 if len(path) == 0 { 241 return d.lexer.Error("empty path") 242 } 243 n := d.state.GetNode(CanonicalizePathBits(path)) 244 dyndeps.implicitOutputs = append(dyndeps.implicitOutputs, n) 245 } 246 return nil 247 }