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