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