github.com/colincross/blueprint@v0.0.0-20150626231830-9c067caf2eb5/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.  Each field except for Comment corresponds with a Ninja variable
    57  // of the same name.
    58  type RuleParams struct {
    59  	Comment        string // The comment that will appear above the definition.
    60  	Command        string // The command that Ninja will run for the rule.
    61  	Depfile        string // The dependency file name.
    62  	Deps           Deps   // The format of the dependency file.
    63  	Description    string // The description that Ninja will print for the rule.
    64  	Generator      bool   // Whether the rule generates the Ninja manifest file.
    65  	Pool           Pool   // The Ninja pool to which the rule belongs.
    66  	Restat         bool   // Whether Ninja should re-stat the rule's outputs.
    67  	Rspfile        string // The response file.
    68  	RspfileContent string // The response file content.
    69  }
    70  
    71  // A BuildParams object contains the set of parameters that make up a Ninja
    72  // build statement.  Each field except for Args corresponds with a part of the
    73  // Ninja build statement.  The Args field contains variable names and values
    74  // that are set within the build statement's scope in the Ninja file.
    75  type BuildParams struct {
    76  	Rule      Rule              // The rule to invoke.
    77  	Outputs   []string          // The list of output targets.
    78  	Inputs    []string          // The list of explicit input dependencies.
    79  	Implicits []string          // The list of implicit dependencies.
    80  	OrderOnly []string          // The list of order-only dependencies.
    81  	Args      map[string]string // The variable/value pairs to set.
    82  	Optional  bool              // Skip outputting a default statement
    83  }
    84  
    85  // A poolDef describes a pool definition.  It does not include the name of the
    86  // pool.
    87  type poolDef struct {
    88  	Comment string
    89  	Depth   int
    90  }
    91  
    92  func parsePoolParams(scope scope, params *PoolParams) (*poolDef,
    93  	error) {
    94  
    95  	def := &poolDef{
    96  		Comment: params.Comment,
    97  		Depth:   params.Depth,
    98  	}
    99  
   100  	return def, nil
   101  }
   102  
   103  func (p *poolDef) WriteTo(nw *ninjaWriter, name string) error {
   104  	if p.Comment != "" {
   105  		err := nw.Comment(p.Comment)
   106  		if err != nil {
   107  			return err
   108  		}
   109  	}
   110  
   111  	err := nw.Pool(name)
   112  	if err != nil {
   113  		return err
   114  	}
   115  
   116  	return nw.ScopedAssign("depth", strconv.Itoa(p.Depth))
   117  }
   118  
   119  // A ruleDef describes a rule definition.  It does not include the name of the
   120  // rule.
   121  type ruleDef struct {
   122  	Comment   string
   123  	Pool      Pool
   124  	Variables map[string]*ninjaString
   125  }
   126  
   127  func parseRuleParams(scope scope, params *RuleParams) (*ruleDef,
   128  	error) {
   129  
   130  	r := &ruleDef{
   131  		Comment:   params.Comment,
   132  		Pool:      params.Pool,
   133  		Variables: make(map[string]*ninjaString),
   134  	}
   135  
   136  	if params.Command == "" {
   137  		return nil, fmt.Errorf("encountered rule params with no command " +
   138  			"specified")
   139  	}
   140  
   141  	if r.Pool != nil && !scope.IsPoolVisible(r.Pool) {
   142  		return nil, fmt.Errorf("Pool %s is not visible in this scope", r.Pool)
   143  	}
   144  
   145  	value, err := parseNinjaString(scope, params.Command)
   146  	if err != nil {
   147  		return nil, fmt.Errorf("error parsing Command param: %s", err)
   148  	}
   149  	r.Variables["command"] = value
   150  
   151  	if params.Depfile != "" {
   152  		value, err = parseNinjaString(scope, params.Depfile)
   153  		if err != nil {
   154  			return nil, fmt.Errorf("error parsing Depfile param: %s", err)
   155  		}
   156  		r.Variables["depfile"] = value
   157  	}
   158  
   159  	if params.Deps != DepsNone {
   160  		r.Variables["deps"] = simpleNinjaString(params.Deps.String())
   161  	}
   162  
   163  	if params.Description != "" {
   164  		value, err = parseNinjaString(scope, params.Description)
   165  		if err != nil {
   166  			return nil, fmt.Errorf("error parsing Description param: %s", err)
   167  		}
   168  		r.Variables["description"] = value
   169  	}
   170  
   171  	if params.Generator {
   172  		r.Variables["generator"] = simpleNinjaString("true")
   173  	}
   174  
   175  	if params.Restat {
   176  		r.Variables["restat"] = simpleNinjaString("true")
   177  	}
   178  
   179  	if params.Rspfile != "" {
   180  		value, err = parseNinjaString(scope, params.Rspfile)
   181  		if err != nil {
   182  			return nil, fmt.Errorf("error parsing Rspfile param: %s", err)
   183  		}
   184  		r.Variables["rspfile"] = value
   185  	}
   186  
   187  	if params.RspfileContent != "" {
   188  		value, err = parseNinjaString(scope, params.RspfileContent)
   189  		if err != nil {
   190  			return nil, fmt.Errorf("error parsing RspfileContent param: %s",
   191  				err)
   192  		}
   193  		r.Variables["rspfile_content"] = value
   194  	}
   195  
   196  	return r, nil
   197  }
   198  
   199  func (r *ruleDef) WriteTo(nw *ninjaWriter, name string,
   200  	pkgNames map[*PackageContext]string) error {
   201  
   202  	if r.Comment != "" {
   203  		err := nw.Comment(r.Comment)
   204  		if err != nil {
   205  			return err
   206  		}
   207  	}
   208  
   209  	err := nw.Rule(name)
   210  	if err != nil {
   211  		return err
   212  	}
   213  
   214  	if r.Pool != nil {
   215  		err = nw.ScopedAssign("pool", r.Pool.fullName(pkgNames))
   216  		if err != nil {
   217  			return err
   218  		}
   219  	}
   220  
   221  	var keys []string
   222  	for k := range r.Variables {
   223  		keys = append(keys, k)
   224  	}
   225  	sort.Strings(keys)
   226  
   227  	for _, name := range keys {
   228  		err = nw.ScopedAssign(name, r.Variables[name].Value(pkgNames))
   229  		if err != nil {
   230  			return err
   231  		}
   232  	}
   233  
   234  	return nil
   235  }
   236  
   237  // A buildDef describes a build target definition.
   238  type buildDef struct {
   239  	Rule      Rule
   240  	Outputs   []*ninjaString
   241  	Inputs    []*ninjaString
   242  	Implicits []*ninjaString
   243  	OrderOnly []*ninjaString
   244  	Args      map[Variable]*ninjaString
   245  	Optional  bool
   246  }
   247  
   248  func parseBuildParams(scope scope, params *BuildParams) (*buildDef,
   249  	error) {
   250  
   251  	rule := params.Rule
   252  
   253  	b := &buildDef{
   254  		Rule: rule,
   255  	}
   256  
   257  	if !scope.IsRuleVisible(rule) {
   258  		return nil, fmt.Errorf("Rule %s is not visible in this scope", rule)
   259  	}
   260  
   261  	if len(params.Outputs) == 0 {
   262  		return nil, errors.New("Outputs param has no elements")
   263  	}
   264  
   265  	var err error
   266  	b.Outputs, err = parseNinjaStrings(scope, params.Outputs)
   267  	if err != nil {
   268  		return nil, fmt.Errorf("error parsing Outputs param: %s", err)
   269  	}
   270  
   271  	b.Inputs, err = parseNinjaStrings(scope, params.Inputs)
   272  	if err != nil {
   273  		return nil, fmt.Errorf("error parsing Inputs param: %s", err)
   274  	}
   275  
   276  	b.Implicits, err = parseNinjaStrings(scope, params.Implicits)
   277  	if err != nil {
   278  		return nil, fmt.Errorf("error parsing Implicits param: %s", err)
   279  	}
   280  
   281  	b.OrderOnly, err = parseNinjaStrings(scope, params.OrderOnly)
   282  	if err != nil {
   283  		return nil, fmt.Errorf("error parsing OrderOnly param: %s", err)
   284  	}
   285  
   286  	b.Optional = params.Optional
   287  
   288  	argNameScope := rule.scope()
   289  
   290  	if len(params.Args) > 0 {
   291  		b.Args = make(map[Variable]*ninjaString)
   292  		for name, value := range params.Args {
   293  			if !rule.isArg(name) {
   294  				return nil, fmt.Errorf("unknown argument %q", name)
   295  			}
   296  
   297  			argVar, err := argNameScope.LookupVariable(name)
   298  			if err != nil {
   299  				// This shouldn't happen.
   300  				return nil, fmt.Errorf("argument lookup error: %s", err)
   301  			}
   302  
   303  			ninjaValue, err := parseNinjaString(scope, value)
   304  			if err != nil {
   305  				return nil, fmt.Errorf("error parsing variable %q: %s", name,
   306  					err)
   307  			}
   308  
   309  			b.Args[argVar] = ninjaValue
   310  		}
   311  	}
   312  
   313  	return b, nil
   314  }
   315  
   316  func (b *buildDef) WriteTo(nw *ninjaWriter, pkgNames map[*PackageContext]string) error {
   317  	var (
   318  		rule          = b.Rule.fullName(pkgNames)
   319  		outputs       = valueList(b.Outputs, pkgNames, outputEscaper)
   320  		explicitDeps  = valueList(b.Inputs, pkgNames, inputEscaper)
   321  		implicitDeps  = valueList(b.Implicits, pkgNames, inputEscaper)
   322  		orderOnlyDeps = valueList(b.OrderOnly, pkgNames, inputEscaper)
   323  	)
   324  	err := nw.Build(rule, outputs, explicitDeps, implicitDeps, orderOnlyDeps)
   325  	if err != nil {
   326  		return err
   327  	}
   328  
   329  	args := make(map[string]string)
   330  
   331  	for argVar, value := range b.Args {
   332  		args[argVar.fullName(pkgNames)] = value.Value(pkgNames)
   333  	}
   334  
   335  	var keys []string
   336  	for k := range args {
   337  		keys = append(keys, k)
   338  	}
   339  	sort.Strings(keys)
   340  
   341  	for _, name := range keys {
   342  		err = nw.ScopedAssign(name, args[name])
   343  		if err != nil {
   344  			return err
   345  		}
   346  	}
   347  
   348  	if !b.Optional {
   349  		nw.Default(outputs...)
   350  	}
   351  
   352  	return nil
   353  }
   354  
   355  func valueList(list []*ninjaString, pkgNames map[*PackageContext]string,
   356  	escaper *strings.Replacer) []string {
   357  
   358  	result := make([]string, len(list))
   359  	for i, ninjaStr := range list {
   360  		result[i] = ninjaStr.ValueWithEscaper(pkgNames, escaper)
   361  	}
   362  	return result
   363  }