Merge pull request #44 from colincross/local

Fix bugs related to local vs. inherited variables
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..de99854
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+out.test
diff --git a/.travis.fix-fork.sh b/.travis.fix-fork.sh
new file mode 100755
index 0000000..af26716
--- /dev/null
+++ b/.travis.fix-fork.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+if echo $TRAVIS_BUILD_DIR | grep -vq "github.com/google/blueprint$" ; then
+  cd ../..
+  mkdir -p google
+  mv $TRAVIS_BUILD_DIR google/blueprint
+  cd google/blueprint
+  export TRAVIS_BUILD_DIR=$PWD
+fi
diff --git a/.travis.install-ninja.sh b/.travis.install-ninja.sh
new file mode 100755
index 0000000..fef0e5b
--- /dev/null
+++ b/.travis.install-ninja.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+# Version of ninja to build -- can be any git revision
+VERSION="v1.6.0"
+
+set -ev
+
+SCRIPT_HASH=$(sha1sum ${BASH_SOURCE[0]} | awk '{print $1}')
+
+cd ~
+if [[ -d ninjabin && "$SCRIPT_HASH" == "$(cat ninjabin/script_hash)" ]]; then
+  exit 0
+fi
+
+git clone https://github.com/martine/ninja
+cd ninja
+./configure.py --bootstrap
+
+mkdir -p ../ninjabin
+rm -f ../ninjabin/ninja
+echo -n $SCRIPT_HASH >../ninjabin/script_hash
+mv ninja ../ninjabin/
diff --git a/.travis.yml b/.travis.yml
index 5b17240..1004a71 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,13 +1,21 @@
 language: go
 
+cache:
+    directories:
+        - $HOME/ninjabin
+
 install:
-    - sudo apt-get install ninja-build
+    - ./.travis.install-ninja.sh
+    - export PATH=$PATH:~/ninjabin
+
+before_script:
+    - source .travis.fix-fork.sh
 
 script:
     - go test ./...
-    - cp build.ninja.in build.ninja.in.orig
     - mkdir stage
     - cd stage 
     - ../bootstrap.bash
     - ninja
-    - diff -us ../build.ninja.in ../build.ninja.in.orig
+    - diff -us ../build.ninja.in .bootstrap/bootstrap.ninja.in
+    - ../tests/test.sh
diff --git a/Blueprints b/Blueprints
index 46b5c84..df3ad60 100644
--- a/Blueprints
+++ b/Blueprints
@@ -98,7 +98,7 @@
     ],
 )
 
-bootstrap_go_binary(
+bootstrap_core_go_binary(
     name = "minibp",
     deps = [
         "blueprint",
@@ -119,7 +119,22 @@
     srcs = ["bpmodify/bpmodify.go"],
 )
 
-bootstrap_go_binary(
+bootstrap_core_go_binary(
     name = "gotestmain",
     srcs = ["gotestmain/gotestmain.go"],
 )
+
+bootstrap_core_go_binary(
+    name = "gotestrunner",
+    srcs = ["gotestrunner/gotestrunner.go"],
+)
+
+bootstrap_core_go_binary(
+    name = "choosestage",
+    srcs = ["choosestage/choosestage.go"],
+)
+
+bootstrap_go_binary{
+    name = "loadplugins",
+    srcs = ["loadplugins/loadplugins.go"],
+}
diff --git a/bootstrap.bash b/bootstrap.bash
index ee5ddb7..ff60e21 100755
--- a/bootstrap.bash
+++ b/bootstrap.bash
@@ -11,6 +11,7 @@
 #
 #   BOOTSTRAP
 #   SRCDIR
+#   BUILDDIR
 #   BOOTSTRAP_MANIFEST
 #   GOROOT
 #   GOOS
@@ -36,6 +37,10 @@
 # the bootstrap script.
 [ -z "$SRCDIR" ] && SRCDIR=`dirname "${BOOTSTRAP}"`
 
+# BUILDDIR should be set to the path to store build results. By default, this
+# is the current directory, but it may be set to an absolute or relative path.
+[ -z "$BUILDDIR" ] && BUILDDIR=.
+
 # TOPNAME should be set to the name of the top-level Blueprints file
 [ -z "$TOPNAME" ] && TOPNAME="Blueprints"
 
@@ -54,6 +59,21 @@
 # If RUN_TESTS is set, behave like -t was passed in as an option.
 [ ! -z "$RUN_TESTS" ] && EXTRA_ARGS="$EXTRA_ARGS -t"
 
+GOTOOLDIR="$GOROOT/pkg/tool/${GOOS}_$GOARCH"
+GOCOMPILE="$GOTOOLDIR/${GOCHAR}g"
+GOLINK="$GOTOOLDIR/${GOCHAR}l"
+
+if [ ! -f $GOCOMPILE ]; then
+  GOCOMPILE="$GOTOOLDIR/compile"
+fi
+if [ ! -f $GOLINK ]; then
+  GOLINK="$GOTOOLDIR/link"
+fi
+if [[ ! -f $GOCOMPILE || ! -f $GOLINK ]]; then
+  echo "Cannot find go tools under $GOROOT"
+  exit 1
+fi
+
 usage() {
     echo "Usage of ${BOOTSTRAP}:"
     echo "  -h: print a help message and exit"
@@ -64,8 +84,9 @@
 # Parse the command line flags.
 IN="$BOOTSTRAP_MANIFEST"
 REGEN_BOOTSTRAP_MANIFEST=false
-while getopts ":hi:rt" opt; do
+while getopts ":b:hi:rt" opt; do
     case $opt in
+        b) BUILDDIR="$OPTARG";;
         h)
             usage
             exit 1
@@ -88,20 +109,22 @@
 if [ $REGEN_BOOTSTRAP_MANIFEST = true ]; then
     # This assumes that the script is being run from a build output directory
     # that has been built in the past.
-    if [ -x .bootstrap/bin/minibp ]; then
+    if [ -x $BUILDDIR/.bootstrap/bin/minibp ]; then
         echo "Regenerating $BOOTSTRAP_MANIFEST"
-        ./.bootstrap/bin/minibp $EXTRA_ARGS -o $BOOTSTRAP_MANIFEST $SRCDIR/$TOPNAME
+        $BUILDDIR/.bootstrap/bin/minibp $EXTRA_ARGS -o $BOOTSTRAP_MANIFEST $SRCDIR/$TOPNAME
     else
-        echo "Executable minibp not found at .bootstrap/bin/minibp" >&2
+        echo "Executable minibp not found at $BUILDDIR/.bootstrap/bin/minibp" >&2
         exit 1
     fi
 fi
 
+mkdir -p $BUILDDIR
+
 sed -e "s|@@SrcDir@@|$SRCDIR|g"                        \
+    -e "s|@@BuildDir@@|$BUILDDIR|g"                    \
     -e "s|@@GoRoot@@|$GOROOT|g"                        \
-    -e "s|@@GoOS@@|$GOOS|g"                            \
-    -e "s|@@GoArch@@|$GOARCH|g"                        \
-    -e "s|@@GoChar@@|$GOCHAR|g"                        \
+    -e "s|@@GoCompile@@|$GOCOMPILE|g"                  \
+    -e "s|@@GoLink@@|$GOLINK|g"                        \
     -e "s|@@Bootstrap@@|$BOOTSTRAP|g"                  \
     -e "s|@@BootstrapManifest@@|$BOOTSTRAP_MANIFEST|g" \
-    $IN > build.ninja
+    $IN > $BUILDDIR/build.ninja
diff --git a/bootstrap/bootstrap.go b/bootstrap/bootstrap.go
index c07f863..ed5debc 100644
--- a/bootstrap/bootstrap.go
+++ b/bootstrap/bootstrap.go
@@ -16,58 +16,36 @@
 
 import (
 	"fmt"
-	"os"
 	"path/filepath"
-	"strconv"
 	"strings"
 
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/pathtools"
 )
 
-const bootstrapDir = ".bootstrap"
+const bootstrapSubDir = ".bootstrap"
+const miniBootstrapSubDir = ".minibootstrap"
 
 var (
 	pctx = blueprint.NewPackageContext("github.com/google/blueprint/bootstrap")
 
-	gcCmd         = pctx.StaticVariable("gcCmd", "$goToolDir/${goChar}g")
-	linkCmd       = pctx.StaticVariable("linkCmd", "$goToolDir/${goChar}l")
-	goTestMainCmd = pctx.StaticVariable("goTestMainCmd", filepath.Join(bootstrapDir, "bin", "gotestmain"))
+	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"))
 
-	// Ninja only reinvokes itself once when it regenerates a .ninja file. For
-	// the re-bootstrap process we need that to happen more than once, so we
-	// invoke an additional Ninja process from the rebootstrap rule.
-	// Unfortunately this seems to cause "warning: bad deps log signature or
-	// version; starting over" messages from Ninja. This warning can be
-	// avoided by having the bootstrap and non-bootstrap build manifests have
-	// a different builddir (so they use different log files).
-	//
-	// This workaround can be avoided entirely by making a simple change to
-	// Ninja that would allow it to rebuild the manifest multiple times rather
-	// than just once.  If the Ninja being used is capable of this, then the
-	// workaround we're doing can be disabled by setting the
-	// BLUEPRINT_NINJA_HAS_MULTIPASS environment variable to a true value.
-	runChildNinja = pctx.VariableFunc("runChildNinja",
-		func(config interface{}) (string, error) {
-			if ninjaHasMultipass(config) {
-				return "", nil
-			} else {
-				return " && ninja", nil
-			}
-		})
-
-	gc = pctx.StaticRule("gc",
+	compile = pctx.StaticRule("compile",
 		blueprint.RuleParams{
-			Command: "GOROOT='$goRoot' $gcCmd -o $out -p $pkgPath -complete " +
+			Command: "GOROOT='$goRoot' $compileCmd -o $out -p $pkgPath -complete " +
 				"$incFlags -pack $in",
-			Description: "${goChar}g $out",
+			Description: "compile $out",
 		},
 		"pkgPath", "incFlags")
 
 	link = pctx.StaticRule("link",
 		blueprint.RuleParams{
 			Command:     "GOROOT='$goRoot' $linkCmd -o $out $libDirFlags $in",
-			Description: "${goChar}l $out",
+			Description: "link $out",
 		},
 		"libDirFlags")
 
@@ -78,9 +56,16 @@
 		},
 		"pkg")
 
+	pluginGenSrc = pctx.StaticRule("pluginGenSrc",
+		blueprint.RuleParams{
+			Command:     "$pluginGenSrcCmd -o $out -p $pkg $plugins",
+			Description: "create $out",
+		},
+		"pkg", "plugins")
+
 	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")
@@ -94,17 +79,24 @@
 
 	bootstrap = pctx.StaticRule("bootstrap",
 		blueprint.RuleParams{
-			Command:     "$bootstrapCmd -i $in",
+			Command:     "BUILDDIR=$buildDir $bootstrapCmd -i $in",
 			Description: "bootstrap $in",
 			Generator:   true,
 		})
 
-	rebootstrap = pctx.StaticRule("rebootstrap",
+	chooseStage = pctx.StaticRule("chooseStage",
 		blueprint.RuleParams{
-			Command:     "$bootstrapCmd -i $in$runChildNinja",
-			Description: "re-bootstrap $in",
-			Generator:   true,
-		})
+			Command:     "$chooseStageCmd --current $current --bootstrap $bootstrapManifest -o $out $in",
+			Description: "choosing next stage",
+		},
+		"current", "generator")
+
+	touch = pctx.StaticRule("touch",
+		blueprint.RuleParams{
+			Command:     "touch $out",
+			Description: "touch $out",
+		},
+		"depfile", "generator")
 
 	// Work around a Ninja issue.  See https://github.com/martine/ninja/pull/634
 	phony = pctx.StaticRule("phony",
@@ -115,12 +107,40 @@
 		},
 		"depfile")
 
-	BinDir     = filepath.Join(bootstrapDir, "bin")
-	minibpFile = filepath.Join(BinDir, "minibp")
+	binDir     = pctx.StaticVariable("BinDir", filepath.Join(bootstrapDir, "bin"))
+	minibpFile = filepath.Join("$BinDir", "minibp")
 
 	docsDir = filepath.Join(bootstrapDir, "docs")
+
+	bootstrapDir     = filepath.Join("$buildDir", bootstrapSubDir)
+	miniBootstrapDir = filepath.Join("$buildDir", miniBootstrapSubDir)
 )
 
+type bootstrapGoCore interface {
+	BuildStage() Stage
+	SetBuildStage(Stage)
+}
+
+func propagateStageBootstrap(mctx blueprint.TopDownMutatorContext) {
+	if mod, ok := mctx.Module().(bootstrapGoCore); !ok || mod.BuildStage() != StageBootstrap {
+		return
+	}
+
+	mctx.VisitDirectDeps(func(mod blueprint.Module) {
+		if m, ok := mod.(bootstrapGoCore); ok {
+			m.SetBuildStage(StageBootstrap)
+		}
+	})
+}
+
+func pluginDeps(ctx blueprint.BottomUpMutatorContext) {
+	if pkg, ok := ctx.Module().(*goPackage); ok {
+		for _, plugin := range pkg.properties.PluginFor {
+			ctx.AddReverseDependency(ctx.Module(), plugin)
+		}
+	}
+}
+
 type goPackageProducer interface {
 	GoPkgRoot() string
 	GoPackageTarget() string
@@ -133,6 +153,7 @@
 
 type goTestProducer interface {
 	GoTestTarget() string
+	BuildStage() Stage
 }
 
 func isGoTestProducer(module blueprint.Module) bool {
@@ -140,6 +161,20 @@
 	return ok
 }
 
+type goPluginProvider interface {
+	GoPkgPath() string
+	IsPluginFor(string) bool
+}
+
+func isGoPluginFor(name string) func(blueprint.Module) bool {
+	return func(module blueprint.Module) bool {
+		if plugin, ok := module.(goPluginProvider); ok {
+			return plugin.IsPluginFor(name)
+		}
+		return false
+	}
+}
+
 func isBootstrapModule(module blueprint.Module) bool {
 	_, isPackage := module.(*goPackage)
 	_, isBinary := module.(*goBinary)
@@ -151,23 +186,13 @@
 	return isBinary
 }
 
-// ninjaHasMultipass returns true if Ninja will perform multiple passes
-// that can regenerate the build manifest.
-func ninjaHasMultipass(config interface{}) bool {
-	envString := os.Getenv("BLUEPRINT_NINJA_HAS_MULTIPASS")
-	envValue, err := strconv.ParseBool(envString)
-	if err != nil {
-		return false
-	}
-	return envValue
-}
-
 // A goPackage is a module for building Go packages.
 type goPackage struct {
 	properties struct {
-		PkgPath  string
-		Srcs     []string
-		TestSrcs []string
+		PkgPath   string
+		Srcs      []string
+		TestSrcs  []string
+		PluginFor []string
 	}
 
 	// The root dir in which the package .a file is located.  The full .a file
@@ -182,6 +207,9 @@
 
 	// The bootstrap Config
 	config *Config
+
+	// The stage in which this module should be built
+	buildStage Stage
 }
 
 var _ goPackageProducer = (*goPackage)(nil)
@@ -189,12 +217,17 @@
 func newGoPackageModuleFactory(config *Config) func() (blueprint.Module, []interface{}) {
 	return func() (blueprint.Module, []interface{}) {
 		module := &goPackage{
-			config: config,
+			buildStage: StagePrimary,
+			config:     config,
 		}
 		return module, []interface{}{&module.properties}
 	}
 }
 
+func (g *goPackage) GoPkgPath() string {
+	return g.properties.PkgPath
+}
+
 func (g *goPackage) GoPkgRoot() string {
 	return g.pkgRoot
 }
@@ -207,8 +240,30 @@
 	return g.testArchiveFile
 }
 
+func (g *goPackage) BuildStage() Stage {
+	return g.buildStage
+}
+
+func (g *goPackage) SetBuildStage(buildStage Stage) {
+	g.buildStage = buildStage
+}
+
+func (g *goPackage) IsPluginFor(name string) bool {
+	for _, plugin := range g.properties.PluginFor {
+		if plugin == name {
+			return true
+		}
+	}
+	return false
+}
+
 func (g *goPackage) GenerateBuildActions(ctx blueprint.ModuleContext) {
-	name := ctx.ModuleName()
+	var (
+		name       = ctx.ModuleName()
+		hasPlugins = false
+		pluginSrc  = ""
+		genSrcs    = []string{}
+	)
 
 	if g.properties.PkgPath == "" {
 		ctx.ModuleErrorf("module %s did not specify a valid pkgPath", name)
@@ -223,27 +278,38 @@
 			filepath.FromSlash(g.properties.PkgPath)+".a")
 	}
 
+	ctx.VisitDepsDepthFirstIf(isGoPluginFor(name),
+		func(module blueprint.Module) { hasPlugins = true })
+	if hasPlugins {
+		pluginSrc = filepath.Join(moduleGenSrcDir(ctx), "plugin.go")
+		genSrcs = append(genSrcs, pluginSrc)
+	}
+
 	// We only actually want to build the builder modules if we're running as
 	// minibp (i.e. we're generating a bootstrap Ninja file).  This is to break
 	// the circular dependence that occurs when the builder requires a new Ninja
 	// file to be built, but building a new ninja file requires the builder to
 	// be built.
-	if g.config.generatingBootstrapper {
+	if g.config.stage == g.BuildStage() {
 		var deps []string
 
+		if hasPlugins && !buildGoPluginLoader(ctx, g.properties.PkgPath, pluginSrc, g.config.stage) {
+			return
+		}
+
 		if g.config.runGoTests {
 			deps = buildGoTest(ctx, testRoot(ctx), g.testArchiveFile,
-				g.properties.PkgPath, g.properties.Srcs,
+				g.properties.PkgPath, g.properties.Srcs, genSrcs,
 				g.properties.TestSrcs)
 		}
 
 		buildGoPackage(ctx, g.pkgRoot, g.properties.PkgPath, g.archiveFile,
-			g.properties.Srcs, deps)
-	} else {
+			g.properties.Srcs, genSrcs, deps)
+	} else if g.config.stage != StageBootstrap {
 		if len(g.properties.TestSrcs) > 0 && g.config.runGoTests {
-			phonyGoTarget(ctx, g.testArchiveFile, g.properties.TestSrcs, nil)
+			phonyGoTarget(ctx, g.testArchiveFile, g.properties.TestSrcs, nil, nil)
 		}
-		phonyGoTarget(ctx, g.archiveFile, g.properties.Srcs, nil)
+		phonyGoTarget(ctx, g.archiveFile, g.properties.Srcs, genSrcs, nil)
 	}
 }
 
@@ -260,12 +326,16 @@
 
 	// The bootstrap Config
 	config *Config
+
+	// The stage in which this module should be built
+	buildStage Stage
 }
 
-func newGoBinaryModuleFactory(config *Config) func() (blueprint.Module, []interface{}) {
+func newGoBinaryModuleFactory(config *Config, buildStage Stage) func() (blueprint.Module, []interface{}) {
 	return func() (blueprint.Module, []interface{}) {
 		module := &goBinary{
-			config: config,
+			config:     config,
+			buildStage: buildStage,
 		}
 		return module, []interface{}{&module.properties}
 	}
@@ -275,33 +345,55 @@
 	return g.testArchiveFile
 }
 
+func (g *goBinary) BuildStage() Stage {
+	return g.buildStage
+}
+
+func (g *goBinary) SetBuildStage(buildStage Stage) {
+	g.buildStage = buildStage
+}
+
 func (g *goBinary) GenerateBuildActions(ctx blueprint.ModuleContext) {
 	var (
 		name        = ctx.ModuleName()
 		objDir      = moduleObjDir(ctx)
 		archiveFile = filepath.Join(objDir, name+".a")
 		aoutFile    = filepath.Join(objDir, "a.out")
-		binaryFile  = filepath.Join(BinDir, name)
+		binaryFile  = filepath.Join("$BinDir", name)
+		hasPlugins  = false
+		pluginSrc   = ""
+		genSrcs     = []string{}
 	)
 
 	if len(g.properties.TestSrcs) > 0 && g.config.runGoTests {
 		g.testArchiveFile = filepath.Join(testRoot(ctx), name+".a")
 	}
 
+	ctx.VisitDepsDepthFirstIf(isGoPluginFor(name),
+		func(module blueprint.Module) { hasPlugins = true })
+	if hasPlugins {
+		pluginSrc = filepath.Join(moduleGenSrcDir(ctx), "plugin.go")
+		genSrcs = append(genSrcs, pluginSrc)
+	}
+
 	// We only actually want to build the builder modules if we're running as
 	// minibp (i.e. we're generating a bootstrap Ninja file).  This is to break
 	// the circular dependence that occurs when the builder requires a new Ninja
 	// file to be built, but building a new ninja file requires the builder to
 	// be built.
-	if g.config.generatingBootstrapper {
+	if g.config.stage == g.BuildStage() {
 		var deps []string
 
+		if hasPlugins && !buildGoPluginLoader(ctx, "main", pluginSrc, g.config.stage) {
+			return
+		}
+
 		if g.config.runGoTests {
 			deps = buildGoTest(ctx, testRoot(ctx), g.testArchiveFile,
-				name, g.properties.Srcs, g.properties.TestSrcs)
+				name, g.properties.Srcs, genSrcs, g.properties.TestSrcs)
 		}
 
-		buildGoPackage(ctx, objDir, name, archiveFile, g.properties.Srcs, deps)
+		buildGoPackage(ctx, objDir, name, archiveFile, g.properties.Srcs, genSrcs, deps)
 
 		var libDirFlags []string
 		ctx.VisitDepsDepthFirstIf(isGoPackageProducer,
@@ -329,24 +421,54 @@
 			Outputs: []string{binaryFile},
 			Inputs:  []string{aoutFile},
 		})
-	} else {
+	} else if g.config.stage != StageBootstrap {
 		if len(g.properties.TestSrcs) > 0 && g.config.runGoTests {
-			phonyGoTarget(ctx, g.testArchiveFile, g.properties.TestSrcs, nil)
+			phonyGoTarget(ctx, g.testArchiveFile, g.properties.TestSrcs, nil, nil)
 		}
 
 		intermediates := []string{aoutFile, archiveFile}
-		phonyGoTarget(ctx, binaryFile, g.properties.Srcs, intermediates)
+		phonyGoTarget(ctx, binaryFile, g.properties.Srcs, genSrcs, intermediates)
 	}
 }
 
+func buildGoPluginLoader(ctx blueprint.ModuleContext, pkgPath, pluginSrc string, stage Stage) bool {
+	ret := true
+	name := ctx.ModuleName()
+
+	var pluginPaths []string
+	ctx.VisitDepsDepthFirstIf(isGoPluginFor(name),
+		func(module blueprint.Module) {
+			plugin := module.(goPluginProvider)
+			pluginPaths = append(pluginPaths, plugin.GoPkgPath())
+			if stage == StageBootstrap {
+				ctx.OtherModuleErrorf(module, "plugin %q may not be included in core module %q",
+					ctx.OtherModuleName(module), name)
+				ret = false
+			}
+		})
+
+	ctx.Build(pctx, blueprint.BuildParams{
+		Rule:      pluginGenSrc,
+		Outputs:   []string{pluginSrc},
+		Implicits: []string{"$pluginGenSrcCmd"},
+		Args: map[string]string{
+			"pkg":     pkgPath,
+			"plugins": strings.Join(pluginPaths, " "),
+		},
+	})
+
+	return ret
+}
+
 func buildGoPackage(ctx blueprint.ModuleContext, pkgRoot string,
-	pkgPath string, archiveFile string, srcs []string, orderDeps []string) {
+	pkgPath string, archiveFile string, srcs []string, genSrcs []string, orderDeps []string) {
 
 	srcDir := moduleSrcDir(ctx)
 	srcFiles := pathtools.PrefixPaths(srcs, srcDir)
+	srcFiles = append(srcFiles, genSrcs...)
 
 	var incFlags []string
-	deps := []string{"$gcCmd"}
+	deps := []string{"$compileCmd"}
 	ctx.VisitDepsDepthFirstIf(isGoPackageProducer,
 		func(module blueprint.Module) {
 			dep := module.(goPackageProducer)
@@ -356,27 +478,26 @@
 			deps = append(deps, target)
 		})
 
-	gcArgs := map[string]string{
+	compileArgs := map[string]string{
 		"pkgPath": pkgPath,
 	}
 
 	if len(incFlags) > 0 {
-		gcArgs["incFlags"] = strings.Join(incFlags, " ")
+		compileArgs["incFlags"] = strings.Join(incFlags, " ")
 	}
 
 	ctx.Build(pctx, blueprint.BuildParams{
-		Rule:      gc,
+		Rule:      compile,
 		Outputs:   []string{archiveFile},
 		Inputs:    srcFiles,
 		OrderOnly: orderDeps,
 		Implicits: deps,
-		Args:      gcArgs,
+		Args:      compileArgs,
 	})
 }
 
-func buildGoTest(ctx blueprint.ModuleContext, testRoot string,
-	testPkgArchive string, pkgPath string, srcs []string,
-	testSrcs []string) []string {
+func buildGoTest(ctx blueprint.ModuleContext, testRoot, testPkgArchive,
+	pkgPath string, srcs, genSrcs, testSrcs []string) []string {
 
 	if len(testSrcs) == 0 {
 		return nil
@@ -391,7 +512,7 @@
 	testPassed := filepath.Join(testRoot, "test.passed")
 
 	buildGoPackage(ctx, testRoot, pkgPath, testPkgArchive,
-		append(srcs, testSrcs...), nil)
+		append(srcs, testSrcs...), genSrcs, nil)
 
 	ctx.Build(pctx, blueprint.BuildParams{
 		Rule:      goTestMain,
@@ -412,10 +533,10 @@
 		})
 
 	ctx.Build(pctx, blueprint.BuildParams{
-		Rule:      gc,
+		Rule:      compile,
 		Outputs:   []string{testArchive},
 		Inputs:    []string{mainFile},
-		Implicits: []string{testPkgArchive},
+		Implicits: []string{"$compileCmd", testPkgArchive},
 		Args: map[string]string{
 			"pkgPath":  "main",
 			"incFlags": "-I " + testRoot,
@@ -433,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]),
@@ -446,7 +568,7 @@
 }
 
 func phonyGoTarget(ctx blueprint.ModuleContext, target string, srcs []string,
-	intermediates []string) {
+	gensrcs []string, intermediates []string) {
 
 	var depTargets []string
 	ctx.VisitDepsDepthFirstIf(isGoPackageProducer,
@@ -458,6 +580,7 @@
 
 	moduleDir := ctx.ModuleDir()
 	srcs = pathtools.PrefixPaths(srcs, filepath.Join("$srcDir", moduleDir))
+	srcs = append(srcs, gensrcs...)
 
 	ctx.Build(pctx, blueprint.BuildParams{
 		Rule:      phony,
@@ -508,13 +631,21 @@
 	// creating the binary that we'll use to generate the non-bootstrap
 	// build.ninja file.
 	var primaryBuilders []*goBinary
+	// rebootstrapDeps contains modules that will be built in StageBootstrap
 	var rebootstrapDeps []string
+	// primaryRebootstrapDeps contains modules that will be built in StagePrimary
+	var primaryRebootstrapDeps []string
 	ctx.VisitAllModulesIf(isBootstrapBinaryModule,
 		func(module blueprint.Module) {
 			binaryModule := module.(*goBinary)
 			binaryModuleName := ctx.ModuleName(binaryModule)
-			binaryModulePath := filepath.Join(BinDir, binaryModuleName)
-			rebootstrapDeps = append(rebootstrapDeps, binaryModulePath)
+			binaryModulePath := filepath.Join("$BinDir", binaryModuleName)
+
+			if binaryModule.BuildStage() == StageBootstrap {
+				rebootstrapDeps = append(rebootstrapDeps, binaryModulePath)
+			} else {
+				primaryRebootstrapDeps = append(primaryRebootstrapDeps, binaryModulePath)
+			}
 			if binaryModule.properties.PrimaryBuilder {
 				primaryBuilders = append(primaryBuilders, binaryModule)
 			}
@@ -541,35 +672,173 @@
 		return
 	}
 
-	primaryBuilderFile := filepath.Join(BinDir, primaryBuilderName)
+	primaryBuilderFile := filepath.Join("$BinDir", primaryBuilderName)
 
 	if s.config.runGoTests {
 		primaryBuilderExtraFlags += " -t"
 	}
 
 	// Get the filename of the top-level Blueprints file to pass to minibp.
-	// This comes stored in a global variable that's set by Main.
 	topLevelBlueprints := filepath.Join("$srcDir",
 		filepath.Base(s.config.topLevelBlueprintsFile))
 
+	rebootstrapDeps = append(rebootstrapDeps, topLevelBlueprints)
+	primaryRebootstrapDeps = append(primaryRebootstrapDeps, topLevelBlueprints)
+
 	mainNinjaFile := filepath.Join(bootstrapDir, "main.ninja.in")
-	mainNinjaDepFile := mainNinjaFile + ".d"
+	mainNinjaTimestampFile := mainNinjaFile + ".timestamp"
+	mainNinjaTimestampDepFile := mainNinjaTimestampFile + ".d"
+	primaryBuilderNinjaFile := filepath.Join(bootstrapDir, "primary.ninja.in")
+	primaryBuilderNinjaTimestampFile := primaryBuilderNinjaFile + ".timestamp"
+	primaryBuilderNinjaTimestampDepFile := primaryBuilderNinjaTimestampFile + ".d"
 	bootstrapNinjaFile := filepath.Join(bootstrapDir, "bootstrap.ninja.in")
 	docsFile := filepath.Join(docsDir, primaryBuilderName+".html")
 
-	rebootstrapDeps = append(rebootstrapDeps, docsFile)
+	primaryRebootstrapDeps = append(primaryRebootstrapDeps, docsFile)
 
-	if s.config.generatingBootstrapper {
+	// If the tests change, be sure to re-run them. These need to be
+	// dependencies for the ninja file so that it's updated after these
+	// run. Otherwise we'd never leave the bootstrap stage, since the
+	// timestamp file would be newer than the ninja file.
+	ctx.VisitAllModulesIf(isGoTestProducer,
+		func(module blueprint.Module) {
+			testModule := module.(goTestProducer)
+			target := testModule.GoTestTarget()
+			if target != "" {
+				if testModule.BuildStage() == StageBootstrap {
+					rebootstrapDeps = append(rebootstrapDeps, target)
+				} else {
+					primaryRebootstrapDeps = append(primaryRebootstrapDeps, target)
+				}
+			}
+		})
+
+	switch s.config.stage {
+	case StageBootstrap:
 		// We're generating a bootstrapper Ninja file, so we need to set things
 		// up to rebuild the build.ninja file using the primary builder.
 
-		// Because the non-bootstrap build.ninja file manually re-invokes Ninja,
-		// its builddir must be different than that of the bootstrap build.ninja
-		// file.  Otherwise we occasionally get "warning: bad deps log signature
-		// or version; starting over" messages from Ninja, presumably because
-		// two Ninja processes try to write to the same log concurrently.
+		// BuildDir must be different between the three stages, otherwise the
+		// cleanup process will remove files from the other builds.
+		ctx.SetBuildDir(pctx, miniBootstrapDir)
+
+		// Generate the Ninja file to build the primary builder. Save the
+		// timestamps and deps, so that we can come back to this stage if
+		// it needs to be regenerated.
+		primarybp := ctx.Rule(pctx, "primarybp",
+			blueprint.RuleParams{
+				Command: fmt.Sprintf("%s --build-primary $runTests -m $bootstrapManifest "+
+					"--timestamp $timestamp --timestampdep $timestampdep "+
+					"-b $buildDir -d $outfile.d -o $outfile $in", minibpFile),
+				Description: "minibp $outfile",
+				Depfile:     "$outfile.d",
+			},
+			"runTests", "timestamp", "timestampdep", "outfile")
+
+		args := map[string]string{
+			"outfile":      primaryBuilderNinjaFile,
+			"timestamp":    primaryBuilderNinjaTimestampFile,
+			"timestampdep": primaryBuilderNinjaTimestampDepFile,
+		}
+
+		if s.config.runGoTests {
+			args["runTests"] = "-t"
+		}
+
+		ctx.Build(pctx, blueprint.BuildParams{
+			Rule:      primarybp,
+			Outputs:   []string{primaryBuilderNinjaFile, primaryBuilderNinjaTimestampFile},
+			Inputs:    []string{topLevelBlueprints},
+			Implicits: rebootstrapDeps,
+			Args:      args,
+		})
+
+		// Rebuild the bootstrap Ninja file using the minibp that we just built.
+		// If this produces a difference, choosestage will retrigger this stage.
+		minibp := ctx.Rule(pctx, "minibp",
+			blueprint.RuleParams{
+				Command: fmt.Sprintf("%s $runTests -m $bootstrapManifest "+
+					"-b $buildDir -d $out.d -o $out $in", minibpFile),
+				Description: "minibp $out",
+				Generator:   true,
+				Depfile:     "$out.d",
+			},
+			"runTests")
+
+		args = map[string]string{}
+
+		if s.config.runGoTests {
+			args["runTests"] = "-t"
+		}
+
+		ctx.Build(pctx, blueprint.BuildParams{
+			Rule:    minibp,
+			Outputs: []string{bootstrapNinjaFile},
+			Inputs:  []string{topLevelBlueprints},
+			// $bootstrapManifest is here so that when it is updated, we
+			// force a rebuild of bootstrap.ninja.in. chooseStage should
+			// have already copied the new version over, but kept the old
+			// timestamps to force this regeneration.
+			Implicits: []string{"$bootstrapManifest", minibpFile},
+			Args:      args,
+		})
+
+		// When the current build.ninja file is a bootstrapper, we always want
+		// to have it replace itself with a non-bootstrapper build.ninja.  To
+		// accomplish that we depend on a file that should never exist and
+		// "build" it using Ninja's built-in phony rule.
+		notAFile := filepath.Join(bootstrapDir, "notAFile")
+		ctx.Build(pctx, blueprint.BuildParams{
+			Rule:    blueprint.Phony,
+			Outputs: []string{notAFile},
+		})
+
+		ctx.Build(pctx, blueprint.BuildParams{
+			Rule:      chooseStage,
+			Outputs:   []string{filepath.Join(bootstrapDir, "build.ninja.in")},
+			Inputs:    []string{bootstrapNinjaFile, primaryBuilderNinjaFile},
+			Implicits: []string{"$chooseStageCmd", "$bootstrapManifest", notAFile},
+			Args: map[string]string{
+				"current": bootstrapNinjaFile,
+			},
+		})
+
+	case StagePrimary:
+		// We're generating a bootstrapper Ninja file, so we need to set things
+		// up to rebuild the build.ninja file using the primary builder.
+
+		// BuildDir must be different between the three stages, otherwise the
+		// cleanup process will remove files from the other builds.
 		ctx.SetBuildDir(pctx, bootstrapDir)
 
+		// We generate the depfile here that includes the dependencies for all
+		// the Blueprints files that contribute to generating the big build
+		// manifest (build.ninja file).  This depfile will be used by the non-
+		// bootstrap build manifest to determine whether it should touch the
+		// timestamp file to trigger a re-bootstrap.
+		bigbp := ctx.Rule(pctx, "bigbp",
+			blueprint.RuleParams{
+				Command: fmt.Sprintf("%s %s -m $bootstrapManifest "+
+					"--timestamp $timestamp --timestampdep $timestampdep "+
+					"-b $buildDir -d $outfile.d -o $outfile $in", primaryBuilderFile,
+					primaryBuilderExtraFlags),
+				Description: fmt.Sprintf("%s $outfile", primaryBuilderName),
+				Depfile:     "$outfile.d",
+			},
+			"timestamp", "timestampdep", "outfile")
+
+		ctx.Build(pctx, blueprint.BuildParams{
+			Rule:      bigbp,
+			Outputs:   []string{mainNinjaFile, mainNinjaTimestampFile},
+			Inputs:    []string{topLevelBlueprints},
+			Implicits: primaryRebootstrapDeps,
+			Args: map[string]string{
+				"timestamp":    mainNinjaTimestampFile,
+				"timestampdep": mainNinjaTimestampDepFile,
+				"outfile":      mainNinjaFile,
+			},
+		})
+
 		// Generate build system docs for the primary builder.  Generating docs reads the source
 		// files used to build the primary builder, but that dependency will be picked up through
 		// the dependency on the primary builder itself.  There are no dependencies on the
@@ -577,7 +846,7 @@
 		// a rebuild of the primary builder.
 		bigbpDocs := ctx.Rule(pctx, "bigbpDocs",
 			blueprint.RuleParams{
-				Command: fmt.Sprintf("%s %s --docs $out %s", primaryBuilderFile,
+				Command: fmt.Sprintf("%s %s -b $buildDir --docs $out %s", primaryBuilderFile,
 					primaryBuilderExtraFlags, topLevelBlueprints),
 				Description: fmt.Sprintf("%s docs $out", primaryBuilderName),
 			})
@@ -588,36 +857,24 @@
 			Implicits: []string{primaryBuilderFile},
 		})
 
-		// We generate the depfile here that includes the dependencies for all
-		// the Blueprints files that contribute to generating the big build
-		// manifest (build.ninja file).  This depfile will be used by the non-
-		// bootstrap build manifest to determine whether it should trigger a re-
-		// bootstrap.  Because the re-bootstrap rule's output is "build.ninja"
-		// we need to force the depfile to have that as its "make target"
-		// (recall that depfiles use a subset of the Makefile syntax).
-		bigbp := ctx.Rule(pctx, "bigbp",
-			blueprint.RuleParams{
-				Command: fmt.Sprintf("%s %s -d %s -m $bootstrapManifest "+
-					"-o $out $in", primaryBuilderFile,
-					primaryBuilderExtraFlags, mainNinjaDepFile),
-				Description: fmt.Sprintf("%s $out", primaryBuilderName),
-				Depfile:     mainNinjaDepFile,
-			})
-
+		// Detect whether we need to rebuild the primary stage by going back to
+		// the bootstrapper. If this is newer than the primaryBuilderNinjaFile,
+		// then chooseStage will trigger a rebuild of primaryBuilderNinjaFile by
+		// returning to the bootstrap stage.
 		ctx.Build(pctx, blueprint.BuildParams{
-			Rule:      bigbp,
-			Outputs:   []string{mainNinjaFile},
-			Inputs:    []string{topLevelBlueprints},
+			Rule:      touch,
+			Outputs:   []string{primaryBuilderNinjaTimestampFile},
 			Implicits: rebootstrapDeps,
+			Args: map[string]string{
+				"depfile":   primaryBuilderNinjaTimestampDepFile,
+				"generator": "true",
+			},
 		})
 
 		// When the current build.ninja file is a bootstrapper, we always want
 		// to have it replace itself with a non-bootstrapper build.ninja.  To
 		// accomplish that we depend on a file that should never exist and
 		// "build" it using Ninja's built-in phony rule.
-		//
-		// We also need to add an implicit dependency on bootstrapNinjaFile so
-		// that it gets generated as part of the bootstrap process.
 		notAFile := filepath.Join(bootstrapDir, "notAFile")
 		ctx.Build(pctx, blueprint.BuildParams{
 			Rule:    blueprint.Phony,
@@ -625,107 +882,77 @@
 		})
 
 		ctx.Build(pctx, blueprint.BuildParams{
-			Rule:      bootstrap,
-			Outputs:   []string{"build.ninja"},
-			Inputs:    []string{mainNinjaFile},
-			Implicits: []string{"$bootstrapCmd", notAFile, bootstrapNinjaFile},
-		})
-
-		// Rebuild the bootstrap Ninja file using the minibp that we just built.
-		// The checkFile tells minibp to compare the new bootstrap file to the
-		// current one.  If the files are the same then minibp sets the new
-		// file's mtime to match that of the current one.  If they're different
-		// then the new file will have a newer timestamp than the current one
-		// and it will trigger a reboostrap by the non-boostrap build manifest.
-		minibp := ctx.Rule(pctx, "minibp",
-			blueprint.RuleParams{
-				Command: fmt.Sprintf("%s $runTests -c $checkFile -m $bootstrapManifest "+
-					"-d $out.d -o $out $in", minibpFile),
-				Description: "minibp $out",
-				Generator:   true,
-				Depfile:     "$out.d",
+			Rule:      chooseStage,
+			Outputs:   []string{filepath.Join(bootstrapDir, "build.ninja.in")},
+			Inputs:    []string{bootstrapNinjaFile, primaryBuilderNinjaFile, mainNinjaFile},
+			Implicits: []string{"$chooseStageCmd", "$bootstrapManifest", notAFile, primaryBuilderNinjaTimestampFile},
+			Args: map[string]string{
+				"current": primaryBuilderNinjaFile,
 			},
-			"checkFile", "runTests")
-
-		args := map[string]string{
-			"checkFile": "$bootstrapManifest",
-		}
-
-		if s.config.runGoTests {
-			args["runTests"] = "-t"
-		}
-
-		ctx.Build(pctx, blueprint.BuildParams{
-			Rule:      minibp,
-			Outputs:   []string{bootstrapNinjaFile},
-			Inputs:    []string{topLevelBlueprints},
-			Implicits: []string{minibpFile},
-			Args:      args,
 		})
-	} else {
-		ctx.VisitAllModulesIf(isGoTestProducer,
-			func(module blueprint.Module) {
-				testModule := module.(goTestProducer)
-				target := testModule.GoTestTarget()
-				if target != "" {
-					rebootstrapDeps = append(rebootstrapDeps, target)
-				}
-			})
+
+		// Create this phony rule so that upgrades don't delete these during
+		// cleanup
+		ctx.Build(pctx, blueprint.BuildParams{
+			Rule:    blueprint.Phony,
+			Outputs: []string{bootstrapNinjaFile},
+		})
+
+	case StageMain:
+		ctx.SetBuildDir(pctx, "${buildDir}")
 
 		// We're generating a non-bootstrapper Ninja file, so we need to set it
-		// up to depend on the bootstrapper Ninja file.  The build.ninja target
-		// also has an implicit dependency on the primary builder and all other
-		// bootstrap go binaries, which will have phony dependencies on all of
-		// their sources.  This will cause any changes to a bootstrap binary's
-		// sources to trigger a re-bootstrap operation, which will rebuild the
-		// binary.
+		// up to re-bootstrap if necessary. We do this by making build.ninja.in
+		// depend on the various Ninja files, the source build.ninja.in, and
+		// on the timestamp files.
 		//
-		// On top of that we need to use the depfile generated by the bigbp
-		// rule.  We do this by depending on that file and then setting up a
-		// phony rule to generate it that uses the depfile.
-		buildNinjaDeps := []string{"$bootstrapCmd", mainNinjaFile}
-		buildNinjaDeps = append(buildNinjaDeps, rebootstrapDeps...)
-
+		// The timestamp files themselves are set up with the same dependencies
+		// as their Ninja files, including their own depfile. If any of the
+		// dependencies need to be updated, we'll touch the timestamp file,
+		// which will tell choosestage to switch to the stage that rebuilds
+		// that Ninja file.
 		ctx.Build(pctx, blueprint.BuildParams{
-			Rule:      rebootstrap,
-			Outputs:   []string{"build.ninja"},
-			Inputs:    []string{"$bootstrapManifest"},
-			Implicits: buildNinjaDeps,
-		})
-
-		ctx.Build(pctx, blueprint.BuildParams{
-			Rule:    phony,
-			Outputs: []string{mainNinjaFile},
-			Inputs:  []string{topLevelBlueprints},
+			Rule:      touch,
+			Outputs:   []string{primaryBuilderNinjaTimestampFile},
+			Implicits: rebootstrapDeps,
 			Args: map[string]string{
-				"depfile": mainNinjaDepFile,
-			},
-		})
-
-		ctx.Build(pctx, blueprint.BuildParams{
-			Rule:      phony,
-			Outputs:   []string{docsFile},
-			Implicits: []string{primaryBuilderFile},
-		})
-
-		// If the bootstrap Ninja invocation caused a new bootstrapNinjaFile to be
-		// generated then that means we need to rebootstrap using it instead of
-		// the current bootstrap manifest.  We enable the Ninja "generator"
-		// behavior so that Ninja doesn't invoke this build just because it's
-		// missing a command line log entry for the bootstrap manifest.
-		ctx.Build(pctx, blueprint.BuildParams{
-			Rule:    cp,
-			Outputs: []string{"$bootstrapManifest"},
-			Inputs:  []string{bootstrapNinjaFile},
-			Args: map[string]string{
+				"depfile":   primaryBuilderNinjaTimestampDepFile,
 				"generator": "true",
 			},
 		})
 
+		ctx.Build(pctx, blueprint.BuildParams{
+			Rule:      touch,
+			Outputs:   []string{mainNinjaTimestampFile},
+			Implicits: primaryRebootstrapDeps,
+			Args: map[string]string{
+				"depfile":   mainNinjaTimestampDepFile,
+				"generator": "true",
+			},
+		})
+
+		ctx.Build(pctx, blueprint.BuildParams{
+			Rule:      chooseStage,
+			Outputs:   []string{filepath.Join(bootstrapDir, "build.ninja.in")},
+			Inputs:    []string{bootstrapNinjaFile, primaryBuilderNinjaFile, mainNinjaFile},
+			Implicits: []string{"$chooseStageCmd", "$bootstrapManifest", primaryBuilderNinjaTimestampFile, mainNinjaTimestampFile},
+			Args: map[string]string{
+				"current":   mainNinjaFile,
+				"generator": "true",
+			},
+		})
+
+		// Create this phony rule so that upgrades don't delete these during
+		// cleanup
+		ctx.Build(pctx, blueprint.BuildParams{
+			Rule:    blueprint.Phony,
+			Outputs: []string{mainNinjaFile, docsFile, "$bootstrapManifest"},
+		})
+
 		if primaryBuilderName == "minibp" {
 			// This is a standalone Blueprint build, so we copy the minibp
 			// binary to the "bin" directory to make it easier to find.
-			finalMinibp := filepath.Join("bin", primaryBuilderName)
+			finalMinibp := filepath.Join("$buildDir", "bin", primaryBuilderName)
 			ctx.Build(pctx, blueprint.BuildParams{
 				Rule:    cp,
 				Inputs:  []string{primaryBuilderFile},
@@ -733,6 +960,13 @@
 			})
 		}
 	}
+
+	ctx.Build(pctx, blueprint.BuildParams{
+		Rule:      bootstrap,
+		Outputs:   []string{"$buildDir/build.ninja"},
+		Inputs:    []string{filepath.Join(bootstrapDir, "build.ninja.in")},
+		Implicits: []string{"$bootstrapCmd"},
+	})
 }
 
 // packageRoot returns the module-specific package root directory path.  This
@@ -759,3 +993,8 @@
 func moduleObjDir(ctx blueprint.ModuleContext) string {
 	return filepath.Join(bootstrapDir, ctx.ModuleName(), "obj")
 }
+
+// moduleGenSrcDir returns the module-specific generated sources path.
+func moduleGenSrcDir(ctx blueprint.ModuleContext) string {
+	return filepath.Join(bootstrapDir, ctx.ModuleName(), "gen")
+}
diff --git a/bootstrap/bpdoc/bpdoc.go b/bootstrap/bpdoc/bpdoc.go
index 50a87df..c9af9c8 100644
--- a/bootstrap/bpdoc/bpdoc.go
+++ b/bootstrap/bpdoc/bpdoc.go
@@ -38,7 +38,7 @@
 // Return the PropertyStructDocs associated with a property struct type.  The type should be in the
 // format <package path>.<type name>
 func (dc *DocCollector) Docs(pkg, name string, defaults reflect.Value) (*PropertyStructDocs, error) {
-	docs := dc.getDocs(name)
+	docs := dc.getDocs(pkg, name)
 
 	if docs == nil {
 		pkgDocs, err := dc.packageDocs(pkg)
@@ -52,7 +52,7 @@
 				if err != nil {
 					return nil, err
 				}
-				docs = dc.putDocs(name, docs)
+				docs = dc.putDocs(pkg, name, docs)
 			}
 		}
 	}
@@ -67,17 +67,21 @@
 	return docs, nil
 }
 
-func (dc *DocCollector) getDocs(name string) *PropertyStructDocs {
+func (dc *DocCollector) getDocs(pkg, name string) *PropertyStructDocs {
 	dc.mutex.Lock()
 	defer dc.mutex.Unlock()
 
+	name = pkg + "." + name
+
 	return dc.docs[name]
 }
 
-func (dc *DocCollector) putDocs(name string, docs *PropertyStructDocs) *PropertyStructDocs {
+func (dc *DocCollector) putDocs(pkg, name string, docs *PropertyStructDocs) *PropertyStructDocs {
 	dc.mutex.Lock()
 	defer dc.mutex.Unlock()
 
+	name = pkg + "." + name
+
 	if dc.docs[name] != nil {
 		return dc.docs[name]
 	} else {
diff --git a/bootstrap/cleanup.go b/bootstrap/cleanup.go
index f3cb634..89c4b3e 100644
--- a/bootstrap/cleanup.go
+++ b/bootstrap/cleanup.go
@@ -32,9 +32,12 @@
 func removeAbandonedFiles(ctx *blueprint.Context, config *Config,
 	srcDir, manifestFile string) error {
 
-	buildDir := "."
-	if config.generatingBootstrapper {
-		buildDir = bootstrapDir
+	buildDir := BuildDir
+	switch config.stage {
+	case StageBootstrap:
+		buildDir = filepath.Join(buildDir, miniBootstrapSubDir)
+	case StagePrimary:
+		buildDir = filepath.Join(buildDir, bootstrapSubDir)
 	}
 
 	targetRules, err := ctx.AllTargets()
@@ -44,11 +47,12 @@
 
 	replacer := strings.NewReplacer(
 		"@@SrcDir@@", srcDir,
+		"@@BuildDir@@", BuildDir,
 		"@@BootstrapManifest@@", manifestFile)
 	targets := make(map[string]bool)
 	for target := range targetRules {
 		replacedTarget := replacer.Replace(target)
-		targets[replacedTarget] = true
+		targets[filepath.Clean(replacedTarget)] = true
 	}
 
 	filePaths, err := parseNinjaLog(buildDir)
diff --git a/bootstrap/command.go b/bootstrap/command.go
index 932cfd7..6a19262 100644
--- a/bootstrap/command.go
+++ b/bootstrap/command.go
@@ -29,19 +29,24 @@
 )
 
 var (
-	outFile      string
-	depFile      string
-	checkFile    string
-	manifestFile string
-	docFile      string
-	cpuprofile   string
-	runGoTests   bool
+	outFile          string
+	depFile          string
+	timestampFile    string
+	timestampDepFile string
+	manifestFile     string
+	docFile          string
+	cpuprofile       string
+	runGoTests       bool
+
+	BuildDir string
 )
 
 func init() {
 	flag.StringVar(&outFile, "o", "build.ninja.in", "the Ninja file to output")
+	flag.StringVar(&BuildDir, "b", ".", "the build output directory")
 	flag.StringVar(&depFile, "d", "", "the dependency file to output")
-	flag.StringVar(&checkFile, "c", "", "the existing file to check against")
+	flag.StringVar(&timestampFile, "timestamp", "", "file to write before the output file")
+	flag.StringVar(&timestampDepFile, "timestampdep", "", "the dependency file for the timestamp file")
 	flag.StringVar(&manifestFile, "m", "", "the bootstrap manifest file")
 	flag.StringVar(&docFile, "docs", "", "build documentation file to output")
 	flag.StringVar(&cpuprofile, "cpuprofile", "", "write cpu profile to file")
@@ -69,19 +74,27 @@
 		fatalf("no Blueprints file specified")
 	}
 
-	generatingBootstrapper := false
+	stage := StageMain
 	if c, ok := config.(ConfigInterface); ok {
-		generatingBootstrapper = c.GeneratingBootstrapper()
+		if c.GeneratingBootstrapper() {
+			stage = StageBootstrap
+		}
+		if c.GeneratingPrimaryBuilder() {
+			stage = StagePrimary
+		}
 	}
 
 	bootstrapConfig := &Config{
-		generatingBootstrapper: generatingBootstrapper,
+		stage: stage,
 		topLevelBlueprintsFile: flag.Arg(0),
 		runGoTests:             runGoTests,
 	}
 
+	ctx.RegisterBottomUpMutator("bootstrap_plugin_deps", pluginDeps)
 	ctx.RegisterModuleType("bootstrap_go_package", newGoPackageModuleFactory(bootstrapConfig))
-	ctx.RegisterModuleType("bootstrap_go_binary", newGoBinaryModuleFactory(bootstrapConfig))
+	ctx.RegisterModuleType("bootstrap_core_go_binary", newGoBinaryModuleFactory(bootstrapConfig, StageBootstrap))
+	ctx.RegisterModuleType("bootstrap_go_binary", newGoBinaryModuleFactory(bootstrapConfig, StagePrimary))
+	ctx.RegisterTopDownMutator("bootstrap_stage", propagateStageBootstrap)
 	ctx.RegisterSingletonType("bootstrap", newSingletonFactory(bootstrapConfig))
 
 	deps, errs := ctx.ParseBlueprintsFiles(bootstrapConfig.topLevelBlueprintsFile)
@@ -118,48 +131,34 @@
 	}
 
 	const outFilePermissions = 0666
+	if timestampFile != "" {
+		err := ioutil.WriteFile(timestampFile, []byte{}, outFilePermissions)
+		if err != nil {
+			fatalf("error writing %s: %s", timestampFile, err)
+		}
+
+		if timestampDepFile != "" {
+			err := deptools.WriteDepFile(timestampDepFile, timestampFile, deps)
+			if err != nil {
+				fatalf("error writing depfile: %s", err)
+			}
+		}
+	}
+
 	err = ioutil.WriteFile(outFile, buf.Bytes(), outFilePermissions)
 	if err != nil {
 		fatalf("error writing %s: %s", outFile, err)
 	}
 
-	if checkFile != "" {
-		checkData, err := ioutil.ReadFile(checkFile)
-		if err != nil {
-			fatalf("error reading %s: %s", checkFile, err)
-		}
-
-		matches := buf.Len() == len(checkData)
-		if matches {
-			for i, value := range buf.Bytes() {
-				if value != checkData[i] {
-					matches = false
-					break
-				}
-			}
-		}
-
-		if matches {
-			// The new file content matches the check-file content, so we set
-			// the new file's mtime and atime to match that of the check-file.
-			checkFileInfo, err := os.Stat(checkFile)
-			if err != nil {
-				fatalf("error stat'ing %s: %s", checkFile, err)
-			}
-
-			time := checkFileInfo.ModTime()
-			err = os.Chtimes(outFile, time, time)
-			if err != nil {
-				fatalf("error setting timestamps for %s: %s", outFile, err)
-			}
-		}
-	}
-
 	if depFile != "" {
 		err := deptools.WriteDepFile(depFile, outFile, deps)
 		if err != nil {
 			fatalf("error writing depfile: %s", err)
 		}
+		err = deptools.WriteDepFile(depFile+".timestamp", outFile+".timestamp", deps)
+		if err != nil {
+			fatalf("error writing depfile: %s", err)
+		}
 	}
 
 	srcDir := filepath.Dir(bootstrapConfig.topLevelBlueprintsFile)
@@ -171,6 +170,7 @@
 
 func fatalf(format string, args ...interface{}) {
 	fmt.Printf(format, args...)
+	fmt.Print("\n")
 	os.Exit(1)
 }
 
diff --git a/bootstrap/config.go b/bootstrap/config.go
index ce96318..5752484 100644
--- a/bootstrap/config.go
+++ b/bootstrap/config.go
@@ -19,16 +19,13 @@
 	// modules.  They are always set to the variable name enclosed in "@@" so
 	// that their values can be easily replaced in the generated Ninja file.
 	srcDir            = pctx.StaticVariable("srcDir", "@@SrcDir@@")
+	buildDir          = pctx.StaticVariable("buildDir", "@@BuildDir@@")
 	goRoot            = pctx.StaticVariable("goRoot", "@@GoRoot@@")
-	goOS              = pctx.StaticVariable("goOS", "@@GoOS@@")
-	goArch            = pctx.StaticVariable("goArch", "@@GoArch@@")
-	goChar            = pctx.StaticVariable("goChar", "@@GoChar@@")
+	compileCmd        = pctx.StaticVariable("compileCmd", "@@GoCompile@@")
+	linkCmd           = pctx.StaticVariable("linkCmd", "@@GoLink@@")
 	bootstrapCmd      = pctx.StaticVariable("bootstrapCmd", "@@Bootstrap@@")
 	bootstrapManifest = pctx.StaticVariable("bootstrapManifest",
 		"@@BootstrapManifest@@")
-
-	goToolDir = pctx.StaticVariable("goToolDir",
-		"$goRoot/pkg/tool/${goOS}_$goArch")
 )
 
 type ConfigInterface interface {
@@ -36,13 +33,21 @@
 	// creating a build.ninja.in file to be used in a build bootstrapping
 	// sequence.
 	GeneratingBootstrapper() bool
+	// GeneratingPrimaryBuilder should return true if this build invocation is
+	// creating a build.ninja.in file to be used to build the primary builder
+	GeneratingPrimaryBuilder() bool
 }
 
+type Stage int
+
+const (
+	StageBootstrap Stage = iota
+	StagePrimary
+	StageMain
+)
+
 type Config struct {
-	// generatingBootstrapper should be true if this build invocation is
-	// creating a build.ninja.in file to be used in a build bootstrapping
-	// sequence.
-	generatingBootstrapper bool
+	stage Stage
 
 	topLevelBlueprintsFile string
 
diff --git a/bootstrap/doc.go b/bootstrap/doc.go
index 573ff7b..4394ebe 100644
--- a/bootstrap/doc.go
+++ b/bootstrap/doc.go
@@ -119,9 +119,8 @@
 //   @@SrcDir@@            - The path to the root source directory (either
 //                           absolute or relative to the build dir)
 //   @@GoRoot@@            - The path to the root directory of the Go toolchain
-//   @@GoOS@@              - The OS string for the Go toolchain
-//   @@GoArch@@            - The CPU architecture for the Go toolchain
-//   @@GoChar@@            - The CPU arch character for the Go toolchain
+//   @@GoCompile@@         - The path to the Go compiler (6g or compile)
+//   @@GoLink@@            - The path to the Go linker (6l or link)
 //   @@Bootstrap@@         - The path to the bootstrap script
 //   @@BootstrapManifest@@ - The path to the source bootstrap Ninja file
 //
diff --git a/bootstrap/minibp/main.go b/bootstrap/minibp/main.go
index ad36c29..4e25bc8 100644
--- a/bootstrap/minibp/main.go
+++ b/bootstrap/minibp/main.go
@@ -21,15 +21,24 @@
 )
 
 var runAsPrimaryBuilder bool
+var buildPrimaryBuilder bool
 
 func init() {
 	flag.BoolVar(&runAsPrimaryBuilder, "p", false, "run as a primary builder")
+	flag.BoolVar(&buildPrimaryBuilder, "build-primary", false, "build the primary builder")
 }
 
-type Config bool
+type Config struct {
+	generatingBootstrapper   bool
+	generatingPrimaryBuilder bool
+}
 
 func (c Config) GeneratingBootstrapper() bool {
-	return bool(c)
+	return c.generatingBootstrapper
+}
+
+func (c Config) GeneratingPrimaryBuilder() bool {
+	return c.generatingPrimaryBuilder
 }
 
 func main() {
@@ -40,7 +49,10 @@
 		ctx.SetIgnoreUnknownModuleTypes(true)
 	}
 
-	config := Config(!runAsPrimaryBuilder)
+	config := Config{
+		generatingBootstrapper:   !runAsPrimaryBuilder && !buildPrimaryBuilder,
+		generatingPrimaryBuilder: !runAsPrimaryBuilder && buildPrimaryBuilder,
+	}
 
 	bootstrap.Main(ctx, config)
 }
diff --git a/build.ninja.in b/build.ninja.in
index 3f43e21..751dec0 100644
--- a/build.ninja.in
+++ b/build.ninja.in
@@ -7,301 +7,325 @@
 #
 #     bootstrap [from Go package github.com/google/blueprint/bootstrap]
 #
-ninja_required_version = 1.1.0
+ninja_required_version = 1.6.0
+
+g.bootstrap.buildDir = @@BuildDir@@
+
+g.bootstrap.BinDir = ${g.bootstrap.buildDir}/.bootstrap/bin
 
 g.bootstrap.bootstrapCmd = @@Bootstrap@@
 
 g.bootstrap.bootstrapManifest = @@BootstrapManifest@@
 
+g.bootstrap.chooseStageCmd = ${g.bootstrap.buildDir}/.bootstrap/bin/choosestage
+
+g.bootstrap.compileCmd = @@GoCompile@@
+
 g.bootstrap.goRoot = @@GoRoot@@
 
-g.bootstrap.goOS = @@GoOS@@
-
-g.bootstrap.goArch = @@GoArch@@
-
-g.bootstrap.goToolDir = ${g.bootstrap.goRoot}/pkg/tool/${g.bootstrap.goOS}_${g.bootstrap.goArch}
-
-g.bootstrap.goChar = @@GoChar@@
-
-g.bootstrap.gcCmd = ${g.bootstrap.goToolDir}/${g.bootstrap.goChar}g
-
-g.bootstrap.linkCmd = ${g.bootstrap.goToolDir}/${g.bootstrap.goChar}l
+g.bootstrap.linkCmd = @@GoLink@@
 
 g.bootstrap.srcDir = @@SrcDir@@
 
-builddir = .bootstrap
+builddir = ${g.bootstrap.buildDir}/.minibootstrap
 
 rule g.bootstrap.bootstrap
-    command = ${g.bootstrap.bootstrapCmd} -i ${in}
+    command = BUILDDIR=${g.bootstrap.buildDir} ${g.bootstrap.bootstrapCmd} -i ${in}
     description = bootstrap ${in}
     generator = true
 
+rule g.bootstrap.chooseStage
+    command = ${g.bootstrap.chooseStageCmd} --current ${current} --bootstrap ${g.bootstrap.bootstrapManifest} -o ${out} ${in}
+    description = choosing next stage
+
+rule g.bootstrap.compile
+    command = GOROOT='${g.bootstrap.goRoot}' ${g.bootstrap.compileCmd} -o ${out} -p ${pkgPath} -complete ${incFlags} -pack ${in}
+    description = compile ${out}
+
 rule g.bootstrap.cp
     command = cp ${in} ${out}
     description = cp ${out}
 
-rule g.bootstrap.gc
-    command = GOROOT='${g.bootstrap.goRoot}' ${g.bootstrap.gcCmd} -o ${out} -p ${pkgPath} -complete ${incFlags} -pack ${in}
-    description = ${g.bootstrap.goChar}g ${out}
-
 rule g.bootstrap.link
     command = GOROOT='${g.bootstrap.goRoot}' ${g.bootstrap.linkCmd} -o ${out} ${libDirFlags} ${in}
-    description = ${g.bootstrap.goChar}l ${out}
+    description = link ${out}
 
 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
 # Module:  blueprint
 # Variant:
 # Type:    bootstrap_go_package
-# Factory: github.com/google/blueprint/bootstrap.func·002
+# Factory: github.com/google/blueprint/bootstrap.func·003
 # Defined: Blueprints:1:1
 
-build .bootstrap/blueprint/pkg/github.com/google/blueprint.a: g.bootstrap.gc $
-        ${g.bootstrap.srcDir}/context.go ${g.bootstrap.srcDir}/live_tracker.go $
-        ${g.bootstrap.srcDir}/mangle.go ${g.bootstrap.srcDir}/module_ctx.go $
+build $
+        ${g.bootstrap.buildDir}/.bootstrap/blueprint/pkg/github.com/google/blueprint.a $
+        : g.bootstrap.compile ${g.bootstrap.srcDir}/context.go $
+        ${g.bootstrap.srcDir}/live_tracker.go ${g.bootstrap.srcDir}/mangle.go $
+        ${g.bootstrap.srcDir}/module_ctx.go $
         ${g.bootstrap.srcDir}/ninja_defs.go $
         ${g.bootstrap.srcDir}/ninja_strings.go $
         ${g.bootstrap.srcDir}/ninja_writer.go $
         ${g.bootstrap.srcDir}/package_ctx.go ${g.bootstrap.srcDir}/scope.go $
         ${g.bootstrap.srcDir}/singleton_ctx.go ${g.bootstrap.srcDir}/unpack.go $
-        | ${g.bootstrap.gcCmd} $
-        .bootstrap/blueprint-parser/pkg/github.com/google/blueprint/parser.a $
-        .bootstrap/blueprint-pathtools/pkg/github.com/google/blueprint/pathtools.a $
-        .bootstrap/blueprint-proptools/pkg/github.com/google/blueprint/proptools.a
-    incFlags = -I .bootstrap/blueprint-parser/pkg -I .bootstrap/blueprint-pathtools/pkg -I .bootstrap/blueprint-proptools/pkg
+        | ${g.bootstrap.compileCmd} $
+        ${g.bootstrap.buildDir}/.bootstrap/blueprint-parser/pkg/github.com/google/blueprint/parser.a $
+        ${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/pkg/github.com/google/blueprint/pathtools.a $
+        ${g.bootstrap.buildDir}/.bootstrap/blueprint-proptools/pkg/github.com/google/blueprint/proptools.a
+    incFlags = -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-parser/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-proptools/pkg
     pkgPath = github.com/google/blueprint
-default .bootstrap/blueprint/pkg/github.com/google/blueprint.a
+default $
+        ${g.bootstrap.buildDir}/.bootstrap/blueprint/pkg/github.com/google/blueprint.a
 
 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
 # Module:  blueprint-bootstrap
 # Variant:
 # Type:    bootstrap_go_package
-# Factory: github.com/google/blueprint/bootstrap.func·002
+# Factory: github.com/google/blueprint/bootstrap.func·003
 # Defined: Blueprints:70:1
 
 build $
-        .bootstrap/blueprint-bootstrap/pkg/github.com/google/blueprint/bootstrap.a $
-        : g.bootstrap.gc ${g.bootstrap.srcDir}/bootstrap/bootstrap.go $
+        ${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap/pkg/github.com/google/blueprint/bootstrap.a $
+        : g.bootstrap.compile ${g.bootstrap.srcDir}/bootstrap/bootstrap.go $
         ${g.bootstrap.srcDir}/bootstrap/cleanup.go $
         ${g.bootstrap.srcDir}/bootstrap/command.go $
         ${g.bootstrap.srcDir}/bootstrap/config.go $
         ${g.bootstrap.srcDir}/bootstrap/doc.go $
-        ${g.bootstrap.srcDir}/bootstrap/writedocs.go | ${g.bootstrap.gcCmd} $
-        .bootstrap/blueprint-parser/pkg/github.com/google/blueprint/parser.a $
-        .bootstrap/blueprint-pathtools/pkg/github.com/google/blueprint/pathtools.a $
-        .bootstrap/blueprint-proptools/pkg/github.com/google/blueprint/proptools.a $
-        .bootstrap/blueprint/pkg/github.com/google/blueprint.a $
-        .bootstrap/blueprint-deptools/pkg/github.com/google/blueprint/deptools.a $
-        .bootstrap/blueprint-bootstrap-bpdoc/pkg/github.com/google/blueprint/bootstrap/bpdoc.a
-    incFlags = -I .bootstrap/blueprint-parser/pkg -I .bootstrap/blueprint-pathtools/pkg -I .bootstrap/blueprint-proptools/pkg -I .bootstrap/blueprint/pkg -I .bootstrap/blueprint-deptools/pkg -I .bootstrap/blueprint-bootstrap-bpdoc/pkg
+        ${g.bootstrap.srcDir}/bootstrap/writedocs.go | $
+        ${g.bootstrap.compileCmd} $
+        ${g.bootstrap.buildDir}/.bootstrap/blueprint-parser/pkg/github.com/google/blueprint/parser.a $
+        ${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/pkg/github.com/google/blueprint/pathtools.a $
+        ${g.bootstrap.buildDir}/.bootstrap/blueprint-proptools/pkg/github.com/google/blueprint/proptools.a $
+        ${g.bootstrap.buildDir}/.bootstrap/blueprint/pkg/github.com/google/blueprint.a $
+        ${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg/github.com/google/blueprint/deptools.a $
+        ${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap-bpdoc/pkg/github.com/google/blueprint/bootstrap/bpdoc.a
+    incFlags = -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-parser/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-proptools/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap-bpdoc/pkg
     pkgPath = github.com/google/blueprint/bootstrap
 default $
-        .bootstrap/blueprint-bootstrap/pkg/github.com/google/blueprint/bootstrap.a
+        ${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap/pkg/github.com/google/blueprint/bootstrap.a
 
 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
 # Module:  blueprint-bootstrap-bpdoc
 # Variant:
 # Type:    bootstrap_go_package
-# Factory: github.com/google/blueprint/bootstrap.func·002
+# Factory: github.com/google/blueprint/bootstrap.func·003
 # Defined: Blueprints:89:1
 
 build $
-        .bootstrap/blueprint-bootstrap-bpdoc/pkg/github.com/google/blueprint/bootstrap/bpdoc.a $
-        : g.bootstrap.gc ${g.bootstrap.srcDir}/bootstrap/bpdoc/bpdoc.go | $
-        ${g.bootstrap.gcCmd} $
-        .bootstrap/blueprint-parser/pkg/github.com/google/blueprint/parser.a $
-        .bootstrap/blueprint-pathtools/pkg/github.com/google/blueprint/pathtools.a $
-        .bootstrap/blueprint-proptools/pkg/github.com/google/blueprint/proptools.a $
-        .bootstrap/blueprint/pkg/github.com/google/blueprint.a
-    incFlags = -I .bootstrap/blueprint-parser/pkg -I .bootstrap/blueprint-pathtools/pkg -I .bootstrap/blueprint-proptools/pkg -I .bootstrap/blueprint/pkg
+        ${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap-bpdoc/pkg/github.com/google/blueprint/bootstrap/bpdoc.a $
+        : g.bootstrap.compile ${g.bootstrap.srcDir}/bootstrap/bpdoc/bpdoc.go | $
+        ${g.bootstrap.compileCmd} $
+        ${g.bootstrap.buildDir}/.bootstrap/blueprint-parser/pkg/github.com/google/blueprint/parser.a $
+        ${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/pkg/github.com/google/blueprint/pathtools.a $
+        ${g.bootstrap.buildDir}/.bootstrap/blueprint-proptools/pkg/github.com/google/blueprint/proptools.a $
+        ${g.bootstrap.buildDir}/.bootstrap/blueprint/pkg/github.com/google/blueprint.a
+    incFlags = -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-parser/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-proptools/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint/pkg
     pkgPath = github.com/google/blueprint/bootstrap/bpdoc
 default $
-        .bootstrap/blueprint-bootstrap-bpdoc/pkg/github.com/google/blueprint/bootstrap/bpdoc.a
+        ${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap-bpdoc/pkg/github.com/google/blueprint/bootstrap/bpdoc.a
 
 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
 # Module:  blueprint-deptools
 # Variant:
 # Type:    bootstrap_go_package
-# Factory: github.com/google/blueprint/bootstrap.func·002
+# Factory: github.com/google/blueprint/bootstrap.func·003
 # Defined: Blueprints:46:1
 
-build .bootstrap/blueprint-deptools/pkg/github.com/google/blueprint/deptools.a $
-        : g.bootstrap.gc ${g.bootstrap.srcDir}/deptools/depfile.go | $
-        ${g.bootstrap.gcCmd}
+build $
+        ${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg/github.com/google/blueprint/deptools.a $
+        : g.bootstrap.compile ${g.bootstrap.srcDir}/deptools/depfile.go | $
+        ${g.bootstrap.compileCmd}
     pkgPath = github.com/google/blueprint/deptools
 default $
-        .bootstrap/blueprint-deptools/pkg/github.com/google/blueprint/deptools.a
+        ${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg/github.com/google/blueprint/deptools.a
 
 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
 # Module:  blueprint-parser
 # Variant:
 # Type:    bootstrap_go_package
-# Factory: github.com/google/blueprint/bootstrap.func·002
+# Factory: github.com/google/blueprint/bootstrap.func·003
 # Defined: Blueprints:31:1
 
-build .bootstrap/blueprint-parser/pkg/github.com/google/blueprint/parser.a: $
-        g.bootstrap.gc ${g.bootstrap.srcDir}/parser/modify.go $
+build $
+        ${g.bootstrap.buildDir}/.bootstrap/blueprint-parser/pkg/github.com/google/blueprint/parser.a $
+        : g.bootstrap.compile ${g.bootstrap.srcDir}/parser/modify.go $
         ${g.bootstrap.srcDir}/parser/parser.go $
         ${g.bootstrap.srcDir}/parser/printer.go $
-        ${g.bootstrap.srcDir}/parser/sort.go | ${g.bootstrap.gcCmd}
+        ${g.bootstrap.srcDir}/parser/sort.go | ${g.bootstrap.compileCmd}
     pkgPath = github.com/google/blueprint/parser
-default .bootstrap/blueprint-parser/pkg/github.com/google/blueprint/parser.a
+default $
+        ${g.bootstrap.buildDir}/.bootstrap/blueprint-parser/pkg/github.com/google/blueprint/parser.a
 
 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
 # Module:  blueprint-pathtools
 # Variant:
 # Type:    bootstrap_go_package
-# Factory: github.com/google/blueprint/bootstrap.func·002
+# Factory: github.com/google/blueprint/bootstrap.func·003
 # Defined: Blueprints:52:1
 
 build $
-        .bootstrap/blueprint-pathtools/pkg/github.com/google/blueprint/pathtools.a $
-        : g.bootstrap.gc ${g.bootstrap.srcDir}/pathtools/lists.go $
-        ${g.bootstrap.srcDir}/pathtools/glob.go | ${g.bootstrap.gcCmd}
+        ${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/pkg/github.com/google/blueprint/pathtools.a $
+        : g.bootstrap.compile ${g.bootstrap.srcDir}/pathtools/lists.go $
+        ${g.bootstrap.srcDir}/pathtools/glob.go | ${g.bootstrap.compileCmd}
     pkgPath = github.com/google/blueprint/pathtools
 default $
-        .bootstrap/blueprint-pathtools/pkg/github.com/google/blueprint/pathtools.a
+        ${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/pkg/github.com/google/blueprint/pathtools.a
 
 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
 # Module:  blueprint-proptools
 # Variant:
 # Type:    bootstrap_go_package
-# Factory: github.com/google/blueprint/bootstrap.func·002
+# Factory: github.com/google/blueprint/bootstrap.func·003
 # Defined: Blueprints:64:1
 
 build $
-        .bootstrap/blueprint-proptools/pkg/github.com/google/blueprint/proptools.a $
-        : g.bootstrap.gc ${g.bootstrap.srcDir}/proptools/proptools.go | $
-        ${g.bootstrap.gcCmd}
+        ${g.bootstrap.buildDir}/.bootstrap/blueprint-proptools/pkg/github.com/google/blueprint/proptools.a $
+        : g.bootstrap.compile ${g.bootstrap.srcDir}/proptools/proptools.go | $
+        ${g.bootstrap.compileCmd}
     pkgPath = github.com/google/blueprint/proptools
 default $
-        .bootstrap/blueprint-proptools/pkg/github.com/google/blueprint/proptools.a
+        ${g.bootstrap.buildDir}/.bootstrap/blueprint-proptools/pkg/github.com/google/blueprint/proptools.a
 
 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
-# Module:  bpfmt
+# Module:  choosestage
 # Variant:
-# Type:    bootstrap_go_binary
-# Factory: github.com/google/blueprint/bootstrap.func·003
-# Defined: Blueprints:110:1
+# Type:    bootstrap_core_go_binary
+# Factory: github.com/google/blueprint/bootstrap.func·005
+# Defined: Blueprints:132:1
 
-build .bootstrap/bpfmt/obj/bpfmt.a: g.bootstrap.gc $
-        ${g.bootstrap.srcDir}/bpfmt/bpfmt.go | ${g.bootstrap.gcCmd} $
-        .bootstrap/blueprint-parser/pkg/github.com/google/blueprint/parser.a
-    incFlags = -I .bootstrap/blueprint-parser/pkg
-    pkgPath = bpfmt
-default .bootstrap/bpfmt/obj/bpfmt.a
+build ${g.bootstrap.buildDir}/.bootstrap/choosestage/obj/choosestage.a: $
+        g.bootstrap.compile ${g.bootstrap.srcDir}/choosestage/choosestage.go | $
+        ${g.bootstrap.compileCmd}
+    pkgPath = choosestage
+default ${g.bootstrap.buildDir}/.bootstrap/choosestage/obj/choosestage.a
 
-build .bootstrap/bpfmt/obj/a.out: g.bootstrap.link $
-        .bootstrap/bpfmt/obj/bpfmt.a | ${g.bootstrap.linkCmd}
-    libDirFlags = -L .bootstrap/blueprint-parser/pkg
-default .bootstrap/bpfmt/obj/a.out
-
-build .bootstrap/bin/bpfmt: g.bootstrap.cp .bootstrap/bpfmt/obj/a.out
-default .bootstrap/bin/bpfmt
-
-# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
-# Module:  bpmodify
-# Variant:
-# Type:    bootstrap_go_binary
-# Factory: github.com/google/blueprint/bootstrap.func·003
-# Defined: Blueprints:116:1
-
-build .bootstrap/bpmodify/obj/bpmodify.a: g.bootstrap.gc $
-        ${g.bootstrap.srcDir}/bpmodify/bpmodify.go | ${g.bootstrap.gcCmd} $
-        .bootstrap/blueprint-parser/pkg/github.com/google/blueprint/parser.a
-    incFlags = -I .bootstrap/blueprint-parser/pkg
-    pkgPath = bpmodify
-default .bootstrap/bpmodify/obj/bpmodify.a
-
-build .bootstrap/bpmodify/obj/a.out: g.bootstrap.link $
-        .bootstrap/bpmodify/obj/bpmodify.a | ${g.bootstrap.linkCmd}
-    libDirFlags = -L .bootstrap/blueprint-parser/pkg
-default .bootstrap/bpmodify/obj/a.out
-
-build .bootstrap/bin/bpmodify: g.bootstrap.cp .bootstrap/bpmodify/obj/a.out
-default .bootstrap/bin/bpmodify
+build ${g.bootstrap.buildDir}/.bootstrap/choosestage/obj/a.out: $
+        g.bootstrap.link $
+        ${g.bootstrap.buildDir}/.bootstrap/choosestage/obj/choosestage.a | $
+        ${g.bootstrap.linkCmd}
+default ${g.bootstrap.buildDir}/.bootstrap/choosestage/obj/a.out
+build ${g.bootstrap.BinDir}/choosestage: g.bootstrap.cp $
+        ${g.bootstrap.buildDir}/.bootstrap/choosestage/obj/a.out
+default ${g.bootstrap.BinDir}/choosestage
 
 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
 # Module:  gotestmain
 # Variant:
-# Type:    bootstrap_go_binary
-# Factory: github.com/google/blueprint/bootstrap.func·003
+# Type:    bootstrap_core_go_binary
+# Factory: github.com/google/blueprint/bootstrap.func·005
 # Defined: Blueprints:122:1
 
-build .bootstrap/gotestmain/obj/gotestmain.a: g.bootstrap.gc $
-        ${g.bootstrap.srcDir}/gotestmain/gotestmain.go | ${g.bootstrap.gcCmd}
+build ${g.bootstrap.buildDir}/.bootstrap/gotestmain/obj/gotestmain.a: $
+        g.bootstrap.compile ${g.bootstrap.srcDir}/gotestmain/gotestmain.go | $
+        ${g.bootstrap.compileCmd}
     pkgPath = gotestmain
-default .bootstrap/gotestmain/obj/gotestmain.a
+default ${g.bootstrap.buildDir}/.bootstrap/gotestmain/obj/gotestmain.a
 
-build .bootstrap/gotestmain/obj/a.out: g.bootstrap.link $
-        .bootstrap/gotestmain/obj/gotestmain.a | ${g.bootstrap.linkCmd}
-default .bootstrap/gotestmain/obj/a.out
-build .bootstrap/bin/gotestmain: g.bootstrap.cp $
-        .bootstrap/gotestmain/obj/a.out
-default .bootstrap/bin/gotestmain
+build ${g.bootstrap.buildDir}/.bootstrap/gotestmain/obj/a.out: $
+        g.bootstrap.link $
+        ${g.bootstrap.buildDir}/.bootstrap/gotestmain/obj/gotestmain.a | $
+        ${g.bootstrap.linkCmd}
+default ${g.bootstrap.buildDir}/.bootstrap/gotestmain/obj/a.out
+build ${g.bootstrap.BinDir}/gotestmain: g.bootstrap.cp $
+        ${g.bootstrap.buildDir}/.bootstrap/gotestmain/obj/a.out
+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_go_binary
-# Factory: github.com/google/blueprint/bootstrap.func·003
+# Type:    bootstrap_core_go_binary
+# Factory: github.com/google/blueprint/bootstrap.func·005
 # Defined: Blueprints:101:1
 
-build .bootstrap/minibp/obj/minibp.a: g.bootstrap.gc $
-        ${g.bootstrap.srcDir}/bootstrap/minibp/main.go | ${g.bootstrap.gcCmd} $
-        .bootstrap/blueprint-parser/pkg/github.com/google/blueprint/parser.a $
-        .bootstrap/blueprint-pathtools/pkg/github.com/google/blueprint/pathtools.a $
-        .bootstrap/blueprint-proptools/pkg/github.com/google/blueprint/proptools.a $
-        .bootstrap/blueprint/pkg/github.com/google/blueprint.a $
-        .bootstrap/blueprint-deptools/pkg/github.com/google/blueprint/deptools.a $
-        .bootstrap/blueprint-bootstrap-bpdoc/pkg/github.com/google/blueprint/bootstrap/bpdoc.a $
-        .bootstrap/blueprint-bootstrap/pkg/github.com/google/blueprint/bootstrap.a
-    incFlags = -I .bootstrap/blueprint-parser/pkg -I .bootstrap/blueprint-pathtools/pkg -I .bootstrap/blueprint-proptools/pkg -I .bootstrap/blueprint/pkg -I .bootstrap/blueprint-deptools/pkg -I .bootstrap/blueprint-bootstrap-bpdoc/pkg -I .bootstrap/blueprint-bootstrap/pkg
+build ${g.bootstrap.buildDir}/.bootstrap/minibp/obj/minibp.a: $
+        g.bootstrap.compile ${g.bootstrap.srcDir}/bootstrap/minibp/main.go | $
+        ${g.bootstrap.compileCmd} $
+        ${g.bootstrap.buildDir}/.bootstrap/blueprint-parser/pkg/github.com/google/blueprint/parser.a $
+        ${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/pkg/github.com/google/blueprint/pathtools.a $
+        ${g.bootstrap.buildDir}/.bootstrap/blueprint-proptools/pkg/github.com/google/blueprint/proptools.a $
+        ${g.bootstrap.buildDir}/.bootstrap/blueprint/pkg/github.com/google/blueprint.a $
+        ${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg/github.com/google/blueprint/deptools.a $
+        ${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap-bpdoc/pkg/github.com/google/blueprint/bootstrap/bpdoc.a $
+        ${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap/pkg/github.com/google/blueprint/bootstrap.a
+    incFlags = -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-parser/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-proptools/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap-bpdoc/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap/pkg
     pkgPath = minibp
-default .bootstrap/minibp/obj/minibp.a
+default ${g.bootstrap.buildDir}/.bootstrap/minibp/obj/minibp.a
 
-build .bootstrap/minibp/obj/a.out: g.bootstrap.link $
-        .bootstrap/minibp/obj/minibp.a | ${g.bootstrap.linkCmd}
-    libDirFlags = -L .bootstrap/blueprint-parser/pkg -L .bootstrap/blueprint-pathtools/pkg -L .bootstrap/blueprint-proptools/pkg -L .bootstrap/blueprint/pkg -L .bootstrap/blueprint-deptools/pkg -L .bootstrap/blueprint-bootstrap-bpdoc/pkg -L .bootstrap/blueprint-bootstrap/pkg
-default .bootstrap/minibp/obj/a.out
+build ${g.bootstrap.buildDir}/.bootstrap/minibp/obj/a.out: g.bootstrap.link $
+        ${g.bootstrap.buildDir}/.bootstrap/minibp/obj/minibp.a | $
+        ${g.bootstrap.linkCmd}
+    libDirFlags = -L ${g.bootstrap.buildDir}/.bootstrap/blueprint-parser/pkg -L ${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/pkg -L ${g.bootstrap.buildDir}/.bootstrap/blueprint-proptools/pkg -L ${g.bootstrap.buildDir}/.bootstrap/blueprint/pkg -L ${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg -L ${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap-bpdoc/pkg -L ${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap/pkg
+default ${g.bootstrap.buildDir}/.bootstrap/minibp/obj/a.out
 
-build .bootstrap/bin/minibp: g.bootstrap.cp .bootstrap/minibp/obj/a.out
-default .bootstrap/bin/minibp
+build ${g.bootstrap.BinDir}/minibp: g.bootstrap.cp $
+        ${g.bootstrap.buildDir}/.bootstrap/minibp/obj/a.out
+default ${g.bootstrap.BinDir}/minibp
 
 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
 # Singleton: bootstrap
-# Factory:   github.com/google/blueprint/bootstrap.func·008
+# Factory:   github.com/google/blueprint/bootstrap.func·012
 
-rule s.bootstrap.bigbpDocs
-    command = .bootstrap/bin/minibp -p --docs ${out} ${g.bootstrap.srcDir}/Blueprints
-    description = minibp docs ${out}
-
-rule s.bootstrap.bigbp
-    command = .bootstrap/bin/minibp -p -d .bootstrap/main.ninja.in.d -m ${g.bootstrap.bootstrapManifest} -o ${out} ${in}
-    depfile = .bootstrap/main.ninja.in.d
-    description = minibp ${out}
+rule s.bootstrap.primarybp
+    command = ${g.bootstrap.BinDir}/minibp --build-primary ${runTests} -m ${g.bootstrap.bootstrapManifest} --timestamp ${timestamp} --timestampdep ${timestampdep} -b ${g.bootstrap.buildDir} -d ${outfile}.d -o ${outfile} ${in}
+    depfile = ${outfile}.d
+    description = minibp ${outfile}
 
 rule s.bootstrap.minibp
-    command = .bootstrap/bin/minibp ${runTests} -c ${checkFile} -m ${g.bootstrap.bootstrapManifest} -d ${out}.d -o ${out} ${in}
+    command = ${g.bootstrap.BinDir}/minibp ${runTests} -m ${g.bootstrap.bootstrapManifest} -b ${g.bootstrap.buildDir} -d ${out}.d -o ${out} ${in}
     depfile = ${out}.d
     description = minibp ${out}
     generator = true
 
-build .bootstrap/docs/minibp.html: s.bootstrap.bigbpDocs | $
-        .bootstrap/bin/minibp
-default .bootstrap/docs/minibp.html
-build .bootstrap/main.ninja.in: s.bootstrap.bigbp $
-        ${g.bootstrap.srcDir}/Blueprints | .bootstrap/bin/bpfmt $
-        .bootstrap/bin/bpmodify .bootstrap/bin/gotestmain $
-        .bootstrap/bin/minibp .bootstrap/docs/minibp.html
-default .bootstrap/main.ninja.in
-build .bootstrap/notAFile: phony
-default .bootstrap/notAFile
-build build.ninja: g.bootstrap.bootstrap .bootstrap/main.ninja.in | $
-        ${g.bootstrap.bootstrapCmd} .bootstrap/notAFile $
-        .bootstrap/bootstrap.ninja.in
-default build.ninja
-build .bootstrap/bootstrap.ninja.in: s.bootstrap.minibp $
-        ${g.bootstrap.srcDir}/Blueprints | .bootstrap/bin/minibp
-    checkFile = ${g.bootstrap.bootstrapManifest}
-default .bootstrap/bootstrap.ninja.in
+build ${g.bootstrap.buildDir}/.bootstrap/primary.ninja.in $
+        ${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}/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
+default ${g.bootstrap.buildDir}/.bootstrap/primary.ninja.in $
+        ${g.bootstrap.buildDir}/.bootstrap/primary.ninja.in.timestamp
+
+build ${g.bootstrap.buildDir}/.bootstrap/bootstrap.ninja.in: $
+        s.bootstrap.minibp ${g.bootstrap.srcDir}/Blueprints | $
+        ${g.bootstrap.bootstrapManifest} ${g.bootstrap.BinDir}/minibp
+default ${g.bootstrap.buildDir}/.bootstrap/bootstrap.ninja.in
+build ${g.bootstrap.buildDir}/.bootstrap/notAFile: phony
+default ${g.bootstrap.buildDir}/.bootstrap/notAFile
+build ${g.bootstrap.buildDir}/.bootstrap/build.ninja.in: $
+        g.bootstrap.chooseStage $
+        ${g.bootstrap.buildDir}/.bootstrap/bootstrap.ninja.in $
+        ${g.bootstrap.buildDir}/.bootstrap/primary.ninja.in | $
+        ${g.bootstrap.chooseStageCmd} ${g.bootstrap.bootstrapManifest} $
+        ${g.bootstrap.buildDir}/.bootstrap/notAFile
+    current = ${g.bootstrap.buildDir}/.bootstrap/bootstrap.ninja.in
+default ${g.bootstrap.buildDir}/.bootstrap/build.ninja.in
+
+build ${g.bootstrap.buildDir}/build.ninja: g.bootstrap.bootstrap $
+        ${g.bootstrap.buildDir}/.bootstrap/build.ninja.in | $
+        ${g.bootstrap.bootstrapCmd}
+default ${g.bootstrap.buildDir}/build.ninja
 
diff --git a/choosestage/choosestage.go b/choosestage/choosestage.go
new file mode 100644
index 0000000..c3d0273
--- /dev/null
+++ b/choosestage/choosestage.go
@@ -0,0 +1,194 @@
+// 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.
+
+// Choose which ninja file (stage) to run next
+//
+// In the common case, this program takes a list of ninja files, compares their
+// mtimes against their $file.timestamp mtimes, and picks the last up to date
+// ninja file to output. That stage is expected to rebuild the next file in the
+// list and call this program again. If none of the ninja files are considered
+// dirty, the last stage is output.
+//
+// One exception is if the current stage's ninja file was rewritten, it will be
+// run again.
+//
+// Another exception is if the source bootstrap file has been updated more
+// recently than the first stage, the source file will be copied to the first
+// stage, and output. This would be expected with a new source drop via git.
+// The timestamp of the first file is not updated so that it can be regenerated
+// with any local changes.
+
+package choosestage
+
+import (
+	"bytes"
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+)
+
+var (
+	outputFile    string
+	currentFile   string
+	bootstrapFile string
+	verbose       bool
+)
+
+func init() {
+	flag.StringVar(&outputFile, "o", "", "Output file")
+	flag.StringVar(&currentFile, "current", "", "Current stage's file")
+	flag.StringVar(&bootstrapFile, "bootstrap", "", "Bootstrap file checked into source")
+	flag.BoolVar(&verbose, "v", false, "Verbose mode")
+}
+
+func compareFiles(a, b string) (bool, error) {
+	aData, err := ioutil.ReadFile(a)
+	if err != nil {
+		return false, err
+	}
+
+	bData, err := ioutil.ReadFile(b)
+	if err != nil {
+		return false, err
+	}
+
+	return bytes.Equal(aData, bData), nil
+}
+
+// If the source bootstrap reference file is newer, then we may have gotten
+// other source updates too. So we need to restart everything with the file
+// that was checked in instead of the bootstrap that we last built.
+func copyBootstrapIfNecessary(bootstrapFile, filename string) (bool, error) {
+	if bootstrapFile == "" {
+		return false, nil
+	}
+
+	bootstrapStat, err := os.Stat(bootstrapFile)
+	if err != nil {
+		return false, err
+	}
+
+	fileStat, err := os.Stat(filename)
+	if err != nil {
+		return false, err
+	}
+
+	time := fileStat.ModTime()
+	if !bootstrapStat.ModTime().After(time) {
+		return false, nil
+	}
+
+	fmt.Printf("Newer source version of %s. Copying to %s\n", filepath.Base(bootstrapFile), filepath.Base(filename))
+	if verbose {
+		fmt.Printf("Source: %s\nBuilt:  %s\n", bootstrapStat.ModTime(), time)
+	}
+
+	data, err := ioutil.ReadFile(bootstrapFile)
+	if err != nil {
+		return false, err
+	}
+
+	err = ioutil.WriteFile(filename, data, 0666)
+	if err != nil {
+		return false, err
+	}
+
+	// Restore timestamp to force regeneration of the bootstrap.ninja.in
+	err = os.Chtimes(filename, time, time)
+	return true, err
+}
+
+func main() {
+	flag.Parse()
+
+	if flag.NArg() == 0 {
+		fmt.Fprintf(os.Stderr, "Must specify at least one ninja file\n")
+		os.Exit(1)
+	}
+
+	if outputFile == "" {
+		fmt.Fprintf(os.Stderr, "Must specify an output file\n")
+		os.Exit(1)
+	}
+
+	gotoFile := flag.Arg(0)
+	if copied, err := copyBootstrapIfNecessary(bootstrapFile, flag.Arg(0)); err != nil {
+		fmt.Fprintf(os.Stderr, "Failed to copy bootstrap ninja file: %s\n", err)
+		os.Exit(1)
+	} else if !copied {
+		for _, fileName := range flag.Args() {
+			timestampName := fileName + ".timestamp"
+
+			// If we're currently running this stage, and the build.ninja.in
+			// file differs from the current stage file, then it has been rebuilt.
+			// Restart the stage.
+			if filepath.Clean(currentFile) == filepath.Clean(fileName) {
+				if _, err := os.Stat(outputFile); !os.IsNotExist(err) {
+					if ok, err := compareFiles(fileName, outputFile); err != nil {
+						fmt.Fprintf(os.Stderr, "Failure when comparing files: %s\n", err)
+						os.Exit(1)
+					} else if !ok {
+						fmt.Printf("Stage %s has changed, restarting\n", filepath.Base(fileName))
+						gotoFile = fileName
+						break
+					}
+				}
+			}
+
+			fileStat, err := os.Stat(fileName)
+			if err != nil {
+				// Regenerate this stage on error
+				break
+			}
+
+			timestampStat, err := os.Stat(timestampName)
+			if err != nil {
+				// This file may not exist. There's no point for
+				// the first stage to have one, as it should be
+				// a subset of the second stage dependencies,
+				// and both will return to the first stage.
+				continue
+			}
+
+			if verbose {
+				fmt.Printf("For %s:\n  file: %s\n  time: %s\n", fileName, fileStat.ModTime(), timestampStat.ModTime())
+			}
+
+			// If the timestamp file has a later modification time, that
+			// means that this stage needs to be regenerated. Break, so
+			// that we run the last found stage.
+			if timestampStat.ModTime().After(fileStat.ModTime()) {
+				break
+			}
+
+			gotoFile = fileName
+		}
+	}
+
+	fmt.Printf("Choosing %s for next stage\n", filepath.Base(gotoFile))
+
+	data, err := ioutil.ReadFile(gotoFile)
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "Can't read file: %s", err)
+		os.Exit(1)
+	}
+
+	err = ioutil.WriteFile(outputFile, data, 0666)
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "Can't write file: %s", err)
+		os.Exit(1)
+	}
+}
diff --git a/context.go b/context.go
index 0327190..dd2dc1d 100644
--- a/context.go
+++ b/context.go
@@ -69,7 +69,7 @@
 	moduleGroups        map[string]*moduleGroup
 	moduleInfo          map[Module]*moduleInfo
 	modulesSorted       []*moduleInfo
-	singletonInfo       map[string]*singletonInfo
+	singletonInfo       []*singletonInfo
 	mutatorInfo         []*mutatorInfo
 	earlyMutatorInfo    []*earlyMutatorInfo
 	variantMutatorNames []string
@@ -195,6 +195,7 @@
 	// set during RegisterSingletonType
 	factory   SingletonFactory
 	singleton Singleton
+	name      string
 
 	// set during PrepareBuildActions
 	actionDefs localBuildActions
@@ -227,7 +228,6 @@
 		moduleFactories:  make(map[string]ModuleFactory),
 		moduleGroups:     make(map[string]*moduleGroup),
 		moduleInfo:       make(map[Module]*moduleInfo),
-		singletonInfo:    make(map[string]*singletonInfo),
 		moduleNinjaNames: make(map[string]*moduleGroup),
 	}
 }
@@ -312,20 +312,24 @@
 
 // RegisterSingletonType registers a singleton type that will be invoked to
 // generate build actions.  Each registered singleton type is instantiated and
-// and invoked exactly once as part of the generate phase.
+// and invoked exactly once as part of the generate phase.  Each registered
+// singleton is invoked in registration order.
 //
 // The singleton type names given here must be unique for the context.  The
 // factory function should be a named function so that its package and name can
 // be included in the generated Ninja file for debugging purposes.
 func (c *Context) RegisterSingletonType(name string, factory SingletonFactory) {
-	if _, present := c.singletonInfo[name]; present {
-		panic(errors.New("singleton name is already registered"))
+	for _, s := range c.singletonInfo {
+		if s.name == name {
+			panic(errors.New("singleton name is already registered"))
+		}
 	}
 
-	c.singletonInfo[name] = &singletonInfo{
+	c.singletonInfo = append(c.singletonInfo, &singletonInfo{
 		factory:   factory,
 		singleton: factory(),
-	}
+		name:      name,
+	})
 }
 
 func singletonPkgPath(singleton Singleton) string {
@@ -1073,6 +1077,11 @@
 		return errs
 	}
 
+	errs = c.runMutators(config)
+	if len(errs) > 0 {
+		return errs
+	}
+
 	c.dependenciesReady = true
 	return nil
 }
@@ -1147,6 +1156,22 @@
 	return
 }
 
+// findMatchingVariant searches the moduleGroup for a module with the same variant as module,
+// and returns the matching module, or nil if one is not found.
+func (c *Context) findMatchingVariant(module *moduleInfo, group *moduleGroup) *moduleInfo {
+	if len(group.modules) == 1 {
+		return group.modules[0]
+	} else {
+		for _, m := range group.modules {
+			if m.variant.equal(module.dependencyVariant) {
+				return m
+			}
+		}
+	}
+
+	return nil
+}
+
 func (c *Context) addDependency(module *moduleInfo, depName string) []error {
 	depsPos := module.propertyPos["deps"]
 
@@ -1172,16 +1197,9 @@
 		}
 	}
 
-	if len(depInfo.modules) == 1 {
-		module.directDeps = append(module.directDeps, depInfo.modules[0])
+	if m := c.findMatchingVariant(module, depInfo); m != nil {
+		module.directDeps = append(module.directDeps, m)
 		return nil
-	} else {
-		for _, m := range depInfo.modules {
-			if m.variant.equal(module.dependencyVariant) {
-				module.directDeps = append(module.directDeps, m)
-				return nil
-			}
-		}
 	}
 
 	return []error{&Error{
@@ -1192,6 +1210,36 @@
 	}}
 }
 
+func (c *Context) addReverseDependency(module *moduleInfo, destName string) []error {
+	if destName == module.properties.Name {
+		return []error{&Error{
+			Err: fmt.Errorf("%q depends on itself", destName),
+			Pos: module.pos,
+		}}
+	}
+
+	destInfo, ok := c.moduleGroups[destName]
+	if !ok {
+		return []error{&Error{
+			Err: fmt.Errorf("%q has a reverse dependency on undefined module %q",
+				module.properties.Name, destName),
+			Pos: module.pos,
+		}}
+	}
+
+	if m := c.findMatchingVariant(module, destInfo); m != nil {
+		m.directDeps = append(m.directDeps, module)
+		return nil
+	}
+
+	return []error{&Error{
+		Err: fmt.Errorf("reverse dependency %q of %q missing variant %q",
+			destName, module.properties.Name,
+			c.prettyPrintVariant(module.dependencyVariant)),
+		Pos: module.pos,
+	}}
+}
+
 func (c *Context) addVariationDependency(module *moduleInfo, variations []Variation,
 	depName string, far bool) []error {
 
@@ -1431,11 +1479,6 @@
 		}
 	}
 
-	errs = c.runMutators(config)
-	if len(errs) > 0 {
-		return nil, errs
-	}
-
 	liveGlobals := newLiveTracker(config)
 
 	c.initSpecialVariables()
@@ -1631,7 +1674,7 @@
 func (c *Context) initSpecialVariables() {
 	c.buildDir = nil
 	c.requiredNinjaMajor = 1
-	c.requiredNinjaMinor = 1
+	c.requiredNinjaMinor = 6
 	c.requiredNinjaMicro = 0
 }
 
@@ -1706,11 +1749,11 @@
 	var deps []string
 	var errs []error
 
-	for name, info := range c.singletonInfo {
+	for _, info := range c.singletonInfo {
 		// The parent scope of the singletonContext's local scope gets overridden to be that of the
 		// calling Go package on a per-call basis.  Since the initial parent scope doesn't matter we
 		// just set it to nil.
-		scope := newLocalScope(nil, singletonNamespacePrefix(name))
+		scope := newLocalScope(nil, singletonNamespacePrefix(info.name))
 
 		sctx := &singletonContext{
 			context: c,
@@ -2429,6 +2472,10 @@
 	buf := bytes.NewBuffer(nil)
 
 	for _, module := range modules {
+		if len(module.actionDefs.variables)+len(module.actionDefs.rules)+len(module.actionDefs.buildDefs) == 0 {
+			continue
+		}
+
 		buf.Reset()
 
 		// In order to make the bootstrap build manifest independent of the
@@ -2488,14 +2535,10 @@
 
 	buf := bytes.NewBuffer(nil)
 
-	singletonNames := make([]string, 0, len(c.singletonInfo))
-	for name := range c.singletonInfo {
-		singletonNames = append(singletonNames, name)
-	}
-	sort.Strings(singletonNames)
-
-	for _, name := range singletonNames {
-		info := c.singletonInfo[name]
+	for _, info := range c.singletonInfo {
+		if len(info.actionDefs.variables)+len(info.actionDefs.rules)+len(info.actionDefs.buildDefs) == 0 {
+			continue
+		}
 
 		// Get the name of the factory function for the module.
 		factory := info.factory
@@ -2504,7 +2547,7 @@
 
 		buf.Reset()
 		infoMap := map[string]interface{}{
-			"name":      name,
+			"name":      info.name,
 			"goFactory": factoryName,
 		}
 		err = headerTemplate.Execute(buf, infoMap)
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/loadplugins/loadplugins.go b/loadplugins/loadplugins.go
new file mode 100644
index 0000000..3c7e1e3
--- /dev/null
+++ b/loadplugins/loadplugins.go
@@ -0,0 +1,67 @@
+// 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 (
+	"bytes"
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"text/template"
+)
+
+var (
+	output = flag.String("o", "", "output filename")
+	pkg    = flag.String("p", "main", "package name")
+)
+
+func main() {
+	flag.Parse()
+
+	if flag.NArg() == 0 {
+		fmt.Fprintln(os.Stderr, "error: must pass at least one input")
+		os.Exit(1)
+	}
+
+	buf := &bytes.Buffer{}
+
+	err := pluginTmpl.Execute(buf, struct {
+		Package string
+		Plugins []string
+	}{
+		filepath.Base(*pkg),
+		flag.Args(),
+	})
+	if err != nil {
+		panic(err)
+	}
+
+	err = ioutil.WriteFile(*output, buf.Bytes(), 0666)
+	if err != nil {
+		panic(err)
+	}
+}
+
+var pluginTmpl = template.Must(template.New("pluginloader").Parse(`
+package {{.Package}}
+
+import (
+{{range .Plugins}}
+	_ "{{.}}"
+{{end}}
+)
+`))
diff --git a/module_ctx.go b/module_ctx.go
index 76d3977..6254596 100644
--- a/module_ctx.go
+++ b/module_ctx.go
@@ -359,8 +359,7 @@
 
 type mutatorContext struct {
 	baseModuleContext
-	name                 string
-	dependenciesModified bool
+	name string
 }
 
 type baseMutatorContext interface {
@@ -389,6 +388,7 @@
 	baseMutatorContext
 
 	AddDependency(module Module, name string)
+	AddReverseDependency(module Module, name string)
 	CreateVariations(...string) []Module
 	CreateLocalVariations(...string) []Module
 	SetDependencyVariation(string)
@@ -464,8 +464,7 @@
 	return mctx.module.logicModule
 }
 
-// Add a dependency to the given module.  The depender can be a specific variant
-// of a module, but the dependee must be a module that has no variations.
+// Add a dependency to the given module.
 // Does not affect the ordering of the current mutator pass, but will be ordered
 // correctly for all future mutator passes.
 func (mctx *mutatorContext) AddDependency(module Module, depName string) {
@@ -473,7 +472,16 @@
 	if len(errs) > 0 {
 		mctx.errs = append(mctx.errs, errs...)
 	}
-	mctx.dependenciesModified = true
+}
+
+// Add a dependency from the destination to the given module.
+// Does not affect the ordering of the current mutator pass, but will be ordered
+// correctly for all future mutator passes.
+func (mctx *mutatorContext) AddReverseDependency(module Module, destName string) {
+	errs := mctx.context.addReverseDependency(mctx.context.moduleInfo[module], destName)
+	if len(errs) > 0 {
+		mctx.errs = append(mctx.errs, errs...)
+	}
 }
 
 func (mctx *mutatorContext) VisitDirectDeps(visit func(Module)) {
diff --git a/tests/bootstrap.bash b/tests/bootstrap.bash
new file mode 100755
index 0000000..4b58b19
--- /dev/null
+++ b/tests/bootstrap.bash
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+export BOOTSTRAP="${BASH_SOURCE[0]}"
+export SRCDIR=".."
+export BOOTSTRAP_MANIFEST="src.build.ninja.in"
+
+../bootstrap.bash "$@"
diff --git a/tests/expected_all b/tests/expected_all
new file mode 100644
index 0000000..b16fc78
--- /dev/null
+++ b/tests/expected_all
@@ -0,0 +1,3 @@
+Choosing bootstrap.ninja.in for next stage
+Choosing primary.ninja.in for next stage
+Choosing main.ninja.in for next stage
diff --git a/tests/expected_manifest b/tests/expected_manifest
new file mode 100644
index 0000000..3970edb
--- /dev/null
+++ b/tests/expected_manifest
@@ -0,0 +1,4 @@
+Newer source version of build.ninja.in. Copying to bootstrap.ninja.in
+Choosing bootstrap.ninja.in for next stage
+Choosing primary.ninja.in for next stage
+Choosing main.ninja.in for next stage
diff --git a/tests/expected_none b/tests/expected_none
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/expected_none
diff --git a/tests/expected_primary b/tests/expected_primary
new file mode 100644
index 0000000..43f2d35
--- /dev/null
+++ b/tests/expected_primary
@@ -0,0 +1,2 @@
+Choosing primary.ninja.in for next stage
+Choosing main.ninja.in for next stage
diff --git a/tests/expected_rebuild_test b/tests/expected_rebuild_test
new file mode 100644
index 0000000..b16fc78
--- /dev/null
+++ b/tests/expected_rebuild_test
@@ -0,0 +1,3 @@
+Choosing bootstrap.ninja.in for next stage
+Choosing primary.ninja.in for next stage
+Choosing main.ninja.in for next stage
diff --git a/tests/expected_regen b/tests/expected_regen
new file mode 100644
index 0000000..e2e10b8
--- /dev/null
+++ b/tests/expected_regen
@@ -0,0 +1,6 @@
+Newer source version of src.build.ninja.in. Copying to bootstrap.ninja.in
+Choosing bootstrap.ninja.in for next stage
+Stage bootstrap.ninja.in has changed, restarting
+Choosing bootstrap.ninja.in for next stage
+Choosing primary.ninja.in for next stage
+Choosing main.ninja.in for next stage
diff --git a/tests/expected_start b/tests/expected_start
new file mode 100644
index 0000000..43f2d35
--- /dev/null
+++ b/tests/expected_start
@@ -0,0 +1,2 @@
+Choosing primary.ninja.in for next stage
+Choosing main.ninja.in for next stage
diff --git a/tests/expected_start2 b/tests/expected_start2
new file mode 100644
index 0000000..4c339e2
--- /dev/null
+++ b/tests/expected_start2
@@ -0,0 +1,4 @@
+Stage bootstrap.ninja.in has changed, restarting
+Choosing bootstrap.ninja.in for next stage
+Choosing primary.ninja.in for next stage
+Choosing main.ninja.in for next stage
diff --git a/tests/expected_start_add_tests b/tests/expected_start_add_tests
new file mode 100644
index 0000000..4c339e2
--- /dev/null
+++ b/tests/expected_start_add_tests
@@ -0,0 +1,4 @@
+Stage bootstrap.ninja.in has changed, restarting
+Choosing bootstrap.ninja.in for next stage
+Choosing primary.ninja.in for next stage
+Choosing main.ninja.in for next stage
diff --git a/tests/test.sh b/tests/test.sh
new file mode 100755
index 0000000..e27ae97
--- /dev/null
+++ b/tests/test.sh
@@ -0,0 +1,94 @@
+#!/bin/bash
+
+# Go to srcdir
+cd $(dirname ${BASH_SOURCE[0]})/..
+
+rm -rf out.test
+mkdir out.test
+cd out.test
+../bootstrap.bash
+
+# Run ninja, filter the output, and compare against expectations
+# $1: Name of test
+function testcase()
+{
+  echo -n "Running $1..."
+  if ! ninja -v -d explain >log_$1 2>&1; then
+    echo " Failed."
+    echo "Test $1 Failed:" >>failed
+    tail log_$1 >>failed
+    return
+  fi
+  grep -E "^(Choosing|Newer|Stage)" log_$1 >test_$1
+  if ! cmp -s test_$1 ../tests/expected_$1; then
+    echo " Failed."
+    echo "Test $1 Failed:" >>failed
+    diff -u ../tests/expected_$1 test_$1 >>failed
+  else
+    echo " Passed."
+  fi
+}
+
+
+
+
+testcase start
+
+# The 2 second sleeps are needed until ninja understands sub-second timestamps
+# https://github.com/martine/ninja/issues/371
+
+# This test affects all bootstrap stages
+sleep 2
+touch ../Blueprints
+testcase all
+
+# This test affects only the primary bootstrap stage
+sleep 2
+touch ../bpmodify/bpmodify.go
+testcase primary
+
+# This test affects nothing, nothing should be done
+sleep 2
+testcase none
+
+# This test will cause the source build.ninja.in to be copied into the first
+# stage.
+sleep 2
+touch ../build.ninja.in
+testcase manifest
+
+# From now on, we're going to be modifying the build.ninja.in, so let's make our
+# own copy
+sleep 2
+../tests/bootstrap.bash -r
+
+sleep 2
+testcase start2
+
+# This is similar to the last test, but incorporates a change into the source
+# build.ninja.in, so that we'll restart into the new version created by the
+# build.
+sleep 2
+echo "# test" >>src.build.ninja.in
+testcase regen
+
+# Add tests to our build by using '-t'
+sleep 2
+../tests/bootstrap.bash -r -t
+
+sleep 2
+testcase start_add_tests
+
+# Make sure that updating a test file causes us to go back to the bootstrap
+# stage
+sleep 2
+touch ../parser/parser_test.go
+testcase rebuild_test
+
+
+
+
+if [ -f failed ]; then
+  cat failed
+  exit 1
+fi