Deterministically report path errors to all callers of dexpreopt.GetGlobalConfig
dexpreopt.GetGlobalConfig uses a OncePer to only parse the config once. If the config contains missing paths, which can happen in partial manifest branches, the missing path errors will only be reported on the first caller to GetGlobalConfig. Fix the nondeterminism by wrapping the PathContext and capturing any path errors and reporting them to all callers to GetGlobalConfig. Bug: 207813628 Test: tradefed branch builds Change-Id: I869c3ae49a819b1251b94b73e0487aa594cd5e22
This commit is contained in:
@@ -252,6 +252,7 @@ func ParseGlobalConfig(ctx android.PathContext, data []byte) (*GlobalConfig, err
|
||||
type globalConfigAndRaw struct {
|
||||
global *GlobalConfig
|
||||
data []byte
|
||||
pathErrors []error
|
||||
}
|
||||
|
||||
// GetGlobalConfig returns the global dexpreopt.config that's created in the
|
||||
@@ -272,16 +273,26 @@ func GetGlobalConfigRawData(ctx android.PathContext) []byte {
|
||||
var globalConfigOnceKey = android.NewOnceKey("DexpreoptGlobalConfig")
|
||||
var testGlobalConfigOnceKey = android.NewOnceKey("TestDexpreoptGlobalConfig")
|
||||
|
||||
type pathContextErrorCollector struct {
|
||||
android.PathContext
|
||||
errors []error
|
||||
}
|
||||
|
||||
func (p *pathContextErrorCollector) Errorf(format string, args ...interface{}) {
|
||||
p.errors = append(p.errors, fmt.Errorf(format, args...))
|
||||
}
|
||||
|
||||
func getGlobalConfigRaw(ctx android.PathContext) globalConfigAndRaw {
|
||||
return ctx.Config().Once(globalConfigOnceKey, func() interface{} {
|
||||
config := ctx.Config().Once(globalConfigOnceKey, func() interface{} {
|
||||
if data, err := ctx.Config().DexpreoptGlobalConfig(ctx); err != nil {
|
||||
panic(err)
|
||||
} else if data != nil {
|
||||
globalConfig, err := ParseGlobalConfig(ctx, data)
|
||||
pathErrorCollectorCtx := &pathContextErrorCollector{PathContext: ctx}
|
||||
globalConfig, err := ParseGlobalConfig(pathErrorCollectorCtx, data)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return globalConfigAndRaw{globalConfig, data}
|
||||
return globalConfigAndRaw{globalConfig, data, pathErrorCollectorCtx.errors}
|
||||
}
|
||||
|
||||
// No global config filename set, see if there is a test config set
|
||||
@@ -291,16 +302,35 @@ func getGlobalConfigRaw(ctx android.PathContext) globalConfigAndRaw {
|
||||
DisablePreopt: true,
|
||||
DisablePreoptBootImages: true,
|
||||
DisableGenerateProfile: true,
|
||||
}, nil}
|
||||
}, nil, nil}
|
||||
})
|
||||
}).(globalConfigAndRaw)
|
||||
|
||||
// Avoid non-deterministic errors by reporting cached path errors on all callers.
|
||||
for _, err := range config.pathErrors {
|
||||
if ctx.Config().AllowMissingDependencies() {
|
||||
// When AllowMissingDependencies it set, report errors through AddMissingDependencies.
|
||||
// If AddMissingDependencies doesn't exist on the current context (for example when
|
||||
// called with a SingletonContext), just swallow the errors since there is no way to
|
||||
// report them.
|
||||
if missingDepsCtx, ok := ctx.(interface {
|
||||
AddMissingDependencies(missingDeps []string)
|
||||
}); ok {
|
||||
missingDepsCtx.AddMissingDependencies([]string{err.Error()})
|
||||
}
|
||||
} else {
|
||||
android.ReportPathErrorf(ctx, "%w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
// SetTestGlobalConfig sets a GlobalConfig that future calls to GetGlobalConfig
|
||||
// will return. It must be called before the first call to GetGlobalConfig for
|
||||
// the config.
|
||||
func SetTestGlobalConfig(config android.Config, globalConfig *GlobalConfig) {
|
||||
config.Once(testGlobalConfigOnceKey, func() interface{} { return globalConfigAndRaw{globalConfig, nil} })
|
||||
config.Once(testGlobalConfigOnceKey, func() interface{} { return globalConfigAndRaw{globalConfig, nil, nil} })
|
||||
}
|
||||
|
||||
// This struct is required to convert ModuleConfig from/to JSON.
|
||||
|
Reference in New Issue
Block a user