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