Merge remote-tracking branch 'goog/mirror-aosp-master' into rvc-dev
* goog/mirror-aosp-master: Support checking syntax of generated Blueprint files Always emit rules for tests and add phony to run them Bug: 155628860 Test: treehugger Change-Id: Ie843b929edf1c11c201792eefcb7772b1e6daeb8
This commit is contained in:
commit
c9944506d5
4 changed files with 168 additions and 25 deletions
|
@ -333,13 +333,11 @@ func (g *goPackage) GenerateBuildActions(ctx blueprint.ModuleContext) {
|
|||
testSrcs = append(g.properties.TestSrcs, g.properties.Linux.TestSrcs...)
|
||||
}
|
||||
|
||||
if g.config.runGoTests {
|
||||
testArchiveFile := filepath.Join(testRoot(ctx, g.config),
|
||||
filepath.FromSlash(g.properties.PkgPath)+".a")
|
||||
g.testResultFile = buildGoTest(ctx, testRoot(ctx, g.config), testArchiveFile,
|
||||
g.properties.PkgPath, srcs, genSrcs,
|
||||
testSrcs)
|
||||
}
|
||||
testArchiveFile := filepath.Join(testRoot(ctx, g.config),
|
||||
filepath.FromSlash(g.properties.PkgPath)+".a")
|
||||
g.testResultFile = buildGoTest(ctx, testRoot(ctx, g.config), testArchiveFile,
|
||||
g.properties.PkgPath, srcs, genSrcs,
|
||||
testSrcs)
|
||||
|
||||
buildGoPackage(ctx, g.pkgRoot, g.properties.PkgPath, g.archiveFile,
|
||||
srcs, genSrcs)
|
||||
|
@ -436,9 +434,10 @@ func (g *goBinary) GenerateBuildActions(ctx blueprint.ModuleContext) {
|
|||
testSrcs = append(g.properties.TestSrcs, g.properties.Linux.TestSrcs...)
|
||||
}
|
||||
|
||||
testDeps := buildGoTest(ctx, testRoot(ctx, g.config), testArchiveFile,
|
||||
name, srcs, genSrcs, testSrcs)
|
||||
if g.config.runGoTests {
|
||||
deps = buildGoTest(ctx, testRoot(ctx, g.config), testArchiveFile,
|
||||
name, srcs, genSrcs, testSrcs)
|
||||
deps = append(deps, testDeps...)
|
||||
}
|
||||
|
||||
buildGoPackage(ctx, objDir, "main", archiveFile, srcs, genSrcs)
|
||||
|
@ -451,7 +450,9 @@ func (g *goBinary) GenerateBuildActions(ctx blueprint.ModuleContext) {
|
|||
linkDeps = append(linkDeps, dep.GoPackageTarget())
|
||||
libDir := dep.GoPkgRoot()
|
||||
libDirFlags = append(libDirFlags, "-L "+libDir)
|
||||
deps = append(deps, dep.GoTestTargets()...)
|
||||
if g.config.runGoTests {
|
||||
deps = append(deps, dep.GoTestTargets()...)
|
||||
}
|
||||
})
|
||||
|
||||
linkArgs := map[string]string{}
|
||||
|
@ -635,17 +636,22 @@ func (s *singleton) GenerateBuildActions(ctx blueprint.SingletonContext) {
|
|||
var primaryBuilders []*goBinary
|
||||
// blueprintTools contains blueprint go binaries that will be built in StageMain
|
||||
var blueprintTools []string
|
||||
ctx.VisitAllModulesIf(isBootstrapBinaryModule,
|
||||
func(module blueprint.Module) {
|
||||
binaryModule := module.(*goBinary)
|
||||
|
||||
// blueprintTests contains the result files from the tests
|
||||
var blueprintTests []string
|
||||
ctx.VisitAllModules(func(module blueprint.Module) {
|
||||
if binaryModule, ok := module.(*goBinary); ok {
|
||||
if binaryModule.properties.Tool_dir {
|
||||
blueprintTools = append(blueprintTools, binaryModule.InstallPath())
|
||||
}
|
||||
if binaryModule.properties.PrimaryBuilder {
|
||||
primaryBuilders = append(primaryBuilders, binaryModule)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if packageModule, ok := module.(goPackageProducer); ok {
|
||||
blueprintTests = append(blueprintTests, packageModule.GoTestTargets()...)
|
||||
}
|
||||
})
|
||||
|
||||
var extraSharedFlagArray []string
|
||||
if s.config.runGoTests {
|
||||
|
@ -759,6 +765,14 @@ func (s *singleton) GenerateBuildActions(ctx blueprint.SingletonContext) {
|
|||
Outputs: []string{"blueprint_tools"},
|
||||
Inputs: blueprintTools,
|
||||
})
|
||||
|
||||
// Add a phony target for running all of the tests
|
||||
ctx.Build(pctx, blueprint.BuildParams{
|
||||
Rule: blueprint.Phony,
|
||||
Outputs: []string{"blueprint_tests"},
|
||||
Inputs: blueprintTests,
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
16
context.go
16
context.go
|
@ -692,7 +692,7 @@ func (c *Context) ParseFileList(rootDir string, filePaths []string,
|
|||
var scopedModuleFactories map[string]ModuleFactory
|
||||
|
||||
var addModule func(module *moduleInfo) []error
|
||||
addModule = func(module *moduleInfo) ([]error) {
|
||||
addModule = func(module *moduleInfo) []error {
|
||||
// Run any load hooks immediately before it is sent to the moduleCh and is
|
||||
// registered by name. This allows load hooks to set and/or modify any aspect
|
||||
// of the module (including names) using information that is not available when
|
||||
|
@ -716,7 +716,7 @@ func (c *Context) ParseFileList(rootDir string, filePaths []string,
|
|||
for _, def := range file.Defs {
|
||||
switch def := def.(type) {
|
||||
case *parser.Module:
|
||||
module, errs := c.processModuleDef(def, file.Name, scopedModuleFactories)
|
||||
module, errs := processModuleDef(def, file.Name, c.moduleFactories, scopedModuleFactories, c.ignoreUnknownModuleTypes)
|
||||
if len(errs) == 0 && module != nil {
|
||||
errs = addModule(module)
|
||||
}
|
||||
|
@ -1349,7 +1349,7 @@ func (c *Context) prettyPrintGroupVariants(group *moduleGroup) string {
|
|||
return strings.Join(variants, "\n ")
|
||||
}
|
||||
|
||||
func (c *Context) newModule(factory ModuleFactory) *moduleInfo {
|
||||
func newModule(factory ModuleFactory) *moduleInfo {
|
||||
logicModule, properties := factory()
|
||||
|
||||
module := &moduleInfo{
|
||||
|
@ -1362,15 +1362,15 @@ func (c *Context) newModule(factory ModuleFactory) *moduleInfo {
|
|||
return module
|
||||
}
|
||||
|
||||
func (c *Context) processModuleDef(moduleDef *parser.Module,
|
||||
relBlueprintsFile string, scopedModuleFactories map[string]ModuleFactory) (*moduleInfo, []error) {
|
||||
func processModuleDef(moduleDef *parser.Module,
|
||||
relBlueprintsFile string, moduleFactories, scopedModuleFactories map[string]ModuleFactory, ignoreUnknownModuleTypes bool) (*moduleInfo, []error) {
|
||||
|
||||
factory, ok := c.moduleFactories[moduleDef.Type]
|
||||
factory, ok := moduleFactories[moduleDef.Type]
|
||||
if !ok && scopedModuleFactories != nil {
|
||||
factory, ok = scopedModuleFactories[moduleDef.Type]
|
||||
}
|
||||
if !ok {
|
||||
if c.ignoreUnknownModuleTypes {
|
||||
if ignoreUnknownModuleTypes {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
@ -1382,7 +1382,7 @@ func (c *Context) processModuleDef(moduleDef *parser.Module,
|
|||
}
|
||||
}
|
||||
|
||||
module := c.newModule(factory)
|
||||
module := newModule(factory)
|
||||
module.typeName = moduleDef.Type
|
||||
|
||||
module.relBlueprintsFile = relBlueprintsFile
|
||||
|
|
|
@ -17,9 +17,11 @@ package blueprint
|
|||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"text/scanner"
|
||||
|
||||
"github.com/google/blueprint/parser"
|
||||
"github.com/google/blueprint/pathtools"
|
||||
"github.com/google/blueprint/proptools"
|
||||
)
|
||||
|
@ -988,7 +990,7 @@ func (mctx *mutatorContext) Rename(name string) {
|
|||
}
|
||||
|
||||
func (mctx *mutatorContext) CreateModule(factory ModuleFactory, props ...interface{}) Module {
|
||||
module := mctx.context.newModule(factory)
|
||||
module := newModule(factory)
|
||||
|
||||
module.relBlueprintsFile = mctx.module.relBlueprintsFile
|
||||
module.pos = mctx.module.pos
|
||||
|
@ -1035,7 +1037,7 @@ type LoadHookContext interface {
|
|||
}
|
||||
|
||||
func (l *loadHookContext) CreateModule(factory ModuleFactory, props ...interface{}) Module {
|
||||
module := l.context.newModule(factory)
|
||||
module := newModule(factory)
|
||||
|
||||
module.relBlueprintsFile = l.module.relBlueprintsFile
|
||||
module.pos = l.module.pos
|
||||
|
@ -1123,3 +1125,44 @@ func runAndRemoveLoadHooks(ctx *Context, config interface{}, module *moduleInfo,
|
|||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Check the syntax of a generated blueprint file.
|
||||
//
|
||||
// This is intended to perform a quick sanity check for generated blueprint
|
||||
// code to ensure that it is syntactically correct, where syntactically correct
|
||||
// means:
|
||||
// * No variable definitions.
|
||||
// * Valid module types.
|
||||
// * Valid property names.
|
||||
// * Valid values for the property type.
|
||||
//
|
||||
// It does not perform any semantic checking of properties, existence of referenced
|
||||
// files, or dependencies.
|
||||
//
|
||||
// At a low level it:
|
||||
// * Parses the contents.
|
||||
// * Invokes relevant factory to create Module instances.
|
||||
// * Unpacks the properties into the Module.
|
||||
// * Does not invoke load hooks or any mutators.
|
||||
//
|
||||
// The filename is only used for reporting errors.
|
||||
func CheckBlueprintSyntax(moduleFactories map[string]ModuleFactory, filename string, contents string) []error {
|
||||
scope := parser.NewScope(nil)
|
||||
file, errs := parser.Parse(filename, strings.NewReader(contents), scope)
|
||||
if len(errs) != 0 {
|
||||
return errs
|
||||
}
|
||||
|
||||
for _, def := range file.Defs {
|
||||
switch def := def.(type) {
|
||||
case *parser.Module:
|
||||
_, moduleErrs := processModuleDef(def, filename, moduleFactories, nil, false)
|
||||
errs = append(errs, moduleErrs...)
|
||||
|
||||
default:
|
||||
panic(fmt.Errorf("unknown definition type: %T", def))
|
||||
}
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
||||
|
|
|
@ -195,3 +195,89 @@ func TestAliases(t *testing.T) {
|
|||
"\n 1:a, 2:a\n 1:a, 2:b\n 1:b, 2:a\n 1:b, 2:b")
|
||||
})
|
||||
}
|
||||
|
||||
func expectedErrors(t *testing.T, errs []error, expectedMessages ...string) {
|
||||
t.Helper()
|
||||
if len(errs) != len(expectedMessages) {
|
||||
t.Errorf("expected %d error, found: %q", len(expectedMessages), errs)
|
||||
} else {
|
||||
for i, expected := range expectedMessages {
|
||||
err := errs[i]
|
||||
if err.Error() != expected {
|
||||
t.Errorf("expected error %q found %q", expected, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckBlueprintSyntax(t *testing.T) {
|
||||
factories := map[string]ModuleFactory{
|
||||
"test": newModuleCtxTestModule,
|
||||
}
|
||||
|
||||
t.Run("valid", func(t *testing.T) {
|
||||
errs := CheckBlueprintSyntax(factories, "path/Blueprint", `
|
||||
test {
|
||||
name: "test",
|
||||
}
|
||||
`)
|
||||
expectedErrors(t, errs)
|
||||
})
|
||||
|
||||
t.Run("syntax error", func(t *testing.T) {
|
||||
errs := CheckBlueprintSyntax(factories, "path/Blueprint", `
|
||||
test {
|
||||
name: "test",
|
||||
|
||||
`)
|
||||
|
||||
expectedErrors(t, errs, `path/Blueprint:5:1: expected "}", found EOF`)
|
||||
})
|
||||
|
||||
t.Run("unknown module type", func(t *testing.T) {
|
||||
errs := CheckBlueprintSyntax(factories, "path/Blueprint", `
|
||||
test2 {
|
||||
name: "test",
|
||||
}
|
||||
`)
|
||||
|
||||
expectedErrors(t, errs, `path/Blueprint:2:1: unrecognized module type "test2"`)
|
||||
})
|
||||
|
||||
t.Run("unknown property name", func(t *testing.T) {
|
||||
errs := CheckBlueprintSyntax(factories, "path/Blueprint", `
|
||||
test {
|
||||
nam: "test",
|
||||
}
|
||||
`)
|
||||
|
||||
expectedErrors(t, errs, `path/Blueprint:3:5: unrecognized property "nam"`)
|
||||
})
|
||||
|
||||
t.Run("invalid property type", func(t *testing.T) {
|
||||
errs := CheckBlueprintSyntax(factories, "path/Blueprint", `
|
||||
test {
|
||||
name: false,
|
||||
}
|
||||
`)
|
||||
|
||||
expectedErrors(t, errs, `path/Blueprint:3:8: can't assign bool value to string property "name"`)
|
||||
})
|
||||
|
||||
t.Run("multiple failures", func(t *testing.T) {
|
||||
errs := CheckBlueprintSyntax(factories, "path/Blueprint", `
|
||||
test {
|
||||
name: false,
|
||||
}
|
||||
|
||||
test2 {
|
||||
name: false,
|
||||
}
|
||||
`)
|
||||
|
||||
expectedErrors(t, errs,
|
||||
`path/Blueprint:3:8: can't assign bool value to string property "name"`,
|
||||
`path/Blueprint:6:1: unrecognized module type "test2"`,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue