1// Copyright 2014 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 15package blueprint 16 17import ( 18 "errors" 19 "fmt" 20 "sort" 21 "strconv" 22 "strings" 23) 24 25// A Deps value indicates the dependency file format that Ninja should expect to 26// be output by a compiler. 27type Deps int 28 29const ( 30 DepsNone Deps = iota 31 DepsGCC 32 DepsMSVC 33) 34 35func (d Deps) String() string { 36 switch d { 37 case DepsNone: 38 return "none" 39 case DepsGCC: 40 return "gcc" 41 case DepsMSVC: 42 return "msvc" 43 default: 44 panic(fmt.Sprintf("unknown deps value: %d", d)) 45 } 46} 47 48// A PoolParams object contains the set of parameters that make up a Ninja pool 49// definition. 50type PoolParams struct { 51 Comment string // The comment that will appear above the definition. 52 Depth int // The Ninja pool depth. 53} 54 55// A RuleParams object contains the set of parameters that make up a Ninja rule 56// definition. 57type RuleParams struct { 58 // These fields correspond to a Ninja variable of the same name. 59 Command string // The command that Ninja will run for the rule. 60 Depfile string // The dependency file name. 61 Deps Deps // The format of the dependency file. 62 Description string // The description that Ninja will print for the rule. 63 Generator bool // Whether the rule generates the Ninja manifest file. 64 Pool Pool // The Ninja pool to which the rule belongs. 65 Restat bool // Whether Ninja should re-stat the rule's outputs. 66 Rspfile string // The response file. 67 RspfileContent string // The response file content. 68 69 // These fields are used internally in Blueprint 70 CommandDeps []string // Command-specific implicit dependencies to prepend to builds 71 CommandOrderOnly []string // Command-specific order-only dependencies to prepend to builds 72 Comment string // The comment that will appear above the definition. 73} 74 75// A BuildParams object contains the set of parameters that make up a Ninja 76// build statement. Each field except for Args corresponds with a part of the 77// Ninja build statement. The Args field contains variable names and values 78// that are set within the build statement's scope in the Ninja file. 79type BuildParams struct { 80 Comment string // The comment that will appear above the definition. 81 Depfile string // The dependency file name. 82 Deps Deps // The format of the dependency file. 83 Description string // The description that Ninja will print for the build. 84 Rule Rule // The rule to invoke. 85 Outputs []string // The list of explicit output targets. 86 ImplicitOutputs []string // The list of implicit output targets. 87 Inputs []string // The list of explicit input dependencies. 88 Implicits []string // The list of implicit input dependencies. 89 OrderOnly []string // The list of order-only dependencies. 90 Args map[string]string // The variable/value pairs to set. 91 Optional bool // Skip outputting a default statement 92} 93 94// A poolDef describes a pool definition. It does not include the name of the 95// pool. 96type poolDef struct { 97 Comment string 98 Depth int 99} 100 101func parsePoolParams(scope scope, params *PoolParams) (*poolDef, 102 error) { 103 104 def := &poolDef{ 105 Comment: params.Comment, 106 Depth: params.Depth, 107 } 108 109 return def, nil 110} 111 112func (p *poolDef) WriteTo(nw *ninjaWriter, name string) error { 113 if p.Comment != "" { 114 err := nw.Comment(p.Comment) 115 if err != nil { 116 return err 117 } 118 } 119 120 err := nw.Pool(name) 121 if err != nil { 122 return err 123 } 124 125 return nw.ScopedAssign("depth", strconv.Itoa(p.Depth)) 126} 127 128// A ruleDef describes a rule definition. It does not include the name of the 129// rule. 130type ruleDef struct { 131 CommandDeps []*ninjaString 132 CommandOrderOnly []*ninjaString 133 Comment string 134 Pool Pool 135 Variables map[string]*ninjaString 136} 137 138func parseRuleParams(scope scope, params *RuleParams) (*ruleDef, 139 error) { 140 141 r := &ruleDef{ 142 Comment: params.Comment, 143 Pool: params.Pool, 144 Variables: make(map[string]*ninjaString), 145 } 146 147 if params.Command == "" { 148 return nil, fmt.Errorf("encountered rule params with no command " + 149 "specified") 150 } 151 152 if r.Pool != nil && !scope.IsPoolVisible(r.Pool) { 153 return nil, fmt.Errorf("Pool %s is not visible in this scope", r.Pool) 154 } 155 156 value, err := parseNinjaString(scope, params.Command) 157 if err != nil { 158 return nil, fmt.Errorf("error parsing Command param: %s", err) 159 } 160 r.Variables["command"] = value 161 162 if params.Depfile != "" { 163 value, err = parseNinjaString(scope, params.Depfile) 164 if err != nil { 165 return nil, fmt.Errorf("error parsing Depfile param: %s", err) 166 } 167 r.Variables["depfile"] = value 168 } 169 170 if params.Deps != DepsNone { 171 r.Variables["deps"] = simpleNinjaString(params.Deps.String()) 172 } 173 174 if params.Description != "" { 175 value, err = parseNinjaString(scope, params.Description) 176 if err != nil { 177 return nil, fmt.Errorf("error parsing Description param: %s", err) 178 } 179 r.Variables["description"] = value 180 } 181 182 if params.Generator { 183 r.Variables["generator"] = simpleNinjaString("true") 184 } 185 186 if params.Restat { 187 r.Variables["restat"] = simpleNinjaString("true") 188 } 189 190 if params.Rspfile != "" { 191 value, err = parseNinjaString(scope, params.Rspfile) 192 if err != nil { 193 return nil, fmt.Errorf("error parsing Rspfile param: %s", err) 194 } 195 r.Variables["rspfile"] = value 196 } 197 198 if params.RspfileContent != "" { 199 value, err = parseNinjaString(scope, params.RspfileContent) 200 if err != nil { 201 return nil, fmt.Errorf("error parsing RspfileContent param: %s", 202 err) 203 } 204 r.Variables["rspfile_content"] = value 205 } 206 207 r.CommandDeps, err = parseNinjaStrings(scope, params.CommandDeps) 208 if err != nil { 209 return nil, fmt.Errorf("error parsing CommandDeps param: %s", err) 210 } 211 212 r.CommandOrderOnly, err = parseNinjaStrings(scope, params.CommandOrderOnly) 213 if err != nil { 214 return nil, fmt.Errorf("error parsing CommandDeps param: %s", err) 215 } 216 217 return r, nil 218} 219 220func (r *ruleDef) WriteTo(nw *ninjaWriter, name string, 221 pkgNames map[*packageContext]string) error { 222 223 if r.Comment != "" { 224 err := nw.Comment(r.Comment) 225 if err != nil { 226 return err 227 } 228 } 229 230 err := nw.Rule(name) 231 if err != nil { 232 return err 233 } 234 235 if r.Pool != nil { 236 err = nw.ScopedAssign("pool", r.Pool.fullName(pkgNames)) 237 if err != nil { 238 return err 239 } 240 } 241 242 err = writeVariables(nw, r.Variables, pkgNames) 243 if err != nil { 244 return err 245 } 246 247 return nil 248} 249 250// A buildDef describes a build target definition. 251type buildDef struct { 252 Comment string 253 Rule Rule 254 RuleDef *ruleDef 255 Outputs []*ninjaString 256 ImplicitOutputs []*ninjaString 257 Inputs []*ninjaString 258 Implicits []*ninjaString 259 OrderOnly []*ninjaString 260 Args map[Variable]*ninjaString 261 Variables map[string]*ninjaString 262 Optional bool 263} 264 265func parseBuildParams(scope scope, params *BuildParams) (*buildDef, 266 error) { 267 268 comment := params.Comment 269 rule := params.Rule 270 271 b := &buildDef{ 272 Comment: comment, 273 Rule: rule, 274 } 275 276 setVariable := func(name string, value *ninjaString) { 277 if b.Variables == nil { 278 b.Variables = make(map[string]*ninjaString) 279 } 280 b.Variables[name] = value 281 } 282 283 if !scope.IsRuleVisible(rule) { 284 return nil, fmt.Errorf("Rule %s is not visible in this scope", rule) 285 } 286 287 if len(params.Outputs) == 0 { 288 return nil, errors.New("Outputs param has no elements") 289 } 290 291 var err error 292 b.Outputs, err = parseNinjaStrings(scope, params.Outputs) 293 if err != nil { 294 return nil, fmt.Errorf("error parsing Outputs param: %s", err) 295 } 296 297 b.ImplicitOutputs, err = parseNinjaStrings(scope, params.ImplicitOutputs) 298 if err != nil { 299 return nil, fmt.Errorf("error parsing ImplicitOutputs param: %s", err) 300 } 301 302 b.Inputs, err = parseNinjaStrings(scope, params.Inputs) 303 if err != nil { 304 return nil, fmt.Errorf("error parsing Inputs param: %s", err) 305 } 306 307 b.Implicits, err = parseNinjaStrings(scope, params.Implicits) 308 if err != nil { 309 return nil, fmt.Errorf("error parsing Implicits param: %s", err) 310 } 311 312 b.OrderOnly, err = parseNinjaStrings(scope, params.OrderOnly) 313 if err != nil { 314 return nil, fmt.Errorf("error parsing OrderOnly param: %s", err) 315 } 316 317 b.Optional = params.Optional 318 319 if params.Depfile != "" { 320 value, err := parseNinjaString(scope, params.Depfile) 321 if err != nil { 322 return nil, fmt.Errorf("error parsing Depfile param: %s", err) 323 } 324 setVariable("depfile", value) 325 } 326 327 if params.Deps != DepsNone { 328 setVariable("deps", simpleNinjaString(params.Deps.String())) 329 } 330 331 if params.Description != "" { 332 value, err := parseNinjaString(scope, params.Description) 333 if err != nil { 334 return nil, fmt.Errorf("error parsing Description param: %s", err) 335 } 336 setVariable("description", value) 337 } 338 339 argNameScope := rule.scope() 340 341 if len(params.Args) > 0 { 342 b.Args = make(map[Variable]*ninjaString) 343 for name, value := range params.Args { 344 if !rule.isArg(name) { 345 return nil, fmt.Errorf("unknown argument %q", name) 346 } 347 348 argVar, err := argNameScope.LookupVariable(name) 349 if err != nil { 350 // This shouldn't happen. 351 return nil, fmt.Errorf("argument lookup error: %s", err) 352 } 353 354 ninjaValue, err := parseNinjaString(scope, value) 355 if err != nil { 356 return nil, fmt.Errorf("error parsing variable %q: %s", name, 357 err) 358 } 359 360 b.Args[argVar] = ninjaValue 361 } 362 } 363 364 return b, nil 365} 366 367func (b *buildDef) WriteTo(nw *ninjaWriter, pkgNames map[*packageContext]string) error { 368 var ( 369 comment = b.Comment 370 rule = b.Rule.fullName(pkgNames) 371 outputs = valueList(b.Outputs, pkgNames, outputEscaper) 372 implicitOuts = valueList(b.ImplicitOutputs, pkgNames, outputEscaper) 373 explicitDeps = valueList(b.Inputs, pkgNames, inputEscaper) 374 implicitDeps = valueList(b.Implicits, pkgNames, inputEscaper) 375 orderOnlyDeps = valueList(b.OrderOnly, pkgNames, inputEscaper) 376 ) 377 378 if b.RuleDef != nil { 379 implicitDeps = append(valueList(b.RuleDef.CommandDeps, pkgNames, inputEscaper), implicitDeps...) 380 orderOnlyDeps = append(valueList(b.RuleDef.CommandOrderOnly, pkgNames, inputEscaper), orderOnlyDeps...) 381 } 382 383 err := nw.Build(comment, rule, outputs, implicitOuts, explicitDeps, implicitDeps, orderOnlyDeps) 384 if err != nil { 385 return err 386 } 387 388 args := make(map[string]string) 389 390 for argVar, value := range b.Args { 391 args[argVar.fullName(pkgNames)] = value.Value(pkgNames) 392 } 393 394 err = writeVariables(nw, b.Variables, pkgNames) 395 if err != nil { 396 return err 397 } 398 399 var keys []string 400 for k := range args { 401 keys = append(keys, k) 402 } 403 sort.Strings(keys) 404 405 for _, name := range keys { 406 err = nw.ScopedAssign(name, args[name]) 407 if err != nil { 408 return err 409 } 410 } 411 412 if !b.Optional { 413 err = nw.Default(outputs...) 414 if err != nil { 415 return err 416 } 417 } 418 419 return nw.BlankLine() 420} 421 422func valueList(list []*ninjaString, pkgNames map[*packageContext]string, 423 escaper *strings.Replacer) []string { 424 425 result := make([]string, len(list)) 426 for i, ninjaStr := range list { 427 result[i] = ninjaStr.ValueWithEscaper(pkgNames, escaper) 428 } 429 return result 430} 431 432func writeVariables(nw *ninjaWriter, variables map[string]*ninjaString, 433 pkgNames map[*packageContext]string) error { 434 var keys []string 435 for k := range variables { 436 keys = append(keys, k) 437 } 438 sort.Strings(keys) 439 440 for _, name := range keys { 441 err := nw.ScopedAssign(name, variables[name].Value(pkgNames)) 442 if err != nil { 443 return err 444 } 445 } 446 return nil 447} 448