diff --git a/android/android_test.go b/android/android_test.go index fb82e3765..64ceedc4d 100644 --- a/android/android_test.go +++ b/android/android_test.go @@ -15,10 +15,32 @@ package android import ( + "io/ioutil" "os" "testing" ) -func TestMain(m *testing.M) { - os.Exit(m.Run()) +var buildDir string + +func setUp() { + var err error + buildDir, err = ioutil.TempDir("", "android_test") + if err != nil { + panic(err) + } +} + +func tearDown() { + os.RemoveAll(buildDir) +} + +func TestMain(m *testing.M) { + run := func() int { + setUp() + defer tearDown() + + return m.Run() + } + + os.Exit(run()) } diff --git a/android/module.go b/android/module.go index a150e6191..68d9f8e75 100644 --- a/android/module.go +++ b/android/module.go @@ -3546,10 +3546,29 @@ func OutputFileForModule(ctx PathContext, module blueprint.Module, tag string) P reportPathError(ctx, err) return nil } + if len(paths) == 0 { + type addMissingDependenciesIntf interface { + AddMissingDependencies([]string) + OtherModuleName(blueprint.Module) string + } + if mctx, ok := ctx.(addMissingDependenciesIntf); ok && ctx.Config().AllowMissingDependencies() { + mctx.AddMissingDependencies([]string{mctx.OtherModuleName(module)}) + } else { + ReportPathErrorf(ctx, "failed to get output files from module %q", pathContextName(ctx, module)) + } + // Return a fake output file to avoid nil dereferences of Path objects later. + // This should never get used for an actual build as the error or missing + // dependency has already been reported. + p, err := pathForSource(ctx, filepath.Join("missing_output_file", pathContextName(ctx, module))) + if err != nil { + reportPathError(ctx, err) + return nil + } + return p + } if len(paths) > 1 { ReportPathErrorf(ctx, "got multiple output files from module %q, expected exactly one", pathContextName(ctx, module)) - return nil } return paths[0] } @@ -3561,18 +3580,12 @@ func outputFilesForModule(ctx PathContext, module blueprint.Module, tag string) return nil, fmt.Errorf("failed to get output file from module %q: %s", pathContextName(ctx, module), err.Error()) } - if len(paths) == 0 { - return nil, fmt.Errorf("failed to get output files from module %q", pathContextName(ctx, module)) - } return paths, nil } else if sourceFileProducer, ok := module.(SourceFileProducer); ok { if tag != "" { return nil, fmt.Errorf("module %q is a SourceFileProducer, not an OutputFileProducer, and so does not support tag %q", pathContextName(ctx, module), tag) } paths := sourceFileProducer.Srcs() - if len(paths) == 0 { - return nil, fmt.Errorf("failed to get output files from module %q", pathContextName(ctx, module)) - } return paths, nil } else { return nil, fmt.Errorf("module %q is not an OutputFileProducer", pathContextName(ctx, module)) diff --git a/android/module_test.go b/android/module_test.go index 0580bef24..1ca742213 100644 --- a/android/module_test.go +++ b/android/module_test.go @@ -15,6 +15,7 @@ package android import ( + "github.com/google/blueprint" "path/filepath" "runtime" "testing" @@ -978,3 +979,88 @@ func TestSetAndroidMkEntriesWithTestOptions(t *testing.T) { }) } } + +type fakeBlueprintModule struct{} + +func (fakeBlueprintModule) Name() string { return "foo" } + +func (fakeBlueprintModule) GenerateBuildActions(blueprint.ModuleContext) {} + +type sourceProducerTestModule struct { + fakeBlueprintModule + source Path +} + +func (s sourceProducerTestModule) Srcs() Paths { return Paths{s.source} } + +type outputFileProducerTestModule struct { + fakeBlueprintModule + output map[string]Path + error map[string]error +} + +func (o outputFileProducerTestModule) OutputFiles(tag string) (Paths, error) { + return PathsIfNonNil(o.output[tag]), o.error[tag] +} + +type pathContextAddMissingDependenciesWrapper struct { + PathContext + missingDeps []string +} + +func (p *pathContextAddMissingDependenciesWrapper) AddMissingDependencies(deps []string) { + p.missingDeps = append(p.missingDeps, deps...) +} +func (p *pathContextAddMissingDependenciesWrapper) OtherModuleName(module blueprint.Module) string { + return module.Name() +} + +func TestOutputFileForModule(t *testing.T) { + testcases := []struct { + name string + module blueprint.Module + tag string + env map[string]string + config func(*config) + expected string + missingDeps []string + }{ + { + name: "SourceFileProducer", + module: &sourceProducerTestModule{source: PathForTesting("foo.txt")}, + expected: "foo.txt", + }, + { + name: "OutputFileProducer", + module: &outputFileProducerTestModule{output: map[string]Path{"": PathForTesting("foo.txt")}}, + expected: "foo.txt", + }, + { + name: "OutputFileProducer_tag", + module: &outputFileProducerTestModule{output: map[string]Path{"foo": PathForTesting("foo.txt")}}, + tag: "foo", + expected: "foo.txt", + }, + { + name: "OutputFileProducer_AllowMissingDependencies", + config: func(config *config) { + config.TestProductVariables.Allow_missing_dependencies = boolPtr(true) + }, + module: &outputFileProducerTestModule{}, + missingDeps: []string{"foo"}, + expected: "missing_output_file/foo", + }, + } + for _, tt := range testcases { + config := TestConfig(buildDir, tt.env, "", nil) + if tt.config != nil { + tt.config(config.config) + } + ctx := &pathContextAddMissingDependenciesWrapper{ + PathContext: PathContextForTesting(config), + } + got := OutputFileForModule(ctx, tt.module, tt.tag) + AssertPathRelativeToTopEquals(t, "expected source path", tt.expected, got) + AssertArrayString(t, "expected missing deps", tt.missingDeps, ctx.missingDeps) + } +}