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
This commit is contained in:
parent
294941bee9
commit
faeb7aa135
|
@ -145,6 +145,7 @@ bootstrap_go_package {
|
|||
],
|
||||
testSrcs: [
|
||||
"cc/cc_test.go",
|
||||
"cc/test_data_test.go",
|
||||
],
|
||||
pluginFor: ["soong_build"],
|
||||
}
|
||||
|
|
|
@ -77,6 +77,7 @@ type ModuleContext interface {
|
|||
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 @@ type SourceFileProducer interface {
|
|||
}
|
||||
|
||||
// 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 @@ func (ctx *androidModuleContext) ExpandSources(srcFiles, excludes []string) Path
|
|||
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 {
|
||||
|
|
|
@ -86,6 +86,11 @@ type Path interface {
|
|||
|
||||
// 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 @@ func (p WritablePaths) Strings() []string {
|
|||
type basePath struct {
|
||||
path string
|
||||
config Config
|
||||
rel string
|
||||
}
|
||||
|
||||
func (p basePath) Ext() string {
|
||||
|
@ -293,6 +299,13 @@ func (p basePath) Base() string {
|
|||
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 @@ var _ Path = SourcePath{}
|
|||
// 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 @@ func safePathForSource(ctx PathContext, path string) SourcePath {
|
|||
// 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 @@ func OptionalPathForSource(ctx PathContext, intermediates string, paths ...strin
|
|||
}
|
||||
|
||||
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 @@ var _ Path = OutputPath{}
|
|||
// 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 @@ var _ resPathProvider = ModuleSrcPath{}
|
|||
// 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 @@ func (p ModuleSrcPath) resPathWithName(ctx ModuleContext, name string) ModuleRes
|
|||
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
|
||||
|
|
|
@ -159,6 +159,23 @@ func (test *testBinary) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkDa
|
|||
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) {
|
||||
|
|
12
cc/test.go
12
cc/test.go
|
@ -19,9 +19,8 @@ import (
|
|||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/google/blueprint"
|
||||
|
||||
"android/soong/android"
|
||||
"github.com/google/blueprint"
|
||||
)
|
||||
|
||||
type TestProperties struct {
|
||||
|
@ -38,6 +37,10 @@ type TestBinaryProperties struct {
|
|||
// 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 @@ type testBinary struct {
|
|||
*binaryDecorator
|
||||
*baseCompiler
|
||||
Properties TestBinaryProperties
|
||||
data android.Paths
|
||||
}
|
||||
|
||||
func (test *testBinary) linkerProps() []interface{} {
|
||||
|
@ -205,6 +209,8 @@ func (test *testBinary) linkerInit(ctx BaseModuleContext) {
|
|||
}
|
||||
|
||||
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) linkerFlags(ctx ModuleContext, flags Flags) Flags {
|
|||
}
|
||||
|
||||
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"
|
||||
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -29,6 +29,12 @@ type fileGroupProperties struct {
|
|||
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) DepsMutator(ctx android.BottomUpMutatorContext) {
|
|||
}
|
||||
|
||||
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 {
|
||||
|
|
Loading…
Reference in New Issue