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