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