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