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 	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