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