am 5aaa669b: Merge remote-tracking branch \'aosp/upstream\'
* commit '5aaa669beb3adefb167012ee6b66e476c8441723':
Add a test runner
Fix bugs related to local vs. inherited variables
diff --git a/Blueprints b/Blueprints
index 70d57db..df3ad60 100644
--- a/Blueprints
+++ b/Blueprints
@@ -125,6 +125,11 @@
)
bootstrap_core_go_binary(
+ name = "gotestrunner",
+ srcs = ["gotestrunner/gotestrunner.go"],
+)
+
+bootstrap_core_go_binary(
name = "choosestage",
srcs = ["choosestage/choosestage.go"],
)
diff --git a/bootstrap/bootstrap.go b/bootstrap/bootstrap.go
index dbc4710..ed5debc 100644
--- a/bootstrap/bootstrap.go
+++ b/bootstrap/bootstrap.go
@@ -30,6 +30,7 @@
pctx = blueprint.NewPackageContext("github.com/google/blueprint/bootstrap")
goTestMainCmd = pctx.StaticVariable("goTestMainCmd", filepath.Join(bootstrapDir, "bin", "gotestmain"))
+ goTestRunnerCmd = pctx.StaticVariable("goTestRunnerCmd", filepath.Join(bootstrapDir, "bin", "gotestrunner"))
chooseStageCmd = pctx.StaticVariable("chooseStageCmd", filepath.Join(bootstrapDir, "bin", "choosestage"))
pluginGenSrcCmd = pctx.StaticVariable("pluginGenSrcCmd", filepath.Join(bootstrapDir, "bin", "loadplugins"))
@@ -64,7 +65,7 @@
test = pctx.StaticRule("test",
blueprint.RuleParams{
- Command: "(cd $pkgSrcDir && $$OLDPWD/$in -test.short) && touch $out",
+ Command: "$goTestRunnerCmd -p $pkgSrcDir -f $out -- $in -test.short",
Description: "test $pkg",
},
"pkg", "pkgSrcDir")
@@ -111,7 +112,7 @@
docsDir = filepath.Join(bootstrapDir, "docs")
- bootstrapDir = filepath.Join("$buildDir", bootstrapSubDir)
+ bootstrapDir = filepath.Join("$buildDir", bootstrapSubDir)
miniBootstrapDir = filepath.Join("$buildDir", miniBootstrapSubDir)
)
@@ -553,9 +554,10 @@
})
ctx.Build(pctx, blueprint.BuildParams{
- Rule: test,
- Outputs: []string{testPassed},
- Inputs: []string{testFile},
+ Rule: test,
+ Outputs: []string{testPassed},
+ Inputs: []string{testFile},
+ Implicits: []string{"$goTestRunnerCmd"},
Args: map[string]string{
"pkg": pkgPath,
"pkgSrcDir": filepath.Dir(testFiles[0]),
diff --git a/build.ninja.in b/build.ninja.in
index a782142..751dec0 100644
--- a/build.ninja.in
+++ b/build.ninja.in
@@ -190,7 +190,7 @@
# Variant:
# Type: bootstrap_core_go_binary
# Factory: github.com/google/blueprint/bootstrap.func·005
-# Defined: Blueprints:127:1
+# Defined: Blueprints:132:1
build ${g.bootstrap.buildDir}/.bootstrap/choosestage/obj/choosestage.a: $
g.bootstrap.compile ${g.bootstrap.srcDir}/choosestage/choosestage.go | $
@@ -230,6 +230,28 @@
default ${g.bootstrap.BinDir}/gotestmain
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Module: gotestrunner
+# Variant:
+# Type: bootstrap_core_go_binary
+# Factory: github.com/google/blueprint/bootstrap.func·005
+# Defined: Blueprints:127:1
+
+build ${g.bootstrap.buildDir}/.bootstrap/gotestrunner/obj/gotestrunner.a: $
+ g.bootstrap.compile ${g.bootstrap.srcDir}/gotestrunner/gotestrunner.go $
+ | ${g.bootstrap.compileCmd}
+ pkgPath = gotestrunner
+default ${g.bootstrap.buildDir}/.bootstrap/gotestrunner/obj/gotestrunner.a
+
+build ${g.bootstrap.buildDir}/.bootstrap/gotestrunner/obj/a.out: $
+ g.bootstrap.link $
+ ${g.bootstrap.buildDir}/.bootstrap/gotestrunner/obj/gotestrunner.a | $
+ ${g.bootstrap.linkCmd}
+default ${g.bootstrap.buildDir}/.bootstrap/gotestrunner/obj/a.out
+build ${g.bootstrap.BinDir}/gotestrunner: g.bootstrap.cp $
+ ${g.bootstrap.buildDir}/.bootstrap/gotestrunner/obj/a.out
+default ${g.bootstrap.BinDir}/gotestrunner
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Module: minibp
# Variant:
# Type: bootstrap_core_go_binary
@@ -279,7 +301,8 @@
${g.bootstrap.buildDir}/.bootstrap/primary.ninja.in.timestamp: $
s.bootstrap.primarybp ${g.bootstrap.srcDir}/Blueprints | $
${g.bootstrap.BinDir}/choosestage ${g.bootstrap.BinDir}/gotestmain $
- ${g.bootstrap.BinDir}/minibp ${g.bootstrap.srcDir}/Blueprints
+ ${g.bootstrap.BinDir}/gotestrunner ${g.bootstrap.BinDir}/minibp $
+ ${g.bootstrap.srcDir}/Blueprints
outfile = ${g.bootstrap.buildDir}/.bootstrap/primary.ninja.in
timestamp = ${g.bootstrap.buildDir}/.bootstrap/primary.ninja.in.timestamp
timestampdep = ${g.bootstrap.buildDir}/.bootstrap/primary.ninja.in.timestamp.d
diff --git a/context.go b/context.go
index 3892f4e..dd2dc1d 100644
--- a/context.go
+++ b/context.go
@@ -469,12 +469,12 @@
}
file.Name = relBlueprintsFile
- subdirs, subdirsPos, err := getStringListFromScope(scope, "subdirs")
+ subdirs, subdirsPos, err := getLocalStringListFromScope(scope, "subdirs")
if err != nil {
errs = append(errs, err)
}
- build, buildPos, err := getStringListFromScope(scope, "build")
+ build, buildPos, err := getLocalStringListFromScope(scope, "build")
if err != nil {
errs = append(errs, err)
}
@@ -799,8 +799,10 @@
return blueprints, deps, errs
}
-func getStringListFromScope(scope *parser.Scope, v string) ([]string, scanner.Position, error) {
- if assignment, err := scope.Get(v); err == nil {
+func getLocalStringListFromScope(scope *parser.Scope, v string) ([]string, scanner.Position, error) {
+ if assignment, local := scope.Get(v); assignment == nil || !local {
+ return nil, scanner.Position{}, nil
+ } else {
switch assignment.Value.Type {
case parser.List:
ret := make([]string, 0, len(assignment.Value.ListValue))
@@ -824,12 +826,12 @@
panic(fmt.Errorf("unknown value type: %d", assignment.Value.Type))
}
}
-
- return nil, scanner.Position{}, nil
}
func getStringFromScope(scope *parser.Scope, v string) (string, scanner.Position, error) {
- if assignment, err := scope.Get(v); err == nil {
+ if assignment, _ := scope.Get(v); assignment == nil {
+ return "", scanner.Position{}, nil
+ } else {
switch assignment.Value.Type {
case parser.String:
return assignment.Value.StringValue, assignment.Pos, nil
@@ -842,8 +844,6 @@
panic(fmt.Errorf("unknown value type: %d", assignment.Value.Type))
}
}
-
- return "", scanner.Position{}, nil
}
func (c *Context) createVariations(origModule *moduleInfo, mutatorName string,
diff --git a/gotestrunner/gotestrunner.go b/gotestrunner/gotestrunner.go
new file mode 100644
index 0000000..20fbe1c
--- /dev/null
+++ b/gotestrunner/gotestrunner.go
@@ -0,0 +1,103 @@
+// 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 main
+
+import (
+ "bufio"
+ "bytes"
+ "flag"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "syscall"
+)
+
+var (
+ chdir = flag.String("p", "", "Change to a path before executing test")
+ touch = flag.String("f", "", "Write a file on success")
+)
+
+// This will copy the stdout from the test process to our stdout
+// unless it only contains "PASS\n".
+func handleStdout(stdout io.Reader) {
+ reader := bufio.NewReader(stdout)
+
+ // This is intentionally 6 instead of 5 to check for EOF
+ buf, _ := reader.Peek(6)
+ if bytes.Equal(buf, []byte("PASS\n")) {
+ return
+ }
+
+ io.Copy(os.Stdout, reader)
+}
+
+func main() {
+ flag.Parse()
+
+ if flag.NArg() == 0 {
+ fmt.Fprintln(os.Stderr, "error: must pass at least one test executable")
+ os.Exit(1)
+ }
+
+ test, err := filepath.Abs(flag.Arg(0))
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "error: Failed to locate test binary: %s", err)
+ }
+
+ cmd := exec.Command(test, flag.Args()[1:]...)
+ if *chdir != "" {
+ cmd.Dir = *chdir
+ }
+
+ cmd.Stderr = os.Stderr
+ stdout, err := cmd.StdoutPipe()
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+
+ err = cmd.Start()
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+
+ handleStdout(stdout)
+
+ if err = cmd.Wait(); err != nil {
+ if e, ok := err.(*exec.ExitError); ok {
+ if status, ok := e.Sys().(syscall.WaitStatus); ok && status.Exited() {
+ os.Exit(status.ExitStatus())
+ } else if status.Signaled() {
+ fmt.Fprintf(os.Stderr, "test got signal %s\n", status.Signal())
+ os.Exit(1)
+ }
+ }
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+
+ if *touch != "" {
+ err = ioutil.WriteFile(*touch, []byte{}, 0666)
+ if err != nil {
+ panic(err)
+ }
+ }
+
+ os.Exit(0)
+}
diff --git a/parser/parser.go b/parser/parser.go
index 6027275..fb931af 100644
--- a/parser/parser.go
+++ b/parser/parser.go
@@ -104,13 +104,13 @@
return p
}
-func (p *parser) errorf(format string, args ...interface{}) {
+func (p *parser) error(err error) {
pos := p.scanner.Position
if !pos.IsValid() {
pos = p.scanner.Pos()
}
- err := &ParseError{
- Err: fmt.Errorf(format, args...),
+ err = &ParseError{
+ Err: err,
Pos: pos,
}
p.errors = append(p.errors, err)
@@ -119,6 +119,10 @@
}
}
+func (p *parser) errorf(format string, args ...interface{}) {
+ p.error(fmt.Errorf(format, args...))
+}
+
func (p *parser) accept(toks ...rune) bool {
for _, tok := range toks {
if p.tok != tok {
@@ -193,18 +197,26 @@
if p.scope != nil {
if assigner == "+=" {
- if old, err := p.scope.Get(assignment.Name.Name); err == nil {
- if old.Referenced {
- p.errorf("modified variable with += after referencing")
+ if old, local := p.scope.Get(assignment.Name.Name); old == nil {
+ p.errorf("modified non-existent variable %q with +=", assignment.Name.Name)
+ } else if !local {
+ p.errorf("modified non-local variable %q with +=", assignment.Name.Name)
+ } else if old.Referenced {
+ p.errorf("modified variable %q with += after referencing",
+ assignment.Name.Name)
+ } else {
+ val, err := p.evaluateOperator(old.Value, assignment.Value, '+', assignment.Pos)
+ if err != nil {
+ p.error(err)
+ } else {
+ old.Value = val
}
- old.Value, err = p.evaluateOperator(old.Value, assignment.Value, '+', assignment.Pos)
- return
}
- }
-
- err := p.scope.Add(assignment)
- if err != nil {
- p.errorf("%s", err.Error())
+ } else {
+ err := p.scope.Add(assignment)
+ if err != nil {
+ p.error(err)
+ }
}
}
@@ -392,7 +404,7 @@
value, err := p.evaluateOperator(value1, value2, operator, pos)
if err != nil {
- p.errorf(err.Error())
+ p.error(err)
return Value{}
}
@@ -427,12 +439,14 @@
default:
variable := p.scanner.TokenText()
if p.eval {
- assignment, err := p.scope.Get(variable)
- if err != nil {
- p.errorf(err.Error())
+ if assignment, local := p.scope.Get(variable); assignment == nil {
+ p.errorf("variable %q is not set", variable)
+ } else {
+ if local {
+ assignment.Referenced = true
+ }
+ value = assignment.Value
}
- assignment.Referenced = true
- value = assignment.Value
}
value.Variable = variable
}
@@ -684,17 +698,22 @@
}
type Scope struct {
- vars map[string]*Assignment
+ vars map[string]*Assignment
+ inheritedVars map[string]*Assignment
}
func NewScope(s *Scope) *Scope {
newScope := &Scope{
- vars: make(map[string]*Assignment),
+ vars: make(map[string]*Assignment),
+ inheritedVars: make(map[string]*Assignment),
}
if s != nil {
for k, v := range s.vars {
- newScope.vars[k] = v
+ newScope.inheritedVars[k] = v
+ }
+ for k, v := range s.inheritedVars {
+ newScope.inheritedVars[k] = v
}
}
@@ -706,6 +725,10 @@
return fmt.Errorf("variable already set, previous assignment: %s", old)
}
+ if old, ok := s.inheritedVars[assignment.Name.Name]; ok {
+ return fmt.Errorf("variable already set in inherited scope, previous assignment: %s", old)
+ }
+
s.vars[assignment.Name.Name] = assignment
return nil
@@ -713,14 +736,19 @@
func (s *Scope) Remove(name string) {
delete(s.vars, name)
+ delete(s.inheritedVars, name)
}
-func (s *Scope) Get(name string) (*Assignment, error) {
+func (s *Scope) Get(name string) (*Assignment, bool) {
if a, ok := s.vars[name]; ok {
- return a, nil
+ return a, true
}
- return nil, fmt.Errorf("variable %s not set", name)
+ if a, ok := s.inheritedVars[name]; ok {
+ return a, false
+ }
+
+ return nil, false
}
func (s *Scope) String() string {
@@ -729,12 +757,19 @@
for k := range s.vars {
vars = append(vars, k)
}
+ for k := range s.inheritedVars {
+ vars = append(vars, k)
+ }
sort.Strings(vars)
ret := []string{}
for _, v := range vars {
- ret = append(ret, s.vars[v].String())
+ if assignment, ok := s.vars[v]; ok {
+ ret = append(ret, assignment.String())
+ } else {
+ ret = append(ret, s.inheritedVars[v].String())
+ }
}
return strings.Join(ret, "\n")