Add VariableFuncContext argument to VariableFuncs

Add a VariableFuncContext argument to VariableFuncs that implements
GlobWithDeps.  This will allow Soong to use optimized glob dependencies
in VariableFuncs.

Bug: 257079828
Test: no dependencies on directories in build.ninja.d
Change-Id: Iee5fc9c9ae3087662a5d1a3d7323a87462299205
This commit is contained in:
Colin Cross 2022-11-03 20:19:48 -07:00
parent 2a9208713e
commit 1b457a5e10
7 changed files with 41 additions and 17 deletions

View file

@ -149,7 +149,7 @@ var (
}, },
"depfile") "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 return config.(BootstrapConfig).HostToolDir(), nil
}) })
) )

View file

@ -25,7 +25,7 @@ import (
) )
func bootstrapVariable(name string, value func(BootstrapConfig) string) blueprint.Variable { 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) c, ok := config.(BootstrapConfig)
if !ok { if !ok {
panic(fmt.Sprintf("Bootstrap rules were passed a configuration that does not include theirs, config=%q", panic(fmt.Sprintf("Bootstrap rules were passed a configuration that does not include theirs, config=%q",

View file

@ -44,7 +44,7 @@ import (
// in a build failure with a "missing and no known rule to make it" error. // in a build failure with a "missing and no known rule to make it" error.
var ( 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 return filepath.Join(config.(BootstrapConfig).SoongOutDir(), "bpglob"), nil
}) })

View file

@ -1798,7 +1798,7 @@ func (c *Context) resolveDependencies(ctx context.Context, config interface{}) (
pprof.Do(ctx, pprof.Labels("blueprint", "ResolveDependencies"), func(ctx context.Context) { pprof.Do(ctx, pprof.Labels("blueprint", "ResolveDependencies"), func(ctx context.Context) {
c.initProviders() c.initProviders()
c.liveGlobals = newLiveTracker(config) c.liveGlobals = newLiveTracker(c, config)
deps, errs = c.generateSingletonBuildActions(config, c.preSingletonInfo, c.liveGlobals) deps, errs = c.generateSingletonBuildActions(config, c.preSingletonInfo, c.liveGlobals)
if len(errs) > 0 { 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 // A localVariable doesn't need the package names or config to
// determine its name or value. // determine its name or value.
name := v.fullName(nil) name := v.fullName(nil)
value, err := v.value(nil) value, err := v.value(nil, nil)
if err != nil { if err != nil {
panic(err) panic(err)
} }

View file

@ -23,14 +23,16 @@ import "sync"
type liveTracker struct { type liveTracker struct {
sync.Mutex sync.Mutex
config interface{} // Used to evaluate variable, rule, and pool values. config interface{} // Used to evaluate variable, rule, and pool values.
ctx *Context // Used to evaluate globs
variables map[Variable]ninjaString variables map[Variable]ninjaString
pools map[Pool]*poolDef pools map[Pool]*poolDef
rules map[Rule]*ruleDef rules map[Rule]*ruleDef
} }
func newLiveTracker(config interface{}) *liveTracker { func newLiveTracker(ctx *Context, config interface{}) *liveTracker {
return &liveTracker{ return &liveTracker{
ctx: ctx,
config: config, config: config,
variables: make(map[Variable]ninjaString), variables: make(map[Variable]ninjaString),
pools: make(map[Pool]*poolDef), pools: make(map[Pool]*poolDef),
@ -153,7 +155,9 @@ func (l *liveTracker) addPool(p Pool) error {
func (l *liveTracker) addVariable(v Variable) error { func (l *liveTracker) addVariable(v Variable) error {
_, ok := l.variables[v] _, ok := l.variables[v]
if !ok { if !ok {
value, err := v.value(l.config) ctx := &variableFuncContext{l.ctx}
value, err := v.value(ctx, l.config)
if err == errVariableIsArg { if err == errVariableIsArg {
// This variable is a placeholder for an argument that can be passed // 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 // to a rule. It has no value and thus doesn't reference any other

View file

@ -59,7 +59,7 @@ type PackageContext interface {
ImportAs(as, pkgPath string) ImportAs(as, pkgPath string)
StaticVariable(name, value string) Variable 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 VariableConfigMethod(name string, method interface{}) Variable
StaticPool(name string, params PoolParams) Pool StaticPool(name string, params PoolParams) Pool
@ -304,7 +304,7 @@ func (v *staticVariable) memoizeFullName(pkgNames map[*packageContext]string) {
v.fullName_ = v.fullName(pkgNames) 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_) ninjaStr, err := parseNinjaString(v.pctx.scope, v.value_)
if err != nil { if err != nil {
err = fmt.Errorf("error parsing variable %s value: %s", v, err) err = fmt.Errorf("error parsing variable %s value: %s", v, err)
@ -320,10 +320,30 @@ func (v *staticVariable) String() string {
type variableFunc struct { type variableFunc struct {
pctx *packageContext pctx *packageContext
name_ string name_ string
value_ func(interface{}) (string, error) value_ func(VariableFuncContext, interface{}) (string, error)
fullName_ string 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 // 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 // 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 // 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 // reference other Ninja variables that are visible within the calling Go
// package. // package.
func (p *packageContext) VariableFunc(name string, func (p *packageContext) VariableFunc(name string,
f func(config interface{}) (string, error)) Variable { f func(ctx VariableFuncContext, config interface{}) (string, error)) Variable {
checkCalledFromInit() checkCalledFromInit()
@ -382,7 +402,7 @@ func (p *packageContext) VariableConfigMethod(name string,
methodValue := reflect.ValueOf(method) methodValue := reflect.ValueOf(method)
validateVariableMethod(name, methodValue) 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)}) result := methodValue.Call([]reflect.Value{reflect.ValueOf(config)})
resultStr := result[0].Interface().(string) resultStr := result[0].Interface().(string)
return resultStr, nil return resultStr, nil
@ -420,8 +440,8 @@ func (v *variableFunc) memoizeFullName(pkgNames map[*packageContext]string) {
v.fullName_ = v.fullName(pkgNames) v.fullName_ = v.fullName(pkgNames)
} }
func (v *variableFunc) value(config interface{}) (ninjaString, error) { func (v *variableFunc) value(ctx VariableFuncContext, config interface{}) (ninjaString, error) {
value, err := v.value_(config) value, err := v.value_(ctx, config)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -484,7 +504,7 @@ func (v *argVariable) memoizeFullName(pkgNames map[*packageContext]string) {
// Nothing to do, full name is known at initialization. // 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 return nil, errVariableIsArg
} }

View file

@ -29,7 +29,7 @@ type Variable interface {
name() string // "foo" name() string // "foo"
fullName(pkgNames map[*packageContext]string) string // "pkg.foo" or "path.to.pkg.foo" fullName(pkgNames map[*packageContext]string) string // "pkg.foo" or "path.to.pkg.foo"
memoizeFullName(pkgNames map[*packageContext]string) // precompute fullName if desired memoizeFullName(pkgNames map[*packageContext]string) // precompute fullName if desired
value(config interface{}) (ninjaString, error) value(ctx VariableFuncContext, config interface{}) (ninjaString, error)
String() string String() string
} }
@ -373,7 +373,7 @@ func (l *localVariable) memoizeFullName(pkgNames map[*packageContext]string) {
// Nothing to do, full name is known at initialization. // 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 return l.value_, nil
} }