Implement plugins for bootstrap go modules

Now that we have multi-stage bootstrapping, we can make the primary
builder build more dynamic. Add the concept of plugins that will be
linked and loaded into bootstrap_go_binary or bootstrap_go_package
modules. It's expected that the plugin's init() functions will do
whatever registration is necessary.

Example Blueprint definition:

    bootstrap_go_binary {
      name: "builder",
      ...
    }

    bootstrap_go_package {
      name: "plugin1",
      pluginFor: ["builder"],
    }

A package may specify more than one plugin if it will be inserted into
more than one go module.

Change-Id: I109835f444196b66fc4018c3fa36ba0875823184
diff --git a/Blueprints b/Blueprints
index 173017a..70d57db 100644
--- a/Blueprints
+++ b/Blueprints
@@ -128,3 +128,8 @@
     name = "choosestage",
     srcs = ["choosestage/choosestage.go"],
 )
+
+bootstrap_go_binary{
+    name = "loadplugins",
+    srcs = ["loadplugins/loadplugins.go"],
+}
diff --git a/bootstrap/bootstrap.go b/bootstrap/bootstrap.go
index 3cc5672..c9f1b68 100644
--- a/bootstrap/bootstrap.go
+++ b/bootstrap/bootstrap.go
@@ -29,8 +29,9 @@
 var (
 	pctx = blueprint.NewPackageContext("github.com/google/blueprint/bootstrap")
 
-	goTestMainCmd  = pctx.StaticVariable("goTestMainCmd", filepath.Join(bootstrapDir, "bin", "gotestmain"))
-	chooseStageCmd = pctx.StaticVariable("chooseStageCmd", filepath.Join(bootstrapDir, "bin", "choosestage"))
+	goTestMainCmd   = pctx.StaticVariable("goTestMainCmd", filepath.Join(bootstrapDir, "bin", "gotestmain"))
+	chooseStageCmd  = pctx.StaticVariable("chooseStageCmd", filepath.Join(bootstrapDir, "bin", "choosestage"))
+	pluginGenSrcCmd = pctx.StaticVariable("pluginGenSrcCmd", filepath.Join(bootstrapDir, "bin", "loadplugins"))
 
 	compile = pctx.StaticRule("compile",
 		blueprint.RuleParams{
@@ -54,6 +55,13 @@
 		},
 		"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",
@@ -121,6 +129,14 @@
 	})
 }
 
+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
@@ -141,6 +157,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)
@@ -155,9 +185,10 @@
 // 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
@@ -189,6 +220,10 @@
 	}
 }
 
+func (g *goPackage) GoPkgPath() string {
+	return g.properties.PkgPath
+}
+
 func (g *goPackage) GoPkgRoot() string {
 	return g.pkgRoot
 }
@@ -209,8 +244,22 @@
 	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)
@@ -225,6 +274,13 @@
 			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
@@ -233,19 +289,23 @@
 	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)
+			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)
 	}
 }
 
@@ -296,12 +356,22 @@
 		archiveFile = filepath.Join(objDir, name+".a")
 		aoutFile    = filepath.Join(objDir, "a.out")
 		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
@@ -310,12 +380,16 @@
 	if g.config.stage == g.BuildStage() {
 		var deps []string
 
-		if g.config.runGoTests {
-			deps = buildGoTest(ctx, testRoot(ctx), g.testArchiveFile,
-				name, g.properties.Srcs, g.properties.TestSrcs)
+		if hasPlugins && !buildGoPluginLoader(ctx, "main", pluginSrc, g.config.stage) {
+			return
 		}
 
-		buildGoPackage(ctx, objDir, name, archiveFile, g.properties.Srcs, deps)
+		if g.config.runGoTests {
+			deps = buildGoTest(ctx, testRoot(ctx), g.testArchiveFile,
+				name, g.properties.Srcs, genSrcs, g.properties.TestSrcs)
+		}
+
+		buildGoPackage(ctx, objDir, name, archiveFile, g.properties.Srcs, genSrcs, deps)
 
 		var libDirFlags []string
 		ctx.VisitDepsDepthFirstIf(isGoPackageProducer,
@@ -345,19 +419,49 @@
 		})
 	} 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{"$compileCmd"}
@@ -388,9 +492,8 @@
 	})
 }
 
-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
@@ -405,7 +508,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,
@@ -460,7 +563,7 @@
 }
 
 func phonyGoTarget(ctx blueprint.ModuleContext, target string, srcs []string,
-	intermediates []string) {
+	gensrcs []string, intermediates []string) {
 
 	var depTargets []string
 	ctx.VisitDepsDepthFirstIf(isGoPackageProducer,
@@ -472,6 +575,7 @@
 
 	moduleDir := ctx.ModuleDir()
 	srcs = pathtools.PrefixPaths(srcs, filepath.Join("$srcDir", moduleDir))
+	srcs = append(srcs, gensrcs...)
 
 	ctx.Build(pctx, blueprint.BuildParams{
 		Rule:      phony,
@@ -884,3 +988,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/command.go b/bootstrap/command.go
index 63f07ec..cf88666 100644
--- a/bootstrap/command.go
+++ b/bootstrap/command.go
@@ -90,6 +90,7 @@
 		runGoTests:             runGoTests,
 	}
 
+	ctx.RegisterBottomUpMutator("bootstrap_plugin_deps", pluginDeps)
 	ctx.RegisterModuleType("bootstrap_go_package", newGoPackageModuleFactory(bootstrapConfig))
 	ctx.RegisterModuleType("bootstrap_core_go_binary", newGoBinaryModuleFactory(bootstrapConfig, StageBootstrap))
 	ctx.RegisterModuleType("bootstrap_go_binary", newGoBinaryModuleFactory(bootstrapConfig, StagePrimary))
diff --git a/build.ninja.in b/build.ninja.in
index feaad7a..49d808a 100644
--- a/build.ninja.in
+++ b/build.ninja.in
@@ -54,7 +54,7 @@
 # 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 $
@@ -80,7 +80,7 @@
 # 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 $
@@ -107,7 +107,7 @@
 # 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 $
@@ -127,7 +127,7 @@
 # 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 $
@@ -142,7 +142,7 @@
 # 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 $
@@ -159,7 +159,7 @@
 # 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 $
@@ -174,7 +174,7 @@
 # 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 $
@@ -189,7 +189,7 @@
 # Module:  choosestage
 # Variant:
 # Type:    bootstrap_core_go_binary
-# Factory: github.com/google/blueprint/bootstrap.func·003
+# Factory: github.com/google/blueprint/bootstrap.func·005
 # Defined: Blueprints:127:1
 
 build ${g.bootstrap.buildDir}/.bootstrap/choosestage/obj/choosestage.a: $
@@ -211,7 +211,7 @@
 # Module:  gotestmain
 # Variant:
 # Type:    bootstrap_core_go_binary
-# Factory: github.com/google/blueprint/bootstrap.func·003
+# Factory: github.com/google/blueprint/bootstrap.func·005
 # Defined: Blueprints:122:1
 
 build ${g.bootstrap.buildDir}/.bootstrap/gotestmain/obj/gotestmain.a: $
@@ -233,7 +233,7 @@
 # Module:  minibp
 # Variant:
 # Type:    bootstrap_core_go_binary
-# Factory: github.com/google/blueprint/bootstrap.func·003
+# Factory: github.com/google/blueprint/bootstrap.func·005
 # Defined: Blueprints:101:1
 
 build ${g.bootstrap.buildDir}/.bootstrap/minibp/obj/minibp.a: $
@@ -262,7 +262,7 @@
 
 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
 # Singleton: bootstrap
-# Factory:   github.com/google/blueprint/bootstrap.func·008
+# Factory:   github.com/google/blueprint/bootstrap.func·012
 
 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}
diff --git a/context.go b/context.go
index c5e8b15..3892f4e 100644
--- a/context.go
+++ b/context.go
@@ -1077,6 +1077,11 @@
 		return errs
 	}
 
+	errs = c.runMutators(config)
+	if len(errs) > 0 {
+		return errs
+	}
+
 	c.dependenciesReady = true
 	return nil
 }
@@ -1151,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"]
 
@@ -1176,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{
@@ -1196,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 {
 
@@ -1435,11 +1479,6 @@
 		}
 	}
 
-	errs = c.runMutators(config)
-	if len(errs) > 0 {
-		return nil, errs
-	}
-
 	liveGlobals := newLiveTracker(config)
 
 	c.initSpecialVariables()
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)) {