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 	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.
     94 type poolDef struct {
     95 	Comment string
     96 	Depth   int
     97 }
     98 
     99 func 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 
    110 func (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.
    128 type ruleDef struct {
    129 	CommandDeps []*ninjaString
    130 	Comment     string
    131 	Pool        Pool
    132 	Variables   map[string]*ninjaString
    133 }
    134 
    135 func 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 
    212 func (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.
    243 type 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 
    257 func 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 
    350 func (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 
    401 func 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 
    411 func 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