github.com/westcoastroms/westcoastroms-build@v0.0.0-20190928114312-2350e5a73030/build/blueprint/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.
    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  		nw.Default(outputs...)
   414  	}
   415  
   416  	return nw.BlankLine()
   417  }
   418  
   419  func valueList(list []*ninjaString, pkgNames map[*packageContext]string,
   420  	escaper *strings.Replacer) []string {
   421  
   422  	result := make([]string, len(list))
   423  	for i, ninjaStr := range list {
   424  		result[i] = ninjaStr.ValueWithEscaper(pkgNames, escaper)
   425  	}
   426  	return result
   427  }
   428  
   429  func writeVariables(nw *ninjaWriter, variables map[string]*ninjaString,
   430  	pkgNames map[*packageContext]string) error {
   431  	var keys []string
   432  	for k := range variables {
   433  		keys = append(keys, k)
   434  	}
   435  	sort.Strings(keys)
   436  
   437  	for _, name := range keys {
   438  		err := nw.ScopedAssign(name, variables[name].Value(pkgNames))
   439  		if err != nil {
   440  			return err
   441  		}
   442  	}
   443  	return nil
   444  }