Support data properties for test binaries

Allow tests to specify a data property that lists files or filegroup
modules that will be packaged alongside the test.  Also add a path
property to filegroup modules to allow shifting the path of the
packaged files, and add ExpandSourcesSubDir to expand the filegroup
sources while including a shifted relative path in the Paths objects.

Test: soong tests, manually adding data to a module
Change-Id: I52a48942660e12755d313ef13279313361b4fc35
diff --git a/Android.bp b/Android.bp
index 23cbad8..4ba6959 100644
--- a/Android.bp
+++ b/Android.bp
@@ -145,6 +145,7 @@
     ],
     testSrcs: [
         "cc/cc_test.go",
+        "cc/test_data_test.go",
     ],
     pluginFor: ["soong_build"],
 }
diff --git a/android/module.go b/android/module.go
index 430563d..b5de1ad 100644
--- a/android/module.go
+++ b/android/module.go
@@ -77,6 +77,7 @@
 	ModuleBuild(pctx blueprint.PackageContext, params ModuleBuildParams)
 
 	ExpandSources(srcFiles, excludes []string) Paths
+	ExpandSourcesSubDir(srcFiles, excludes []string, subDir string) Paths
 	Glob(globPattern string, excludes []string) Paths
 
 	InstallFile(installPath OutputPath, srcPath Path, deps ...Path) OutputPath
@@ -742,9 +743,14 @@
 }
 
 // Returns a list of paths expanded from globs and modules referenced using ":module" syntax.
-// ExpandSourceDeps must have already been called during the dependency resolution phase.
+// ExtractSourcesDeps must have already been called during the dependency resolution phase.
 func (ctx *androidModuleContext) ExpandSources(srcFiles, excludes []string) Paths {
+	return ctx.ExpandSourcesSubDir(srcFiles, excludes, "")
+}
+
+func (ctx *androidModuleContext) ExpandSourcesSubDir(srcFiles, excludes []string, subDir string) Paths {
 	prefix := PathForModuleSrc(ctx).String()
+
 	for i, e := range excludes {
 		j := findStringInSlice(e, srcFiles)
 		if j != -1 {
@@ -754,23 +760,28 @@
 		excludes[i] = filepath.Join(prefix, e)
 	}
 
-	globbedSrcFiles := make(Paths, 0, len(srcFiles))
+	expandedSrcFiles := make(Paths, 0, len(srcFiles))
 	for _, s := range srcFiles {
 		if m := SrcIsModule(s); m != "" {
 			module := ctx.GetDirectDepWithTag(m, SourceDepTag)
 			if srcProducer, ok := module.(SourceFileProducer); ok {
-				globbedSrcFiles = append(globbedSrcFiles, srcProducer.Srcs()...)
+				expandedSrcFiles = append(expandedSrcFiles, srcProducer.Srcs()...)
 			} else {
 				ctx.ModuleErrorf("srcs dependency %q is not a source file producing module", m)
 			}
 		} else if pathtools.IsGlob(s) {
-			globbedSrcFiles = append(globbedSrcFiles, ctx.Glob(filepath.Join(prefix, s), excludes)...)
+			globbedSrcFiles := ctx.Glob(filepath.Join(prefix, s), excludes)
+			expandedSrcFiles = append(expandedSrcFiles, globbedSrcFiles...)
+			for i, s := range expandedSrcFiles {
+				expandedSrcFiles[i] = s.(ModuleSrcPath).WithSubDir(ctx, subDir)
+			}
 		} else {
-			globbedSrcFiles = append(globbedSrcFiles, PathForModuleSrc(ctx, s))
+			s := PathForModuleSrc(ctx, s).WithSubDir(ctx, subDir)
+			expandedSrcFiles = append(expandedSrcFiles, s)
 		}
 	}
 
-	return globbedSrcFiles
+	return expandedSrcFiles
 }
 
 func (ctx *androidModuleContext) Glob(globPattern string, excludes []string) Paths {
diff --git a/android/paths.go b/android/paths.go
index ac7d81e..037c98d 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -86,6 +86,11 @@
 
 	// Base returns the last element of the path
 	Base() string
+
+	// Rel returns the portion of the path relative to the directory it was created from.  For
+	// example, Rel on a PathsForModuleSrc would return the path relative to the module source
+	// directory.
+	Rel() string
 }
 
 // WritablePath is a type of path that can be used as an output for build rules.
@@ -283,6 +288,7 @@
 type basePath struct {
 	path   string
 	config Config
+	rel    string
 }
 
 func (p basePath) Ext() string {
@@ -293,6 +299,13 @@
 	return filepath.Base(p.path)
 }
 
+func (p basePath) Rel() string {
+	if p.rel != "" {
+		return p.rel
+	}
+	return p.path
+}
+
 // SourcePath is a Path representing a file path rooted from SrcDir
 type SourcePath struct {
 	basePath
@@ -304,7 +317,7 @@
 // code that is embedding ninja variables in paths
 func safePathForSource(ctx PathContext, path string) SourcePath {
 	p := validateSafePath(ctx, path)
-	ret := SourcePath{basePath{p, pathConfig(ctx)}}
+	ret := SourcePath{basePath{p, pathConfig(ctx), ""}}
 
 	abs, err := filepath.Abs(ret.String())
 	if err != nil {
@@ -330,7 +343,7 @@
 // will return a usable, but invalid SourcePath, and report a ModuleError.
 func PathForSource(ctx PathContext, paths ...string) SourcePath {
 	p := validatePath(ctx, paths...)
-	ret := SourcePath{basePath{p, pathConfig(ctx)}}
+	ret := SourcePath{basePath{p, pathConfig(ctx), ""}}
 
 	abs, err := filepath.Abs(ret.String())
 	if err != nil {
@@ -365,7 +378,7 @@
 	}
 
 	p := validatePath(ctx, paths...)
-	path := SourcePath{basePath{p, pathConfig(ctx)}}
+	path := SourcePath{basePath{p, pathConfig(ctx), ""}}
 
 	abs, err := filepath.Abs(path.String())
 	if err != nil {
@@ -476,7 +489,7 @@
 // OutputPath, and report a ModuleError.
 func PathForOutput(ctx PathContext, paths ...string) OutputPath {
 	path := validatePath(ctx, paths...)
-	return OutputPath{basePath{path, pathConfig(ctx)}}
+	return OutputPath{basePath{path, pathConfig(ctx), ""}}
 }
 
 func (p OutputPath) writablePath() {}
@@ -516,8 +529,10 @@
 // PathForModuleSrc returns a ModuleSrcPath representing the paths... under the
 // module's local source directory.
 func PathForModuleSrc(ctx ModuleContext, paths ...string) ModuleSrcPath {
-	path := validatePath(ctx, paths...)
-	return ModuleSrcPath{PathForSource(ctx, ctx.ModuleDir(), path)}
+	p := validatePath(ctx, paths...)
+	path := ModuleSrcPath{PathForSource(ctx, ctx.ModuleDir(), p)}
+	path.basePath.rel = p
+	return path
 }
 
 // OptionalPathForModuleSrc returns an OptionalPath. The OptionalPath contains a
@@ -542,6 +557,18 @@
 	return PathForModuleRes(ctx, p.path, name)
 }
 
+func (p ModuleSrcPath) WithSubDir(ctx ModuleContext, subdir string) ModuleSrcPath {
+	subdir = PathForModuleSrc(ctx, subdir).String()
+	var err error
+	rel, err := filepath.Rel(subdir, p.path)
+	if err != nil {
+		ctx.ModuleErrorf("source file %q is not under path %q", p.path, subdir)
+		return p
+	}
+	p.rel = rel
+	return p
+}
+
 // ModuleOutPath is a Path representing a module's output directory.
 type ModuleOutPath struct {
 	OutputPath
diff --git a/cc/androidmk.go b/cc/androidmk.go
index c0be111..182938c 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -159,6 +159,23 @@
 	if Bool(test.Properties.Test_per_src) {
 		ret.SubName = "_" + test.binaryDecorator.Properties.Stem
 	}
+
+	var testFiles []string
+	for _, d := range test.data {
+		rel := d.Rel()
+		path := d.String()
+		if !strings.HasSuffix(path, rel) {
+			panic(fmt.Errorf("path %q does not end with %q", path, rel))
+		}
+		path = strings.TrimSuffix(path, rel)
+		testFiles = append(testFiles, path+":"+rel)
+	}
+	if len(testFiles) > 0 {
+		ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) error {
+			fmt.Fprintln(w, "LOCAL_TEST_DATA := "+strings.Join(testFiles, " "))
+			return nil
+		})
+	}
 }
 
 func (test *testLibrary) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
diff --git a/cc/test.go b/cc/test.go
index f60996c..d3556bf 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -19,9 +19,8 @@
 	"runtime"
 	"strings"
 
-	"github.com/google/blueprint"
-
 	"android/soong/android"
+	"github.com/google/blueprint"
 )
 
 type TestProperties struct {
@@ -38,6 +37,10 @@
 	// relative_install_path. Useful if several tests need to be in the same
 	// directory, but test_per_src doesn't work.
 	No_named_install_directory *bool
+
+	// list of files or filegroup modules that provide data that should be installed alongside
+	// the test
+	Data []string
 }
 
 func init() {
@@ -191,6 +194,7 @@
 	*binaryDecorator
 	*baseCompiler
 	Properties TestBinaryProperties
+	data       android.Paths
 }
 
 func (test *testBinary) linkerProps() []interface{} {
@@ -205,6 +209,8 @@
 }
 
 func (test *testBinary) linkerDeps(ctx DepsContext, deps Deps) Deps {
+	android.ExtractSourcesDeps(ctx, test.Properties.Data)
+
 	deps = test.testDecorator.linkerDeps(ctx, deps)
 	deps = test.binaryDecorator.linkerDeps(ctx, deps)
 	return deps
@@ -217,6 +223,8 @@
 }
 
 func (test *testBinary) install(ctx ModuleContext, file android.Path) {
+	test.data = ctx.ExpandSources(test.Properties.Data, nil)
+
 	test.binaryDecorator.baseInstaller.dir = "nativetest"
 	test.binaryDecorator.baseInstaller.dir64 = "nativetest64"
 
diff --git a/cc/test_data_test.go b/cc/test_data_test.go
new file mode 100644
index 0000000..e3b1214
--- /dev/null
+++ b/cc/test_data_test.go
@@ -0,0 +1,204 @@
+// Copyright 2017 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 cc
+
+import (
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"testing"
+
+	"android/soong/android"
+	"github.com/google/blueprint"
+)
+
+type dataFile struct {
+	path string
+	file string
+}
+
+var testDataTests = []struct {
+	name    string
+	modules string
+	data    []dataFile
+}{
+	{
+		name: "data files",
+		modules: `
+			test {
+				name: "foo",
+				data: [
+					"baz",
+					"bar/baz",
+				],
+			}`,
+		data: []dataFile{
+			{"dir", "baz"},
+			{"dir", "bar/baz"},
+		},
+	},
+	{
+		name: "filegroup",
+		modules: `
+			filegroup {
+				name: "fg",
+				srcs: [
+					"baz",
+					"bar/baz",
+				],
+			}
+
+			test {
+				name: "foo",
+				data: [":fg"],
+			}`,
+		data: []dataFile{
+			{"dir", "baz"},
+			{"dir", "bar/baz"},
+		},
+	},
+	{
+		name: "relative filegroup",
+		modules: `
+			filegroup {
+				name: "fg",
+				srcs: [
+					"bar/baz",
+				],
+				path: "bar",
+			}
+
+			test {
+				name: "foo",
+				data: [":fg"],
+			}`,
+		data: []dataFile{
+			{"dir/bar", "baz"},
+		},
+	},
+	{
+		name: "relative filegroup trailing slash",
+		modules: `
+			filegroup {
+				name: "fg",
+				srcs: [
+					"bar/baz",
+				],
+				path: "bar/",
+			}
+
+			test {
+				name: "foo",
+				data: [":fg"],
+			}`,
+		data: []dataFile{
+			{"dir/bar", "baz"},
+		},
+	},
+}
+
+func TestDataTests(t *testing.T) {
+	buildDir, err := ioutil.TempDir("", "soong_test_test")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer os.RemoveAll(buildDir)
+
+	config := android.TestConfig(buildDir)
+
+	for _, test := range testDataTests {
+		t.Run(test.name, func(t *testing.T) {
+			ctx := android.NewContext()
+			ctx.MockFileSystem(map[string][]byte{
+				"Blueprints":     []byte(`subdirs = ["dir"]`),
+				"dir/Blueprints": []byte(test.modules),
+				"dir/baz":        nil,
+				"dir/bar/baz":    nil,
+			})
+			ctx.RegisterModuleType("test", newTest)
+
+			_, errs := ctx.ParseBlueprintsFiles("Blueprints")
+			fail(t, errs)
+			_, errs = ctx.PrepareBuildActions(config)
+			fail(t, errs)
+
+			foo := findModule(ctx, "foo")
+			if foo == nil {
+				t.Fatalf("failed to find module foo")
+			}
+
+			got := foo.(*testDataTest).data
+			if len(got) != len(test.data) {
+				t.Errorf("expected %d data files, got %d",
+					len(test.data), len(got))
+			}
+
+			for i := range got {
+				if i >= len(test.data) {
+					break
+				}
+
+				path := filepath.Join(test.data[i].path, test.data[i].file)
+				if test.data[i].file != got[i].Rel() ||
+					path != got[i].String() {
+					fmt.Errorf("expected %s:%s got %s:%s",
+						path, test.data[i].file,
+						got[i].String(), got[i].Rel())
+				}
+			}
+		})
+	}
+}
+
+type testDataTest struct {
+	android.ModuleBase
+	data       android.Paths
+	Properties struct {
+		Data []string
+	}
+}
+
+func newTest() (blueprint.Module, []interface{}) {
+	m := &testDataTest{}
+	return android.InitAndroidModule(m, &m.Properties)
+}
+
+func (test *testDataTest) DepsMutator(ctx android.BottomUpMutatorContext) {
+	android.ExtractSourcesDeps(ctx, test.Properties.Data)
+}
+
+func (test *testDataTest) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	test.data = ctx.ExpandSources(test.Properties.Data, nil)
+}
+
+func findModule(ctx *blueprint.Context, name string) blueprint.Module {
+	var ret blueprint.Module
+	ctx.VisitAllModules(func(m blueprint.Module) {
+		if ctx.ModuleName(m) == name {
+			ret = m
+		}
+	})
+	return ret
+}
+
+func fail(t *testing.T, errs []error) {
+	if len(errs) > 0 {
+		for _, err := range errs {
+			t.Error(err)
+		}
+		t.FailNow()
+	}
+}
diff --git a/genrule/filegroup.go b/genrule/filegroup.go
index 9b53c9f..c1d08a8 100644
--- a/genrule/filegroup.go
+++ b/genrule/filegroup.go
@@ -29,6 +29,12 @@
 	Srcs []string
 
 	Exclude_srcs []string
+
+	// The base path to the files.  May be used by other modules to determine which portion
+	// of the path to use.  For example, when a filegroup is used as data in a cc_test rule,
+	// the base path is stripped off the path and the remaining path is used as the
+	// installation directory.
+	Path string
 }
 
 type fileGroup struct {
@@ -53,7 +59,7 @@
 }
 
 func (fg *fileGroup) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	fg.srcs = ctx.ExpandSources(fg.properties.Srcs, fg.properties.Exclude_srcs)
+	fg.srcs = ctx.ExpandSourcesSubDir(fg.properties.Srcs, fg.properties.Exclude_srcs, fg.properties.Path)
 }
 
 func (fg *fileGroup) Srcs() android.Paths {