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")