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 Validations []string // The list of validations to run when this rule runs. 91 Args map[string]string // The variable/value pairs to set. 92 Optional bool // Skip outputting a default statement 93} 94 95// A poolDef describes a pool definition. It does not include the name of the 96// pool. 97type poolDef struct { 98 Comment string 99 Depth int 100} 101 102func parsePoolParams(scope scope, params *PoolParams) (*poolDef, 103 error) { 104 105 def := &poolDef{ 106 Comment: params.Comment, 107 Depth: params.Depth, 108 } 109 110 return def, nil 111} 112 113func (p *poolDef) WriteTo(nw *ninjaWriter, name string) error { 114 if p.Comment != "" { 115 err := nw.Comment(p.Comment) 116 if err != nil { 117 return err 118 } 119 } 120 121 err := nw.Pool(name) 122 if err != nil { 123 return err 124 } 125 126 return nw.ScopedAssign("depth", strconv.Itoa(p.Depth)) 127} 128 129// A ruleDef describes a rule definition. It does not include the name of the 130// rule. 131type ruleDef struct { 132 CommandDeps []*ninjaString 133 CommandOrderOnly []*ninjaString 134 Comment string 135 Pool Pool 136 Variables map[string]*ninjaString 137} 138 139func parseRuleParams(scope scope, params *RuleParams) (*ruleDef, 140 error) { 141 142 r := &ruleDef{ 143 Comment: params.Comment, 144 Pool: params.Pool, 145 Variables: make(map[string]*ninjaString), 146 } 147 148 if params.Command == "" { 149 return nil, fmt.Errorf("encountered rule params with no command " + 150 "specified") 151 } 152 153 if r.Pool != nil && !scope.IsPoolVisible(r.Pool) { 154 return nil, fmt.Errorf("Pool %s is not visible in this scope", r.Pool) 155 } 156 157 value, err := parseNinjaString(scope, params.Command) 158 if err != nil { 159 return nil, fmt.Errorf("error parsing Command param: %s", err) 160 } 161 r.Variables["command"] = value 162 163 if params.Depfile != "" { 164 value, err = parseNinjaString(scope, params.Depfile) 165 if err != nil { 166 return nil, fmt.Errorf("error parsing Depfile param: %s", err) 167 } 168 r.Variables["depfile"] = value 169 } 170 171 if params.Deps != DepsNone { 172 r.Variables["deps"] = simpleNinjaString(params.Deps.String()) 173 } 174 175 if params.Description != "" { 176 value, err = parseNinjaString(scope, params.Description) 177 if err != nil { 178 return nil, fmt.Errorf("error parsing Description param: %s", err) 179 } 180 r.Variables["description"] = value 181 } 182 183 if params.Generator { 184 r.Variables["generator"] = simpleNinjaString("true") 185 } 186 187 if params.Restat { 188 r.Variables["restat"] = simpleNinjaString("true") 189 } 190 191 if params.Rspfile != "" { 192 value, err = parseNinjaString(scope, params.Rspfile) 193 if err != nil { 194 return nil, fmt.Errorf("error parsing Rspfile param: %s", err) 195 } 196 r.Variables["rspfile"] = value 197 } 198 199 if params.RspfileContent != "" { 200 value, err = parseNinjaString(scope, params.RspfileContent) 201 if err != nil { 202 return nil, fmt.Errorf("error parsing RspfileContent param: %s", 203 err) 204 } 205 r.Variables["rspfile_content"] = value 206 } 207 208 r.CommandDeps, err = parseNinjaStrings(scope, params.CommandDeps) 209 if err != nil { 210 return nil, fmt.Errorf("error parsing CommandDeps param: %s", err) 211 } 212 213 r.CommandOrderOnly, err = parseNinjaStrings(scope, params.CommandOrderOnly) 214 if err != nil { 215 return nil, fmt.Errorf("error parsing CommandDeps param: %s", err) 216 } 217 218 return r, nil 219} 220 221func (r *ruleDef) WriteTo(nw *ninjaWriter, name string, nameTracker *nameTracker) 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", nameTracker.Pool(r.Pool)) 237 if err != nil { 238 return err 239 } 240 } 241 242 err = writeVariables(nw, r.Variables, nameTracker) 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 OutputStrings []string 257 ImplicitOutputs []*ninjaString 258 ImplicitOutputStrings []string 259 Inputs []*ninjaString 260 InputStrings []string 261 Implicits []*ninjaString 262 ImplicitStrings []string 263 OrderOnly []*ninjaString 264 OrderOnlyStrings []string 265 Validations []*ninjaString 266 ValidationStrings []string 267 Args map[Variable]*ninjaString 268 Variables map[string]*ninjaString 269 Optional bool 270} 271 272func formatTags(tags map[string]string, rule Rule) string { 273 // Maps in golang do not have a guaranteed iteration order, nor is there an 274 // ordered map type in the stdlib, but we need to deterministically generate 275 // the ninja file. 276 keys := make([]string, 0, len(tags)) 277 for k := range tags { 278 keys = append(keys, k) 279 } 280 sort.Strings(keys) 281 pairs := make([]string, 0, len(keys)) 282 for _, k := range keys { 283 pairs = append(pairs, k+"="+tags[k]) 284 } 285 pairs = append(pairs, "rule_name="+rule.name()) 286 return strings.Join(pairs, ";") 287} 288 289func parseBuildParams(scope scope, params *BuildParams, 290 tags map[string]string) (*buildDef, error) { 291 292 comment := params.Comment 293 rule := params.Rule 294 295 b := &buildDef{ 296 Comment: comment, 297 Rule: rule, 298 } 299 300 setVariable := func(name string, value *ninjaString) { 301 if b.Variables == nil { 302 b.Variables = make(map[string]*ninjaString) 303 } 304 b.Variables[name] = value 305 } 306 307 if !scope.IsRuleVisible(rule) { 308 return nil, fmt.Errorf("Rule %s is not visible in this scope", rule) 309 } 310 311 if len(params.Outputs) == 0 { 312 return nil, errors.New("Outputs param has no elements") 313 } 314 315 var err error 316 b.Outputs, b.OutputStrings, err = parseNinjaOrSimpleStrings(scope, params.Outputs) 317 if err != nil { 318 return nil, fmt.Errorf("error parsing Outputs param: %s", err) 319 } 320 321 b.ImplicitOutputs, b.ImplicitOutputStrings, err = parseNinjaOrSimpleStrings(scope, params.ImplicitOutputs) 322 if err != nil { 323 return nil, fmt.Errorf("error parsing ImplicitOutputs param: %s", err) 324 } 325 326 b.Inputs, b.InputStrings, err = parseNinjaOrSimpleStrings(scope, params.Inputs) 327 if err != nil { 328 return nil, fmt.Errorf("error parsing Inputs param: %s", err) 329 } 330 331 b.Implicits, b.ImplicitStrings, err = parseNinjaOrSimpleStrings(scope, params.Implicits) 332 if err != nil { 333 return nil, fmt.Errorf("error parsing Implicits param: %s", err) 334 } 335 336 b.OrderOnly, b.OrderOnlyStrings, err = parseNinjaOrSimpleStrings(scope, params.OrderOnly) 337 if err != nil { 338 return nil, fmt.Errorf("error parsing OrderOnly param: %s", err) 339 } 340 341 b.Validations, b.ValidationStrings, err = parseNinjaOrSimpleStrings(scope, params.Validations) 342 if err != nil { 343 return nil, fmt.Errorf("error parsing Validations param: %s", err) 344 } 345 346 b.Optional = params.Optional 347 348 if params.Depfile != "" { 349 value, err := parseNinjaString(scope, params.Depfile) 350 if err != nil { 351 return nil, fmt.Errorf("error parsing Depfile param: %s", err) 352 } 353 setVariable("depfile", value) 354 } 355 356 if params.Deps != DepsNone { 357 setVariable("deps", simpleNinjaString(params.Deps.String())) 358 } 359 360 if params.Description != "" { 361 value, err := parseNinjaString(scope, params.Description) 362 if err != nil { 363 return nil, fmt.Errorf("error parsing Description param: %s", err) 364 } 365 setVariable("description", value) 366 } 367 368 if len(tags) > 0 { 369 setVariable("tags", simpleNinjaString(formatTags(tags, rule))) 370 } 371 372 argNameScope := rule.scope() 373 374 if len(params.Args) > 0 { 375 b.Args = make(map[Variable]*ninjaString) 376 for name, value := range params.Args { 377 if !rule.isArg(name) { 378 return nil, fmt.Errorf("unknown argument %q", name) 379 } 380 381 argVar, err := argNameScope.LookupVariable(name) 382 if err != nil { 383 // This shouldn't happen. 384 return nil, fmt.Errorf("argument lookup error: %s", err) 385 } 386 387 ninjaValue, err := parseNinjaString(scope, value) 388 if err != nil { 389 return nil, fmt.Errorf("error parsing variable %q: %s", name, 390 err) 391 } 392 393 b.Args[argVar] = ninjaValue 394 } 395 } 396 397 return b, nil 398} 399 400func (b *buildDef) WriteTo(nw *ninjaWriter, nameTracker *nameTracker) error { 401 var ( 402 comment = b.Comment 403 rule = nameTracker.Rule(b.Rule) 404 outputs = b.Outputs 405 implicitOuts = b.ImplicitOutputs 406 explicitDeps = b.Inputs 407 implicitDeps = b.Implicits 408 orderOnlyDeps = b.OrderOnly 409 validations = b.Validations 410 outputStrings = b.OutputStrings 411 implicitOutStrings = b.ImplicitOutputStrings 412 explicitDepStrings = b.InputStrings 413 implicitDepStrings = b.ImplicitStrings 414 orderOnlyDepStrings = b.OrderOnlyStrings 415 validationStrings = b.ValidationStrings 416 ) 417 418 if b.RuleDef != nil { 419 implicitDeps = append(b.RuleDef.CommandDeps, implicitDeps...) 420 orderOnlyDeps = append(b.RuleDef.CommandOrderOnly, orderOnlyDeps...) 421 } 422 423 err := nw.Build(comment, rule, outputs, implicitOuts, explicitDeps, implicitDeps, orderOnlyDeps, validations, 424 outputStrings, implicitOutStrings, explicitDepStrings, 425 implicitDepStrings, orderOnlyDepStrings, validationStrings, 426 nameTracker) 427 if err != nil { 428 return err 429 } 430 431 err = writeVariables(nw, b.Variables, nameTracker) 432 if err != nil { 433 return err 434 } 435 436 type nameValuePair struct { 437 name, value string 438 } 439 440 args := make([]nameValuePair, 0, len(b.Args)) 441 442 for argVar, value := range b.Args { 443 fullName := nameTracker.Variable(argVar) 444 args = append(args, nameValuePair{fullName, value.Value(nameTracker)}) 445 } 446 sort.Slice(args, func(i, j int) bool { return args[i].name < args[j].name }) 447 448 for _, pair := range args { 449 err = nw.ScopedAssign(pair.name, pair.value) 450 if err != nil { 451 return err 452 } 453 } 454 455 if !b.Optional { 456 err = nw.Default(nameTracker, outputs, outputStrings) 457 if err != nil { 458 return err 459 } 460 } 461 462 return nw.BlankLine() 463} 464 465func writeVariables(nw *ninjaWriter, variables map[string]*ninjaString, nameTracker *nameTracker) error { 466 var keys []string 467 for k := range variables { 468 keys = append(keys, k) 469 } 470 sort.Strings(keys) 471 472 for _, name := range keys { 473 err := nw.ScopedAssign(name, variables[name].Value(nameTracker)) 474 if err != nil { 475 return err 476 } 477 } 478 return nil 479} 480