Support tagged module references

There are cases where a module needs to refer to an intermediate
output of another module instead of its final output.  For example,
a module may want to use the .jar containing .class files from
another module whose final output is a .jar containing classes.dex
files.  Support a new ":module{.tag}" format in any property that
is annotated with `android:"path"`, which will query the target
module for its ".tag" output(s).

Test: path_properties_test.go, paths_test.go
Test: no unexpected changes in build.ninja
Change-Id: Icd3c9b0d83ff125771767c04046fcffb9fc3f65a
diff --git a/Android.bp b/Android.bp
index c9a48b4..4db98f8 100644
--- a/Android.bp
+++ b/Android.bp
@@ -81,6 +81,7 @@
         "android/arch_test.go",
         "android/config_test.go",
         "android/expand_test.go",
+        "android/module_test.go",
         "android/namespace_test.go",
         "android/neverallow_test.go",
         "android/onceper_test.go",
diff --git a/android/module.go b/android/module.go
index 1c36279..eb9b0fc 100644
--- a/android/module.go
+++ b/android/module.go
@@ -1462,39 +1462,60 @@
 	return -1
 }
 
-func SrcIsModule(s string) string {
+// SrcIsModule decodes module references in the format ":name" into the module name, or empty string if the input
+// was not a module reference.
+func SrcIsModule(s string) (module string) {
 	if len(s) > 1 && s[0] == ':' {
 		return s[1:]
 	}
 	return ""
 }
 
-type sourceDependencyTag struct {
-	blueprint.BaseDependencyTag
+// SrcIsModule decodes module references in the format ":name{.tag}" into the module name and tag, ":name" into the
+// module name and an empty string for the tag, or empty strings if the input was not a module reference.
+func SrcIsModuleWithTag(s string) (module, tag string) {
+	if len(s) > 1 && s[0] == ':' {
+		module = s[1:]
+		if tagStart := strings.IndexByte(module, '{'); tagStart > 0 {
+			if module[len(module)-1] == '}' {
+				tag = module[tagStart+1 : len(module)-1]
+				module = module[:tagStart]
+				return module, tag
+			}
+		}
+		return module, ""
+	}
+	return "", ""
 }
 
-var SourceDepTag sourceDependencyTag
+type sourceOrOutputDependencyTag struct {
+	blueprint.BaseDependencyTag
+	tag string
+}
+
+func sourceOrOutputDepTag(tag string) blueprint.DependencyTag {
+	return sourceOrOutputDependencyTag{tag: tag}
+}
+
+var SourceDepTag = sourceOrOutputDepTag("")
 
 // Adds necessary dependencies to satisfy filegroup or generated sources modules listed in srcFiles
 // using ":module" syntax, if any.
 //
 // Deprecated: tag the property with `android:"path"` instead.
 func ExtractSourcesDeps(ctx BottomUpMutatorContext, srcFiles []string) {
-	var deps []string
 	set := make(map[string]bool)
 
 	for _, s := range srcFiles {
-		if m := SrcIsModule(s); m != "" {
-			if _, found := set[m]; found {
-				ctx.ModuleErrorf("found source dependency duplicate: %q!", m)
+		if m, t := SrcIsModuleWithTag(s); m != "" {
+			if _, found := set[s]; found {
+				ctx.ModuleErrorf("found source dependency duplicate: %q!", s)
 			} else {
-				set[m] = true
-				deps = append(deps, m)
+				set[s] = true
+				ctx.AddDependency(ctx.Module(), sourceOrOutputDepTag(t), m)
 			}
 		}
 	}
-
-	ctx.AddDependency(ctx.Module(), SourceDepTag, deps...)
 }
 
 // Adds necessary dependencies to satisfy filegroup or generated sources modules specified in s
@@ -1503,16 +1524,25 @@
 // Deprecated: tag the property with `android:"path"` instead.
 func ExtractSourceDeps(ctx BottomUpMutatorContext, s *string) {
 	if s != nil {
-		if m := SrcIsModule(*s); m != "" {
-			ctx.AddDependency(ctx.Module(), SourceDepTag, m)
+		if m, t := SrcIsModuleWithTag(*s); m != "" {
+			ctx.AddDependency(ctx.Module(), sourceOrOutputDepTag(t), m)
 		}
 	}
 }
 
+// A module that implements SourceFileProducer can be referenced from any property that is tagged with `android:"path"`
+// using the ":module" syntax and provides a list of paths to be used as if they were listed in the property.
 type SourceFileProducer interface {
 	Srcs() Paths
 }
 
+// A module that implements OutputFileProducer can be referenced from any property that is tagged with `android:"path"`
+// using the ":module" syntax or ":module{.tag}" syntax and provides a list of otuput files to be used as if they were
+// listed in the property.
+type OutputFileProducer interface {
+	OutputFiles(tag string) (Paths, error)
+}
+
 type HostToolProvider interface {
 	HostToolPath() OptionalPath
 }
diff --git a/android/module_test.go b/android/module_test.go
new file mode 100644
index 0000000..c790a68
--- /dev/null
+++ b/android/module_test.go
@@ -0,0 +1,141 @@
+// Copyright 2015 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+import "testing"
+
+func TestSrcIsModule(t *testing.T) {
+	type args struct {
+		s string
+	}
+	tests := []struct {
+		name       string
+		args       args
+		wantModule string
+	}{
+		{
+			name: "file",
+			args: args{
+				s: "foo",
+			},
+			wantModule: "",
+		},
+		{
+			name: "module",
+			args: args{
+				s: ":foo",
+			},
+			wantModule: "foo",
+		},
+		{
+			name: "tag",
+			args: args{
+				s: ":foo{.bar}",
+			},
+			wantModule: "foo{.bar}",
+		},
+		{
+			name: "extra colon",
+			args: args{
+				s: ":foo:bar",
+			},
+			wantModule: "foo:bar",
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if gotModule := SrcIsModule(tt.args.s); gotModule != tt.wantModule {
+				t.Errorf("SrcIsModule() = %v, want %v", gotModule, tt.wantModule)
+			}
+		})
+	}
+}
+
+func TestSrcIsModuleWithTag(t *testing.T) {
+	type args struct {
+		s string
+	}
+	tests := []struct {
+		name       string
+		args       args
+		wantModule string
+		wantTag    string
+	}{
+		{
+			name: "file",
+			args: args{
+				s: "foo",
+			},
+			wantModule: "",
+			wantTag:    "",
+		},
+		{
+			name: "module",
+			args: args{
+				s: ":foo",
+			},
+			wantModule: "foo",
+			wantTag:    "",
+		},
+		{
+			name: "tag",
+			args: args{
+				s: ":foo{.bar}",
+			},
+			wantModule: "foo",
+			wantTag:    ".bar",
+		},
+		{
+			name: "empty tag",
+			args: args{
+				s: ":foo{}",
+			},
+			wantModule: "foo",
+			wantTag:    "",
+		},
+		{
+			name: "extra colon",
+			args: args{
+				s: ":foo:bar",
+			},
+			wantModule: "foo:bar",
+		},
+		{
+			name: "invalid tag",
+			args: args{
+				s: ":foo{.bar",
+			},
+			wantModule: "foo{.bar",
+		},
+		{
+			name: "invalid tag 2",
+			args: args{
+				s: ":foo.bar}",
+			},
+			wantModule: "foo.bar}",
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			gotModule, gotTag := SrcIsModuleWithTag(tt.args.s)
+			if gotModule != tt.wantModule {
+				t.Errorf("SrcIsModuleWithTag() gotModule = %v, want %v", gotModule, tt.wantModule)
+			}
+			if gotTag != tt.wantTag {
+				t.Errorf("SrcIsModuleWithTag() gotTag = %v, want %v", gotTag, tt.wantTag)
+			}
+		})
+	}
+}
diff --git a/android/path_properties.go b/android/path_properties.go
index 1a12290..af7af59 100644
--- a/android/path_properties.go
+++ b/android/path_properties.go
@@ -39,14 +39,12 @@
 		pathProperties := pathPropertiesForPropertyStruct(ctx, ps)
 		pathProperties = FirstUniqueStrings(pathProperties)
 
-		var deps []string
 		for _, s := range pathProperties {
-			if m := SrcIsModule(s); m != "" {
-				deps = append(deps, m)
+			if m, t := SrcIsModuleWithTag(s); m != "" {
+				ctx.AddDependency(ctx.Module(), sourceOrOutputDepTag(t), m)
 			}
 		}
 
-		ctx.AddDependency(ctx.Module(), SourceDepTag, deps...)
 	}
 }
 
diff --git a/android/path_properties_test.go b/android/path_properties_test.go
index ecc2d21..fa187fa 100644
--- a/android/path_properties_test.go
+++ b/android/path_properties_test.go
@@ -41,8 +41,10 @@
 }
 
 func (p *pathDepsMutatorTestModule) GenerateAndroidBuildActions(ctx ModuleContext) {
-	ctx.VisitDirectDepsWithTag(SourceDepTag, func(dep Module) {
-		p.sourceDeps = append(p.sourceDeps, ctx.OtherModuleName(dep))
+	ctx.VisitDirectDeps(func(dep Module) {
+		if _, ok := ctx.OtherModuleDependencyTag(dep).(sourceOrOutputDependencyTag); ok {
+			p.sourceDeps = append(p.sourceDeps, ctx.OtherModuleName(dep))
+		}
 	})
 }
 
@@ -59,7 +61,7 @@
 				name: "foo",
 				foo: ":a",
 				bar: [":b"],
-				baz: ":c",
+				baz: ":c{.bar}",
 				qux: ":d",
 			}`,
 			deps: []string{"a", "b", "c"},
diff --git a/android/paths.go b/android/paths.go
index da387a8..3915ff4 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -217,21 +217,23 @@
 	return ret
 }
 
-// PathsForModuleSrc returns Paths rooted from the module's local source directory.  It expands globs and references
-// to SourceFileProducer modules using the ":name" syntax.  Properties passed as the paths argument must have been
-// annotated with struct tag `android:"path"` so that dependencies on SourceFileProducer modules will have already
-// been handled by the path_properties mutator.  If ctx.Config().AllowMissingDependencies() is true, then any missing
-// SourceFileProducer dependencies will cause the module to be marked as having missing dependencies.
+// PathsForModuleSrc returns Paths rooted from the module's local source directory.  It expands globs, references to
+// SourceFileProducer modules using the ":name" syntax, and references to OutputFileProducer modules using the
+// ":name{.tag}" syntax.  Properties passed as the paths argument must have been annotated with struct tag
+// `android:"path"` so that dependencies on SourceFileProducer modules will have already been handled by the
+// path_properties mutator.  If ctx.Config().AllowMissingDependencies() is true then any missing SourceFileProducer or
+// OutputFileProducer dependencies will cause the module to be marked as having missing dependencies.
 func PathsForModuleSrc(ctx ModuleContext, paths []string) Paths {
 	return PathsForModuleSrcExcludes(ctx, paths, nil)
 }
 
 // PathsForModuleSrcExcludes returns Paths rooted from the module's local source directory, excluding paths listed in
-// the excludes arguments.  It expands globs and references to SourceFileProducer modules in both paths and excludes
-// using the ":name" syntax.  Properties passed as the paths or excludes argument must have been annotated with struct
-// tag `android:"path"` so that dependencies on SourceFileProducer modules will have already been handled by the
-// path_properties mutator.  If ctx.Config().AllowMissingDependencies() is true, then any missing SourceFileProducer
-// dependencies will cause the module to be marked as having missing dependencies.
+// the excludes arguments.  It expands globs, references to SourceFileProducer modules using the ":name" syntax, and
+// references to OutputFileProducer modules using the ":name{.tag}" syntax.  Properties passed as the paths or excludes
+// argument must have been annotated with struct tag `android:"path"` so that dependencies on SourceFileProducer modules
+// will have already been handled by the path_properties mutator.  If ctx.Config().AllowMissingDependencies() is
+// truethen any missing SourceFileProducer or OutputFileProducer dependencies will cause the module to be marked as
+// having missing dependencies.
 func PathsForModuleSrcExcludes(ctx ModuleContext, paths, excludes []string) Paths {
 	ret, missingDeps := PathsAndMissingDepsForModuleSrcExcludes(ctx, paths, excludes)
 	if ctx.Config().AllowMissingDependencies() {
@@ -245,12 +247,13 @@
 }
 
 // PathsAndMissingDepsForModuleSrcExcludes returns Paths rooted from the module's local source directory, excluding
-// paths listed in the excludes arguments, and a list of missing dependencies.  It expands globs and references to
-// SourceFileProducer modules in both paths and excludes using the ":name" syntax.  Properties passed as the paths or
-// excludes argument must have been annotated with struct tag `android:"path"` so that dependencies on
-// SourceFileProducer modules will have already been handled by the path_properties mutator.  If
-// ctx.Config().AllowMissingDependencies() is true, then any missing SourceFileProducer dependencies will be returned,
-// and they will NOT cause the module to be marked as having missing dependencies.
+// paths listed in the excludes arguments, and a list of missing dependencies.  It expands globs, references to
+// SourceFileProducer modules using the ":name" syntax, and references to OutputFileProducer modules using the
+// ":name{.tag}" syntax.  Properties passed as the paths or excludes argument must have been annotated with struct tag
+// `android:"path"` so that dependencies on SourceFileProducer modules will have already been handled by the
+// path_properties mutator.  If ctx.Config().AllowMissingDependencies() is true then any missing SourceFileProducer or
+// OutputFileProducer dependencies will be returned, and they will NOT cause the module to be marked as having missing
+// dependencies.
 func PathsAndMissingDepsForModuleSrcExcludes(ctx ModuleContext, paths, excludes []string) (Paths, []string) {
 	prefix := pathForModuleSrc(ctx).String()
 
@@ -262,16 +265,24 @@
 	var missingExcludeDeps []string
 
 	for _, e := range excludes {
-		if m := SrcIsModule(e); m != "" {
-			module := ctx.GetDirectDepWithTag(m, SourceDepTag)
+		if m, t := SrcIsModuleWithTag(e); m != "" {
+			module := ctx.GetDirectDepWithTag(m, sourceOrOutputDepTag(t))
 			if module == nil {
 				missingExcludeDeps = append(missingExcludeDeps, m)
 				continue
 			}
-			if srcProducer, ok := module.(SourceFileProducer); ok {
+			if outProducer, ok := module.(OutputFileProducer); ok {
+				outputFiles, err := outProducer.OutputFiles(t)
+				if err != nil {
+					ctx.ModuleErrorf("path dependency %q: %s", e, err)
+				}
+				expandedExcludes = append(expandedExcludes, outputFiles.Strings()...)
+			} else if t != "" {
+				ctx.ModuleErrorf("path dependency %q is not an output file producing module", e)
+			} else if srcProducer, ok := module.(SourceFileProducer); ok {
 				expandedExcludes = append(expandedExcludes, srcProducer.Srcs().Strings()...)
 			} else {
-				ctx.ModuleErrorf("srcs dependency %q is not a source file producing module", m)
+				ctx.ModuleErrorf("path dependency %q is not a source file producing module", e)
 			}
 		} else {
 			expandedExcludes = append(expandedExcludes, filepath.Join(prefix, e))
@@ -307,12 +318,20 @@
 }
 
 func expandOneSrcPath(ctx ModuleContext, s string, expandedExcludes []string) (Paths, error) {
-	if m := SrcIsModule(s); m != "" {
-		module := ctx.GetDirectDepWithTag(m, SourceDepTag)
+	if m, t := SrcIsModuleWithTag(s); m != "" {
+		module := ctx.GetDirectDepWithTag(m, sourceOrOutputDepTag(t))
 		if module == nil {
 			return nil, missingDependencyError{[]string{m}}
 		}
-		if srcProducer, ok := module.(SourceFileProducer); ok {
+		if outProducer, ok := module.(OutputFileProducer); ok {
+			outputFiles, err := outProducer.OutputFiles(t)
+			if err != nil {
+				return nil, fmt.Errorf("path dependency %q: %s", s, err)
+			}
+			return outputFiles, nil
+		} else if t != "" {
+			return nil, fmt.Errorf("path dependency %q is not an output file producing module", s)
+		} else if srcProducer, ok := module.(SourceFileProducer); ok {
 			moduleSrcs := srcProducer.Srcs()
 			for _, e := range expandedExcludes {
 				for j := 0; j < len(moduleSrcs); j++ {
@@ -324,7 +343,7 @@
 			}
 			return moduleSrcs, nil
 		} else {
-			return nil, fmt.Errorf("path dependency %q is not a source file producing module", m)
+			return nil, fmt.Errorf("path dependency %q is not a source file producing module", s)
 		}
 	} else if pathtools.IsGlob(s) {
 		paths := ctx.GlobFiles(pathForModuleSrc(ctx, s).String(), expandedExcludes)
diff --git a/android/paths_test.go b/android/paths_test.go
index b52d713..85c8d84 100644
--- a/android/paths_test.go
+++ b/android/paths_test.go
@@ -760,6 +760,45 @@
 	}
 }
 
+type pathForModuleSrcOutputFileProviderModule struct {
+	ModuleBase
+	props struct {
+		Outs   []string
+		Tagged []string
+	}
+
+	outs   Paths
+	tagged Paths
+}
+
+func pathForModuleSrcOutputFileProviderModuleFactory() Module {
+	module := &pathForModuleSrcOutputFileProviderModule{}
+	module.AddProperties(&module.props)
+	InitAndroidModule(module)
+	return module
+}
+
+func (p *pathForModuleSrcOutputFileProviderModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+	for _, out := range p.props.Outs {
+		p.outs = append(p.outs, PathForModuleOut(ctx, out))
+	}
+
+	for _, tagged := range p.props.Tagged {
+		p.tagged = append(p.tagged, PathForModuleOut(ctx, tagged))
+	}
+}
+
+func (p *pathForModuleSrcOutputFileProviderModule) OutputFiles(tag string) (Paths, error) {
+	switch tag {
+	case "":
+		return p.outs, nil
+	case ".tagged":
+		return p.tagged, nil
+	default:
+		return nil, fmt.Errorf("unsupported tag %q", tag)
+	}
+}
+
 type pathForModuleSrcTestCase struct {
 	name string
 	bp   string
@@ -776,6 +815,7 @@
 			ctx := NewTestContext()
 
 			ctx.RegisterModuleType("test", ModuleFactoryAdaptor(pathForModuleSrcTestModuleFactory))
+			ctx.RegisterModuleType("output_file_provider", ModuleFactoryAdaptor(pathForModuleSrcOutputFileProviderModuleFactory))
 			ctx.RegisterModuleType("filegroup", ModuleFactoryAdaptor(FileGroupFactory))
 
 			fgBp := `
@@ -785,9 +825,18 @@
 				}
 			`
 
+			ofpBp := `
+				output_file_provider {
+					name: "b",
+					outs: ["gen/b"],
+					tagged: ["gen/c"],
+				}
+			`
+
 			mockFS := map[string][]byte{
 				"fg/Android.bp":     []byte(fgBp),
 				"foo/Android.bp":    []byte(test.bp),
+				"ofp/Android.bp":    []byte(ofpBp),
 				"fg/src/a":          nil,
 				"foo/src/b":         nil,
 				"foo/src/c":         nil,
@@ -799,7 +848,7 @@
 			ctx.MockFileSystem(mockFS)
 
 			ctx.Register()
-			_, errs := ctx.ParseFileList(".", []string{"fg/Android.bp", "foo/Android.bp"})
+			_, errs := ctx.ParseFileList(".", []string{"fg/Android.bp", "foo/Android.bp", "ofp/Android.bp"})
 			FailIfErrored(t, errs)
 			_, errs = ctx.PrepareBuildActions(config)
 			FailIfErrored(t, errs)
@@ -826,6 +875,12 @@
 }
 
 func TestPathsForModuleSrc(t *testing.T) {
+	buildDir, err := ioutil.TempDir("", "soong_paths_for_module_src_test")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer os.RemoveAll(buildDir)
+
 	tests := []pathForModuleSrcTestCase{
 		{
 			name: "path",
@@ -871,6 +926,26 @@
 			rels: []string{"src/a"},
 		},
 		{
+			name: "output file provider",
+			bp: `
+			test {
+				name: "foo",
+				srcs: [":b"],
+			}`,
+			srcs: []string{buildDir + "/.intermediates/ofp/b/gen/b"},
+			rels: []string{"gen/b"},
+		},
+		{
+			name: "output file provider tagged",
+			bp: `
+			test {
+				name: "foo",
+				srcs: [":b{.tagged}"],
+			}`,
+			srcs: []string{buildDir + "/.intermediates/ofp/b/gen/c"},
+			rels: []string{"gen/c"},
+		},
+		{
 			name: "special characters glob",
 			bp: `
 			test {
@@ -882,16 +957,16 @@
 		},
 	}
 
-	buildDir, err := ioutil.TempDir("", "soong_paths_for_module_src_test")
+	testPathForModuleSrc(t, buildDir, tests)
+}
+
+func TestPathForModuleSrc(t *testing.T) {
+	buildDir, err := ioutil.TempDir("", "soong_path_for_module_src_test")
 	if err != nil {
 		t.Fatal(err)
 	}
 	defer os.RemoveAll(buildDir)
 
-	testPathForModuleSrc(t, buildDir, tests)
-}
-
-func TestPathForModuleSrc(t *testing.T) {
 	tests := []pathForModuleSrcTestCase{
 		{
 			name: "path",
@@ -924,6 +999,26 @@
 			rel: "src/a",
 		},
 		{
+			name: "output file provider",
+			bp: `
+			test {
+				name: "foo",
+				src: ":b",
+			}`,
+			src: buildDir + "/.intermediates/ofp/b/gen/b",
+			rel: "gen/b",
+		},
+		{
+			name: "output file provider tagged",
+			bp: `
+			test {
+				name: "foo",
+				src: ":b{.tagged}",
+			}`,
+			src: buildDir + "/.intermediates/ofp/b/gen/c",
+			rel: "gen/c",
+		},
+		{
 			name: "special characters glob",
 			bp: `
 			test {
@@ -935,12 +1030,6 @@
 		},
 	}
 
-	buildDir, err := ioutil.TempDir("", "soong_path_for_module_src_test")
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer os.RemoveAll(buildDir)
-
 	testPathForModuleSrc(t, buildDir, tests)
 }
 
diff --git a/android/prebuilt_test.go b/android/prebuilt_test.go
index e182641..a5b85c8 100644
--- a/android/prebuilt_test.go
+++ b/android/prebuilt_test.go
@@ -15,6 +15,7 @@
 package android
 
 import (
+	"fmt"
 	"io/ioutil"
 	"os"
 	"testing"
@@ -250,8 +251,13 @@
 	return &p.prebuilt
 }
 
-func (p *prebuiltModule) Srcs() Paths {
-	return Paths{p.src}
+func (p *prebuiltModule) OutputFiles(tag string) (Paths, error) {
+	switch tag {
+	case "":
+		return Paths{p.src}, nil
+	default:
+		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+	}
 }
 
 type sourceModule struct {
diff --git a/apex/apex.go b/apex/apex.go
index 51d0718..aed7d6f 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -560,11 +560,16 @@
 	return String(a.properties.Certificate)
 }
 
-func (a *apexBundle) Srcs() android.Paths {
-	if file, ok := a.outputFiles[imageApex]; ok {
-		return android.Paths{file}
-	} else {
-		return nil
+func (a *apexBundle) OutputFiles(tag string) (android.Paths, error) {
+	switch tag {
+	case "":
+		if file, ok := a.outputFiles[imageApex]; ok {
+			return android.Paths{file}, nil
+		} else {
+			return nil, nil
+		}
+	default:
+		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
 	}
 }
 
@@ -1383,8 +1388,13 @@
 	p.properties.Source = src
 }
 
-func (p *Prebuilt) Srcs() android.Paths {
-	return android.Paths{p.outputApex}
+func (p *Prebuilt) OutputFiles(tag string) (android.Paths, error) {
+	switch tag {
+	case "":
+		return android.Paths{p.outputApex}, nil
+	default:
+		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+	}
 }
 
 func (p *Prebuilt) InstallFilename() string {
diff --git a/bpf/bpf.go b/bpf/bpf.go
index dcbf9ad..90ec963 100644
--- a/bpf/bpf.go
+++ b/bpf/bpf.go
@@ -122,13 +122,18 @@
 	}
 }
 
-// Implements SourceFileProducer interface so that the obj output can be used in the data property
+// Implements OutputFileFileProducer interface so that the obj output can be used in the data property
 // of other modules.
-func (bpf *bpf) Srcs() android.Paths {
-	return bpf.objs
+func (bpf *bpf) OutputFiles(tag string) (android.Paths, error) {
+	switch tag {
+	case "":
+		return bpf.objs, nil
+	default:
+		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+	}
 }
 
-var _ android.SourceFileProducer = (*bpf)(nil)
+var _ android.OutputFileProducer = (*bpf)(nil)
 
 func bpfFactory() android.Module {
 	module := &bpf{}
diff --git a/cc/cc.go b/cc/cc.go
index 09496fc..e61857d 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -19,6 +19,7 @@
 // is handled in builder.go
 
 import (
+	"fmt"
 	"io"
 	"strconv"
 	"strings"
@@ -1925,11 +1926,16 @@
 	return c.outputFile
 }
 
-func (c *Module) Srcs() android.Paths {
-	if c.outputFile.Valid() {
-		return android.Paths{c.outputFile.Path()}
+func (c *Module) OutputFiles(tag string) (android.Paths, error) {
+	switch tag {
+	case "":
+		if c.outputFile.Valid() {
+			return android.Paths{c.outputFile.Path()}, nil
+		}
+		return android.Paths{}, nil
+	default:
+		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
 	}
-	return android.Paths{}
 }
 
 func (c *Module) static() bool {
@@ -2006,7 +2012,11 @@
 }
 
 func (c *Module) IDEInfo(dpInfo *android.IdeInfo) {
-	dpInfo.Srcs = append(dpInfo.Srcs, c.Srcs().Strings()...)
+	outputFiles, err := c.OutputFiles("")
+	if err != nil {
+		panic(err)
+	}
+	dpInfo.Srcs = append(dpInfo.Srcs, outputFiles.Strings()...)
 }
 
 func (c *Module) AndroidMkWriteAdditionalDependenciesForSourceAbiDiff(w io.Writer) {
diff --git a/java/app_test.go b/java/app_test.go
index 559afcc..9aedebd 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -130,8 +130,12 @@
 		foo.Output(expectedOutput)
 	}
 
-	if g, w := foo.Module().(*AndroidApp).Srcs().Strings(), expectedOutputs; !reflect.DeepEqual(g, w) {
-		t.Errorf("want Srcs() = %q, got %q", w, g)
+	outputFiles, err := foo.Module().(*AndroidApp).OutputFiles("")
+	if err != nil {
+		t.Fatal(err)
+	}
+	if g, w := outputFiles.Strings(), expectedOutputs; !reflect.DeepEqual(g, w) {
+		t.Errorf(`want OutputFiles("") = %q, got %q`, w, g)
 	}
 }
 
diff --git a/java/droiddoc.go b/java/droiddoc.go
index 57da6b6..0ec7799 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -497,8 +497,13 @@
 	stubsSrcJar android.WritablePath
 }
 
-func (j *Javadoc) Srcs() android.Paths {
-	return android.Paths{j.stubsSrcJar}
+func (j *Javadoc) OutputFiles(tag string) (android.Paths, error) {
+	switch tag {
+	case "":
+		return android.Paths{j.stubsSrcJar}, nil
+	default:
+		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+	}
 }
 
 func JavadocFactory() android.Module {
@@ -519,7 +524,7 @@
 	return module
 }
 
-var _ android.SourceFileProducer = (*Javadoc)(nil)
+var _ android.OutputFileProducer = (*Javadoc)(nil)
 
 func (j *Javadoc) sdkVersion() string {
 	return String(j.properties.Sdk_version)
diff --git a/java/java.go b/java/java.go
index 31c6afe..bfbd98c 100644
--- a/java/java.go
+++ b/java/java.go
@@ -351,15 +351,20 @@
 	dexpreopter
 }
 
-func (j *Module) Srcs() android.Paths {
-	return append(android.Paths{j.outputFile}, j.extraOutputFiles...)
+func (j *Module) OutputFiles(tag string) (android.Paths, error) {
+	switch tag {
+	case "":
+		return append(android.Paths{j.outputFile}, j.extraOutputFiles...), nil
+	default:
+		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+	}
 }
 
 func (j *Module) DexJarFile() android.Path {
 	return j.dexJarFile
 }
 
-var _ android.SourceFileProducer = (*Module)(nil)
+var _ android.OutputFileProducer = (*Module)(nil)
 
 type Dependency interface {
 	HeaderJars() android.Paths
@@ -813,8 +818,6 @@
 			}
 		default:
 			switch tag {
-			case android.DefaultsDepTag, android.SourceDepTag:
-				// Nothing to do
 			case systemModulesTag:
 				if deps.systemModules != nil {
 					panic("Found two system module dependencies")
@@ -824,8 +827,6 @@
 					panic("Missing directory for system module dependency")
 				}
 				deps.systemModules = sm.outputFile
-			default:
-				ctx.ModuleErrorf("depends on non-java module %q", otherName)
 			}
 		}
 	})