diff --git a/bootstrap/bootstrap.go b/bootstrap/bootstrap.go index 1dce684..e0a6e06 100644 --- a/bootstrap/bootstrap.go +++ b/bootstrap/bootstrap.go @@ -149,7 +149,7 @@ var ( }, "depfile") - _ = pctx.VariableFunc("ToolDir", func(config interface{}) (string, error) { + _ = pctx.VariableFunc("ToolDir", func(ctx blueprint.VariableFuncContext, config interface{}) (string, error) { return config.(BootstrapConfig).HostToolDir(), nil }) ) diff --git a/bootstrap/config.go b/bootstrap/config.go index 802aa05..1d256ba 100644 --- a/bootstrap/config.go +++ b/bootstrap/config.go @@ -25,7 +25,7 @@ import ( ) func bootstrapVariable(name string, value func(BootstrapConfig) string) blueprint.Variable { - return pctx.VariableFunc(name, func(config interface{}) (string, error) { + return pctx.VariableFunc(name, func(ctx blueprint.VariableFuncContext, config interface{}) (string, error) { c, ok := config.(BootstrapConfig) if !ok { panic(fmt.Sprintf("Bootstrap rules were passed a configuration that does not include theirs, config=%q", diff --git a/bootstrap/glob.go b/bootstrap/glob.go index 70495dc..77de891 100644 --- a/bootstrap/glob.go +++ b/bootstrap/glob.go @@ -44,7 +44,7 @@ import ( // in a build failure with a "missing and no known rule to make it" error. var ( - _ = pctx.VariableFunc("globCmd", func(config interface{}) (string, error) { + _ = pctx.VariableFunc("globCmd", func(ctx blueprint.VariableFuncContext, config interface{}) (string, error) { return filepath.Join(config.(BootstrapConfig).SoongOutDir(), "bpglob"), nil }) diff --git a/context.go b/context.go index 5448f1e..fa6c1fe 100644 --- a/context.go +++ b/context.go @@ -1798,7 +1798,7 @@ func (c *Context) resolveDependencies(ctx context.Context, config interface{}) ( pprof.Do(ctx, pprof.Labels("blueprint", "ResolveDependencies"), func(ctx context.Context) { c.initProviders() - c.liveGlobals = newLiveTracker(config) + c.liveGlobals = newLiveTracker(c, config) deps, errs = c.generateSingletonBuildActions(config, c.preSingletonInfo, c.liveGlobals) if len(errs) > 0 { @@ -4413,7 +4413,7 @@ func (c *Context) writeLocalBuildActions(nw *ninjaWriter, // A localVariable doesn't need the package names or config to // determine its name or value. name := v.fullName(nil) - value, err := v.value(nil) + value, err := v.value(nil, nil) if err != nil { panic(err) } diff --git a/live_tracker.go b/live_tracker.go index 1d48e58..ef9c8a9 100644 --- a/live_tracker.go +++ b/live_tracker.go @@ -23,14 +23,16 @@ import "sync" type liveTracker struct { sync.Mutex config interface{} // Used to evaluate variable, rule, and pool values. + ctx *Context // Used to evaluate globs variables map[Variable]ninjaString pools map[Pool]*poolDef rules map[Rule]*ruleDef } -func newLiveTracker(config interface{}) *liveTracker { +func newLiveTracker(ctx *Context, config interface{}) *liveTracker { return &liveTracker{ + ctx: ctx, config: config, variables: make(map[Variable]ninjaString), pools: make(map[Pool]*poolDef), @@ -153,7 +155,9 @@ func (l *liveTracker) addPool(p Pool) error { func (l *liveTracker) addVariable(v Variable) error { _, ok := l.variables[v] if !ok { - value, err := v.value(l.config) + ctx := &variableFuncContext{l.ctx} + + value, err := v.value(ctx, l.config) if err == errVariableIsArg { // This variable is a placeholder for an argument that can be passed // to a rule. It has no value and thus doesn't reference any other diff --git a/package_ctx.go b/package_ctx.go index 1eafdb9..07b7a9c 100644 --- a/package_ctx.go +++ b/package_ctx.go @@ -59,7 +59,7 @@ type PackageContext interface { ImportAs(as, pkgPath string) StaticVariable(name, value string) Variable - VariableFunc(name string, f func(config interface{}) (string, error)) Variable + VariableFunc(name string, f func(ctx VariableFuncContext, config interface{}) (string, error)) Variable VariableConfigMethod(name string, method interface{}) Variable StaticPool(name string, params PoolParams) Pool @@ -304,7 +304,7 @@ func (v *staticVariable) memoizeFullName(pkgNames map[*packageContext]string) { v.fullName_ = v.fullName(pkgNames) } -func (v *staticVariable) value(interface{}) (ninjaString, error) { +func (v *staticVariable) value(VariableFuncContext, interface{}) (ninjaString, error) { ninjaStr, err := parseNinjaString(v.pctx.scope, v.value_) if err != nil { err = fmt.Errorf("error parsing variable %s value: %s", v, err) @@ -320,10 +320,30 @@ func (v *staticVariable) String() string { type variableFunc struct { pctx *packageContext name_ string - value_ func(interface{}) (string, error) + value_ func(VariableFuncContext, interface{}) (string, error) fullName_ string } +// VariableFuncContext is passed to VariableFunc functions. +type VariableFuncContext interface { + // GlobWithDeps returns a list of files and directories that match the + // specified pattern but do not match any of the patterns in excludes. + // Any directories will have a '/' suffix. It also adds efficient + // dependencies to rerun the primary builder whenever a file matching + // the pattern as added or removed, without rerunning if a file that + // does not match the pattern is added to a searched directory. + GlobWithDeps(globPattern string, excludes []string) ([]string, error) +} + +type variableFuncContext struct { + context *Context +} + +func (v *variableFuncContext) GlobWithDeps(pattern string, + excludes []string) ([]string, error) { + return v.context.glob(pattern, excludes) +} + // VariableFunc returns a Variable whose value is determined by a function that // takes a config object as input and returns either the variable value or an // error. It may only be called during a Go package's initialization - either @@ -336,7 +356,7 @@ type variableFunc struct { // reference other Ninja variables that are visible within the calling Go // package. func (p *packageContext) VariableFunc(name string, - f func(config interface{}) (string, error)) Variable { + f func(ctx VariableFuncContext, config interface{}) (string, error)) Variable { checkCalledFromInit() @@ -382,7 +402,7 @@ func (p *packageContext) VariableConfigMethod(name string, methodValue := reflect.ValueOf(method) validateVariableMethod(name, methodValue) - fun := func(config interface{}) (string, error) { + fun := func(ctx VariableFuncContext, config interface{}) (string, error) { result := methodValue.Call([]reflect.Value{reflect.ValueOf(config)}) resultStr := result[0].Interface().(string) return resultStr, nil @@ -420,8 +440,8 @@ func (v *variableFunc) memoizeFullName(pkgNames map[*packageContext]string) { v.fullName_ = v.fullName(pkgNames) } -func (v *variableFunc) value(config interface{}) (ninjaString, error) { - value, err := v.value_(config) +func (v *variableFunc) value(ctx VariableFuncContext, config interface{}) (ninjaString, error) { + value, err := v.value_(ctx, config) if err != nil { return nil, err } @@ -484,7 +504,7 @@ func (v *argVariable) memoizeFullName(pkgNames map[*packageContext]string) { // Nothing to do, full name is known at initialization. } -func (v *argVariable) value(config interface{}) (ninjaString, error) { +func (v *argVariable) value(ctx VariableFuncContext, config interface{}) (ninjaString, error) { return nil, errVariableIsArg } diff --git a/scope.go b/scope.go index 3f39eb7..9585559 100644 --- a/scope.go +++ b/scope.go @@ -29,7 +29,7 @@ type Variable interface { name() string // "foo" fullName(pkgNames map[*packageContext]string) string // "pkg.foo" or "path.to.pkg.foo" memoizeFullName(pkgNames map[*packageContext]string) // precompute fullName if desired - value(config interface{}) (ninjaString, error) + value(ctx VariableFuncContext, config interface{}) (ninjaString, error) String() string } @@ -373,7 +373,7 @@ func (l *localVariable) memoizeFullName(pkgNames map[*packageContext]string) { // Nothing to do, full name is known at initialization. } -func (l *localVariable) value(interface{}) (ninjaString, error) { +func (l *localVariable) value(VariableFuncContext, interface{}) (ninjaString, error) { return l.value_, nil }