Wrap PackageContext and SingletonContext

Wrap blueprint.PackageContext so that the *Func methods can provide
an android.Config instead of an interface{}.  The modified signatures
means that every method in ModuleContext and SingletonContext
that takes a blueprint.PackageContext now needs to be wrapped to
take an android.PackageContext.

SingletonContext wasn't previously wrapped at all, but as long
as it is, wrap everything like ModuleContext does.  This requires
updating every Singleton to use the android-specific methods.

Test: builds, all Soong tests pass
Change-Id: I4f22085ebca7def6c5cde49e8210b59d994ba625
diff --git a/Android.bp b/Android.bp
index d32c957..886d63d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -56,6 +56,7 @@
         "android/prebuilt.go",
         "android/proto.go",
         "android/register.go",
+        "android/singleton.go",
         "android/testing.go",
         "android/util.go",
         "android/variable.go",
diff --git a/android/androidmk.go b/android/androidmk.go
index 19d5ea6..aff43fa 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -53,13 +53,13 @@
 
 type AndroidMkExtraFunc func(w io.Writer, outputFile Path)
 
-func AndroidMkSingleton() blueprint.Singleton {
+func AndroidMkSingleton() Singleton {
 	return &androidMkSingleton{}
 }
 
 type androidMkSingleton struct{}
 
-func (c *androidMkSingleton) GenerateBuildActions(ctx blueprint.SingletonContext) {
+func (c *androidMkSingleton) GenerateBuildActions(ctx SingletonContext) {
 	config := ctx.Config().(Config)
 
 	if !config.EmbeddedInMake() {
@@ -68,10 +68,8 @@
 
 	var androidMkModulesList []Module
 
-	ctx.VisitAllModules(func(module blueprint.Module) {
-		if amod, ok := module.(Module); ok {
-			androidMkModulesList = append(androidMkModulesList, amod)
-		}
+	ctx.VisitAllModules(func(module Module) {
+		androidMkModulesList = append(androidMkModulesList, module)
 	})
 
 	sort.Sort(AndroidModulesByName{androidMkModulesList, ctx})
@@ -86,14 +84,13 @@
 		ctx.Errorf(err.Error())
 	}
 
-	ctx.Build(pctx, blueprint.BuildParams{
-		Rule:     blueprint.Phony,
-		Outputs:  []string{transMk.String()},
-		Optional: true,
+	ctx.Build(pctx, BuildParams{
+		Rule:   blueprint.Phony,
+		Output: transMk,
 	})
 }
 
-func translateAndroidMk(ctx blueprint.SingletonContext, mkFile string, mods []Module) error {
+func translateAndroidMk(ctx SingletonContext, mkFile string, mods []Module) error {
 	buf := &bytes.Buffer{}
 
 	fmt.Fprintln(buf, "LOCAL_MODULE_MAKEFILE := $(lastword $(MAKEFILE_LIST))")
@@ -145,7 +142,7 @@
 	return ioutil.WriteFile(mkFile, buf.Bytes(), 0666)
 }
 
-func translateAndroidMkModule(ctx blueprint.SingletonContext, w io.Writer, mod blueprint.Module) error {
+func translateAndroidMkModule(ctx SingletonContext, w io.Writer, mod blueprint.Module) error {
 	provider, ok := mod.(AndroidMkDataProvider)
 	if !ok {
 		return nil
diff --git a/android/api_levels.go b/android/api_levels.go
index 2c4ae1a..bdfbc43 100644
--- a/android/api_levels.go
+++ b/android/api_levels.go
@@ -16,22 +16,19 @@
 
 import (
 	"encoding/json"
-	"path/filepath"
-
-	"github.com/google/blueprint"
 )
 
 func init() {
 	RegisterSingletonType("api_levels", ApiLevelsSingleton)
 }
 
-func ApiLevelsSingleton() blueprint.Singleton {
+func ApiLevelsSingleton() Singleton {
 	return &apiLevelsSingleton{}
 }
 
 type apiLevelsSingleton struct{}
 
-func createApiLevelsJson(ctx blueprint.SingletonContext, file string,
+func createApiLevelsJson(ctx SingletonContext, file WritablePath,
 	apiLevelsMap map[string]int) {
 
 	jsonStr, err := json.Marshal(apiLevelsMap)
@@ -39,21 +36,21 @@
 		ctx.Errorf(err.Error())
 	}
 
-	ctx.Build(pctx, blueprint.BuildParams{
+	ctx.Build(pctx, BuildParams{
 		Rule:        WriteFile,
-		Description: "generate " + filepath.Base(file),
-		Outputs:     []string{file},
+		Description: "generate " + file.Base(),
+		Output:      file,
 		Args: map[string]string{
 			"content": string(jsonStr[:]),
 		},
 	})
 }
 
-func GetApiLevelsJson(ctx PathContext) Path {
+func GetApiLevelsJson(ctx PathContext) WritablePath {
 	return PathForOutput(ctx, "api_levels.json")
 }
 
-func (a *apiLevelsSingleton) GenerateBuildActions(ctx blueprint.SingletonContext) {
+func (a *apiLevelsSingleton) GenerateBuildActions(ctx SingletonContext) {
 	baseApiLevel := 9000
 	apiLevelsMap := map[string]int{
 		"G":     9,
@@ -74,5 +71,5 @@
 	}
 
 	apiLevelsJson := GetApiLevelsJson(ctx)
-	createApiLevelsJson(ctx, apiLevelsJson.String(), apiLevelsMap)
+	createApiLevelsJson(ctx, apiLevelsJson, apiLevelsMap)
 }
diff --git a/android/makevars.go b/android/makevars.go
index 024e015..d323613 100644
--- a/android/makevars.go
+++ b/android/makevars.go
@@ -21,7 +21,6 @@
 	"os"
 	"strconv"
 
-	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
 
@@ -66,7 +65,7 @@
 
 type MakeVarsProvider func(ctx MakeVarsContext)
 
-func RegisterMakeVarsProvider(pctx blueprint.PackageContext, provider MakeVarsProvider) {
+func RegisterMakeVarsProvider(pctx PackageContext, provider MakeVarsProvider) {
 	makeVarsProviders = append(makeVarsProviders, makeVarsProvider{pctx, provider})
 }
 
@@ -76,14 +75,14 @@
 	RegisterSingletonType("makevars", makeVarsSingletonFunc)
 }
 
-func makeVarsSingletonFunc() blueprint.Singleton {
+func makeVarsSingletonFunc() Singleton {
 	return &makeVarsSingleton{}
 }
 
 type makeVarsSingleton struct{}
 
 type makeVarsProvider struct {
-	pctx blueprint.PackageContext
+	pctx PackageContext
 	call MakeVarsProvider
 }
 
@@ -91,8 +90,8 @@
 
 type makeVarsContext struct {
 	config Config
-	ctx    blueprint.SingletonContext
-	pctx   blueprint.PackageContext
+	ctx    SingletonContext
+	pctx   PackageContext
 	vars   []makeVarsVariable
 }
 
@@ -105,7 +104,7 @@
 	strict bool
 }
 
-func (s *makeVarsSingleton) GenerateBuildActions(ctx blueprint.SingletonContext) {
+func (s *makeVarsSingleton) GenerateBuildActions(ctx SingletonContext) {
 	config := ctx.Config().(Config)
 
 	if !config.EmbeddedInMake() {
diff --git a/android/module.go b/android/module.go
index 66859fa..0eb4820 100644
--- a/android/module.go
+++ b/android/module.go
@@ -17,6 +17,7 @@
 import (
 	"fmt"
 	"path/filepath"
+	"sort"
 	"strings"
 
 	"github.com/google/blueprint"
@@ -78,7 +79,7 @@
 	blueprint.BaseModuleContext
 
 	// Deprecated: use ModuleContext.Build instead.
-	ModuleBuild(pctx blueprint.PackageContext, params ModuleBuildParams)
+	ModuleBuild(pctx PackageContext, params ModuleBuildParams)
 
 	ExpandSources(srcFiles, excludes []string) Paths
 	ExpandSourcesSubDir(srcFiles, excludes []string, subDir string) Paths
@@ -115,15 +116,15 @@
 	VisitDepsDepthFirstIf(pred func(Module) bool, visit func(Module))
 	WalkDeps(visit func(Module, Module) bool)
 
-	Variable(pctx blueprint.PackageContext, name, value string)
-	Rule(pctx blueprint.PackageContext, name string, params blueprint.RuleParams, argNames ...string) blueprint.Rule
+	Variable(pctx PackageContext, name, value string)
+	Rule(pctx PackageContext, name string, params blueprint.RuleParams, argNames ...string) blueprint.Rule
 	// Similar to blueprint.ModuleContext.Build, but takes Paths instead of []string,
 	// and performs more verification.
-	Build(pctx blueprint.PackageContext, params BuildParams)
+	Build(pctx PackageContext, params BuildParams)
 
-	PrimaryModule() blueprint.Module
-	FinalModule() blueprint.Module
-	VisitAllModuleVariants(visit func(blueprint.Module))
+	PrimaryModule() Module
+	FinalModule() Module
+	VisitAllModuleVariants(visit func(Module))
 
 	GetMissingDependencies() []string
 }
@@ -325,8 +326,8 @@
 
 	// Used by buildTargetSingleton to create checkbuild and per-directory build targets
 	// Only set on the final variant of each module
-	installTarget    string
-	checkbuildTarget string
+	installTarget    WritablePath
+	checkbuildTarget WritablePath
 	blueprintDir     string
 
 	hooks hooks
@@ -464,36 +465,35 @@
 	return false
 }
 
-func (a *ModuleBase) generateModuleTarget(ctx blueprint.ModuleContext) {
+func (a *ModuleBase) generateModuleTarget(ctx ModuleContext) {
 	allInstalledFiles := Paths{}
 	allCheckbuildFiles := Paths{}
-	ctx.VisitAllModuleVariants(func(module blueprint.Module) {
-		a := module.(Module).base()
+	ctx.VisitAllModuleVariants(func(module Module) {
+		a := module.base()
 		allInstalledFiles = append(allInstalledFiles, a.installFiles...)
 		allCheckbuildFiles = append(allCheckbuildFiles, a.checkbuildFiles...)
 	})
 
-	deps := []string{}
+	var deps Paths
 
 	if len(allInstalledFiles) > 0 {
-		name := ctx.ModuleName() + "-install"
-		ctx.Build(pctx, blueprint.BuildParams{
+		name := PathForPhony(ctx, ctx.ModuleName()+"-install")
+		ctx.Build(pctx, BuildParams{
 			Rule:      blueprint.Phony,
-			Outputs:   []string{name},
-			Implicits: allInstalledFiles.Strings(),
-			Optional:  ctx.Config().(Config).EmbeddedInMake(),
+			Output:    name,
+			Implicits: allInstalledFiles,
+			Default:   !ctx.Config().(Config).EmbeddedInMake(),
 		})
 		deps = append(deps, name)
 		a.installTarget = name
 	}
 
 	if len(allCheckbuildFiles) > 0 {
-		name := ctx.ModuleName() + "-checkbuild"
-		ctx.Build(pctx, blueprint.BuildParams{
+		name := PathForPhony(ctx, ctx.ModuleName()+"-checkbuild")
+		ctx.Build(pctx, BuildParams{
 			Rule:      blueprint.Phony,
-			Outputs:   []string{name},
-			Implicits: allCheckbuildFiles.Strings(),
-			Optional:  true,
+			Output:    name,
+			Implicits: allCheckbuildFiles,
 		})
 		deps = append(deps, name)
 		a.checkbuildTarget = name
@@ -505,11 +505,10 @@
 			suffix = "-soong"
 		}
 
-		ctx.Build(pctx, blueprint.BuildParams{
+		ctx.Build(pctx, BuildParams{
 			Rule:      blueprint.Phony,
-			Outputs:   []string{ctx.ModuleName() + suffix},
+			Output:    PathForPhony(ctx, ctx.ModuleName()+suffix),
 			Implicits: deps,
-			Optional:  true,
 		})
 
 		a.blueprintDir = ctx.ModuleDir()
@@ -525,23 +524,23 @@
 	}
 }
 
-func (a *ModuleBase) GenerateBuildActions(ctx blueprint.ModuleContext) {
-	androidCtx := &androidModuleContext{
+func (a *ModuleBase) GenerateBuildActions(blueprintCtx blueprint.ModuleContext) {
+	ctx := &androidModuleContext{
 		module:                 a.module,
-		ModuleContext:          ctx,
-		androidBaseContextImpl: a.androidBaseContextFactory(ctx),
-		installDeps:            a.computeInstallDeps(ctx),
+		ModuleContext:          blueprintCtx,
+		androidBaseContextImpl: a.androidBaseContextFactory(blueprintCtx),
+		installDeps:            a.computeInstallDeps(blueprintCtx),
 		installFiles:           a.installFiles,
-		missingDeps:            ctx.GetMissingDependencies(),
+		missingDeps:            blueprintCtx.GetMissingDependencies(),
 	}
 
 	desc := "//" + ctx.ModuleDir() + ":" + ctx.ModuleName() + " "
 	var suffix []string
-	if androidCtx.Os().Class != Device && androidCtx.Os().Class != Generic {
-		suffix = append(suffix, androidCtx.Os().String())
+	if ctx.Os().Class != Device && ctx.Os().Class != Generic {
+		suffix = append(suffix, ctx.Os().String())
 	}
-	if !androidCtx.PrimaryArch() {
-		suffix = append(suffix, androidCtx.Arch().ArchType.String())
+	if !ctx.PrimaryArch() {
+		suffix = append(suffix, ctx.Arch().ArchType.String())
 	}
 
 	ctx.Variable(pctx, "moduleDesc", desc)
@@ -553,13 +552,13 @@
 	ctx.Variable(pctx, "moduleDescSuffix", s)
 
 	if a.Enabled() {
-		a.module.GenerateAndroidBuildActions(androidCtx)
+		a.module.GenerateAndroidBuildActions(ctx)
 		if ctx.Failed() {
 			return
 		}
 
-		a.installFiles = append(a.installFiles, androidCtx.installFiles...)
-		a.checkbuildFiles = append(a.checkbuildFiles, androidCtx.checkbuildFiles...)
+		a.installFiles = append(a.installFiles, ctx.installFiles...)
+		a.checkbuildFiles = append(a.checkbuildFiles, ctx.checkbuildFiles...)
 	}
 
 	if a == ctx.FinalModule().(Module).base() {
@@ -569,7 +568,7 @@
 		}
 	}
 
-	a.buildParams = androidCtx.buildParams
+	a.buildParams = ctx.buildParams
 }
 
 type androidBaseContextImpl struct {
@@ -594,7 +593,7 @@
 }
 
 func (a *androidModuleContext) ninjaError(desc string, outputs []string, err error) {
-	a.ModuleContext.Build(pctx, blueprint.BuildParams{
+	a.ModuleContext.Build(pctx.PackageContext, blueprint.BuildParams{
 		Rule:        ErrorRule,
 		Description: desc,
 		Outputs:     outputs,
@@ -606,17 +605,14 @@
 	return
 }
 
-func (a *androidModuleContext) ModuleBuild(pctx blueprint.PackageContext, params ModuleBuildParams) {
+func (a *androidModuleContext) ModuleBuild(pctx PackageContext, params ModuleBuildParams) {
 	a.Build(pctx, BuildParams(params))
 }
 
-func (a *androidModuleContext) Build(pctx blueprint.PackageContext, params BuildParams) {
-	if a.config.captureBuild {
-		a.buildParams = append(a.buildParams, params)
-	}
-
+func convertBuildParams(params BuildParams) blueprint.BuildParams {
 	bparams := blueprint.BuildParams{
 		Rule:            params.Rule,
+		Description:     params.Description,
 		Deps:            params.Deps,
 		Outputs:         params.Outputs.Strings(),
 		ImplicitOutputs: params.ImplicitOutputs.Strings(),
@@ -627,10 +623,6 @@
 		Optional:        !params.Default,
 	}
 
-	if params.Description != "" {
-		bparams.Description = "${moduleDesc}" + params.Description + "${moduleDescSuffix}"
-	}
-
 	if params.Depfile != nil {
 		bparams.Depfile = params.Depfile.String()
 	}
@@ -647,6 +639,30 @@
 		bparams.Implicits = append(bparams.Implicits, params.Implicit.String())
 	}
 
+	return bparams
+}
+
+func (a *androidModuleContext) Variable(pctx PackageContext, name, value string) {
+	a.ModuleContext.Variable(pctx.PackageContext, name, value)
+}
+
+func (a *androidModuleContext) Rule(pctx PackageContext, name string, params blueprint.RuleParams,
+	argNames ...string) blueprint.Rule {
+
+	return a.ModuleContext.Rule(pctx.PackageContext, name, params, argNames...)
+}
+
+func (a *androidModuleContext) Build(pctx PackageContext, params BuildParams) {
+	if a.config.captureBuild {
+		a.buildParams = append(a.buildParams, params)
+	}
+
+	bparams := convertBuildParams(params)
+
+	if bparams.Description != "" {
+		bparams.Description = "${moduleDesc}" + params.Description + "${moduleDescSuffix}"
+	}
+
 	if a.missingDeps != nil {
 		a.ninjaError(bparams.Description, bparams.Outputs,
 			fmt.Errorf("module %s missing dependencies: %s\n",
@@ -654,7 +670,7 @@
 		return
 	}
 
-	a.ModuleContext.Build(pctx, bparams)
+	a.ModuleContext.Build(pctx.PackageContext, bparams)
 }
 
 func (a *androidModuleContext) GetMissingDependencies() []string {
@@ -751,6 +767,20 @@
 	})
 }
 
+func (a *androidModuleContext) VisitAllModuleVariants(visit func(Module)) {
+	a.ModuleContext.VisitAllModuleVariants(func(module blueprint.Module) {
+		visit(module.(Module))
+	})
+}
+
+func (a *androidModuleContext) PrimaryModule() Module {
+	return a.ModuleContext.PrimaryModule().(Module)
+}
+
+func (a *androidModuleContext) FinalModule() Module {
+	return a.ModuleContext.FinalModule().(Module)
+}
+
 func (a *androidBaseContextImpl) Target() Target {
 	return a.target
 }
@@ -1027,7 +1057,7 @@
 	RegisterSingletonType("buildtarget", BuildTargetSingleton)
 }
 
-func BuildTargetSingleton() blueprint.Singleton {
+func BuildTargetSingleton() Singleton {
 	return &buildTargetSingleton{}
 }
 
@@ -1038,29 +1068,28 @@
 
 type buildTargetSingleton struct{}
 
-func (c *buildTargetSingleton) GenerateBuildActions(ctx blueprint.SingletonContext) {
-	checkbuildDeps := []string{}
+func (c *buildTargetSingleton) GenerateBuildActions(ctx SingletonContext) {
+	var checkbuildDeps Paths
 
-	mmTarget := func(dir string) string {
-		return "MODULES-IN-" + strings.Replace(filepath.Clean(dir), "/", "-", -1)
+	mmTarget := func(dir string) WritablePath {
+		return PathForPhony(ctx,
+			"MODULES-IN-"+strings.Replace(filepath.Clean(dir), "/", "-", -1))
 	}
 
-	modulesInDir := make(map[string][]string)
+	modulesInDir := make(map[string]Paths)
 
-	ctx.VisitAllModules(func(module blueprint.Module) {
-		if a, ok := module.(Module); ok {
-			blueprintDir := a.base().blueprintDir
-			installTarget := a.base().installTarget
-			checkbuildTarget := a.base().checkbuildTarget
+	ctx.VisitAllModules(func(module Module) {
+		blueprintDir := module.base().blueprintDir
+		installTarget := module.base().installTarget
+		checkbuildTarget := module.base().checkbuildTarget
 
-			if checkbuildTarget != "" {
-				checkbuildDeps = append(checkbuildDeps, checkbuildTarget)
-				modulesInDir[blueprintDir] = append(modulesInDir[blueprintDir], checkbuildTarget)
-			}
+		if checkbuildTarget != nil {
+			checkbuildDeps = append(checkbuildDeps, checkbuildTarget)
+			modulesInDir[blueprintDir] = append(modulesInDir[blueprintDir], checkbuildTarget)
+		}
 
-			if installTarget != "" {
-				modulesInDir[blueprintDir] = append(modulesInDir[blueprintDir], installTarget)
-			}
+		if installTarget != nil {
+			modulesInDir[blueprintDir] = append(modulesInDir[blueprintDir], installTarget)
 		}
 	})
 
@@ -1070,11 +1099,10 @@
 	}
 
 	// Create a top-level checkbuild target that depends on all modules
-	ctx.Build(pctx, blueprint.BuildParams{
+	ctx.Build(pctx, BuildParams{
 		Rule:      blueprint.Phony,
-		Outputs:   []string{"checkbuild" + suffix},
+		Output:    PathForPhony(ctx, "checkbuild"+suffix),
 		Implicits: checkbuildDeps,
-		Optional:  true,
 	})
 
 	// Make will generate the MODULES-IN-* targets
@@ -1082,6 +1110,15 @@
 		return
 	}
 
+	sortedKeys := func(m map[string]Paths) []string {
+		s := make([]string, 0, len(m))
+		for k := range m {
+			s = append(s, k)
+		}
+		sort.Strings(s)
+		return s
+	}
+
 	// Ensure ancestor directories are in modulesInDir
 	dirs := sortedKeys(modulesInDir)
 	for _, dir := range dirs {
@@ -1108,28 +1145,26 @@
 	// depends on the MODULES-IN-* targets of all of its subdirectories that contain Android.bp
 	// files.
 	for _, dir := range dirs {
-		ctx.Build(pctx, blueprint.BuildParams{
+		ctx.Build(pctx, BuildParams{
 			Rule:      blueprint.Phony,
-			Outputs:   []string{mmTarget(dir)},
+			Output:    mmTarget(dir),
 			Implicits: modulesInDir[dir],
 			// HACK: checkbuild should be an optional build, but force it
 			// enabled for now in standalone builds
-			Optional: ctx.Config().(Config).EmbeddedInMake(),
+			Default: !ctx.Config().(Config).EmbeddedInMake(),
 		})
 	}
 
 	// Create (host|host-cross|target)-<OS> phony rules to build a reduced checkbuild.
 	osDeps := map[OsType]Paths{}
-	ctx.VisitAllModules(func(module blueprint.Module) {
-		if a, ok := module.(Module); ok {
-			if a.Enabled() {
-				os := a.Target().Os
-				osDeps[os] = append(osDeps[os], a.base().checkbuildFiles...)
-			}
+	ctx.VisitAllModules(func(module Module) {
+		if module.Enabled() {
+			os := module.Target().Os
+			osDeps[os] = append(osDeps[os], module.base().checkbuildFiles...)
 		}
 	})
 
-	osClass := make(map[string][]string)
+	osClass := make(map[string]Paths)
 	for os, deps := range osDeps {
 		var className string
 
@@ -1144,25 +1179,23 @@
 			continue
 		}
 
-		name := className + "-" + os.Name
+		name := PathForPhony(ctx, className+"-"+os.Name)
 		osClass[className] = append(osClass[className], name)
 
-		ctx.Build(pctx, blueprint.BuildParams{
+		ctx.Build(pctx, BuildParams{
 			Rule:      blueprint.Phony,
-			Outputs:   []string{name},
-			Implicits: deps.Strings(),
-			Optional:  true,
+			Output:    name,
+			Implicits: deps,
 		})
 	}
 
 	// Wrap those into host|host-cross|target phony rules
 	osClasses := sortedKeys(osClass)
 	for _, class := range osClasses {
-		ctx.Build(pctx, blueprint.BuildParams{
+		ctx.Build(pctx, BuildParams{
 			Rule:      blueprint.Phony,
-			Outputs:   []string{class},
+			Output:    PathForPhony(ctx, class),
 			Implicits: osClass[class],
-			Optional:  true,
 		})
 	}
 }
diff --git a/android/package_ctx.go b/android/package_ctx.go
index d32e82b..1626f76 100644
--- a/android/package_ctx.go
+++ b/android/package_ctx.go
@@ -22,14 +22,14 @@
 	"github.com/google/blueprint/pathtools"
 )
 
-// AndroidPackageContext is a wrapper for blueprint.PackageContext that adds
+// PackageContext is a wrapper for blueprint.PackageContext that adds
 // some android-specific helper functions.
-type AndroidPackageContext struct {
+type PackageContext struct {
 	blueprint.PackageContext
 }
 
-func NewPackageContext(pkgPath string) AndroidPackageContext {
-	return AndroidPackageContext{blueprint.NewPackageContext(pkgPath)}
+func NewPackageContext(pkgPath string) PackageContext {
+	return PackageContext{blueprint.NewPackageContext(pkgPath)}
 }
 
 // configErrorWrapper can be used with Path functions when a Context is not
@@ -39,7 +39,7 @@
 // The most common use here will be with VariableFunc, where only a config is
 // provided, and an error should be returned.
 type configErrorWrapper struct {
-	pctx   AndroidPackageContext
+	pctx   PackageContext
 	config Config
 	errors []error
 }
@@ -61,13 +61,43 @@
 	return nil
 }
 
+// VariableFunc wraps blueprint.PackageContext.VariableFunc, converting the interface{} config
+// argument to a Config.
+func (p PackageContext) VariableFunc(name string,
+	f func(Config) (string, error)) blueprint.Variable {
+
+	return p.PackageContext.VariableFunc(name, func(config interface{}) (string, error) {
+		return f(config.(Config))
+	})
+}
+
+// PoolFunc wraps blueprint.PackageContext.PoolFunc, converting the interface{} config
+// argument to a Config.
+func (p PackageContext) PoolFunc(name string,
+	f func(Config) (blueprint.PoolParams, error)) blueprint.Pool {
+
+	return p.PackageContext.PoolFunc(name, func(config interface{}) (blueprint.PoolParams, error) {
+		return f(config.(Config))
+	})
+}
+
+// RuleFunc wraps blueprint.PackageContext.RuleFunc, converting the interface{} config
+// argument to a Config.
+func (p PackageContext) RuleFunc(name string,
+	f func(Config) (blueprint.RuleParams, error), argNames ...string) blueprint.Rule {
+
+	return p.PackageContext.RuleFunc(name, func(config interface{}) (blueprint.RuleParams, error) {
+		return f(config.(Config))
+	}, argNames...)
+}
+
 // SourcePathVariable returns a Variable whose value is the source directory
 // appended with the supplied path. It may only be called during a Go package's
 // initialization - either from the init() function or as part of a
 // package-scoped variable's initialization.
-func (p AndroidPackageContext) SourcePathVariable(name, path string) blueprint.Variable {
-	return p.VariableFunc(name, func(config interface{}) (string, error) {
-		ctx := &configErrorWrapper{p, config.(Config), []error{}}
+func (p PackageContext) SourcePathVariable(name, path string) blueprint.Variable {
+	return p.VariableFunc(name, func(config Config) (string, error) {
+		ctx := &configErrorWrapper{p, config, []error{}}
 		p := safePathForSource(ctx, path)
 		if len(ctx.errors) > 0 {
 			return "", ctx.errors[0]
@@ -80,9 +110,9 @@
 // appended with the supplied paths, joined with separator. It may only be
 // called during a Go package's initialization - either from the init()
 // function or as part of a package-scoped variable's initialization.
-func (p AndroidPackageContext) SourcePathsVariable(name, separator string, paths ...string) blueprint.Variable {
-	return p.VariableFunc(name, func(config interface{}) (string, error) {
-		ctx := &configErrorWrapper{p, config.(Config), []error{}}
+func (p PackageContext) SourcePathsVariable(name, separator string, paths ...string) blueprint.Variable {
+	return p.VariableFunc(name, func(config Config) (string, error) {
+		ctx := &configErrorWrapper{p, config, []error{}}
 		var ret []string
 		for _, path := range paths {
 			p := safePathForSource(ctx, path)
@@ -100,14 +130,14 @@
 // The environment variable is not required to point to a path inside the source tree.
 // It may only be called during a Go package's initialization - either from the init() function or
 // as part of a package-scoped variable's initialization.
-func (p AndroidPackageContext) SourcePathVariableWithEnvOverride(name, path, env string) blueprint.Variable {
-	return p.VariableFunc(name, func(config interface{}) (string, error) {
-		ctx := &configErrorWrapper{p, config.(Config), []error{}}
+func (p PackageContext) SourcePathVariableWithEnvOverride(name, path, env string) blueprint.Variable {
+	return p.VariableFunc(name, func(config Config) (string, error) {
+		ctx := &configErrorWrapper{p, config, []error{}}
 		p := safePathForSource(ctx, path)
 		if len(ctx.errors) > 0 {
 			return "", ctx.errors[0]
 		}
-		return config.(Config).GetenvWithDefault(env, p.String()), nil
+		return config.GetenvWithDefault(env, p.String()), nil
 	})
 }
 
@@ -115,8 +145,8 @@
 // in the bin directory for host targets. It may only be called during a Go
 // package's initialization - either from the init() function or as part of a
 // package-scoped variable's initialization.
-func (p AndroidPackageContext) HostBinToolVariable(name, path string) blueprint.Variable {
-	return p.VariableFunc(name, func(config interface{}) (string, error) {
+func (p PackageContext) HostBinToolVariable(name, path string) blueprint.Variable {
+	return p.VariableFunc(name, func(config Config) (string, error) {
 		po, err := p.HostBinToolPath(config, path)
 		if err != nil {
 			return "", err
@@ -125,8 +155,8 @@
 	})
 }
 
-func (p AndroidPackageContext) HostBinToolPath(config interface{}, path string) (Path, error) {
-	ctx := &configErrorWrapper{p, config.(Config), []error{}}
+func (p PackageContext) HostBinToolPath(config Config, path string) (Path, error) {
+	ctx := &configErrorWrapper{p, config, []error{}}
 	pa := PathForOutput(ctx, "host", ctx.config.PrebuiltOS(), "bin", path)
 	if len(ctx.errors) > 0 {
 		return nil, ctx.errors[0]
@@ -138,9 +168,9 @@
 // tool in the frameworks directory for host targets. It may only be called
 // during a Go package's initialization - either from the init() function or as
 // part of a package-scoped variable's initialization.
-func (p AndroidPackageContext) HostJavaToolVariable(name, path string) blueprint.Variable {
-	return p.VariableFunc(name, func(config interface{}) (string, error) {
-		ctx := &configErrorWrapper{p, config.(Config), []error{}}
+func (p PackageContext) HostJavaToolVariable(name, path string) blueprint.Variable {
+	return p.VariableFunc(name, func(config Config) (string, error) {
+		ctx := &configErrorWrapper{p, config, []error{}}
 		p := PathForOutput(ctx, "host", ctx.config.PrebuiltOS(), "framework", path)
 		if len(ctx.errors) > 0 {
 			return "", ctx.errors[0]
@@ -149,8 +179,8 @@
 	})
 }
 
-func (p AndroidPackageContext) HostJavaToolPath(config interface{}, path string) (Path, error) {
-	ctx := &configErrorWrapper{p, config.(Config), []error{}}
+func (p PackageContext) HostJavaToolPath(config Config, path string) (Path, error) {
+	ctx := &configErrorWrapper{p, config, []error{}}
 	pa := PathForOutput(ctx, "host", ctx.config.PrebuiltOS(), "framework", path)
 	if len(ctx.errors) > 0 {
 		return nil, ctx.errors[0]
@@ -162,9 +192,9 @@
 // directory appended with the supplied path. It may only be called during a Go
 // package's initialization - either from the init() function or as part of a
 // package-scoped variable's initialization.
-func (p AndroidPackageContext) IntermediatesPathVariable(name, path string) blueprint.Variable {
-	return p.VariableFunc(name, func(config interface{}) (string, error) {
-		ctx := &configErrorWrapper{p, config.(Config), []error{}}
+func (p PackageContext) IntermediatesPathVariable(name, path string) blueprint.Variable {
+	return p.VariableFunc(name, func(config Config) (string, error) {
+		ctx := &configErrorWrapper{p, config, []error{}}
 		p := PathForIntermediates(ctx, path)
 		if len(ctx.errors) > 0 {
 			return "", ctx.errors[0]
@@ -177,11 +207,11 @@
 // list of present source paths prefixed with the supplied prefix. It may only
 // be called during a Go package's initialization - either from the init()
 // function or as part of a package-scoped variable's initialization.
-func (p AndroidPackageContext) PrefixedExistentPathsForSourcesVariable(
+func (p PackageContext) PrefixedExistentPathsForSourcesVariable(
 	name, prefix string, paths []string) blueprint.Variable {
 
-	return p.VariableFunc(name, func(config interface{}) (string, error) {
-		ctx := &configErrorWrapper{p, config.(Config), []error{}}
+	return p.VariableFunc(name, func(config Config) (string, error) {
+		ctx := &configErrorWrapper{p, config, []error{}}
 		paths := ExistentPathsForSources(ctx, "", paths)
 		if len(ctx.errors) > 0 {
 			return "", ctx.errors[0]
@@ -196,7 +226,7 @@
 }
 
 // AndroidStaticRule wraps blueprint.StaticRule and provides a default Pool if none is specified
-func (p AndroidPackageContext) AndroidStaticRule(name string, params blueprint.RuleParams,
+func (p PackageContext) AndroidStaticRule(name string, params blueprint.RuleParams,
 	argNames ...string) blueprint.Rule {
 	return p.AndroidRuleFunc(name, func(Config) (blueprint.RuleParams, error) {
 		return params, nil
@@ -204,16 +234,16 @@
 }
 
 // AndroidGomaStaticRule wraps blueprint.StaticRule but uses goma's parallelism if goma is enabled
-func (p AndroidPackageContext) AndroidGomaStaticRule(name string, params blueprint.RuleParams,
+func (p PackageContext) AndroidGomaStaticRule(name string, params blueprint.RuleParams,
 	argNames ...string) blueprint.Rule {
 	return p.StaticRule(name, params, argNames...)
 }
 
-func (p AndroidPackageContext) AndroidRuleFunc(name string,
+func (p PackageContext) AndroidRuleFunc(name string,
 	f func(Config) (blueprint.RuleParams, error), argNames ...string) blueprint.Rule {
-	return p.PackageContext.RuleFunc(name, func(config interface{}) (blueprint.RuleParams, error) {
-		params, err := f(config.(Config))
-		if config.(Config).UseGoma() && params.Pool == nil {
+	return p.RuleFunc(name, func(config Config) (blueprint.RuleParams, error) {
+		params, err := f(config)
+		if config.UseGoma() && params.Pool == nil {
 			// When USE_GOMA=true is set and the rule is not supported by goma, restrict jobs to the
 			// local parallelism value
 			params.Pool = localPool
diff --git a/android/paths.go b/android/paths.go
index 4adaa2d..b2ee627 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -449,6 +449,10 @@
 	return p.path
 }
 
+func (p basePath) String() string {
+	return p.path
+}
+
 // SourcePath is a Path representing a file path rooted from SrcDir
 type SourcePath struct {
 	basePath
@@ -885,6 +889,13 @@
 	return validateSafePath(ctx, pathComponents...)
 }
 
+func PathForPhony(ctx PathContext, phony string) WritablePath {
+	if strings.ContainsAny(phony, "$/") {
+		reportPathError(ctx, "Phony target contains invalid character ($ or /): %s", phony)
+	}
+	return OutputPath{basePath{phony, pathConfig(ctx), ""}}
+}
+
 type testPath struct {
 	basePath
 }
diff --git a/android/register.go b/android/register.go
index 81a266d..78ae481 100644
--- a/android/register.go
+++ b/android/register.go
@@ -44,7 +44,7 @@
 
 type ModuleFactory func() Module
 
-// ModuleFactoryAdaptor Wraps a ModuleFactory into a blueprint.ModuleFactory by converting an Module
+// ModuleFactoryAdaptor wraps a ModuleFactory into a blueprint.ModuleFactory by converting a Module
 // into a blueprint.Module and a list of property structs
 func ModuleFactoryAdaptor(factory ModuleFactory) blueprint.ModuleFactory {
 	return func() (blueprint.Module, []interface{}) {
@@ -53,16 +53,27 @@
 	}
 }
 
+type SingletonFactory func() Singleton
+
+// SingletonFactoryAdaptor wraps a SingletonFactory into a blueprint.SingletonFactory by converting
+// a Singleton into a blueprint.Singleton
+func SingletonFactoryAdaptor(factory SingletonFactory) blueprint.SingletonFactory {
+	return func() blueprint.Singleton {
+		singleton := factory()
+		return singletonAdaptor{singleton}
+	}
+}
+
 func RegisterModuleType(name string, factory ModuleFactory) {
 	moduleTypes = append(moduleTypes, moduleType{name, ModuleFactoryAdaptor(factory)})
 }
 
-func RegisterSingletonType(name string, factory blueprint.SingletonFactory) {
-	singletons = append(singletons, singleton{name, factory})
+func RegisterSingletonType(name string, factory SingletonFactory) {
+	singletons = append(singletons, singleton{name, SingletonFactoryAdaptor(factory)})
 }
 
-func RegisterPreSingletonType(name string, factory blueprint.SingletonFactory) {
-	preSingletons = append(preSingletons, singleton{name, factory})
+func RegisterPreSingletonType(name string, factory SingletonFactory) {
+	preSingletons = append(preSingletons, singleton{name, SingletonFactoryAdaptor(factory)})
 }
 
 type Context struct {
diff --git a/android/singleton.go b/android/singleton.go
new file mode 100644
index 0000000..f2f575f
--- /dev/null
+++ b/android/singleton.go
@@ -0,0 +1,162 @@
+// Copyright 2017 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 android
+
+import (
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/pathtools"
+)
+
+// SingletonContext
+type SingletonContext interface {
+	// TODO(ccross): make this return an android.Config
+	Config() interface{}
+
+	ModuleName(module blueprint.Module) string
+	ModuleDir(module blueprint.Module) string
+	ModuleSubDir(module blueprint.Module) string
+	ModuleType(module blueprint.Module) string
+	BlueprintFile(module blueprint.Module) string
+
+	ModuleErrorf(module blueprint.Module, format string, args ...interface{})
+	Errorf(format string, args ...interface{})
+	Failed() bool
+
+	Variable(pctx PackageContext, name, value string)
+	Rule(pctx PackageContext, name string, params RuleParams, argNames ...string) blueprint.Rule
+	Build(pctx PackageContext, params BuildParams)
+	RequireNinjaVersion(major, minor, micro int)
+
+	// SetNinjaBuildDir sets the value of the top-level "builddir" Ninja variable
+	// that controls where Ninja stores its build log files.  This value can be
+	// set at most one time for a single build, later calls are ignored.
+	SetNinjaBuildDir(pctx PackageContext, value string)
+
+	// Eval takes a string with embedded ninja variables, and returns a string
+	// with all of the variables recursively expanded. Any variables references
+	// are expanded in the scope of the PackageContext.
+	Eval(pctx PackageContext, ninjaStr string) (string, error)
+
+	VisitAllModules(visit func(Module))
+	VisitAllModulesIf(pred func(Module) bool, visit func(Module))
+	VisitDepsDepthFirst(module Module, visit func(Module))
+	VisitDepsDepthFirstIf(module Module, pred func(Module) bool,
+		visit func(Module))
+
+	VisitAllModuleVariants(module Module, visit func(Module))
+
+	PrimaryModule(module Module) Module
+	FinalModule(module Module) Module
+
+	AddNinjaFileDeps(deps ...string)
+
+	// GlobWithDeps returns a list of files that match the specified pattern but do not match any
+	// of the patterns in excludes.  It also adds efficient dependencies to rerun the primary
+	// builder whenever a file matching the pattern as added or removed, without rerunning if a
+	// file that does not match the pattern is added to a searched directory.
+	GlobWithDeps(pattern string, excludes []string) ([]string, error)
+
+	Fs() pathtools.FileSystem
+}
+
+type singletonAdaptor struct {
+	Singleton
+}
+
+func (s singletonAdaptor) GenerateBuildActions(ctx blueprint.SingletonContext) {
+	s.Singleton.GenerateBuildActions(singletonContextAdaptor{ctx})
+}
+
+type Singleton interface {
+	GenerateBuildActions(SingletonContext)
+}
+
+type singletonContextAdaptor struct {
+	blueprint.SingletonContext
+}
+
+func (s singletonContextAdaptor) Variable(pctx PackageContext, name, value string) {
+	s.SingletonContext.Variable(pctx.PackageContext, name, value)
+}
+
+func (s singletonContextAdaptor) Rule(pctx PackageContext, name string, params RuleParams, argNames ...string) blueprint.Rule {
+	return s.SingletonContext.Rule(pctx.PackageContext, name, params.RuleParams, argNames...)
+}
+
+func (s singletonContextAdaptor) Build(pctx PackageContext, params BuildParams) {
+	bparams := convertBuildParams(params)
+	s.SingletonContext.Build(pctx.PackageContext, bparams)
+
+}
+
+func (s singletonContextAdaptor) SetNinjaBuildDir(pctx PackageContext, value string) {
+	s.SingletonContext.SetNinjaBuildDir(pctx.PackageContext, value)
+}
+
+func (s singletonContextAdaptor) Eval(pctx PackageContext, ninjaStr string) (string, error) {
+	return s.SingletonContext.Eval(pctx.PackageContext, ninjaStr)
+}
+
+// visitAdaptor wraps a visit function that takes an android.Module parameter into
+// a function that takes an blueprint.Module parameter and only calls the visit function if the
+// blueprint.Module is an android.Module.
+func visitAdaptor(visit func(Module)) func(blueprint.Module) {
+	return func(module blueprint.Module) {
+		if aModule, ok := module.(Module); ok {
+			visit(aModule)
+		}
+	}
+}
+
+// predAdaptor wraps a pred function that takes an android.Module parameter
+// into a function that takes an blueprint.Module parameter and only calls the visit function if the
+// blueprint.Module is an android.Module, otherwise returns false.
+func predAdaptor(pred func(Module) bool) func(blueprint.Module) bool {
+	return func(module blueprint.Module) bool {
+		if aModule, ok := module.(Module); ok {
+			return pred(aModule)
+		} else {
+			return false
+		}
+	}
+}
+
+func (s singletonContextAdaptor) VisitAllModules(visit func(Module)) {
+	s.SingletonContext.VisitAllModules(visitAdaptor(visit))
+}
+
+func (s singletonContextAdaptor) VisitAllModulesIf(pred func(Module) bool, visit func(Module)) {
+	s.SingletonContext.VisitAllModulesIf(predAdaptor(pred), visitAdaptor(visit))
+}
+
+func (s singletonContextAdaptor) VisitDepsDepthFirst(module Module, visit func(Module)) {
+	s.SingletonContext.VisitDepsDepthFirst(module, visitAdaptor(visit))
+}
+
+func (s singletonContextAdaptor) VisitDepsDepthFirstIf(module Module, pred func(Module) bool, visit func(Module)) {
+	s.SingletonContext.VisitDepsDepthFirstIf(module, predAdaptor(pred), visitAdaptor(visit))
+}
+
+func (s singletonContextAdaptor) VisitAllModuleVariants(module Module, visit func(Module)) {
+	s.SingletonContext.VisitAllModuleVariants(module, visitAdaptor(visit))
+}
+
+func (s singletonContextAdaptor) PrimaryModule(module Module) Module {
+	return s.SingletonContext.PrimaryModule(module).(Module)
+}
+
+func (s singletonContextAdaptor) FinalModule(module Module) Module {
+	return s.SingletonContext.FinalModule(module).(Module)
+}
diff --git a/cc/androidmk.go b/cc/androidmk.go
index 065d0aa..44e977f 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -318,7 +318,7 @@
 	ret.Class = "SHARED_LIBRARIES"
 
 	ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
-		path, file := filepath.Split(c.installPath)
+		path, file := filepath.Split(c.installPath.String())
 		stem := strings.TrimSuffix(file, filepath.Ext(file))
 		fmt.Fprintln(w, "LOCAL_SYSTEM_SHARED_LIBRARIES :=")
 		fmt.Fprintln(w, "LOCAL_MODULE_SUFFIX := "+outputFile.Ext())
diff --git a/cc/cmakelists.go b/cc/cmakelists.go
index 13a2e8e..9b32182 100644
--- a/cc/cmakelists.go
+++ b/cc/cmakelists.go
@@ -23,8 +23,6 @@
 	"path"
 	"path/filepath"
 	"strings"
-
-	"github.com/google/blueprint"
 )
 
 // This singleton generates CMakeLists.txt files. It does so for each blueprint Android.bp resulting in a cc.Module
@@ -35,7 +33,7 @@
 	android.RegisterSingletonType("cmakelists_generator", cMakeListsGeneratorSingleton)
 }
 
-func cMakeListsGeneratorSingleton() blueprint.Singleton {
+func cMakeListsGeneratorSingleton() android.Singleton {
 	return &cmakelistsGeneratorSingleton{}
 }
 
@@ -57,14 +55,14 @@
 // This is done to ease investigating bug reports.
 var outputDebugInfo = false
 
-func (c *cmakelistsGeneratorSingleton) GenerateBuildActions(ctx blueprint.SingletonContext) {
+func (c *cmakelistsGeneratorSingleton) GenerateBuildActions(ctx android.SingletonContext) {
 	if getEnvVariable(envVariableGenerateCMakeLists, ctx) != envVariableTrue {
 		return
 	}
 
 	outputDebugInfo = (getEnvVariable(envVariableGenerateDebugInfo, ctx) == envVariableTrue)
 
-	ctx.VisitAllModules(func(module blueprint.Module) {
+	ctx.VisitAllModules(func(module android.Module) {
 		if ccModule, ok := module.(*Module); ok {
 			if compiledModule, ok := ccModule.compiler.(CompiledInterface); ok {
 				generateCLionProject(compiledModule, ctx, ccModule)
@@ -81,7 +79,7 @@
 	return
 }
 
-func getEnvVariable(name string, ctx blueprint.SingletonContext) string {
+func getEnvVariable(name string, ctx android.SingletonContext) string {
 	// Using android.Config.Getenv instead of os.getEnv to guarantee soong will
 	// re-run in case this environment variable changes.
 	return ctx.Config().(android.Config).Getenv(name)
@@ -116,7 +114,7 @@
 	return nil
 }
 
-func generateCLionProject(compiledModule CompiledInterface, ctx blueprint.SingletonContext, ccModule *Module) {
+func generateCLionProject(compiledModule CompiledInterface, ctx android.SingletonContext, ccModule *Module) {
 	srcs := compiledModule.Srcs()
 	if len(srcs) == 0 {
 		return
@@ -287,7 +285,7 @@
 	return flag
 }
 
-func parseCompilerParameters(params []string, ctx blueprint.SingletonContext, f *os.File) compilerParameters {
+func parseCompilerParameters(params []string, ctx android.SingletonContext, f *os.File) compilerParameters {
 	var compilerParameters = makeCompilerParameters()
 
 	for i, str := range params {
@@ -388,7 +386,7 @@
 	c1.flags = append(c1.flags, c2.flags...)
 }
 
-func evalVariable(ctx blueprint.SingletonContext, str string) (string, error) {
+func evalVariable(ctx android.SingletonContext, str string) (string, error) {
 	evaluated, err := ctx.Eval(pctx, str)
 	if err == nil {
 		return evaluated, nil
@@ -396,7 +394,7 @@
 	return "", err
 }
 
-func getCMakeListsForModule(module *Module, ctx blueprint.SingletonContext) string {
+func getCMakeListsForModule(module *Module, ctx android.SingletonContext) string {
 	return filepath.Join(getAndroidSrcRootDirectory(ctx),
 		cLionOutputProjectsDirectory,
 		path.Dir(ctx.BlueprintFile(module)),
@@ -406,7 +404,7 @@
 		cMakeListsFilename)
 }
 
-func getAndroidSrcRootDirectory(ctx blueprint.SingletonContext) string {
+func getAndroidSrcRootDirectory(ctx android.SingletonContext) string {
 	srcPath, _ := filepath.Abs(android.PathForSource(ctx).String())
 	return srcPath
 }
diff --git a/cc/config/global.go b/cc/config/global.go
index 4322436..8881b4b 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -216,14 +216,14 @@
 		[]string{"libnativehelper/include_deprecated"})
 
 	pctx.SourcePathVariable("ClangDefaultBase", ClangDefaultBase)
-	pctx.VariableFunc("ClangBase", func(config interface{}) (string, error) {
-		if override := config.(android.Config).Getenv("LLVM_PREBUILTS_BASE"); override != "" {
+	pctx.VariableFunc("ClangBase", func(config android.Config) (string, error) {
+		if override := config.Getenv("LLVM_PREBUILTS_BASE"); override != "" {
 			return override, nil
 		}
 		return "${ClangDefaultBase}", nil
 	})
-	pctx.VariableFunc("ClangVersion", func(config interface{}) (string, error) {
-		if override := config.(android.Config).Getenv("LLVM_PREBUILTS_VERSION"); override != "" {
+	pctx.VariableFunc("ClangVersion", func(config android.Config) (string, error) {
+		if override := config.Getenv("LLVM_PREBUILTS_VERSION"); override != "" {
 			return override, nil
 		}
 		return ClangDefaultVersion, nil
@@ -231,8 +231,8 @@
 	pctx.StaticVariable("ClangPath", "${ClangBase}/${HostPrebuiltTag}/${ClangVersion}")
 	pctx.StaticVariable("ClangBin", "${ClangPath}/bin")
 
-	pctx.VariableFunc("ClangShortVersion", func(config interface{}) (string, error) {
-		if override := config.(android.Config).Getenv("LLVM_RELEASE_VERSION"); override != "" {
+	pctx.VariableFunc("ClangShortVersion", func(config android.Config) (string, error) {
+		if override := config.Getenv("LLVM_RELEASE_VERSION"); override != "" {
 			return override, nil
 		}
 		return ClangDefaultShortVersion, nil
@@ -258,8 +258,8 @@
 			"frameworks/rs/script_api/include",
 		})
 
-	pctx.VariableFunc("CcWrapper", func(config interface{}) (string, error) {
-		if override := config.(android.Config).Getenv("CC_WRAPPER"); override != "" {
+	pctx.VariableFunc("CcWrapper", func(config android.Config) (string, error) {
+		if override := config.Getenv("CC_WRAPPER"); override != "" {
 			return override + " ", nil
 		}
 		return "", nil
diff --git a/cc/config/tidy.go b/cc/config/tidy.go
index a2fa5a2..76a5f9e 100644
--- a/cc/config/tidy.go
+++ b/cc/config/tidy.go
@@ -25,8 +25,8 @@
 	// Global tidy checks include only google*, performance*,
 	// and misc-macro-parentheses, but not google-readability*
 	// or google-runtime-references.
-	pctx.VariableFunc("TidyDefaultGlobalChecks", func(config interface{}) (string, error) {
-		if override := config.(android.Config).Getenv("DEFAULT_GLOBAL_TIDY_CHECKS"); override != "" {
+	pctx.VariableFunc("TidyDefaultGlobalChecks", func(config android.Config) (string, error) {
+		if override := config.Getenv("DEFAULT_GLOBAL_TIDY_CHECKS"); override != "" {
 			return override, nil
 		}
 		return strings.Join([]string{
@@ -41,8 +41,8 @@
 
 	// There are too many clang-tidy warnings in external and vendor projects.
 	// Enable only some google checks for these projects.
-	pctx.VariableFunc("TidyExternalVendorChecks", func(config interface{}) (string, error) {
-		if override := config.(android.Config).Getenv("DEFAULT_EXTERNAL_VENDOR_TIDY_CHECKS"); override != "" {
+	pctx.VariableFunc("TidyExternalVendorChecks", func(config android.Config) (string, error) {
+		if override := config.Getenv("DEFAULT_EXTERNAL_VENDOR_TIDY_CHECKS"); override != "" {
 			return override, nil
 		}
 		return strings.Join([]string{
diff --git a/cc/config/x86_darwin_host.go b/cc/config/x86_darwin_host.go
index 8d805c9..dbaa6fa 100644
--- a/cc/config/x86_darwin_host.go
+++ b/cc/config/x86_darwin_host.go
@@ -107,25 +107,25 @@
 )
 
 func init() {
-	pctx.VariableFunc("macSdkPath", func(config interface{}) (string, error) {
-		xcodeselect := config.(android.Config).HostSystemTool("xcode-select")
+	pctx.VariableFunc("macSdkPath", func(config android.Config) (string, error) {
+		xcodeselect := config.HostSystemTool("xcode-select")
 		bytes, err := exec.Command(xcodeselect, "--print-path").Output()
 		return strings.TrimSpace(string(bytes)), err
 	})
-	pctx.VariableFunc("macSdkRoot", func(config interface{}) (string, error) {
-		return xcrunSdk(config.(android.Config), "--show-sdk-path")
+	pctx.VariableFunc("macSdkRoot", func(config android.Config) (string, error) {
+		return xcrunSdk(config, "--show-sdk-path")
 	})
 	pctx.StaticVariable("macMinVersion", "10.8")
-	pctx.VariableFunc("MacArPath", func(config interface{}) (string, error) {
-		return xcrun(config.(android.Config), "--find", "ar")
+	pctx.VariableFunc("MacArPath", func(config android.Config) (string, error) {
+		return xcrun(config, "--find", "ar")
 	})
 
-	pctx.VariableFunc("MacStripPath", func(config interface{}) (string, error) {
-		return xcrun(config.(android.Config), "--find", "strip")
+	pctx.VariableFunc("MacStripPath", func(config android.Config) (string, error) {
+		return xcrun(config, "--find", "strip")
 	})
 
-	pctx.VariableFunc("MacToolPath", func(config interface{}) (string, error) {
-		path, err := xcrun(config.(android.Config), "--find", "ld")
+	pctx.VariableFunc("MacToolPath", func(config android.Config) (string, error) {
+		path, err := xcrun(config, "--find", "ld")
 		return filepath.Dir(path), err
 	})
 
diff --git a/cc/library.go b/cc/library.go
index 192496a..cf10617 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -85,9 +85,6 @@
 	VariantIsShared bool `blueprint:"mutated"`
 	// This variant is static
 	VariantIsStatic bool `blueprint:"mutated"`
-	// Location of the static library in the sysroot. Empty if the library is
-	// not included in the NDK.
-	NdkSysrootPath string `blueprint:"mutated"`
 }
 
 type FlagExporterProperties struct {
@@ -246,6 +243,10 @@
 	// Source Abi Diff
 	sAbiDiff android.OptionalPath
 
+	// Location of the static library in the sysroot. Empty if the library is
+	// not included in the NDK.
+	ndkSysrootPath android.Path
+
 	// Decorated interafaces
 	*baseCompiler
 	*baseLinker
@@ -742,7 +743,7 @@
 			Input:       file,
 		})
 
-		library.MutatedProperties.NdkSysrootPath = installPath.String()
+		library.ndkSysrootPath = installPath
 	}
 }
 
diff --git a/cc/ndk_headers.go b/cc/ndk_headers.go
index bfbf0f5..d7c2a06 100644
--- a/cc/ndk_headers.go
+++ b/cc/ndk_headers.go
@@ -74,7 +74,7 @@
 
 	properties headerProperies
 
-	installPaths []string
+	installPaths android.Paths
 	licensePath  android.ModuleSrcPath
 }
 
@@ -139,7 +139,7 @@
 				"expected header install path (%q) not equal to actual install path %q",
 				installPath, installedPath))
 		}
-		m.installPaths = append(m.installPaths, installPath.String())
+		m.installPaths = append(m.installPaths, installPath)
 	}
 
 	if len(m.installPaths) == 0 {
@@ -186,7 +186,7 @@
 
 	properties preprocessedHeaderProperies
 
-	installPaths []string
+	installPaths android.Paths
 	licensePath  android.ModuleSrcPath
 }
 
@@ -208,7 +208,7 @@
 		installDir := getHeaderInstallDir(ctx, header, String(m.properties.From), String(m.properties.To))
 		installPath := installDir.Join(ctx, header.Base())
 		installPaths = append(installPaths, installPath)
-		m.installPaths = append(m.installPaths, installPath.String())
+		m.installPaths = append(m.installPaths, installPath)
 	}
 
 	if len(m.installPaths) == 0 {
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index 96a90fb..e69128c 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -98,7 +98,7 @@
 	properties libraryProperties
 
 	versionScriptPath android.ModuleGenPath
-	installPath       string
+	installPath       android.Path
 }
 
 // OMG GO
@@ -344,7 +344,7 @@
 
 	installDir := getNdkInstallBase(ctx).Join(ctx, fmt.Sprintf(
 		"platforms/android-%s/arch-%s/usr/%s", apiLevel, arch, libDir))
-	stub.installPath = ctx.InstallFile(installDir, path.Base(), path).String()
+	stub.installPath = ctx.InstallFile(installDir, path.Base(), path)
 }
 
 func newStubLibrary() *Module {
diff --git a/cc/ndk_sysroot.go b/cc/ndk_sysroot.go
index e213965..4324458 100644
--- a/cc/ndk_sysroot.go
+++ b/cc/ndk_sysroot.go
@@ -53,8 +53,6 @@
 // TODO(danalbert): Write `ndk_static_library` rule.
 
 import (
-	"github.com/google/blueprint"
-
 	"android/soong/android"
 )
 
@@ -76,32 +74,32 @@
 	return getNdkInstallBase(ctx).Join(ctx, "sysroot")
 }
 
-func getNdkSysrootTimestampFile(ctx android.PathContext) android.Path {
+func getNdkSysrootTimestampFile(ctx android.PathContext) android.WritablePath {
 	return android.PathForOutput(ctx, "ndk.timestamp")
 }
 
-func NdkSingleton() blueprint.Singleton {
+func NdkSingleton() android.Singleton {
 	return &ndkSingleton{}
 }
 
 type ndkSingleton struct{}
 
-func (n *ndkSingleton) GenerateBuildActions(ctx blueprint.SingletonContext) {
-	installPaths := []string{}
-	licensePaths := []string{}
-	ctx.VisitAllModules(func(module blueprint.Module) {
+func (n *ndkSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+	var installPaths android.Paths
+	var licensePaths android.Paths
+	ctx.VisitAllModules(func(module android.Module) {
 		if m, ok := module.(android.Module); ok && !m.Enabled() {
 			return
 		}
 
 		if m, ok := module.(*headerModule); ok {
 			installPaths = append(installPaths, m.installPaths...)
-			licensePaths = append(licensePaths, m.licensePath.String())
+			licensePaths = append(licensePaths, m.licensePath)
 		}
 
 		if m, ok := module.(*preprocessedHeaderModule); ok {
 			installPaths = append(installPaths, m.installPaths...)
-			licensePaths = append(licensePaths, m.licensePath.String())
+			licensePaths = append(licensePaths, m.licensePath)
 		}
 
 		if m, ok := module.(*Module); ok {
@@ -110,30 +108,28 @@
 			}
 
 			if library, ok := m.linker.(*libraryDecorator); ok {
-				if library.MutatedProperties.NdkSysrootPath != "" {
-					installPaths = append(installPaths, library.MutatedProperties.NdkSysrootPath)
+				if library.ndkSysrootPath != nil {
+					installPaths = append(installPaths, library.ndkSysrootPath)
 				}
 			}
 		}
 	})
 
 	combinedLicense := getNdkInstallBase(ctx).Join(ctx, "NOTICE")
-	ctx.Build(pctx, blueprint.BuildParams{
+	ctx.Build(pctx, android.BuildParams{
 		Rule:        android.Cat,
 		Description: "combine licenses",
-		Outputs:     []string{combinedLicense.String()},
+		Output:      combinedLicense,
 		Inputs:      licensePaths,
-		Optional:    true,
 	})
 
-	depPaths := append(installPaths, combinedLicense.String())
+	depPaths := append(installPaths, combinedLicense)
 
 	// There's a dummy "ndk" rule defined in ndk/Android.mk that depends on
 	// this. `m ndk` will build the sysroots.
-	ctx.Build(pctx, blueprint.BuildParams{
+	ctx.Build(pctx, android.BuildParams{
 		Rule:      android.Touch,
-		Outputs:   []string{getNdkSysrootTimestampFile(ctx).String()},
+		Output:    getNdkSysrootTimestampFile(ctx),
 		Implicits: depPaths,
-		Optional:  true,
 	})
 }
diff --git a/java/config/config.go b/java/config/config.go
index 49481be..466563f 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -64,9 +64,9 @@
 
 	pctx.VariableConfigMethod("hostPrebuiltTag", android.Config.PrebuiltOS)
 
-	pctx.VariableFunc("JavaHome", func(config interface{}) (string, error) {
+	pctx.VariableFunc("JavaHome", func(config android.Config) (string, error) {
 		// This is set up and guaranteed by soong_ui
-		return config.(android.Config).Getenv("ANDROID_JAVA_HOME"), nil
+		return config.Getenv("ANDROID_JAVA_HOME"), nil
 	})
 
 	pctx.SourcePathVariable("JavaToolchain", "${JavaHome}/bin")
@@ -85,9 +85,9 @@
 	pctx.HostBinToolVariable("SoongZipCmd", "soong_zip")
 	pctx.HostBinToolVariable("MergeZipsCmd", "merge_zips")
 	pctx.HostBinToolVariable("Zip2ZipCmd", "zip2zip")
-	pctx.VariableFunc("DxCmd", func(config interface{}) (string, error) {
-		if config.(android.Config).IsEnvFalse("USE_D8") {
-			if config.(android.Config).UnbundledBuild() || config.(android.Config).IsPdkBuild() {
+	pctx.VariableFunc("DxCmd", func(config android.Config) (string, error) {
+		if config.IsEnvFalse("USE_D8") {
+			if config.UnbundledBuild() || config.IsPdkBuild() {
 				return "prebuilts/build-tools/common/bin/dx", nil
 			} else {
 				path, err := pctx.HostBinToolPath(config, "dx")
@@ -104,9 +104,9 @@
 			return path.String(), nil
 		}
 	})
-	pctx.VariableFunc("TurbineJar", func(config interface{}) (string, error) {
+	pctx.VariableFunc("TurbineJar", func(config android.Config) (string, error) {
 		turbine := "turbine.jar"
-		if config.(android.Config).UnbundledBuild() {
+		if config.UnbundledBuild() {
 			return "prebuilts/build-tools/common/framework/" + turbine, nil
 		} else {
 			path, err := pctx.HostJavaToolPath(config, turbine)
@@ -122,8 +122,8 @@
 
 	pctx.HostBinToolVariable("SoongJavacWrapper", "soong_javac_wrapper")
 
-	pctx.VariableFunc("JavacWrapper", func(config interface{}) (string, error) {
-		if override := config.(android.Config).Getenv("JAVAC_WRAPPER"); override != "" {
+	pctx.VariableFunc("JavacWrapper", func(config android.Config) (string, error) {
+		if override := config.Getenv("JAVAC_WRAPPER"); override != "" {
 			return override + " ", nil
 		}
 		return "", nil
diff --git a/java/config/error_prone.go b/java/config/error_prone.go
index 31cbf2c..862217f 100644
--- a/java/config/error_prone.go
+++ b/java/config/error_prone.go
@@ -14,6 +14,8 @@
 
 package config
 
+import "android/soong/android"
+
 var (
 	// These will be filled out by external/error_prone/soong/error_prone.go if it is available
 	ErrorProneJavacJar    string
@@ -25,7 +27,7 @@
 
 // Wrapper that grabs value of val late so it can be initialized by a later module's init function
 func errorProneVar(name string, val *string) {
-	pctx.VariableFunc(name, func(config interface{}) (string, error) {
+	pctx.VariableFunc(name, func(config android.Config) (string, error) {
 		return *val, nil
 	})
 }
diff --git a/java/gen.go b/java/gen.go
index b5973ec..7a0dcac 100644
--- a/java/gen.go
+++ b/java/gen.go
@@ -28,8 +28,6 @@
 	pctx.HostBinToolVariable("aidlCmd", "aidl")
 	pctx.SourcePathVariable("logtagsCmd", "build/tools/java-event-log-tags.py")
 	pctx.SourcePathVariable("mergeLogtagsCmd", "build/tools/merge-event-log-tags.py")
-
-	pctx.IntermediatesPathVariable("allLogtagsFile", "all-event-log-tags.txt")
 }
 
 var (
@@ -117,7 +115,7 @@
 	return outSrcFiles
 }
 
-func LogtagsSingleton() blueprint.Singleton {
+func LogtagsSingleton() android.Singleton {
 	return &logtagsSingleton{}
 }
 
@@ -127,18 +125,18 @@
 
 type logtagsSingleton struct{}
 
-func (l *logtagsSingleton) GenerateBuildActions(ctx blueprint.SingletonContext) {
+func (l *logtagsSingleton) GenerateBuildActions(ctx android.SingletonContext) {
 	var allLogtags android.Paths
-	ctx.VisitAllModules(func(module blueprint.Module) {
+	ctx.VisitAllModules(func(module android.Module) {
 		if logtags, ok := module.(logtagsProducer); ok {
 			allLogtags = append(allLogtags, logtags.logtags()...)
 		}
 	})
 
-	ctx.Build(pctx, blueprint.BuildParams{
+	ctx.Build(pctx, android.BuildParams{
 		Rule:        mergeLogtags,
 		Description: "merge logtags",
-		Outputs:     []string{"$allLogtagsFile"},
-		Inputs:      allLogtags.Strings(),
+		Output:      android.PathForIntermediates(ctx, "all-event-log-tags.txt"),
+		Inputs:      allLogtags,
 	})
 }