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 	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 	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.
     95 type poolDef struct {
     96 	Comment string
     97 	Depth   int
     98 }
     99 
    100 func 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 
    111 func (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.
    129 type ruleDef struct {
    130 	CommandDeps []*ninjaString
    131 	Comment     string
    132 	Pool        Pool
    133 	Variables   map[string]*ninjaString
    134 }
    135 
    136 func 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 
    213 func (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.
    244 type 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 
    258 func 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 
    360 func (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 
    411 func 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 
    421 func 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