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 	"fmt"
     19 
     20 	"github.com/google/blueprint/pathtools"
     21 )
     22 
     23 type Singleton interface {
     24 	GenerateBuildActions(SingletonContext)
     25 }
     26 
     27 type SingletonContext interface {
     28 	Config() interface{}
     29 
     30 	Name() string
     31 
     32 	ModuleName(module Module) string
     33 	ModuleDir(module Module) string
     34 	ModuleSubDir(module Module) string
     35 	ModuleType(module Module) string
     36 	BlueprintFile(module Module) string
     37 
     38 	ModuleErrorf(module Module, format string, args ...interface{})
     39 	Errorf(format string, args ...interface{})
     40 	Failed() bool
     41 
     42 	Variable(pctx PackageContext, name, value string)
     43 	Rule(pctx PackageContext, name string, params RuleParams, argNames ...string) Rule
     44 	Build(pctx PackageContext, params BuildParams)
     45 	RequireNinjaVersion(major, minor, micro int)
     46 
     47 	// SetNinjaBuildDir sets the value of the top-level "builddir" Ninja variable
     48 	// that controls where Ninja stores its build log files.  This value can be
     49 	// set at most one time for a single build, later calls are ignored.
     50 	SetNinjaBuildDir(pctx PackageContext, value string)
     51 
     52 	// AddSubninja adds a ninja file to include with subninja. This should likely
     53 	// only ever be used inside bootstrap to handle glob rules.
     54 	AddSubninja(file string)
     55 
     56 	// Eval takes a string with embedded ninja variables, and returns a string
     57 	// with all of the variables recursively expanded. Any variables references
     58 	// are expanded in the scope of the PackageContext.
     59 	Eval(pctx PackageContext, ninjaStr string) (string, error)
     60 
     61 	VisitAllModules(visit func(Module))
     62 	VisitAllModulesIf(pred func(Module) bool, visit func(Module))
     63 	VisitDepsDepthFirst(module Module, visit func(Module))
     64 	VisitDepsDepthFirstIf(module Module, pred func(Module) bool,
     65 		visit func(Module))
     66 
     67 	VisitAllModuleVariants(module Module, visit func(Module))
     68 
     69 	PrimaryModule(module Module) Module
     70 	FinalModule(module Module) Module
     71 
     72 	AddNinjaFileDeps(deps ...string)
     73 
     74 	// GlobWithDeps returns a list of files and directories that match the
     75 	// specified pattern but do not match any of the patterns in excludes.
     76 	// Any directories will have a '/' suffix. It also adds efficient
     77 	// dependencies to rerun the primary builder whenever a file matching
     78 	// the pattern as added or removed, without rerunning if a file that
     79 	// does not match the pattern is added to a searched directory.
     80 	GlobWithDeps(pattern string, excludes []string) ([]string, error)
     81 
     82 	Fs() pathtools.FileSystem
     83 }
     84 
     85 var _ SingletonContext = (*singletonContext)(nil)
     86 
     87 type singletonContext struct {
     88 	name    string
     89 	context *Context
     90 	config  interface{}
     91 	scope   *localScope
     92 	globals *liveTracker
     93 
     94 	ninjaFileDeps []string
     95 	errs          []error
     96 
     97 	actionDefs localBuildActions
     98 }
     99 
    100 func (s *singletonContext) Config() interface{} {
    101 	return s.config
    102 }
    103 
    104 func (s *singletonContext) Name() string {
    105 	return s.name
    106 }
    107 
    108 func (s *singletonContext) ModuleName(logicModule Module) string {
    109 	return s.context.ModuleName(logicModule)
    110 }
    111 
    112 func (s *singletonContext) ModuleDir(logicModule Module) string {
    113 	return s.context.ModuleDir(logicModule)
    114 }
    115 
    116 func (s *singletonContext) ModuleSubDir(logicModule Module) string {
    117 	return s.context.ModuleSubDir(logicModule)
    118 }
    119 
    120 func (s *singletonContext) ModuleType(logicModule Module) string {
    121 	return s.context.ModuleType(logicModule)
    122 }
    123 
    124 func (s *singletonContext) BlueprintFile(logicModule Module) string {
    125 	return s.context.BlueprintFile(logicModule)
    126 }
    127 
    128 func (s *singletonContext) error(err error) {
    129 	if err != nil {
    130 		s.errs = append(s.errs, err)
    131 	}
    132 }
    133 
    134 func (s *singletonContext) ModuleErrorf(logicModule Module, format string,
    135 	args ...interface{}) {
    136 
    137 	s.error(s.context.ModuleErrorf(logicModule, format, args...))
    138 }
    139 
    140 func (s *singletonContext) Errorf(format string, args ...interface{}) {
    141 	// TODO: Make this not result in the error being printed as "internal error"
    142 	s.error(fmt.Errorf(format, args...))
    143 }
    144 
    145 func (s *singletonContext) Failed() bool {
    146 	return len(s.errs) > 0
    147 }
    148 
    149 func (s *singletonContext) Variable(pctx PackageContext, name, value string) {
    150 	s.scope.ReparentTo(pctx)
    151 
    152 	v, err := s.scope.AddLocalVariable(name, value)
    153 	if err != nil {
    154 		panic(err)
    155 	}
    156 
    157 	s.actionDefs.variables = append(s.actionDefs.variables, v)
    158 }
    159 
    160 func (s *singletonContext) Rule(pctx PackageContext, name string,
    161 	params RuleParams, argNames ...string) Rule {
    162 
    163 	s.scope.ReparentTo(pctx)
    164 
    165 	r, err := s.scope.AddLocalRule(name, &params, argNames...)
    166 	if err != nil {
    167 		panic(err)
    168 	}
    169 
    170 	s.actionDefs.rules = append(s.actionDefs.rules, r)
    171 
    172 	return r
    173 }
    174 
    175 func (s *singletonContext) Build(pctx PackageContext, params BuildParams) {
    176 	s.scope.ReparentTo(pctx)
    177 
    178 	def, err := parseBuildParams(s.scope, &params)
    179 	if err != nil {
    180 		panic(err)
    181 	}
    182 
    183 	s.actionDefs.buildDefs = append(s.actionDefs.buildDefs, def)
    184 }
    185 
    186 func (s *singletonContext) Eval(pctx PackageContext, str string) (string, error) {
    187 	s.scope.ReparentTo(pctx)
    188 
    189 	ninjaStr, err := parseNinjaString(s.scope, str)
    190 	if err != nil {
    191 		return "", err
    192 	}
    193 
    194 	err = s.globals.addNinjaStringDeps(ninjaStr)
    195 	if err != nil {
    196 		return "", err
    197 	}
    198 
    199 	return ninjaStr.Eval(s.globals.variables)
    200 }
    201 
    202 func (s *singletonContext) RequireNinjaVersion(major, minor, micro int) {
    203 	s.context.requireNinjaVersion(major, minor, micro)
    204 }
    205 
    206 func (s *singletonContext) SetNinjaBuildDir(pctx PackageContext, value string) {
    207 	s.scope.ReparentTo(pctx)
    208 
    209 	ninjaValue, err := parseNinjaString(s.scope, value)
    210 	if err != nil {
    211 		panic(err)
    212 	}
    213 
    214 	s.context.setNinjaBuildDir(ninjaValue)
    215 }
    216 
    217 func (s *singletonContext) AddSubninja(file string) {
    218 	s.context.subninjas = append(s.context.subninjas, file)
    219 }
    220 
    221 func (s *singletonContext) VisitAllModules(visit func(Module)) {
    222 	var visitingModule Module
    223 	defer func() {
    224 		if r := recover(); r != nil {
    225 			panic(newPanicErrorf(r, "VisitAllModules(%s) for module %s",
    226 				funcName(visit), visitingModule))
    227 		}
    228 	}()
    229 
    230 	s.context.VisitAllModules(func(m Module) {
    231 		visitingModule = m
    232 		visit(m)
    233 	})
    234 }
    235 
    236 func (s *singletonContext) VisitAllModulesIf(pred func(Module) bool,
    237 	visit func(Module)) {
    238 
    239 	s.context.VisitAllModulesIf(pred, visit)
    240 }
    241 
    242 func (s *singletonContext) VisitDepsDepthFirst(module Module,
    243 	visit func(Module)) {
    244 
    245 	s.context.VisitDepsDepthFirst(module, visit)
    246 }
    247 
    248 func (s *singletonContext) VisitDepsDepthFirstIf(module Module,
    249 	pred func(Module) bool, visit func(Module)) {
    250 
    251 	s.context.VisitDepsDepthFirstIf(module, pred, visit)
    252 }
    253 
    254 func (s *singletonContext) PrimaryModule(module Module) Module {
    255 	return s.context.PrimaryModule(module)
    256 }
    257 
    258 func (s *singletonContext) FinalModule(module Module) Module {
    259 	return s.context.FinalModule(module)
    260 }
    261 
    262 func (s *singletonContext) VisitAllModuleVariants(module Module, visit func(Module)) {
    263 	s.context.VisitAllModuleVariants(module, visit)
    264 }
    265 
    266 func (s *singletonContext) AddNinjaFileDeps(deps ...string) {
    267 	s.ninjaFileDeps = append(s.ninjaFileDeps, deps...)
    268 }
    269 
    270 func (s *singletonContext) GlobWithDeps(pattern string,
    271 	excludes []string) ([]string, error) {
    272 	return s.context.glob(pattern, excludes)
    273 }
    274 
    275 func (s *singletonContext) Fs() pathtools.FileSystem {
    276 	return s.context.fs
    277 }
    278