Rename common to android

Rename the "common" package to "android", because common is too
generic.  Also removes all android.Android naming stutter.

Ran:
gomvpkg -from 'android/soong/common' -to 'android/soong/android'
gorename -from '"android/soong/android".AndroidModuleContext' -to 'ModuleContext'
gorename -from '"android/soong/android".AndroidBaseContext' -to 'BaseContext'
gorename -from '"android/soong/android".AndroidModuleBase' -to 'ModuleBase'
gorename -from '"android/soong/android".AndroidBottomUpMutatorContext' -to 'BottomUpMutatorContext'
gorename -from '"android/soong/android".AndroidTopDownMutatorContext' -to 'TopDownMutatorContext'
gorename -from '"android/soong/android".AndroidModule' -to 'Module'

Change-Id: I3b23590b8ce7c8a1ea1139411d84a53163288da7
diff --git a/android/androidmk.go b/android/androidmk.go
new file mode 100644
index 0000000..c8dce0f
--- /dev/null
+++ b/android/androidmk.go
@@ -0,0 +1,215 @@
+// 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 android
+
+import (
+	"bytes"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"sort"
+
+	"android/soong"
+
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
+)
+
+func init() {
+	soong.RegisterSingletonType("androidmk", AndroidMkSingleton)
+}
+
+type AndroidMkDataProvider interface {
+	AndroidMk() (AndroidMkData, error)
+}
+
+type AndroidMkData struct {
+	Class      string
+	SubName    string
+	OutputFile OptionalPath
+	Disabled   bool
+
+	Custom func(w io.Writer, name, prefix string) error
+
+	Extra []func(w io.Writer, outputFile Path) error
+}
+
+func AndroidMkSingleton() blueprint.Singleton {
+	return &androidMkSingleton{}
+}
+
+type androidMkSingleton struct{}
+
+func (c *androidMkSingleton) GenerateBuildActions(ctx blueprint.SingletonContext) {
+	config := ctx.Config().(Config)
+
+	if !config.EmbeddedInMake() {
+		return
+	}
+
+	ctx.SetNinjaBuildDir(pctx, filepath.Join(config.buildDir, ".."))
+
+	var androidMkModulesList []Module
+
+	ctx.VisitAllModules(func(module blueprint.Module) {
+		if amod, ok := module.(Module); ok {
+			androidMkModulesList = append(androidMkModulesList, amod)
+		}
+	})
+
+	sort.Sort(AndroidModulesByName{androidMkModulesList, ctx})
+
+	transMk := PathForOutput(ctx, "Android"+proptools.String(config.ProductVariables.Make_suffix)+".mk")
+	if ctx.Failed() {
+		return
+	}
+
+	err := translateAndroidMk(ctx, transMk.String(), androidMkModulesList)
+	if err != nil {
+		ctx.Errorf(err.Error())
+	}
+
+	ctx.Build(pctx, blueprint.BuildParams{
+		Rule:     blueprint.Phony,
+		Outputs:  []string{transMk.String()},
+		Optional: true,
+	})
+}
+
+func translateAndroidMk(ctx blueprint.SingletonContext, mkFile string, mods []Module) error {
+	buf := &bytes.Buffer{}
+
+	fmt.Fprintln(buf, "LOCAL_MODULE_MAKEFILE := $(lastword $(MAKEFILE_LIST))")
+
+	for _, mod := range mods {
+		err := translateAndroidMkModule(ctx, buf, mod)
+		if err != nil {
+			os.Remove(mkFile)
+			return err
+		}
+	}
+
+	// Don't write to the file if it hasn't changed
+	if _, err := os.Stat(mkFile); !os.IsNotExist(err) {
+		if data, err := ioutil.ReadFile(mkFile); err == nil {
+			matches := buf.Len() == len(data)
+
+			if matches {
+				for i, value := range buf.Bytes() {
+					if value != data[i] {
+						matches = false
+						break
+					}
+				}
+			}
+
+			if matches {
+				return nil
+			}
+		}
+	}
+
+	return ioutil.WriteFile(mkFile, buf.Bytes(), 0666)
+}
+
+func translateAndroidMkModule(ctx blueprint.SingletonContext, w io.Writer, mod blueprint.Module) error {
+	name := ctx.ModuleName(mod)
+
+	provider, ok := mod.(AndroidMkDataProvider)
+	if !ok {
+		return nil
+	}
+
+	amod := mod.(Module).base()
+	data, err := provider.AndroidMk()
+	if err != nil {
+		return err
+	}
+
+	if !amod.Enabled() {
+		return err
+	}
+
+	if data.SubName != "" {
+		name += "_" + data.SubName
+	}
+
+	hostCross := false
+	if amod.Host() && amod.HostType() != CurrentHostType() {
+		hostCross = true
+	}
+
+	if data.Custom != nil {
+		prefix := ""
+		if amod.Host() {
+			if hostCross {
+				prefix = "HOST_CROSS_"
+			} else {
+				prefix = "HOST_"
+			}
+			if amod.Arch().ArchType != ctx.Config().(Config).HostArches[amod.HostType()][0].ArchType {
+				prefix = "2ND_" + prefix
+			}
+		} else {
+			prefix = "TARGET_"
+			if amod.Arch().ArchType != ctx.Config().(Config).DeviceArches[0].ArchType {
+				prefix = "2ND_" + prefix
+			}
+		}
+
+		return data.Custom(w, name, prefix)
+	}
+
+	if data.Disabled {
+		return nil
+	}
+
+	if !data.OutputFile.Valid() {
+		return err
+	}
+
+	fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
+	fmt.Fprintln(w, "LOCAL_PATH :=", filepath.Dir(ctx.BlueprintFile(mod)))
+	fmt.Fprintln(w, "LOCAL_MODULE :=", name)
+	fmt.Fprintln(w, "LOCAL_MODULE_CLASS :=", data.Class)
+	fmt.Fprintln(w, "LOCAL_MULTILIB :=", amod.commonProperties.Compile_multilib)
+	fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", data.OutputFile.String())
+
+	archStr := amod.Arch().ArchType.String()
+	if amod.Host() {
+		if hostCross {
+			fmt.Fprintln(w, "LOCAL_MODULE_HOST_CROSS_ARCH :=", archStr)
+		} else {
+			fmt.Fprintln(w, "LOCAL_MODULE_HOST_ARCH :=", archStr)
+		}
+		fmt.Fprintln(w, "LOCAL_MODULE_HOST_OS :=", amod.HostType().String())
+		fmt.Fprintln(w, "LOCAL_IS_HOST_MODULE := true")
+	} else {
+		fmt.Fprintln(w, "LOCAL_MODULE_TARGET_ARCH :=", archStr)
+	}
+
+	for _, extra := range data.Extra {
+		err = extra(w, data.OutputFile.Path())
+		if err != nil {
+			return err
+		}
+	}
+
+	fmt.Fprintln(w, "include $(BUILD_PREBUILT)")
+
+	return err
+}
diff --git a/android/arch.go b/android/arch.go
new file mode 100644
index 0000000..952b7207
--- /dev/null
+++ b/android/arch.go
@@ -0,0 +1,1061 @@
+// 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 android
+
+import (
+	"fmt"
+	"reflect"
+	"runtime"
+	"strings"
+
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
+)
+
+func init() {
+	RegisterBottomUpMutator("defaults_deps", defaultsDepsMutator)
+	RegisterTopDownMutator("defaults", defaultsMutator)
+
+	RegisterBottomUpMutator("host_or_device", HostOrDeviceMutator)
+	RegisterBottomUpMutator("host_type", HostTypeMutator)
+	RegisterBottomUpMutator("arch", ArchMutator)
+}
+
+var (
+	Arm    = newArch("arm", "lib32")
+	Arm64  = newArch("arm64", "lib64")
+	Mips   = newArch("mips", "lib32")
+	Mips64 = newArch("mips64", "lib64")
+	X86    = newArch("x86", "lib32")
+	X86_64 = newArch("x86_64", "lib64")
+
+	Common = ArchType{
+		Name: "common",
+	}
+)
+
+var archTypeMap = map[string]ArchType{
+	"arm":    Arm,
+	"arm64":  Arm64,
+	"mips":   Mips,
+	"mips64": Mips64,
+	"x86":    X86,
+	"x86_64": X86_64,
+}
+
+/*
+Example blueprints file containing all variant property groups, with comment listing what type
+of variants get properties in that group:
+
+module {
+    arch: {
+        arm: {
+            // Host or device variants with arm architecture
+        },
+        arm64: {
+            // Host or device variants with arm64 architecture
+        },
+        mips: {
+            // Host or device variants with mips architecture
+        },
+        mips64: {
+            // Host or device variants with mips64 architecture
+        },
+        x86: {
+            // Host or device variants with x86 architecture
+        },
+        x86_64: {
+            // Host or device variants with x86_64 architecture
+        },
+    },
+    multilib: {
+        lib32: {
+            // Host or device variants for 32-bit architectures
+        },
+        lib64: {
+            // Host or device variants for 64-bit architectures
+        },
+    },
+    target: {
+        android: {
+            // Device variants
+        },
+        host: {
+            // Host variants
+        },
+        linux: {
+            // Linux host variants
+        },
+        darwin: {
+            // Darwin host variants
+        },
+        windows: {
+            // Windows host variants
+        },
+        not_windows: {
+            // Non-windows host variants
+        },
+    },
+}
+*/
+
+type Embed interface{}
+
+type archProperties struct {
+	// Properties to vary by target architecture
+	Arch struct {
+		// Properties for module variants being built to run on arm (host or device)
+		Arm struct {
+			Embed `blueprint:"filter(android:\"arch_variant\")"`
+
+			// Arm arch variants
+			Armv5te      interface{} `blueprint:"filter(android:\"arch_variant\")"`
+			Armv7_a      interface{} `blueprint:"filter(android:\"arch_variant\")"`
+			Armv7_a_neon interface{} `blueprint:"filter(android:\"arch_variant\")"`
+
+			// Arm cpu variants
+			Cortex_a7      interface{} `blueprint:"filter(android:\"arch_variant\")"`
+			Cortex_a8      interface{} `blueprint:"filter(android:\"arch_variant\")"`
+			Cortex_a9      interface{} `blueprint:"filter(android:\"arch_variant\")"`
+			Cortex_a15     interface{} `blueprint:"filter(android:\"arch_variant\")"`
+			Cortex_a53     interface{} `blueprint:"filter(android:\"arch_variant\")"`
+			Cortex_a53_a57 interface{} `blueprint:"filter(android:\"arch_variant\")"`
+			Krait          interface{} `blueprint:"filter(android:\"arch_variant\")"`
+			Denver         interface{} `blueprint:"filter(android:\"arch_variant\")"`
+		}
+
+		// Properties for module variants being built to run on arm64 (host or device)
+		Arm64 struct {
+			Embed `blueprint:"filter(android:\"arch_variant\")"`
+
+			// Arm64 arch variants
+			Armv8_a interface{} `blueprint:"filter(android:\"arch_variant\")"`
+
+			// Arm64 cpu variants
+			Cortex_a53 interface{} `blueprint:"filter(android:\"arch_variant\")"`
+			Denver64   interface{} `blueprint:"filter(android:\"arch_variant\")"`
+		}
+
+		// Properties for module variants being built to run on mips (host or device)
+		Mips struct {
+			Embed `blueprint:"filter(android:\"arch_variant\")"`
+
+			// Mips arch variants
+			Mips32_fp          interface{} `blueprint:"filter(android:\"arch_variant\")"`
+			Mips32r2_fp        interface{} `blueprint:"filter(android:\"arch_variant\")"`
+			Mips32r2_fp_xburst interface{} `blueprint:"filter(android:\"arch_variant\")"`
+			Mips32r2dsp_fp     interface{} `blueprint:"filter(android:\"arch_variant\")"`
+			Mips32r2dspr2_fp   interface{} `blueprint:"filter(android:\"arch_variant\")"`
+			Mips32r6           interface{} `blueprint:"filter(android:\"arch_variant\")"`
+
+			// Mips arch features
+			Rev6 interface{} `blueprint:"filter(android:\"arch_variant\")"`
+		}
+
+		// Properties for module variants being built to run on mips64 (host or device)
+		Mips64 struct {
+			Embed `blueprint:"filter(android:\"arch_variant\")"`
+
+			// Mips64 arch variants
+			Mips64r2 interface{} `blueprint:"filter(android:\"arch_variant\")"`
+			Mips64r6 interface{} `blueprint:"filter(android:\"arch_variant\")"`
+
+			// Mips64 arch features
+			Rev6 interface{} `blueprint:"filter(android:\"arch_variant\")"`
+		}
+
+		// Properties for module variants being built to run on x86 (host or device)
+		X86 struct {
+			Embed `blueprint:"filter(android:\"arch_variant\")"`
+
+			// X86 arch variants
+			Atom        interface{} `blueprint:"filter(android:\"arch_variant\")"`
+			Haswell     interface{} `blueprint:"filter(android:\"arch_variant\")"`
+			Ivybridge   interface{} `blueprint:"filter(android:\"arch_variant\")"`
+			Sandybridge interface{} `blueprint:"filter(android:\"arch_variant\")"`
+			Silvermont  interface{} `blueprint:"filter(android:\"arch_variant\")"`
+			// Generic variant for X86 on X86_64
+			X86_64 interface{} `blueprint:"filter(android:\"arch_variant\")"`
+
+			// X86 arch features
+			Ssse3  interface{} `blueprint:"filter(android:\"arch_variant\")"`
+			Sse4   interface{} `blueprint:"filter(android:\"arch_variant\")"`
+			Sse4_1 interface{} `blueprint:"filter(android:\"arch_variant\")"`
+			Sse4_2 interface{} `blueprint:"filter(android:\"arch_variant\")"`
+			Aes_ni interface{} `blueprint:"filter(android:\"arch_variant\")"`
+			Avx    interface{} `blueprint:"filter(android:\"arch_variant\")"`
+			Popcnt interface{} `blueprint:"filter(android:\"arch_variant\")"`
+			Movbe  interface{} `blueprint:"filter(android:\"arch_variant\")"`
+		}
+
+		// Properties for module variants being built to run on x86_64 (host or device)
+		X86_64 struct {
+			Embed `blueprint:"filter(android:\"arch_variant\")"`
+
+			// X86 arch variants
+			Haswell     interface{} `blueprint:"filter(android:\"arch_variant\")"`
+			Ivybridge   interface{} `blueprint:"filter(android:\"arch_variant\")"`
+			Sandybridge interface{} `blueprint:"filter(android:\"arch_variant\")"`
+			Silvermont  interface{} `blueprint:"filter(android:\"arch_variant\")"`
+
+			// X86 arch features
+			Ssse3  interface{} `blueprint:"filter(android:\"arch_variant\")"`
+			Sse4   interface{} `blueprint:"filter(android:\"arch_variant\")"`
+			Sse4_1 interface{} `blueprint:"filter(android:\"arch_variant\")"`
+			Sse4_2 interface{} `blueprint:"filter(android:\"arch_variant\")"`
+			Aes_ni interface{} `blueprint:"filter(android:\"arch_variant\")"`
+			Avx    interface{} `blueprint:"filter(android:\"arch_variant\")"`
+			Popcnt interface{} `blueprint:"filter(android:\"arch_variant\")"`
+		}
+	}
+
+	// Properties to vary by 32-bit or 64-bit
+	Multilib struct {
+		// Properties for module variants being built to run on 32-bit devices
+		Lib32 interface{} `blueprint:"filter(android:\"arch_variant\")"`
+		// Properties for module variants being built to run on 64-bit devices
+		Lib64 interface{} `blueprint:"filter(android:\"arch_variant\")"`
+	}
+	// Properties to vary by build target (host or device, os, os+archictecture)
+	Target struct {
+		// Properties for module variants being built to run on the host
+		Host interface{} `blueprint:"filter(android:\"arch_variant\")"`
+		// Properties for module variants being built to run on the device
+		Android interface{} `blueprint:"filter(android:\"arch_variant\")"`
+		// Properties for module variants being built to run on arm devices
+		Android_arm interface{} `blueprint:"filter(android:\"arch_variant\")"`
+		// Properties for module variants being built to run on arm64 devices
+		Android_arm64 interface{} `blueprint:"filter(android:\"arch_variant\")"`
+		// Properties for module variants being built to run on mips devices
+		Android_mips interface{} `blueprint:"filter(android:\"arch_variant\")"`
+		// Properties for module variants being built to run on mips64 devices
+		Android_mips64 interface{} `blueprint:"filter(android:\"arch_variant\")"`
+		// Properties for module variants being built to run on x86 devices
+		Android_x86 interface{} `blueprint:"filter(android:\"arch_variant\")"`
+		// Properties for module variants being built to run on x86_64 devices
+		Android_x86_64 interface{} `blueprint:"filter(android:\"arch_variant\")"`
+		// Properties for module variants being built to run on devices that support 64-bit
+		Android64 interface{} `blueprint:"filter(android:\"arch_variant\")"`
+		// Properties for module variants being built to run on devices that do not support 64-bit
+		Android32 interface{} `blueprint:"filter(android:\"arch_variant\")"`
+		// Properties for module variants being built to run on linux hosts
+		Linux interface{} `blueprint:"filter(android:\"arch_variant\")"`
+		// Properties for module variants being built to run on linux x86 hosts
+		Linux_x86 interface{} `blueprint:"filter(android:\"arch_variant\")"`
+		// Properties for module variants being built to run on linux x86_64 hosts
+		Linux_x86_64 interface{} `blueprint:"filter(android:\"arch_variant\")"`
+		// Properties for module variants being built to run on darwin hosts
+		Darwin interface{} `blueprint:"filter(android:\"arch_variant\")"`
+		// Properties for module variants being built to run on darwin x86 hosts
+		Darwin_x86 interface{} `blueprint:"filter(android:\"arch_variant\")"`
+		// Properties for module variants being built to run on darwin x86_64 hosts
+		Darwin_x86_64 interface{} `blueprint:"filter(android:\"arch_variant\")"`
+		// Properties for module variants being built to run on windows hosts
+		Windows interface{} `blueprint:"filter(android:\"arch_variant\")"`
+		// Properties for module variants being built to run on windows x86 hosts
+		Windows_x86 interface{} `blueprint:"filter(android:\"arch_variant\")"`
+		// Properties for module variants being built to run on windows x86_64 hosts
+		Windows_x86_64 interface{} `blueprint:"filter(android:\"arch_variant\")"`
+		// Properties for module variants being built to run on linux or darwin hosts
+		Not_windows interface{} `blueprint:"filter(android:\"arch_variant\")"`
+	}
+}
+
+var archFeatureMap = map[ArchType]map[string][]string{}
+
+func RegisterArchFeatures(arch ArchType, variant string, features ...string) {
+	archField := proptools.FieldNameForProperty(arch.Name)
+	variantField := proptools.FieldNameForProperty(variant)
+	archStruct := reflect.ValueOf(archProperties{}.Arch).FieldByName(archField)
+	if variant != "" {
+		if !archStruct.FieldByName(variantField).IsValid() {
+			panic(fmt.Errorf("Invalid variant %q for arch %q", variant, arch))
+		}
+	}
+	for _, feature := range features {
+		field := proptools.FieldNameForProperty(feature)
+		if !archStruct.FieldByName(field).IsValid() {
+			panic(fmt.Errorf("Invalid feature %q for arch %q variant %q", feature, arch, variant))
+		}
+	}
+	if archFeatureMap[arch] == nil {
+		archFeatureMap[arch] = make(map[string][]string)
+	}
+	archFeatureMap[arch][variant] = features
+}
+
+// An Arch indicates a single CPU architecture.
+type Arch struct {
+	ArchType     ArchType
+	ArchVariant  string
+	CpuVariant   string
+	Abi          []string
+	ArchFeatures []string
+}
+
+func (a Arch) String() string {
+	s := a.ArchType.String()
+	if a.ArchVariant != "" {
+		s += "_" + a.ArchVariant
+	}
+	if a.CpuVariant != "" {
+		s += "_" + a.CpuVariant
+	}
+	return s
+}
+
+type ArchType struct {
+	Name     string
+	Multilib string
+}
+
+func newArch(name, multilib string) ArchType {
+	return ArchType{
+		Name:     name,
+		Multilib: multilib,
+	}
+}
+
+func (a ArchType) String() string {
+	return a.Name
+}
+
+type HostOrDeviceSupported int
+
+const (
+	_ HostOrDeviceSupported = iota
+	HostSupported
+	DeviceSupported
+	HostAndDeviceSupported
+	HostAndDeviceDefault
+)
+
+type HostOrDevice int
+
+const (
+	_ HostOrDevice = iota
+	Host
+	Device
+)
+
+func (hod HostOrDevice) String() string {
+	switch hod {
+	case Device:
+		return "device"
+	case Host:
+		return "host"
+	default:
+		panic(fmt.Sprintf("unexpected HostOrDevice value %d", hod))
+	}
+}
+
+func (hod HostOrDevice) Property() string {
+	switch hod {
+	case Device:
+		return "android"
+	case Host:
+		return "host"
+	default:
+		panic(fmt.Sprintf("unexpected HostOrDevice value %d", hod))
+	}
+}
+
+func (hod HostOrDevice) Host() bool {
+	if hod == 0 {
+		panic("HostOrDevice unset")
+	}
+	return hod == Host
+}
+
+func (hod HostOrDevice) Device() bool {
+	if hod == 0 {
+		panic("HostOrDevice unset")
+	}
+	return hod == Device
+}
+
+var hostOrDeviceName = map[HostOrDevice]string{
+	Device: "device",
+	Host:   "host",
+}
+
+type HostType int
+
+const (
+	NoHostType HostType = iota
+	Linux
+	Darwin
+	Windows
+)
+
+func CurrentHostType() HostType {
+	switch runtime.GOOS {
+	case "linux":
+		return Linux
+	case "darwin":
+		return Darwin
+	default:
+		panic(fmt.Sprintf("unsupported OS: %s", runtime.GOOS))
+	}
+}
+
+func (ht HostType) String() string {
+	switch ht {
+	case Linux:
+		return "linux"
+	case Darwin:
+		return "darwin"
+	case Windows:
+		return "windows"
+	default:
+		panic(fmt.Sprintf("unexpected HostType value %d", ht))
+	}
+}
+
+func (ht HostType) Field() string {
+	switch ht {
+	case Linux:
+		return "Linux"
+	case Darwin:
+		return "Darwin"
+	case Windows:
+		return "Windows"
+	default:
+		panic(fmt.Sprintf("unexpected HostType value %d", ht))
+	}
+}
+
+var (
+	commonArch = Arch{
+		ArchType: Common,
+	}
+)
+
+func HostOrDeviceMutator(mctx BottomUpMutatorContext) {
+	var module Module
+	var ok bool
+	if module, ok = mctx.Module().(Module); !ok {
+		return
+	}
+
+	hods := []HostOrDevice{}
+
+	if module.base().HostSupported() {
+		hods = append(hods, Host)
+	}
+
+	if module.base().DeviceSupported() {
+		hods = append(hods, Device)
+	}
+
+	if len(hods) == 0 {
+		return
+	}
+
+	hodNames := []string{}
+	for _, hod := range hods {
+		hodNames = append(hodNames, hod.String())
+	}
+
+	modules := mctx.CreateVariations(hodNames...)
+	for i, m := range modules {
+		m.(Module).base().SetHostOrDevice(hods[i])
+	}
+}
+
+func HostTypeMutator(mctx BottomUpMutatorContext) {
+	var module Module
+	var ok bool
+	if module, ok = mctx.Module().(Module); !ok {
+		return
+	}
+
+	if !module.base().HostSupported() || !module.base().HostOrDevice().Host() {
+		return
+	}
+
+	buildTypes, err := decodeHostTypesProductVariables(mctx.Config().(Config).ProductVariables)
+	if err != nil {
+		mctx.ModuleErrorf("%s", err.Error())
+		return
+	}
+
+	typeNames := []string{}
+	for _, ht := range buildTypes {
+		typeNames = append(typeNames, ht.String())
+	}
+
+	modules := mctx.CreateVariations(typeNames...)
+	for i, m := range modules {
+		m.(Module).base().SetHostType(buildTypes[i])
+	}
+}
+
+func ArchMutator(mctx BottomUpMutatorContext) {
+	var module Module
+	var ok bool
+	if module, ok = mctx.Module().(Module); !ok {
+		return
+	}
+
+	moduleArches := []Arch{}
+	multilib := module.base().commonProperties.Compile_multilib
+
+	if module.base().HostSupported() && module.base().HostOrDevice().Host() {
+		hostModuleArches, err := decodeMultilib(multilib, mctx.Config().(Config).HostArches[module.base().HostType()])
+		if err != nil {
+			mctx.ModuleErrorf("%s", err.Error())
+		}
+
+		moduleArches = append(moduleArches, hostModuleArches...)
+	}
+
+	if module.base().DeviceSupported() && module.base().HostOrDevice().Device() {
+		deviceModuleArches, err := decodeMultilib(multilib, mctx.Config().(Config).DeviceArches)
+		if err != nil {
+			mctx.ModuleErrorf("%s", err.Error())
+		}
+
+		moduleArches = append(moduleArches, deviceModuleArches...)
+	}
+
+	if len(moduleArches) == 0 {
+		return
+	}
+
+	archNames := []string{}
+	for _, arch := range moduleArches {
+		archNames = append(archNames, arch.String())
+	}
+
+	modules := mctx.CreateVariations(archNames...)
+
+	for i, m := range modules {
+		m.(Module).base().SetArch(moduleArches[i])
+		m.(Module).base().setArchProperties(mctx)
+	}
+}
+
+func InitArchModule(m Module,
+	propertyStructs ...interface{}) (blueprint.Module, []interface{}) {
+
+	base := m.base()
+
+	base.generalProperties = append(base.generalProperties,
+		propertyStructs...)
+
+	for _, properties := range base.generalProperties {
+		propertiesValue := reflect.ValueOf(properties)
+		if propertiesValue.Kind() != reflect.Ptr {
+			panic(fmt.Errorf("properties must be a pointer to a struct, got %T",
+				propertiesValue.Interface()))
+		}
+
+		propertiesValue = propertiesValue.Elem()
+		if propertiesValue.Kind() != reflect.Struct {
+			panic(fmt.Errorf("properties must be a pointer to a struct, got %T",
+				propertiesValue.Interface()))
+		}
+
+		archProperties := &archProperties{}
+		forEachInterface(reflect.ValueOf(archProperties), func(v reflect.Value) {
+			newValue := proptools.CloneEmptyProperties(propertiesValue)
+			v.Set(newValue)
+		})
+
+		base.archProperties = append(base.archProperties, archProperties)
+	}
+
+	var allProperties []interface{}
+	allProperties = append(allProperties, base.generalProperties...)
+	for _, asp := range base.archProperties {
+		allProperties = append(allProperties, asp)
+	}
+
+	return m, allProperties
+}
+
+var variantReplacer = strings.NewReplacer("-", "_", ".", "_")
+
+func (a *ModuleBase) appendProperties(ctx BottomUpMutatorContext,
+	dst, src interface{}, field, srcPrefix string) interface{} {
+
+	srcField := reflect.ValueOf(src).FieldByName(field)
+	if !srcField.IsValid() {
+		ctx.ModuleErrorf("field %q does not exist", srcPrefix)
+		return nil
+	}
+
+	ret := srcField
+
+	if srcField.Kind() == reflect.Struct {
+		srcField = srcField.FieldByName("Embed")
+	}
+
+	src = srcField.Elem().Interface()
+
+	filter := func(property string,
+		dstField, srcField reflect.StructField,
+		dstValue, srcValue interface{}) (bool, error) {
+
+		srcProperty := srcPrefix + "." + property
+
+		if !proptools.HasTag(dstField, "android", "arch_variant") {
+			if ctx.ContainsProperty(srcProperty) {
+				return false, fmt.Errorf("can't be specific to a build variant")
+			} else {
+				return false, nil
+			}
+		}
+
+		return true, nil
+	}
+
+	order := func(property string,
+		dstField, srcField reflect.StructField,
+		dstValue, srcValue interface{}) (proptools.Order, error) {
+		if proptools.HasTag(dstField, "android", "variant_prepend") {
+			return proptools.Prepend, nil
+		} else {
+			return proptools.Append, nil
+		}
+	}
+
+	err := proptools.ExtendProperties(dst, src, filter, order)
+	if err != nil {
+		if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
+			ctx.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
+		} else {
+			panic(err)
+		}
+	}
+
+	return ret.Interface()
+}
+
+// Rewrite the module's properties structs to contain arch-specific values.
+func (a *ModuleBase) setArchProperties(ctx BottomUpMutatorContext) {
+	arch := a.commonProperties.CompileArch
+	hod := a.commonProperties.CompileHostOrDevice
+	ht := a.commonProperties.CompileHostType
+
+	if arch.ArchType == Common {
+		return
+	}
+
+	for i := range a.generalProperties {
+		genProps := a.generalProperties[i]
+		archProps := a.archProperties[i]
+		// Handle arch-specific properties in the form:
+		// arch: {
+		//     arm64: {
+		//         key: value,
+		//     },
+		// },
+		t := arch.ArchType
+
+		field := proptools.FieldNameForProperty(t.Name)
+		prefix := "arch." + t.Name
+		archStruct := a.appendProperties(ctx, genProps, archProps.Arch, field, prefix)
+
+		// Handle arch-variant-specific properties in the form:
+		// arch: {
+		//     variant: {
+		//         key: value,
+		//     },
+		// },
+		v := variantReplacer.Replace(arch.ArchVariant)
+		if v != "" {
+			field := proptools.FieldNameForProperty(v)
+			prefix := "arch." + t.Name + "." + v
+			a.appendProperties(ctx, genProps, archStruct, field, prefix)
+		}
+
+		// Handle cpu-variant-specific properties in the form:
+		// arch: {
+		//     variant: {
+		//         key: value,
+		//     },
+		// },
+		c := variantReplacer.Replace(arch.CpuVariant)
+		if c != "" {
+			field := proptools.FieldNameForProperty(c)
+			prefix := "arch." + t.Name + "." + c
+			a.appendProperties(ctx, genProps, archStruct, field, prefix)
+		}
+
+		// Handle arch-feature-specific properties in the form:
+		// arch: {
+		//     feature: {
+		//         key: value,
+		//     },
+		// },
+		for _, feature := range arch.ArchFeatures {
+			field := proptools.FieldNameForProperty(feature)
+			prefix := "arch." + t.Name + "." + feature
+			a.appendProperties(ctx, genProps, archStruct, field, prefix)
+		}
+
+		// Handle multilib-specific properties in the form:
+		// multilib: {
+		//     lib32: {
+		//         key: value,
+		//     },
+		// },
+		field = proptools.FieldNameForProperty(t.Multilib)
+		prefix = "multilib." + t.Multilib
+		a.appendProperties(ctx, genProps, archProps.Multilib, field, prefix)
+
+		// Handle host-or-device-specific properties in the form:
+		// target: {
+		//     host: {
+		//         key: value,
+		//     },
+		// },
+		hodProperty := hod.Property()
+		field = proptools.FieldNameForProperty(hodProperty)
+		prefix = "target." + hodProperty
+		a.appendProperties(ctx, genProps, archProps.Target, field, prefix)
+
+		// Handle host target properties in the form:
+		// target: {
+		//     linux: {
+		//         key: value,
+		//     },
+		//     not_windows: {
+		//         key: value,
+		//     },
+		//     linux_x86: {
+		//         key: value,
+		//     },
+		//     linux_arm: {
+		//         key: value,
+		//     },
+		// },
+		if hod.Host() {
+			field := ht.Field()
+			prefix := "target." + ht.String()
+			a.appendProperties(ctx, genProps, archProps.Target, field, prefix)
+
+			t := arch.ArchType
+			field = ht.Field() + "_" + t.Name
+			prefix = "target." + ht.String() + "_" + t.Name
+			a.appendProperties(ctx, genProps, archProps.Target, field, prefix)
+
+			if ht != Windows {
+				field := "Not_windows"
+				prefix := "target.not_windows"
+				a.appendProperties(ctx, genProps, archProps.Target, field, prefix)
+			}
+		}
+
+		// Handle 64-bit device properties in the form:
+		// target {
+		//     android64 {
+		//         key: value,
+		//     },
+		//     android32 {
+		//         key: value,
+		//     },
+		// },
+		// WARNING: this is probably not what you want to use in your blueprints file, it selects
+		// options for all targets on a device that supports 64-bit binaries, not just the targets
+		// that are being compiled for 64-bit.  Its expected use case is binaries like linker and
+		// debuggerd that need to know when they are a 32-bit process running on a 64-bit device
+		if hod.Device() {
+			if true /* && target_is_64_bit */ {
+				field := "Android64"
+				prefix := "target.android64"
+				a.appendProperties(ctx, genProps, archProps.Target, field, prefix)
+			} else {
+				field := "Android32"
+				prefix := "target.android32"
+				a.appendProperties(ctx, genProps, archProps.Target, field, prefix)
+			}
+		}
+
+		// Handle device architecture properties in the form:
+		// target {
+		//     android_arm {
+		//         key: value,
+		//     },
+		//     android_x86 {
+		//         key: value,
+		//     },
+		// },
+		if hod.Device() {
+			t := arch.ArchType
+			field := "Android_" + t.Name
+			prefix := "target.android_" + t.Name
+			a.appendProperties(ctx, genProps, archProps.Target, field, prefix)
+		}
+
+		if ctx.Failed() {
+			return
+		}
+	}
+}
+
+func forEachInterface(v reflect.Value, f func(reflect.Value)) {
+	switch v.Kind() {
+	case reflect.Interface:
+		f(v)
+	case reflect.Struct:
+		for i := 0; i < v.NumField(); i++ {
+			forEachInterface(v.Field(i), f)
+		}
+	case reflect.Ptr:
+		forEachInterface(v.Elem(), f)
+	default:
+		panic(fmt.Errorf("Unsupported kind %s", v.Kind()))
+	}
+}
+
+// Get a list of HostTypes from the product variables
+func decodeHostTypesProductVariables(variables productVariables) ([]HostType, error) {
+	ret := []HostType{CurrentHostType()}
+
+	if variables.CrossHost != nil && *variables.CrossHost != "" {
+		switch *variables.CrossHost {
+		case "windows":
+			ret = append(ret, Windows)
+		default:
+			return nil, fmt.Errorf("Unsupported secondary host: %s", *variables.CrossHost)
+		}
+	}
+
+	return ret, nil
+}
+
+// Convert the arch product variables into a list of host and device Arch structs
+func decodeArchProductVariables(variables productVariables) (map[HostType][]Arch, []Arch, error) {
+	if variables.HostArch == nil {
+		return nil, nil, fmt.Errorf("No host primary architecture set")
+	}
+
+	hostArch, err := decodeArch(*variables.HostArch, nil, nil, nil)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	hostArches := []Arch{hostArch}
+
+	if variables.HostSecondaryArch != nil && *variables.HostSecondaryArch != "" {
+		hostSecondaryArch, err := decodeArch(*variables.HostSecondaryArch, nil, nil, nil)
+		if err != nil {
+			return nil, nil, err
+		}
+		hostArches = append(hostArches, hostSecondaryArch)
+	}
+
+	hostTypeArches := map[HostType][]Arch{
+		CurrentHostType(): hostArches,
+	}
+
+	if variables.CrossHost != nil && *variables.CrossHost != "" {
+		if variables.CrossHostArch == nil || *variables.CrossHostArch == "" {
+			return nil, nil, fmt.Errorf("No cross-host primary architecture set")
+		}
+
+		crossHostArch, err := decodeArch(*variables.CrossHostArch, nil, nil, nil)
+		if err != nil {
+			return nil, nil, err
+		}
+
+		crossHostArches := []Arch{crossHostArch}
+
+		if variables.CrossHostSecondaryArch != nil && *variables.CrossHostSecondaryArch != "" {
+			crossHostSecondaryArch, err := decodeArch(*variables.CrossHostSecondaryArch, nil, nil, nil)
+			if err != nil {
+				return nil, nil, err
+			}
+			crossHostArches = append(crossHostArches, crossHostSecondaryArch)
+		}
+
+		switch *variables.CrossHost {
+		case "windows":
+			hostTypeArches[Windows] = crossHostArches
+		default:
+			return nil, nil, fmt.Errorf("Unsupported cross-host: %s", *variables.CrossHost)
+		}
+	}
+
+	if variables.DeviceArch == nil {
+		return nil, nil, fmt.Errorf("No device primary architecture set")
+	}
+
+	deviceArch, err := decodeArch(*variables.DeviceArch, variables.DeviceArchVariant,
+		variables.DeviceCpuVariant, variables.DeviceAbi)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	deviceArches := []Arch{deviceArch}
+
+	if variables.DeviceSecondaryArch != nil && *variables.DeviceSecondaryArch != "" {
+		deviceSecondaryArch, err := decodeArch(*variables.DeviceSecondaryArch,
+			variables.DeviceSecondaryArchVariant, variables.DeviceSecondaryCpuVariant,
+			variables.DeviceSecondaryAbi)
+		if err != nil {
+			return nil, nil, err
+		}
+		deviceArches = append(deviceArches, deviceSecondaryArch)
+	}
+
+	return hostTypeArches, deviceArches, nil
+}
+
+func decodeMegaDevice() ([]Arch, error) {
+	archSettings := []struct {
+		arch        string
+		archVariant string
+		cpuVariant  string
+		abi         []string
+	}{
+		// armv5 is only used for unbundled apps
+		//{"arm", "armv5te", "", []string{"armeabi"}},
+		{"arm", "armv7-a", "generic", []string{"armeabi-v7a"}},
+		{"arm", "armv7-a-neon", "generic", []string{"armeabi-v7a"}},
+		{"arm", "armv7-a-neon", "cortex-a7", []string{"armeabi-v7a"}},
+		{"arm", "armv7-a-neon", "cortex-a8", []string{"armeabi-v7a"}},
+		{"arm", "armv7-a-neon", "cortex-a9", []string{"armeabi-v7a"}},
+		{"arm", "armv7-a-neon", "cortex-a15", []string{"armeabi-v7a"}},
+		{"arm", "armv7-a-neon", "cortex-a53", []string{"armeabi-v7a"}},
+		{"arm", "armv7-a-neon", "cortex-a53.a57", []string{"armeabi-v7a"}},
+		{"arm", "armv7-a-neon", "denver", []string{"armeabi-v7a"}},
+		{"arm", "armv7-a-neon", "krait", []string{"armeabi-v7a"}},
+		{"arm64", "armv8-a", "cortex-a53", []string{"arm64-v8a"}},
+		{"arm64", "armv8-a", "denver64", []string{"arm64-v8a"}},
+		{"mips", "mips32-fp", "", []string{"mips"}},
+		{"mips", "mips32r2-fp", "", []string{"mips"}},
+		{"mips", "mips32r2-fp-xburst", "", []string{"mips"}},
+		{"mips", "mips32r6", "", []string{"mips"}},
+		// mips32r2dsp[r2]-fp fails in the assembler for divdf3.c in compiler-rt:
+		// (same errors in make and soong)
+		//   Error: invalid operands `mtlo $ac0,$11'
+		//   Error: invalid operands `mthi $ac0,$12'
+		//{"mips", "mips32r2dsp-fp", "", []string{"mips"}},
+		//{"mips", "mips32r2dspr2-fp", "", []string{"mips"}},
+		// mips64r2 is mismatching 64r2 and 64r6 libraries during linking to libgcc
+		//{"mips64", "mips64r2", "", []string{"mips64"}},
+		{"mips64", "mips64r6", "", []string{"mips64"}},
+		{"x86", "", "", []string{"x86"}},
+		{"x86", "atom", "", []string{"x86"}},
+		{"x86", "haswell", "", []string{"x86"}},
+		{"x86", "ivybridge", "", []string{"x86"}},
+		{"x86", "sandybridge", "", []string{"x86"}},
+		{"x86", "silvermont", "", []string{"x86"}},
+		{"x86", "x86_64", "", []string{"x86"}},
+		{"x86_64", "", "", []string{"x86_64"}},
+		{"x86_64", "haswell", "", []string{"x86_64"}},
+		{"x86_64", "ivybridge", "", []string{"x86_64"}},
+		{"x86_64", "sandybridge", "", []string{"x86_64"}},
+		{"x86_64", "silvermont", "", []string{"x86_64"}},
+	}
+
+	var ret []Arch
+
+	for _, config := range archSettings {
+		arch, err := decodeArch(config.arch, &config.archVariant,
+			&config.cpuVariant, &config.abi)
+		if err != nil {
+			return nil, err
+		}
+		ret = append(ret, arch)
+	}
+
+	return ret, nil
+}
+
+// Convert a set of strings from product variables into a single Arch struct
+func decodeArch(arch string, archVariant, cpuVariant *string, abi *[]string) (Arch, error) {
+	stringPtr := func(p *string) string {
+		if p != nil {
+			return *p
+		}
+		return ""
+	}
+
+	slicePtr := func(p *[]string) []string {
+		if p != nil {
+			return *p
+		}
+		return nil
+	}
+
+	archType, ok := archTypeMap[arch]
+	if !ok {
+		return Arch{}, fmt.Errorf("unknown arch %q", arch)
+	}
+
+	a := Arch{
+		ArchType:    archType,
+		ArchVariant: stringPtr(archVariant),
+		CpuVariant:  stringPtr(cpuVariant),
+		Abi:         slicePtr(abi),
+	}
+
+	if a.ArchVariant == a.ArchType.Name || a.ArchVariant == "generic" {
+		a.ArchVariant = ""
+	}
+
+	if a.CpuVariant == a.ArchType.Name || a.CpuVariant == "generic" {
+		a.CpuVariant = ""
+	}
+
+	for i := 0; i < len(a.Abi); i++ {
+		if a.Abi[i] == "" {
+			a.Abi = append(a.Abi[:i], a.Abi[i+1:]...)
+			i--
+		}
+	}
+
+	if featureMap, ok := archFeatureMap[archType]; ok {
+		a.ArchFeatures = featureMap[a.ArchVariant]
+	}
+
+	return a, nil
+}
+
+// Use the module multilib setting to select one or more arches from an arch list
+func decodeMultilib(multilib string, arches []Arch) ([]Arch, error) {
+	buildArches := []Arch{}
+	switch multilib {
+	case "common":
+		buildArches = append(buildArches, commonArch)
+	case "both":
+		buildArches = append(buildArches, arches...)
+	case "first":
+		buildArches = append(buildArches, arches[0])
+	case "32":
+		for _, a := range arches {
+			if a.ArchType.Multilib == "lib32" {
+				buildArches = append(buildArches, a)
+			}
+		}
+	case "64":
+		for _, a := range arches {
+			if a.ArchType.Multilib == "lib64" {
+				buildArches = append(buildArches, a)
+			}
+		}
+	default:
+		return nil, fmt.Errorf(`compile_multilib must be "both", "first", "32", or "64", found %q`,
+			multilib)
+		//buildArches = append(buildArches, arches[0])
+	}
+
+	return buildArches, nil
+}
diff --git a/android/config.go b/android/config.go
new file mode 100644
index 0000000..8701960
--- /dev/null
+++ b/android/config.go
@@ -0,0 +1,323 @@
+// 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 android
+
+import (
+	"encoding/json"
+	"fmt"
+	"os"
+	"path/filepath"
+	"runtime"
+	"strings"
+	"sync"
+
+	"github.com/google/blueprint/proptools"
+)
+
+var Bool = proptools.Bool
+
+// The configuration file name
+const configFileName = "soong.config"
+const productVariablesFileName = "soong.variables"
+
+// A FileConfigurableOptions contains options which can be configured by the
+// config file. These will be included in the config struct.
+type FileConfigurableOptions struct {
+	Mega_device *bool `json:",omitempty"`
+}
+
+func (f *FileConfigurableOptions) SetDefaultConfig() {
+	*f = FileConfigurableOptions{}
+}
+
+type Config struct {
+	*config
+}
+
+// A config object represents the entire build configuration for Android.
+type config struct {
+	FileConfigurableOptions
+	ProductVariables productVariables
+
+	ConfigFileName           string
+	ProductVariablesFileName string
+
+	DeviceArches []Arch
+	HostArches   map[HostType][]Arch
+
+	srcDir   string // the path of the root source directory
+	buildDir string // the path of the build output directory
+
+	envLock   sync.Mutex
+	envDeps   map[string]string
+	envFrozen bool
+
+	inMake bool
+}
+
+type jsonConfigurable interface {
+	SetDefaultConfig()
+}
+
+func loadConfig(config *config) error {
+	err := loadFromConfigFile(&config.FileConfigurableOptions, config.ConfigFileName)
+	if err != nil {
+		return err
+	}
+
+	return loadFromConfigFile(&config.ProductVariables, config.ProductVariablesFileName)
+}
+
+// loads configuration options from a JSON file in the cwd.
+func loadFromConfigFile(configurable jsonConfigurable, filename string) error {
+	// Try to open the file
+	configFileReader, err := os.Open(filename)
+	defer configFileReader.Close()
+	if os.IsNotExist(err) {
+		// Need to create a file, so that blueprint & ninja don't get in
+		// a dependency tracking loop.
+		// Make a file-configurable-options with defaults, write it out using
+		// a json writer.
+		configurable.SetDefaultConfig()
+		err = saveToConfigFile(configurable, filename)
+		if err != nil {
+			return err
+		}
+	} else {
+		// Make a decoder for it
+		jsonDecoder := json.NewDecoder(configFileReader)
+		err = jsonDecoder.Decode(configurable)
+		if err != nil {
+			return fmt.Errorf("config file: %s did not parse correctly: "+err.Error(), filename)
+		}
+	}
+
+	// No error
+	return nil
+}
+
+func saveToConfigFile(config jsonConfigurable, filename string) error {
+	data, err := json.MarshalIndent(&config, "", "    ")
+	if err != nil {
+		return fmt.Errorf("cannot marshal config data: %s", err.Error())
+	}
+
+	configFileWriter, err := os.Create(filename)
+	if err != nil {
+		return fmt.Errorf("cannot create empty config file %s: %s\n", filename, err.Error())
+	}
+	defer configFileWriter.Close()
+
+	_, err = configFileWriter.Write(data)
+	if err != nil {
+		return fmt.Errorf("default config file: %s could not be written: %s", filename, err.Error())
+	}
+
+	_, err = configFileWriter.WriteString("\n")
+	if err != nil {
+		return fmt.Errorf("default config file: %s could not be written: %s", filename, err.Error())
+	}
+
+	return nil
+}
+
+// New creates a new Config object.  The srcDir argument specifies the path to
+// the root source directory. It also loads the config file, if found.
+func NewConfig(srcDir, buildDir string) (Config, error) {
+	// Make a config with default options
+	config := Config{
+		config: &config{
+			ConfigFileName:           filepath.Join(buildDir, configFileName),
+			ProductVariablesFileName: filepath.Join(buildDir, productVariablesFileName),
+
+			srcDir:   srcDir,
+			buildDir: buildDir,
+			envDeps:  make(map[string]string),
+		},
+	}
+
+	// Sanity check the build and source directories. This won't catch strange
+	// configurations with symlinks, but at least checks the obvious cases.
+	absBuildDir, err := filepath.Abs(buildDir)
+	if err != nil {
+		return Config{}, err
+	}
+
+	absSrcDir, err := filepath.Abs(srcDir)
+	if err != nil {
+		return Config{}, err
+	}
+
+	if strings.HasPrefix(absSrcDir, absBuildDir) {
+		return Config{}, fmt.Errorf("Build dir must not contain source directory")
+	}
+
+	// Load any configurable options from the configuration file
+	err = loadConfig(config.config)
+	if err != nil {
+		return Config{}, err
+	}
+
+	inMakeFile := filepath.Join(buildDir, ".soong.in_make")
+	if _, err := os.Stat(inMakeFile); err == nil {
+		config.inMake = true
+	}
+
+	hostArches, deviceArches, err := decodeArchProductVariables(config.ProductVariables)
+	if err != nil {
+		return Config{}, err
+	}
+
+	if Bool(config.Mega_device) {
+		deviceArches, err = decodeMegaDevice()
+		if err != nil {
+			return Config{}, err
+		}
+	}
+
+	config.HostArches = hostArches
+	config.DeviceArches = deviceArches
+
+	return config, nil
+}
+
+func (c *config) RemoveAbandonedFiles() bool {
+	return false
+}
+
+// PrebuiltOS returns the name of the host OS used in prebuilts directories
+func (c *config) PrebuiltOS() string {
+	switch runtime.GOOS {
+	case "linux":
+		return "linux-x86"
+	case "darwin":
+		return "darwin-x86"
+	default:
+		panic("Unknown GOOS")
+	}
+}
+
+// GoRoot returns the path to the root directory of the Go toolchain.
+func (c *config) GoRoot() string {
+	return fmt.Sprintf("%s/prebuilts/go/%s", c.srcDir, c.PrebuiltOS())
+}
+
+func (c *config) CpPreserveSymlinksFlags() string {
+	switch runtime.GOOS {
+	case "darwin":
+		return "-R"
+	case "linux":
+		return "-d"
+	default:
+		return ""
+	}
+}
+
+func (c *config) Getenv(key string) string {
+	var val string
+	var exists bool
+	c.envLock.Lock()
+	if val, exists = c.envDeps[key]; !exists {
+		if c.envFrozen {
+			panic("Cannot access new environment variables after envdeps are frozen")
+		}
+		val = os.Getenv(key)
+		c.envDeps[key] = val
+	}
+	c.envLock.Unlock()
+	return val
+}
+
+func (c *config) EnvDeps() map[string]string {
+	c.envLock.Lock()
+	c.envFrozen = true
+	c.envLock.Unlock()
+	return c.envDeps
+}
+
+func (c *config) EmbeddedInMake() bool {
+	return c.inMake
+}
+
+// DeviceName returns the name of the current device target
+// TODO: take an AndroidModuleContext to select the device name for multi-device builds
+func (c *config) DeviceName() string {
+	return *c.ProductVariables.DeviceName
+}
+
+func (c *config) DeviceUsesClang() bool {
+	if c.ProductVariables.DeviceUsesClang != nil {
+		return *c.ProductVariables.DeviceUsesClang
+	}
+	return true
+}
+
+func (c *config) ResourceOverlays() []SourcePath {
+	return nil
+}
+
+func (c *config) PlatformVersion() string {
+	return "M"
+}
+
+func (c *config) PlatformSdkVersion() string {
+	return "22"
+}
+
+func (c *config) BuildNumber() string {
+	return "000000"
+}
+
+func (c *config) ProductAaptConfig() []string {
+	return []string{"normal", "large", "xlarge", "hdpi", "xhdpi", "xxhdpi"}
+}
+
+func (c *config) ProductAaptPreferredConfig() string {
+	return "xhdpi"
+}
+
+func (c *config) ProductAaptCharacteristics() string {
+	return "nosdcard"
+}
+
+func (c *config) DefaultAppCertificateDir(ctx PathContext) SourcePath {
+	return PathForSource(ctx, "build/target/product/security")
+}
+
+func (c *config) DefaultAppCertificate(ctx PathContext) SourcePath {
+	return c.DefaultAppCertificateDir(ctx).Join(ctx, "testkey")
+}
+
+func (c *config) AllowMissingDependencies() bool {
+	return Bool(c.ProductVariables.Allow_missing_dependencies)
+}
+
+func (c *config) SkipDeviceInstall() bool {
+	return c.EmbeddedInMake() || Bool(c.Mega_device)
+}
+
+func (c *config) SanitizeHost() []string {
+	if c.ProductVariables.SanitizeHost == nil {
+		return nil
+	}
+	return *c.ProductVariables.SanitizeHost
+}
+
+func (c *config) SanitizeDevice() []string {
+	if c.ProductVariables.SanitizeDevice == nil {
+		return nil
+	}
+	return *c.ProductVariables.SanitizeDevice
+}
diff --git a/android/defaults.go b/android/defaults.go
new file mode 100644
index 0000000..f9cf0cc
--- /dev/null
+++ b/android/defaults.go
@@ -0,0 +1,133 @@
+// 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 android
+
+import (
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
+)
+
+type defaultsDependencyTag struct {
+	blueprint.BaseDependencyTag
+}
+
+var DefaultsDepTag defaultsDependencyTag
+
+type defaultsProperties struct {
+	Defaults []string
+}
+
+type DefaultableModule struct {
+	defaultsProperties    defaultsProperties
+	defaultableProperties []interface{}
+}
+
+func (d *DefaultableModule) defaults() *defaultsProperties {
+	return &d.defaultsProperties
+}
+
+func (d *DefaultableModule) setProperties(props []interface{}) {
+	d.defaultableProperties = props
+}
+
+type Defaultable interface {
+	defaults() *defaultsProperties
+	setProperties([]interface{})
+	applyDefaults(TopDownMutatorContext, Defaults)
+}
+
+var _ Defaultable = (*DefaultableModule)(nil)
+
+func InitDefaultableModule(module Module, d Defaultable,
+	props ...interface{}) (blueprint.Module, []interface{}) {
+
+	d.setProperties(props)
+
+	props = append(props, d.defaults())
+
+	return module, props
+}
+
+type DefaultsModule struct {
+	defaultProperties []interface{}
+}
+
+type Defaults interface {
+	isDefaults() bool
+	setProperties([]interface{})
+	properties() []interface{}
+}
+
+func (d *DefaultsModule) isDefaults() bool {
+	return true
+}
+
+func (d *DefaultsModule) properties() []interface{} {
+	return d.defaultProperties
+}
+
+func (d *DefaultsModule) setProperties(props []interface{}) {
+	d.defaultProperties = props
+}
+
+func InitDefaultsModule(module Module, d Defaults, props ...interface{}) (blueprint.Module, []interface{}) {
+	d.setProperties(props)
+
+	return module, props
+}
+
+var _ Defaults = (*DefaultsModule)(nil)
+
+func (defaultable *DefaultableModule) applyDefaults(ctx TopDownMutatorContext,
+	defaults Defaults) {
+
+	for _, prop := range defaultable.defaultableProperties {
+		for _, def := range defaults.properties() {
+			if proptools.TypeEqual(prop, def) {
+				err := proptools.PrependProperties(prop, def, nil)
+				if err != nil {
+					if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
+						ctx.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
+					} else {
+						panic(err)
+					}
+				}
+			}
+		}
+	}
+}
+
+func defaultsDepsMutator(ctx BottomUpMutatorContext) {
+	if defaultable, ok := ctx.Module().(Defaultable); ok {
+		ctx.AddDependency(ctx.Module(), DefaultsDepTag, defaultable.defaults().Defaults...)
+	}
+}
+
+func defaultsMutator(ctx TopDownMutatorContext) {
+	if defaultable, ok := ctx.Module().(Defaultable); ok {
+		for _, defaultsDep := range defaultable.defaults().Defaults {
+			ctx.VisitDirectDeps(func(m blueprint.Module) {
+				if ctx.OtherModuleName(m) == defaultsDep {
+					if defaultsModule, ok := m.(Defaults); ok {
+						defaultable.applyDefaults(ctx, defaultsModule)
+					} else {
+						ctx.PropertyErrorf("defaults", "module %s is not an defaults module",
+							ctx.OtherModuleName(m))
+					}
+				}
+			})
+		}
+	}
+}
diff --git a/android/defs.go b/android/defs.go
new file mode 100644
index 0000000..be28e8b
--- /dev/null
+++ b/android/defs.go
@@ -0,0 +1,73 @@
+// 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 android
+
+import (
+	"github.com/google/blueprint"
+	_ "github.com/google/blueprint/bootstrap"
+)
+
+var (
+	pctx = NewPackageContext("android/soong/common")
+
+	cpPreserveSymlinks = pctx.VariableConfigMethod("cpPreserveSymlinks",
+		Config.CpPreserveSymlinksFlags)
+
+	// A phony rule that is not the built-in Ninja phony rule.  The built-in
+	// phony rule has special behavior that is sometimes not desired.  See the
+	// Ninja docs for more details.
+	Phony = pctx.StaticRule("Phony",
+		blueprint.RuleParams{
+			Command:     "# phony $out",
+			Description: "phony $out",
+		})
+
+	// GeneratedFile is a rule for indicating that a given file was generated
+	// while running soong.  This allows the file to be cleaned up if it ever
+	// stops being generated by soong.
+	GeneratedFile = pctx.StaticRule("GeneratedFile",
+		blueprint.RuleParams{
+			Command:     "# generated $out",
+			Description: "generated $out",
+			Generator:   true,
+		})
+
+	// A copy rule.
+	Cp = pctx.StaticRule("Cp",
+		blueprint.RuleParams{
+			Command:     "cp $cpPreserveSymlinks $cpFlags $in $out",
+			Description: "cp $out",
+		},
+		"cpFlags")
+
+	// A symlink rule.
+	Symlink = pctx.StaticRule("Symlink",
+		blueprint.RuleParams{
+			Command:     "ln -f -s $fromPath $out",
+			Description: "symlink $out",
+		},
+		"fromPath")
+
+	ErrorRule = pctx.StaticRule("Error",
+		blueprint.RuleParams{
+			Command:     `echo "$error" && false`,
+			Description: "error building $out",
+		},
+		"error")
+)
+
+func init() {
+	pctx.Import("github.com/google/blueprint/bootstrap")
+}
diff --git a/android/env.go b/android/env.go
new file mode 100644
index 0000000..f9d8030
--- /dev/null
+++ b/android/env.go
@@ -0,0 +1,55 @@
+// 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 android
+
+import (
+	"android/soong"
+	"android/soong/env"
+
+	"github.com/google/blueprint"
+)
+
+// This file supports dependencies on environment variables.  During build manifest generation,
+// any dependency on an environment variable is added to a list.  During the singleton phase
+// a JSON file is written containing the current value of all used environment variables.
+// The next time the top-level build script is run, it uses the soong_env executable to
+// compare the contents of the environment variables, rewriting the file if necessary to cause
+// a manifest regeneration.
+
+func init() {
+	soong.RegisterSingletonType("env", EnvSingleton)
+}
+
+func EnvSingleton() blueprint.Singleton {
+	return &envSingleton{}
+}
+
+type envSingleton struct{}
+
+func (c *envSingleton) GenerateBuildActions(ctx blueprint.SingletonContext) {
+	envDeps := ctx.Config().(Config).EnvDeps()
+
+	envFile := PathForOutput(ctx, ".soong.environment")
+	if ctx.Failed() {
+		return
+	}
+
+	err := env.WriteEnvFile(envFile.String(), envDeps)
+	if err != nil {
+		ctx.Errorf(err.Error())
+	}
+
+	ctx.AddNinjaFileDeps(envFile.String())
+}
diff --git a/android/glob.go b/android/glob.go
new file mode 100644
index 0000000..aec5fa9
--- /dev/null
+++ b/android/glob.go
@@ -0,0 +1,120 @@
+// 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 android
+
+import (
+	"fmt"
+	"path/filepath"
+
+	"github.com/google/blueprint"
+
+	"android/soong/glob"
+)
+
+// This file supports globbing source files in Blueprints files.
+//
+// The build.ninja file needs to be regenerated any time a file matching the glob is added
+// or removed.  The naive solution is to have the build.ninja file depend on all the
+// traversed directories, but this will cause the regeneration step to run every time a
+// non-matching file is added to a traversed directory, including backup files created by
+// editors.
+//
+// The solution implemented here optimizes out regenerations when the directory modifications
+// don't match the glob by having the build.ninja file depend on an intermedate file that
+// is only updated when a file matching the glob is added or removed.  The intermediate file
+// depends on the traversed directories via a depfile.  The depfile is used to avoid build
+// errors if a directory is deleted - a direct dependency on the deleted directory would result
+// in a build failure with a "missing and no known rule to make it" error.
+
+var (
+	globCmd = filepath.Join("${bootstrap.BinDir}", "soong_glob")
+
+	// globRule rule traverses directories to produce a list of files that match $glob
+	// and writes it to $out if it has changed, and writes the directories to $out.d
+	globRule = pctx.StaticRule("globRule",
+		blueprint.RuleParams{
+			Command:     fmt.Sprintf(`%s -o $out $excludes "$glob"`, globCmd),
+			CommandDeps: []string{globCmd},
+			Description: "glob $glob",
+
+			Restat:  true,
+			Deps:    blueprint.DepsGCC,
+			Depfile: "$out.d",
+		},
+		"glob", "excludes")
+)
+
+func hasGlob(in []string) bool {
+	for _, s := range in {
+		if glob.IsGlob(s) {
+			return true
+		}
+	}
+
+	return false
+}
+
+// The subset of ModuleContext and SingletonContext needed by Glob
+type globContext interface {
+	Build(pctx blueprint.PackageContext, params blueprint.BuildParams)
+	AddNinjaFileDeps(deps ...string)
+}
+
+func Glob(ctx globContext, outDir string, globPattern string, excludes []string) ([]string, error) {
+	fileListFile := filepath.Join(outDir, "glob", globToString(globPattern)+".glob")
+	depFile := fileListFile + ".d"
+
+	// Get a globbed file list, and write out fileListFile and depFile
+	files, err := glob.GlobWithDepFile(globPattern, fileListFile, depFile, excludes)
+	if err != nil {
+		return nil, err
+	}
+
+	GlobRule(ctx, globPattern, excludes, fileListFile, depFile)
+
+	// Make build.ninja depend on the fileListFile
+	ctx.AddNinjaFileDeps(fileListFile)
+
+	return files, nil
+}
+
+func GlobRule(ctx globContext, globPattern string, excludes []string,
+	fileListFile, depFile string) {
+
+	// Create a rule to rebuild fileListFile if a directory in depFile changes.  fileListFile
+	// will only be rewritten if it has changed, preventing unnecesary build.ninja regenerations.
+	ctx.Build(pctx, blueprint.BuildParams{
+		Rule:    globRule,
+		Outputs: []string{fileListFile},
+		Args: map[string]string{
+			"glob":     globPattern,
+			"excludes": JoinWithPrefixAndQuote(excludes, "-e "),
+		},
+	})
+}
+
+func globToString(glob string) string {
+	ret := ""
+	for _, c := range glob {
+		if c >= 'a' && c <= 'z' ||
+			c >= 'A' && c <= 'Z' ||
+			c >= '0' && c <= '9' ||
+			c == '_' || c == '-' || c == '/' {
+			ret += string(c)
+		}
+	}
+
+	return ret
+}
diff --git a/android/makevars.go b/android/makevars.go
new file mode 100644
index 0000000..d9c5c6d
--- /dev/null
+++ b/android/makevars.go
@@ -0,0 +1,242 @@
+// Copyright 2016 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 (
+	"bytes"
+	"fmt"
+	"io/ioutil"
+	"os"
+
+	"android/soong"
+
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
+)
+
+///////////////////////////////////////////////////////////////////////////////
+// Interface for other packages to use to declare make variables
+type MakeVarsContext interface {
+	Config() Config
+
+	// Verify the make variable matches the Soong version, fail the build
+	// if it does not. If the make variable is empty, just set it.
+	Strict(name, ninjaStr string)
+	// Check to see if the make variable matches the Soong version, warn if
+	// it does not. If the make variable is empty, just set it.
+	Check(name, ninjaStr string)
+
+	// These are equivalent to the above, but sort the make and soong
+	// variables before comparing them. They also show the unique entries
+	// in each list when displaying the difference, instead of the entire
+	// string.
+	StrictSorted(name, ninjaStr string)
+	CheckSorted(name, ninjaStr string)
+}
+
+type MakeVarsProvider func(ctx MakeVarsContext)
+
+func RegisterMakeVarsProvider(pctx blueprint.PackageContext, provider MakeVarsProvider) {
+	makeVarsProviders = append(makeVarsProviders, makeVarsProvider{pctx, provider})
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+func init() {
+	soong.RegisterSingletonType("makevars", makeVarsSingletonFunc)
+}
+
+func makeVarsSingletonFunc() blueprint.Singleton {
+	return &makeVarsSingleton{}
+}
+
+type makeVarsSingleton struct{}
+
+type makeVarsProvider struct {
+	pctx blueprint.PackageContext
+	call MakeVarsProvider
+}
+
+var makeVarsProviders []makeVarsProvider
+
+type makeVarsContext struct {
+	config Config
+	ctx    blueprint.SingletonContext
+	pctx   blueprint.PackageContext
+	vars   []makeVarsVariable
+}
+
+var _ MakeVarsContext = &makeVarsContext{}
+
+type makeVarsVariable struct {
+	name   string
+	value  string
+	sort   bool
+	strict bool
+}
+
+func (s *makeVarsSingleton) GenerateBuildActions(ctx blueprint.SingletonContext) {
+	config := ctx.Config().(Config)
+
+	if !config.EmbeddedInMake() {
+		return
+	}
+
+	outFile := PathForOutput(ctx, "make_vars"+proptools.String(config.ProductVariables.Make_suffix)+".mk").String()
+
+	if ctx.Failed() {
+		return
+	}
+
+	vars := []makeVarsVariable{}
+	for _, provider := range makeVarsProviders {
+		mctx := &makeVarsContext{
+			config: config,
+			ctx:    ctx,
+			pctx:   provider.pctx,
+		}
+
+		provider.call(mctx)
+
+		vars = append(vars, mctx.vars...)
+	}
+
+	if ctx.Failed() {
+		return
+	}
+
+	outBytes := s.writeVars(vars)
+
+	if _, err := os.Stat(outFile); err == nil {
+		if data, err := ioutil.ReadFile(outFile); err == nil {
+			if bytes.Equal(data, outBytes) {
+				return
+			}
+		}
+	}
+
+	if err := ioutil.WriteFile(outFile, outBytes, 0666); err != nil {
+		ctx.Errorf(err.Error())
+	}
+}
+
+func (s *makeVarsSingleton) writeVars(vars []makeVarsVariable) []byte {
+	buf := &bytes.Buffer{}
+
+	fmt.Fprintln(buf, `# Autogenerated file
+
+# Compares SOONG_$(1) against $(1), and warns if they are not equal.
+#
+# If the original variable is empty, then just set it to the SOONG_ version.
+#
+# $(1): Name of the variable to check
+# $(2): If not-empty, sort the values before comparing
+# $(3): Extra snippet to run if it does not match
+define soong-compare-var
+ifneq ($$($(1)),)
+  my_val_make := $(if $(2),$$(sort $$($(1))),$$($(1)))
+  my_val_soong := $(if $(2),$$(sort $$(SOONG_$(1))),$$(SOONG_$(1)))
+  ifneq ($$(my_val_make),$$(my_val_soong))
+    $$(warning $(1) does not match between Make and Soong:)
+    $(if $(2),$$(warning Make  adds: $$(filter-out $$(my_val_soong),$$(my_val_make))),$$(warning Make : $$(my_val_make)))
+    $(if $(2),$$(warning Soong adds: $$(filter-out $$(my_val_make),$$(my_val_soong))),$$(warning Soong: $$(my_val_soong)))
+    $(3)
+  endif
+  my_val_make :=
+  my_val_soong :=
+else
+  $(1) := $$(SOONG_$(1))
+endif
+endef
+
+my_check_failed := false
+
+`)
+
+	// Write all the strict checks out first so that if one of them errors,
+	// we get all of the strict errors printed, but not the non-strict
+	// warnings.
+	for _, v := range vars {
+		if !v.strict {
+			continue
+		}
+
+		sort := ""
+		if v.sort {
+			sort = "true"
+		}
+
+		fmt.Fprintf(buf, "SOONG_%s := %s\n", v.name, v.value)
+		fmt.Fprintf(buf, "$(eval $(call soong-compare-var,%s,%s,my_check_failed := true))\n\n", v.name, sort)
+	}
+
+	fmt.Fprintln(buf, `
+ifneq ($(my_check_failed),false)
+  $(error Soong variable check failed)
+endif
+my_check_failed :=
+
+
+`)
+
+	for _, v := range vars {
+		if v.strict {
+			continue
+		}
+
+		sort := ""
+		if v.sort {
+			sort = "true"
+		}
+
+		fmt.Fprintf(buf, "SOONG_%s := %s\n", v.name, v.value)
+		fmt.Fprintf(buf, "$(eval $(call soong-compare-var,%s,%s))\n\n", v.name, sort)
+	}
+
+	fmt.Fprintln(buf, "\nsoong-compare-var :=")
+
+	return buf.Bytes()
+}
+
+func (c *makeVarsContext) Config() Config {
+	return c.config
+}
+
+func (c *makeVarsContext) addVariable(name, ninjaStr string, strict, sort bool) {
+	value, err := c.ctx.Eval(c.pctx, ninjaStr)
+	if err != nil {
+		c.ctx.Errorf(err.Error())
+	}
+	c.vars = append(c.vars, makeVarsVariable{
+		name:   name,
+		value:  value,
+		strict: strict,
+		sort:   sort,
+	})
+}
+
+func (c *makeVarsContext) Strict(name, ninjaStr string) {
+	c.addVariable(name, ninjaStr, true, false)
+}
+func (c *makeVarsContext) StrictSorted(name, ninjaStr string) {
+	c.addVariable(name, ninjaStr, true, true)
+}
+
+func (c *makeVarsContext) Check(name, ninjaStr string) {
+	c.addVariable(name, ninjaStr, false, false)
+}
+func (c *makeVarsContext) CheckSorted(name, ninjaStr string) {
+	c.addVariable(name, ninjaStr, false, true)
+}
diff --git a/android/module.go b/android/module.go
new file mode 100644
index 0000000..08abf78
--- /dev/null
+++ b/android/module.go
@@ -0,0 +1,695 @@
+// 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 android
+
+import (
+	"fmt"
+	"path/filepath"
+	"strings"
+
+	"android/soong"
+	"android/soong/glob"
+
+	"github.com/google/blueprint"
+)
+
+var (
+	DeviceSharedLibrary = "shared_library"
+	DeviceStaticLibrary = "static_library"
+	DeviceExecutable    = "executable"
+	HostSharedLibrary   = "host_shared_library"
+	HostStaticLibrary   = "host_static_library"
+	HostExecutable      = "host_executable"
+)
+
+type ModuleBuildParams struct {
+	Rule      blueprint.Rule
+	Output    WritablePath
+	Outputs   WritablePaths
+	Input     Path
+	Inputs    Paths
+	Implicit  Path
+	Implicits Paths
+	OrderOnly Paths
+	Default   bool
+	Args      map[string]string
+}
+
+type androidBaseContext interface {
+	Arch() Arch
+	HostOrDevice() HostOrDevice
+	HostType() HostType
+	Host() bool
+	Device() bool
+	Darwin() bool
+	Debug() bool
+	AConfig() Config
+	Proprietary() bool
+	InstallInData() bool
+}
+
+type BaseContext interface {
+	blueprint.BaseModuleContext
+	androidBaseContext
+}
+
+type ModuleContext interface {
+	blueprint.ModuleContext
+	androidBaseContext
+
+	// Similar to Build, but takes Paths instead of []string,
+	// and performs more verification.
+	ModuleBuild(pctx blueprint.PackageContext, params ModuleBuildParams)
+
+	ExpandSources(srcFiles, excludes []string) Paths
+	Glob(outDir, globPattern string, excludes []string) Paths
+
+	InstallFile(installPath OutputPath, srcPath Path, deps ...Path) OutputPath
+	InstallFileName(installPath OutputPath, name string, srcPath Path, deps ...Path) OutputPath
+	CheckbuildFile(srcPath Path)
+
+	AddMissingDependencies(deps []string)
+}
+
+type Module interface {
+	blueprint.Module
+
+	GenerateAndroidBuildActions(ModuleContext)
+
+	base() *ModuleBase
+	Enabled() bool
+	HostOrDevice() HostOrDevice
+	InstallInData() bool
+}
+
+type commonProperties struct {
+	Name string
+	Deps []string
+	Tags []string
+
+	// emit build rules for this module
+	Enabled *bool `android:"arch_variant"`
+
+	// control whether this module compiles for 32-bit, 64-bit, or both.  Possible values
+	// are "32" (compile for 32-bit only), "64" (compile for 64-bit only), "both" (compile for both
+	// architectures), or "first" (compile for 64-bit on a 64-bit platform, and 32-bit on a 32-bit
+	// platform
+	Compile_multilib string
+
+	// whether this is a proprietary vendor module, and should be installed into /vendor
+	Proprietary bool
+
+	// Set by HostOrDeviceMutator
+	CompileHostOrDevice HostOrDevice `blueprint:"mutated"`
+
+	// Set by HostTypeMutator
+	CompileHostType HostType `blueprint:"mutated"`
+
+	// Set by ArchMutator
+	CompileArch Arch `blueprint:"mutated"`
+
+	// Set by InitAndroidModule
+	HostOrDeviceSupported HostOrDeviceSupported `blueprint:"mutated"`
+}
+
+type hostAndDeviceProperties struct {
+	Host_supported   bool
+	Device_supported bool
+}
+
+type Multilib string
+
+const (
+	MultilibBoth    Multilib = "both"
+	MultilibFirst   Multilib = "first"
+	MultilibCommon  Multilib = "common"
+	MultilibDefault Multilib = ""
+)
+
+func InitAndroidModule(m Module,
+	propertyStructs ...interface{}) (blueprint.Module, []interface{}) {
+
+	base := m.base()
+	base.module = m
+
+	propertyStructs = append(propertyStructs, &base.commonProperties, &base.variableProperties)
+
+	return m, propertyStructs
+}
+
+func InitAndroidArchModule(m Module, hod HostOrDeviceSupported, defaultMultilib Multilib,
+	propertyStructs ...interface{}) (blueprint.Module, []interface{}) {
+
+	_, propertyStructs = InitAndroidModule(m, propertyStructs...)
+
+	base := m.base()
+	base.commonProperties.HostOrDeviceSupported = hod
+	base.commonProperties.Compile_multilib = string(defaultMultilib)
+
+	switch hod {
+	case HostAndDeviceSupported:
+		// Default to module to device supported, host not supported, can override in module
+		// properties
+		base.hostAndDeviceProperties.Device_supported = true
+		fallthrough
+	case HostAndDeviceDefault:
+		propertyStructs = append(propertyStructs, &base.hostAndDeviceProperties)
+	}
+
+	return InitArchModule(m, propertyStructs...)
+}
+
+// A AndroidModuleBase object contains the properties that are common to all Android
+// modules.  It should be included as an anonymous field in every module
+// struct definition.  InitAndroidModule should then be called from the module's
+// factory function, and the return values from InitAndroidModule should be
+// returned from the factory function.
+//
+// The AndroidModuleBase type is responsible for implementing the
+// GenerateBuildActions method to support the blueprint.Module interface. This
+// method will then call the module's GenerateAndroidBuildActions method once
+// for each build variant that is to be built. GenerateAndroidBuildActions is
+// passed a AndroidModuleContext rather than the usual blueprint.ModuleContext.
+// AndroidModuleContext exposes extra functionality specific to the Android build
+// system including details about the particular build variant that is to be
+// generated.
+//
+// For example:
+//
+//     import (
+//         "android/soong/common"
+//         "github.com/google/blueprint"
+//     )
+//
+//     type myModule struct {
+//         common.AndroidModuleBase
+//         properties struct {
+//             MyProperty string
+//         }
+//     }
+//
+//     func NewMyModule() (blueprint.Module, []interface{}) {
+//         m := &myModule{}
+//         return common.InitAndroidModule(m, &m.properties)
+//     }
+//
+//     func (m *myModule) GenerateAndroidBuildActions(ctx common.AndroidModuleContext) {
+//         // Get the CPU architecture for the current build variant.
+//         variantArch := ctx.Arch()
+//
+//         // ...
+//     }
+type ModuleBase struct {
+	// Putting the curiously recurring thing pointing to the thing that contains
+	// the thing pattern to good use.
+	module Module
+
+	commonProperties        commonProperties
+	variableProperties      variableProperties
+	hostAndDeviceProperties hostAndDeviceProperties
+	generalProperties       []interface{}
+	archProperties          []*archProperties
+
+	noAddressSanitizer bool
+	installFiles       Paths
+	checkbuildFiles    Paths
+
+	// Used by buildTargetSingleton to create checkbuild and per-directory build targets
+	// Only set on the final variant of each module
+	installTarget    string
+	checkbuildTarget string
+	blueprintDir     string
+}
+
+func (a *ModuleBase) base() *ModuleBase {
+	return a
+}
+
+func (a *ModuleBase) SetHostOrDevice(hod HostOrDevice) {
+	a.commonProperties.CompileHostOrDevice = hod
+}
+
+func (a *ModuleBase) SetHostType(ht HostType) {
+	a.commonProperties.CompileHostType = ht
+}
+
+func (a *ModuleBase) SetArch(arch Arch) {
+	a.commonProperties.CompileArch = arch
+}
+
+func (a *ModuleBase) HostOrDevice() HostOrDevice {
+	return a.commonProperties.CompileHostOrDevice
+}
+
+func (a *ModuleBase) HostType() HostType {
+	return a.commonProperties.CompileHostType
+}
+
+func (a *ModuleBase) Host() bool {
+	return a.HostOrDevice().Host()
+}
+
+func (a *ModuleBase) Arch() Arch {
+	return a.commonProperties.CompileArch
+}
+
+func (a *ModuleBase) HostSupported() bool {
+	return a.commonProperties.HostOrDeviceSupported == HostSupported ||
+		a.commonProperties.HostOrDeviceSupported == HostAndDeviceSupported &&
+			a.hostAndDeviceProperties.Host_supported
+}
+
+func (a *ModuleBase) DeviceSupported() bool {
+	return a.commonProperties.HostOrDeviceSupported == DeviceSupported ||
+		a.commonProperties.HostOrDeviceSupported == HostAndDeviceSupported &&
+			a.hostAndDeviceProperties.Device_supported
+}
+
+func (a *ModuleBase) Enabled() bool {
+	if a.commonProperties.Enabled == nil {
+		if a.HostSupported() && a.HostOrDevice().Host() && a.HostType() == Windows {
+			return false
+		} else {
+			return true
+		}
+	}
+	return *a.commonProperties.Enabled
+}
+
+func (a *ModuleBase) computeInstallDeps(
+	ctx blueprint.ModuleContext) Paths {
+
+	result := Paths{}
+	ctx.VisitDepsDepthFirstIf(isFileInstaller,
+		func(m blueprint.Module) {
+			fileInstaller := m.(fileInstaller)
+			files := fileInstaller.filesToInstall()
+			result = append(result, files...)
+		})
+
+	return result
+}
+
+func (a *ModuleBase) filesToInstall() Paths {
+	return a.installFiles
+}
+
+func (p *ModuleBase) NoAddressSanitizer() bool {
+	return p.noAddressSanitizer
+}
+
+func (p *ModuleBase) InstallInData() bool {
+	return false
+}
+
+func (a *ModuleBase) generateModuleTarget(ctx blueprint.ModuleContext) {
+	if a != ctx.FinalModule().(Module).base() {
+		return
+	}
+
+	allInstalledFiles := Paths{}
+	allCheckbuildFiles := Paths{}
+	ctx.VisitAllModuleVariants(func(module blueprint.Module) {
+		a := module.(Module).base()
+		allInstalledFiles = append(allInstalledFiles, a.installFiles...)
+		allCheckbuildFiles = append(allCheckbuildFiles, a.checkbuildFiles...)
+	})
+
+	deps := []string{}
+
+	if len(allInstalledFiles) > 0 {
+		name := ctx.ModuleName() + "-install"
+		ctx.Build(pctx, blueprint.BuildParams{
+			Rule:      blueprint.Phony,
+			Outputs:   []string{name},
+			Implicits: allInstalledFiles.Strings(),
+			Optional:  ctx.Config().(Config).EmbeddedInMake(),
+		})
+		deps = append(deps, name)
+		a.installTarget = name
+	}
+
+	if len(allCheckbuildFiles) > 0 {
+		name := ctx.ModuleName() + "-checkbuild"
+		ctx.Build(pctx, blueprint.BuildParams{
+			Rule:      blueprint.Phony,
+			Outputs:   []string{name},
+			Implicits: allCheckbuildFiles.Strings(),
+			Optional:  true,
+		})
+		deps = append(deps, name)
+		a.checkbuildTarget = name
+	}
+
+	if len(deps) > 0 {
+		suffix := ""
+		if ctx.Config().(Config).EmbeddedInMake() {
+			suffix = "-soong"
+		}
+
+		ctx.Build(pctx, blueprint.BuildParams{
+			Rule:      blueprint.Phony,
+			Outputs:   []string{ctx.ModuleName() + suffix},
+			Implicits: deps,
+			Optional:  true,
+		})
+
+		a.blueprintDir = ctx.ModuleDir()
+	}
+}
+
+func (a *ModuleBase) androidBaseContextFactory(ctx blueprint.BaseModuleContext) androidBaseContextImpl {
+	return androidBaseContextImpl{
+		arch:          a.commonProperties.CompileArch,
+		hod:           a.commonProperties.CompileHostOrDevice,
+		ht:            a.commonProperties.CompileHostType,
+		proprietary:   a.commonProperties.Proprietary,
+		config:        ctx.Config().(Config),
+		installInData: a.module.InstallInData(),
+	}
+}
+
+func (a *ModuleBase) GenerateBuildActions(ctx blueprint.ModuleContext) {
+	androidCtx := &androidModuleContext{
+		ModuleContext:          ctx,
+		androidBaseContextImpl: a.androidBaseContextFactory(ctx),
+		installDeps:            a.computeInstallDeps(ctx),
+		installFiles:           a.installFiles,
+		missingDeps:            ctx.GetMissingDependencies(),
+	}
+
+	if !a.Enabled() {
+		return
+	}
+
+	a.module.GenerateAndroidBuildActions(androidCtx)
+	if ctx.Failed() {
+		return
+	}
+
+	a.installFiles = append(a.installFiles, androidCtx.installFiles...)
+	a.checkbuildFiles = append(a.checkbuildFiles, androidCtx.checkbuildFiles...)
+
+	a.generateModuleTarget(ctx)
+	if ctx.Failed() {
+		return
+	}
+}
+
+type androidBaseContextImpl struct {
+	arch          Arch
+	hod           HostOrDevice
+	ht            HostType
+	debug         bool
+	config        Config
+	proprietary   bool
+	installInData bool
+}
+
+type androidModuleContext struct {
+	blueprint.ModuleContext
+	androidBaseContextImpl
+	installDeps     Paths
+	installFiles    Paths
+	checkbuildFiles Paths
+	missingDeps     []string
+}
+
+func (a *androidModuleContext) ninjaError(outputs []string, err error) {
+	a.ModuleContext.Build(pctx, blueprint.BuildParams{
+		Rule:     ErrorRule,
+		Outputs:  outputs,
+		Optional: true,
+		Args: map[string]string{
+			"error": err.Error(),
+		},
+	})
+	return
+}
+
+func (a *androidModuleContext) Build(pctx blueprint.PackageContext, params blueprint.BuildParams) {
+	if a.missingDeps != nil && params.Rule != globRule {
+		a.ninjaError(params.Outputs, fmt.Errorf("module %s missing dependencies: %s\n",
+			a.ModuleName(), strings.Join(a.missingDeps, ", ")))
+		return
+	}
+
+	params.Optional = true
+	a.ModuleContext.Build(pctx, params)
+}
+
+func (a *androidModuleContext) ModuleBuild(pctx blueprint.PackageContext, params ModuleBuildParams) {
+	bparams := blueprint.BuildParams{
+		Rule:      params.Rule,
+		Outputs:   params.Outputs.Strings(),
+		Inputs:    params.Inputs.Strings(),
+		Implicits: params.Implicits.Strings(),
+		OrderOnly: params.OrderOnly.Strings(),
+		Args:      params.Args,
+		Optional:  !params.Default,
+	}
+
+	if params.Output != nil {
+		bparams.Outputs = append(bparams.Outputs, params.Output.String())
+	}
+	if params.Input != nil {
+		bparams.Inputs = append(bparams.Inputs, params.Input.String())
+	}
+	if params.Implicit != nil {
+		bparams.Implicits = append(bparams.Implicits, params.Implicit.String())
+	}
+
+	if a.missingDeps != nil {
+		a.ninjaError(bparams.Outputs, fmt.Errorf("module %s missing dependencies: %s\n",
+			a.ModuleName(), strings.Join(a.missingDeps, ", ")))
+		return
+	}
+
+	a.ModuleContext.Build(pctx, bparams)
+}
+
+func (a *androidModuleContext) GetMissingDependencies() []string {
+	return a.missingDeps
+}
+
+func (a *androidModuleContext) AddMissingDependencies(deps []string) {
+	if deps != nil {
+		a.missingDeps = append(a.missingDeps, deps...)
+	}
+}
+
+func (a *androidBaseContextImpl) Arch() Arch {
+	return a.arch
+}
+
+func (a *androidBaseContextImpl) HostOrDevice() HostOrDevice {
+	return a.hod
+}
+
+func (a *androidBaseContextImpl) HostType() HostType {
+	return a.ht
+}
+
+func (a *androidBaseContextImpl) Host() bool {
+	return a.hod.Host()
+}
+
+func (a *androidBaseContextImpl) Device() bool {
+	return a.hod.Device()
+}
+
+func (a *androidBaseContextImpl) Darwin() bool {
+	return a.hod.Host() && a.ht == Darwin
+}
+
+func (a *androidBaseContextImpl) Debug() bool {
+	return a.debug
+}
+
+func (a *androidBaseContextImpl) AConfig() Config {
+	return a.config
+}
+
+func (a *androidBaseContextImpl) Proprietary() bool {
+	return a.proprietary
+}
+
+func (a *androidBaseContextImpl) InstallInData() bool {
+	return a.installInData
+}
+
+func (a *androidModuleContext) InstallFileName(installPath OutputPath, name string, srcPath Path,
+	deps ...Path) OutputPath {
+
+	fullInstallPath := installPath.Join(a, name)
+
+	if a.Host() || !a.AConfig().SkipDeviceInstall() {
+		deps = append(deps, a.installDeps...)
+
+		a.ModuleBuild(pctx, ModuleBuildParams{
+			Rule:      Cp,
+			Output:    fullInstallPath,
+			Input:     srcPath,
+			OrderOnly: Paths(deps),
+			Default:   !a.AConfig().EmbeddedInMake(),
+		})
+
+		a.installFiles = append(a.installFiles, fullInstallPath)
+	}
+	a.checkbuildFiles = append(a.checkbuildFiles, srcPath)
+	return fullInstallPath
+}
+
+func (a *androidModuleContext) InstallFile(installPath OutputPath, srcPath Path, deps ...Path) OutputPath {
+	return a.InstallFileName(installPath, filepath.Base(srcPath.String()), srcPath, deps...)
+}
+
+func (a *androidModuleContext) CheckbuildFile(srcPath Path) {
+	a.checkbuildFiles = append(a.checkbuildFiles, srcPath)
+}
+
+type fileInstaller interface {
+	filesToInstall() Paths
+}
+
+func isFileInstaller(m blueprint.Module) bool {
+	_, ok := m.(fileInstaller)
+	return ok
+}
+
+func isAndroidModule(m blueprint.Module) bool {
+	_, ok := m.(Module)
+	return ok
+}
+
+func findStringInSlice(str string, slice []string) int {
+	for i, s := range slice {
+		if s == str {
+			return i
+		}
+	}
+	return -1
+}
+
+func (ctx *androidModuleContext) ExpandSources(srcFiles, excludes []string) Paths {
+	prefix := PathForModuleSrc(ctx).String()
+	for i, e := range excludes {
+		j := findStringInSlice(e, srcFiles)
+		if j != -1 {
+			srcFiles = append(srcFiles[:j], srcFiles[j+1:]...)
+		}
+
+		excludes[i] = filepath.Join(prefix, e)
+	}
+
+	globbedSrcFiles := make(Paths, 0, len(srcFiles))
+	for _, s := range srcFiles {
+		if glob.IsGlob(s) {
+			globbedSrcFiles = append(globbedSrcFiles, ctx.Glob("src_glob", filepath.Join(prefix, s), excludes)...)
+		} else {
+			globbedSrcFiles = append(globbedSrcFiles, PathForModuleSrc(ctx, s))
+		}
+	}
+
+	return globbedSrcFiles
+}
+
+func (ctx *androidModuleContext) Glob(outDir, globPattern string, excludes []string) Paths {
+	ret, err := Glob(ctx, PathForModuleOut(ctx, outDir).String(), globPattern, excludes)
+	if err != nil {
+		ctx.ModuleErrorf("glob: %s", err.Error())
+	}
+	return pathsForModuleSrcFromFullPath(ctx, ret)
+}
+
+func init() {
+	soong.RegisterSingletonType("buildtarget", BuildTargetSingleton)
+}
+
+func BuildTargetSingleton() blueprint.Singleton {
+	return &buildTargetSingleton{}
+}
+
+type buildTargetSingleton struct{}
+
+func (c *buildTargetSingleton) GenerateBuildActions(ctx blueprint.SingletonContext) {
+	checkbuildDeps := []string{}
+
+	dirModules := make(map[string][]string)
+
+	ctx.VisitAllModules(func(module blueprint.Module) {
+		if a, ok := module.(Module); ok {
+			blueprintDir := a.base().blueprintDir
+			installTarget := a.base().installTarget
+			checkbuildTarget := a.base().checkbuildTarget
+
+			if checkbuildTarget != "" {
+				checkbuildDeps = append(checkbuildDeps, checkbuildTarget)
+				dirModules[blueprintDir] = append(dirModules[blueprintDir], checkbuildTarget)
+			}
+
+			if installTarget != "" {
+				dirModules[blueprintDir] = append(dirModules[blueprintDir], installTarget)
+			}
+		}
+	})
+
+	suffix := ""
+	if ctx.Config().(Config).EmbeddedInMake() {
+		suffix = "-soong"
+	}
+
+	// Create a top-level checkbuild target that depends on all modules
+	ctx.Build(pctx, blueprint.BuildParams{
+		Rule:      blueprint.Phony,
+		Outputs:   []string{"checkbuild" + suffix},
+		Implicits: checkbuildDeps,
+		Optional:  true,
+	})
+
+	// Create a mm/<directory> target that depends on all modules in a directory
+	dirs := sortedKeys(dirModules)
+	for _, dir := range dirs {
+		ctx.Build(pctx, blueprint.BuildParams{
+			Rule:      blueprint.Phony,
+			Outputs:   []string{filepath.Join("mm", dir)},
+			Implicits: dirModules[dir],
+			// HACK: checkbuild should be an optional build, but force it
+			// enabled for now in standalone builds
+			Optional: ctx.Config().(Config).EmbeddedInMake(),
+		})
+	}
+}
+
+type AndroidModulesByName struct {
+	slice []Module
+	ctx   interface {
+		ModuleName(blueprint.Module) string
+		ModuleSubDir(blueprint.Module) string
+	}
+}
+
+func (s AndroidModulesByName) Len() int { return len(s.slice) }
+func (s AndroidModulesByName) Less(i, j int) bool {
+	mi, mj := s.slice[i], s.slice[j]
+	ni, nj := s.ctx.ModuleName(mi), s.ctx.ModuleName(mj)
+
+	if ni != nj {
+		return ni < nj
+	} else {
+		return s.ctx.ModuleSubDir(mi) < s.ctx.ModuleSubDir(mj)
+	}
+}
+func (s AndroidModulesByName) Swap(i, j int) { s.slice[i], s.slice[j] = s.slice[j], s.slice[i] }
diff --git a/android/mutator.go b/android/mutator.go
new file mode 100644
index 0000000..9405f2d
--- /dev/null
+++ b/android/mutator.go
@@ -0,0 +1,69 @@
+// 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 android
+
+import (
+	"android/soong"
+
+	"github.com/google/blueprint"
+)
+
+type AndroidTopDownMutator func(TopDownMutatorContext)
+
+type TopDownMutatorContext interface {
+	blueprint.TopDownMutatorContext
+	androidBaseContext
+}
+
+type androidTopDownMutatorContext struct {
+	blueprint.TopDownMutatorContext
+	androidBaseContextImpl
+}
+
+type AndroidBottomUpMutator func(BottomUpMutatorContext)
+
+type BottomUpMutatorContext interface {
+	blueprint.BottomUpMutatorContext
+	androidBaseContext
+}
+
+type androidBottomUpMutatorContext struct {
+	blueprint.BottomUpMutatorContext
+	androidBaseContextImpl
+}
+
+func RegisterBottomUpMutator(name string, mutator AndroidBottomUpMutator) {
+	soong.RegisterBottomUpMutator(name, func(ctx blueprint.BottomUpMutatorContext) {
+		if a, ok := ctx.Module().(Module); ok {
+			actx := &androidBottomUpMutatorContext{
+				BottomUpMutatorContext: ctx,
+				androidBaseContextImpl: a.base().androidBaseContextFactory(ctx),
+			}
+			mutator(actx)
+		}
+	})
+}
+
+func RegisterTopDownMutator(name string, mutator AndroidTopDownMutator) {
+	soong.RegisterTopDownMutator(name, func(ctx blueprint.TopDownMutatorContext) {
+		if a, ok := ctx.Module().(Module); ok {
+			actx := &androidTopDownMutatorContext{
+				TopDownMutatorContext:  ctx,
+				androidBaseContextImpl: a.base().androidBaseContextFactory(ctx),
+			}
+			mutator(actx)
+		}
+	})
+}
diff --git a/android/package_ctx.go b/android/package_ctx.go
new file mode 100644
index 0000000..56ba2d8
--- /dev/null
+++ b/android/package_ctx.go
@@ -0,0 +1,133 @@
+// 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 android
+
+import (
+	"fmt"
+
+	"github.com/google/blueprint"
+)
+
+// AndroidPackageContext is a wrapper for blueprint.PackageContext that adds
+// some android-specific helper functions.
+type AndroidPackageContext struct {
+	blueprint.PackageContext
+}
+
+func NewPackageContext(pkgPath string) AndroidPackageContext {
+	return AndroidPackageContext{blueprint.NewPackageContext(pkgPath)}
+}
+
+// configErrorWrapper can be used with Path functions when a Context is not
+// available. A Config can be provided, and errors are stored as a list for
+// later retrieval.
+//
+// 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
+	config Config
+	errors []error
+}
+
+var _ PathContext = &configErrorWrapper{}
+var _ errorfContext = &configErrorWrapper{}
+
+func (e *configErrorWrapper) Config() interface{} {
+	return e.config
+}
+func (e *configErrorWrapper) Errorf(format string, args ...interface{}) {
+	e.errors = append(e.errors, fmt.Errorf(format, args...))
+}
+func (e *configErrorWrapper) AddNinjaFileDeps(deps ...string) {
+	e.pctx.AddNinjaFileDeps(deps...)
+}
+
+// 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{}}
+		p := safePathForSource(ctx, path)
+		if len(ctx.errors) > 0 {
+			return "", ctx.errors[0]
+		}
+		return p.String(), nil
+	})
+}
+
+// HostBinVariable returns a Variable whose value is the path to a host tool
+// 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) {
+		ctx := &configErrorWrapper{p, config.(Config), []error{}}
+		p := PathForOutput(ctx, "host", ctx.config.PrebuiltOS(), "bin", path)
+		if len(ctx.errors) > 0 {
+			return "", ctx.errors[0]
+		}
+		return p.String(), nil
+	})
+}
+
+// HostJavaToolVariable returns a Variable whose value is the path to a host
+// 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{}}
+		p := PathForOutput(ctx, "host", ctx.config.PrebuiltOS(), "framework", path)
+		if len(ctx.errors) > 0 {
+			return "", ctx.errors[0]
+		}
+		return p.String(), nil
+	})
+}
+
+// IntermediatesPathVariable returns a Variable whose value is the intermediate
+// 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{}}
+		p := PathForIntermediates(ctx, path)
+		if len(ctx.errors) > 0 {
+			return "", ctx.errors[0]
+		}
+		return p.String(), nil
+	})
+}
+
+// PrefixedPathsForOptionalSourceVariable returns a Variable whose value is the
+// 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) PrefixedPathsForOptionalSourceVariable(
+	name, prefix string, paths []string) blueprint.Variable {
+
+	return p.VariableFunc(name, func(config interface{}) (string, error) {
+		ctx := &configErrorWrapper{p, config.(Config), []error{}}
+		paths := PathsForOptionalSource(ctx, "", paths)
+		if len(ctx.errors) > 0 {
+			return "", ctx.errors[0]
+		}
+		return JoinWithPrefix(paths.Strings(), prefix), nil
+	})
+}
diff --git a/android/paths.go b/android/paths.go
new file mode 100644
index 0000000..910ebd2
--- /dev/null
+++ b/android/paths.go
@@ -0,0 +1,668 @@
+// 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 android
+
+import (
+	"fmt"
+	"os"
+	"path/filepath"
+	"reflect"
+	"strings"
+
+	"android/soong/glob"
+
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/pathtools"
+)
+
+// PathContext is the subset of a (Module|Singleton)Context required by the
+// Path methods.
+type PathContext interface {
+	Config() interface{}
+	AddNinjaFileDeps(deps ...string)
+}
+
+var _ PathContext = blueprint.SingletonContext(nil)
+var _ PathContext = blueprint.ModuleContext(nil)
+
+// errorfContext is the interface containing the Errorf method matching the
+// Errorf method in blueprint.SingletonContext.
+type errorfContext interface {
+	Errorf(format string, args ...interface{})
+}
+
+var _ errorfContext = blueprint.SingletonContext(nil)
+
+// moduleErrorf is the interface containing the ModuleErrorf method matching
+// the ModuleErrorf method in blueprint.ModuleContext.
+type moduleErrorf interface {
+	ModuleErrorf(format string, args ...interface{})
+}
+
+var _ moduleErrorf = blueprint.ModuleContext(nil)
+
+// pathConfig returns the android Config interface associated to the context.
+// Panics if the context isn't affiliated with an android build.
+func pathConfig(ctx PathContext) Config {
+	if ret, ok := ctx.Config().(Config); ok {
+		return ret
+	}
+	panic("Paths may only be used on Soong builds")
+}
+
+// reportPathError will register an error with the attached context. It
+// attempts ctx.ModuleErrorf for a better error message first, then falls
+// back to ctx.Errorf.
+func reportPathError(ctx PathContext, format string, args ...interface{}) {
+	if mctx, ok := ctx.(moduleErrorf); ok {
+		mctx.ModuleErrorf(format, args...)
+	} else if ectx, ok := ctx.(errorfContext); ok {
+		ectx.Errorf(format, args...)
+	} else {
+		panic(fmt.Sprintf(format, args...))
+	}
+}
+
+type Path interface {
+	// Returns the path in string form
+	String() string
+
+	// Returns the current file extension of the path
+	Ext() string
+}
+
+// WritablePath is a type of path that can be used as an output for build rules.
+type WritablePath interface {
+	Path
+
+	writablePath()
+}
+
+type genPathProvider interface {
+	genPathWithExt(ctx ModuleContext, ext string) ModuleGenPath
+}
+type objPathProvider interface {
+	objPathWithExt(ctx ModuleContext, subdir, ext string) ModuleObjPath
+}
+type resPathProvider interface {
+	resPathWithName(ctx ModuleContext, name string) ModuleResPath
+}
+
+// GenPathWithExt derives a new file path in ctx's generated sources directory
+// from the current path, but with the new extension.
+func GenPathWithExt(ctx ModuleContext, p Path, ext string) ModuleGenPath {
+	if path, ok := p.(genPathProvider); ok {
+		return path.genPathWithExt(ctx, ext)
+	}
+	reportPathError(ctx, "Tried to create generated file from unsupported path: %s(%s)", reflect.TypeOf(p).Name(), p)
+	return PathForModuleGen(ctx)
+}
+
+// ObjPathWithExt derives a new file path in ctx's object directory from the
+// current path, but with the new extension.
+func ObjPathWithExt(ctx ModuleContext, p Path, subdir, ext string) ModuleObjPath {
+	if path, ok := p.(objPathProvider); ok {
+		return path.objPathWithExt(ctx, subdir, ext)
+	}
+	reportPathError(ctx, "Tried to create object file from unsupported path: %s (%s)", reflect.TypeOf(p).Name(), p)
+	return PathForModuleObj(ctx)
+}
+
+// ResPathWithName derives a new path in ctx's output resource directory, using
+// the current path to create the directory name, and the `name` argument for
+// the filename.
+func ResPathWithName(ctx ModuleContext, p Path, name string) ModuleResPath {
+	if path, ok := p.(resPathProvider); ok {
+		return path.resPathWithName(ctx, name)
+	}
+	reportPathError(ctx, "Tried to create object file from unsupported path: %s (%s)", reflect.TypeOf(p).Name(), p)
+	return PathForModuleRes(ctx)
+}
+
+// OptionalPath is a container that may or may not contain a valid Path.
+type OptionalPath struct {
+	valid bool
+	path  Path
+}
+
+// OptionalPathForPath returns an OptionalPath containing the path.
+func OptionalPathForPath(path Path) OptionalPath {
+	if path == nil {
+		return OptionalPath{}
+	}
+	return OptionalPath{valid: true, path: path}
+}
+
+// Valid returns whether there is a valid path
+func (p OptionalPath) Valid() bool {
+	return p.valid
+}
+
+// Path returns the Path embedded in this OptionalPath. You must be sure that
+// there is a valid path, since this method will panic if there is not.
+func (p OptionalPath) Path() Path {
+	if !p.valid {
+		panic("Requesting an invalid path")
+	}
+	return p.path
+}
+
+// String returns the string version of the Path, or "" if it isn't valid.
+func (p OptionalPath) String() string {
+	if p.valid {
+		return p.path.String()
+	} else {
+		return ""
+	}
+}
+
+// Paths is a slice of Path objects, with helpers to operate on the collection.
+type Paths []Path
+
+// PathsForSource returns Paths rooted from SrcDir
+func PathsForSource(ctx PathContext, paths []string) Paths {
+	if pathConfig(ctx).AllowMissingDependencies() {
+		if modCtx, ok := ctx.(ModuleContext); ok {
+			ret := make(Paths, 0, len(paths))
+			intermediates := filepath.Join(modCtx.ModuleDir(), modCtx.ModuleName(), modCtx.ModuleSubDir(), "missing")
+			for _, path := range paths {
+				p := OptionalPathForSource(ctx, intermediates, path)
+				if p.Valid() {
+					ret = append(ret, p.Path())
+				} else {
+					modCtx.AddMissingDependencies([]string{path})
+				}
+			}
+			return ret
+		}
+	}
+	ret := make(Paths, len(paths))
+	for i, path := range paths {
+		ret[i] = PathForSource(ctx, path)
+	}
+	return ret
+}
+
+// PathsForOptionalSource returns a list of Paths rooted from SrcDir that are
+// found in the tree. If any are not found, they are omitted from the list,
+// and dependencies are added so that we're re-run when they are added.
+func PathsForOptionalSource(ctx PathContext, intermediates string, paths []string) Paths {
+	ret := make(Paths, 0, len(paths))
+	for _, path := range paths {
+		p := OptionalPathForSource(ctx, intermediates, path)
+		if p.Valid() {
+			ret = append(ret, p.Path())
+		}
+	}
+	return ret
+}
+
+// PathsForModuleSrc returns Paths rooted from the module's local source
+// directory
+func PathsForModuleSrc(ctx ModuleContext, paths []string) Paths {
+	ret := make(Paths, len(paths))
+	for i, path := range paths {
+		ret[i] = PathForModuleSrc(ctx, path)
+	}
+	return ret
+}
+
+// pathsForModuleSrcFromFullPath returns Paths rooted from the module's local
+// source directory, but strip the local source directory from the beginning of
+// each string.
+func pathsForModuleSrcFromFullPath(ctx ModuleContext, paths []string) Paths {
+	prefix := filepath.Join(ctx.AConfig().srcDir, ctx.ModuleDir()) + "/"
+	ret := make(Paths, 0, len(paths))
+	for _, p := range paths {
+		path := filepath.Clean(p)
+		if !strings.HasPrefix(path, prefix) {
+			reportPathError(ctx, "Path '%s' is not in module source directory '%s'", p, prefix)
+			continue
+		}
+		ret = append(ret, PathForModuleSrc(ctx, path[len(prefix):]))
+	}
+	return ret
+}
+
+// PathsWithOptionalDefaultForModuleSrc returns Paths rooted from the module's
+// local source directory. If none are provided, use the default if it exists.
+func PathsWithOptionalDefaultForModuleSrc(ctx ModuleContext, input []string, def string) Paths {
+	if len(input) > 0 {
+		return PathsForModuleSrc(ctx, input)
+	}
+	// Use Glob so that if the default doesn't exist, a dependency is added so that when it
+	// is created, we're run again.
+	path := filepath.Join(ctx.AConfig().srcDir, ctx.ModuleDir(), def)
+	return ctx.Glob("default", path, []string{})
+}
+
+// Strings returns the Paths in string form
+func (p Paths) Strings() []string {
+	if p == nil {
+		return nil
+	}
+	ret := make([]string, len(p))
+	for i, path := range p {
+		ret[i] = path.String()
+	}
+	return ret
+}
+
+// WritablePaths is a slice of WritablePaths, used for multiple outputs.
+type WritablePaths []WritablePath
+
+// Strings returns the string forms of the writable paths.
+func (p WritablePaths) Strings() []string {
+	if p == nil {
+		return nil
+	}
+	ret := make([]string, len(p))
+	for i, path := range p {
+		ret[i] = path.String()
+	}
+	return ret
+}
+
+type basePath struct {
+	path   string
+	config Config
+}
+
+func (p basePath) Ext() string {
+	return filepath.Ext(p.path)
+}
+
+// SourcePath is a Path representing a file path rooted from SrcDir
+type SourcePath struct {
+	basePath
+}
+
+var _ Path = SourcePath{}
+
+// safePathForSource is for paths that we expect are safe -- only for use by go
+// code that is embedding ninja variables in paths
+func safePathForSource(ctx PathContext, path string) SourcePath {
+	p := validateSafePath(ctx, path)
+	ret := SourcePath{basePath{p, pathConfig(ctx)}}
+
+	abs, err := filepath.Abs(ret.String())
+	if err != nil {
+		reportPathError(ctx, "%s", err.Error())
+		return ret
+	}
+	buildroot, err := filepath.Abs(pathConfig(ctx).buildDir)
+	if err != nil {
+		reportPathError(ctx, "%s", err.Error())
+		return ret
+	}
+	if strings.HasPrefix(abs, buildroot) {
+		reportPathError(ctx, "source path %s is in output", abs)
+		return ret
+	}
+
+	return ret
+}
+
+// PathForSource returns a SourcePath for the provided paths... (which are
+// joined together with filepath.Join). This also validates that the path
+// doesn't escape the source dir, or is contained in the build dir. On error, it
+// will return a usable, but invalid SourcePath, and report a ModuleError.
+func PathForSource(ctx PathContext, paths ...string) SourcePath {
+	p := validatePath(ctx, paths...)
+	ret := SourcePath{basePath{p, pathConfig(ctx)}}
+
+	abs, err := filepath.Abs(ret.String())
+	if err != nil {
+		reportPathError(ctx, "%s", err.Error())
+		return ret
+	}
+	buildroot, err := filepath.Abs(pathConfig(ctx).buildDir)
+	if err != nil {
+		reportPathError(ctx, "%s", err.Error())
+		return ret
+	}
+	if strings.HasPrefix(abs, buildroot) {
+		reportPathError(ctx, "source path %s is in output", abs)
+		return ret
+	}
+
+	if _, err = os.Stat(ret.String()); err != nil {
+		if os.IsNotExist(err) {
+			reportPathError(ctx, "source path %s does not exist", ret)
+		} else {
+			reportPathError(ctx, "%s: %s", ret, err.Error())
+		}
+	}
+	return ret
+}
+
+// OptionalPathForSource returns an OptionalPath with the SourcePath if the
+// path exists, or an empty OptionalPath if it doesn't exist. Dependencies are added
+// so that the ninja file will be regenerated if the state of the path changes.
+func OptionalPathForSource(ctx PathContext, intermediates string, paths ...string) OptionalPath {
+	if len(paths) == 0 {
+		// For when someone forgets the 'intermediates' argument
+		panic("Missing path(s)")
+	}
+
+	p := validatePath(ctx, paths...)
+	path := SourcePath{basePath{p, pathConfig(ctx)}}
+
+	abs, err := filepath.Abs(path.String())
+	if err != nil {
+		reportPathError(ctx, "%s", err.Error())
+		return OptionalPath{}
+	}
+	buildroot, err := filepath.Abs(pathConfig(ctx).buildDir)
+	if err != nil {
+		reportPathError(ctx, "%s", err.Error())
+		return OptionalPath{}
+	}
+	if strings.HasPrefix(abs, buildroot) {
+		reportPathError(ctx, "source path %s is in output", abs)
+		return OptionalPath{}
+	}
+
+	if glob.IsGlob(path.String()) {
+		reportPathError(ctx, "path may not contain a glob: %s", path.String())
+		return OptionalPath{}
+	}
+
+	if gctx, ok := ctx.(globContext); ok {
+		// Use glob to produce proper dependencies, even though we only want
+		// a single file.
+		files, err := Glob(gctx, PathForIntermediates(ctx, intermediates).String(), path.String(), nil)
+		if err != nil {
+			reportPathError(ctx, "glob: %s", err.Error())
+			return OptionalPath{}
+		}
+
+		if len(files) == 0 {
+			return OptionalPath{}
+		}
+	} else {
+		// We cannot add build statements in this context, so we fall back to
+		// AddNinjaFileDeps
+		files, dirs, err := pathtools.Glob(path.String())
+		if err != nil {
+			reportPathError(ctx, "glob: %s", err.Error())
+			return OptionalPath{}
+		}
+
+		ctx.AddNinjaFileDeps(dirs...)
+
+		if len(files) == 0 {
+			return OptionalPath{}
+		}
+
+		ctx.AddNinjaFileDeps(path.String())
+	}
+	return OptionalPathForPath(path)
+}
+
+func (p SourcePath) String() string {
+	return filepath.Join(p.config.srcDir, p.path)
+}
+
+// Join creates a new SourcePath with paths... joined with the current path. The
+// provided paths... may not use '..' to escape from the current path.
+func (p SourcePath) Join(ctx PathContext, paths ...string) SourcePath {
+	path := validatePath(ctx, paths...)
+	return PathForSource(ctx, p.path, path)
+}
+
+// OverlayPath returns the overlay for `path' if it exists. This assumes that the
+// SourcePath is the path to a resource overlay directory.
+func (p SourcePath) OverlayPath(ctx ModuleContext, path Path) OptionalPath {
+	var relDir string
+	if moduleSrcPath, ok := path.(ModuleSrcPath); ok {
+		relDir = moduleSrcPath.sourcePath.path
+	} else if srcPath, ok := path.(SourcePath); ok {
+		relDir = srcPath.path
+	} else {
+		reportPathError(ctx, "Cannot find relative path for %s(%s)", reflect.TypeOf(path).Name(), path)
+		return OptionalPath{}
+	}
+	dir := filepath.Join(p.config.srcDir, p.path, relDir)
+	// Use Glob so that we are run again if the directory is added.
+	if glob.IsGlob(dir) {
+		reportPathError(ctx, "Path may not contain a glob: %s", dir)
+	}
+	paths, err := Glob(ctx, PathForModuleOut(ctx, "overlay").String(), dir, []string{})
+	if err != nil {
+		reportPathError(ctx, "glob: %s", err.Error())
+		return OptionalPath{}
+	}
+	if len(paths) == 0 {
+		return OptionalPath{}
+	}
+	relPath, err := filepath.Rel(p.config.srcDir, paths[0])
+	if err != nil {
+		reportPathError(ctx, "%s", err.Error())
+		return OptionalPath{}
+	}
+	return OptionalPathForPath(PathForSource(ctx, relPath))
+}
+
+// OutputPath is a Path representing a file path rooted from the build directory
+type OutputPath struct {
+	basePath
+}
+
+var _ Path = OutputPath{}
+
+// PathForOutput returns an OutputPath for the provided paths... (which are
+// joined together with filepath.Join). This also validates that the path
+// does not escape the build dir. On error, it will return a usable, but invalid
+// OutputPath, and report a ModuleError.
+func PathForOutput(ctx PathContext, paths ...string) OutputPath {
+	path := validatePath(ctx, paths...)
+	return OutputPath{basePath{path, pathConfig(ctx)}}
+}
+
+func (p OutputPath) writablePath() {}
+
+func (p OutputPath) String() string {
+	return filepath.Join(p.config.buildDir, p.path)
+}
+
+func (p OutputPath) RelPathString() string {
+	return p.path
+}
+
+// Join creates a new OutputPath with paths... joined with the current path. The
+// provided paths... may not use '..' to escape from the current path.
+func (p OutputPath) Join(ctx PathContext, paths ...string) OutputPath {
+	path := validatePath(ctx, paths...)
+	return PathForOutput(ctx, p.path, path)
+}
+
+// PathForIntermediates returns an OutputPath representing the top-level
+// intermediates directory.
+func PathForIntermediates(ctx PathContext, paths ...string) OutputPath {
+	path := validatePath(ctx, paths...)
+	return PathForOutput(ctx, ".intermediates", path)
+}
+
+// ModuleSrcPath is a Path representing a file rooted from a module's local source dir
+type ModuleSrcPath struct {
+	basePath
+	sourcePath SourcePath
+	moduleDir  string
+}
+
+var _ Path = ModuleSrcPath{}
+var _ genPathProvider = ModuleSrcPath{}
+var _ objPathProvider = ModuleSrcPath{}
+var _ resPathProvider = ModuleSrcPath{}
+
+// PathForModuleSrc returns a ModuleSrcPath representing the paths... under the
+// module's local source directory.
+func PathForModuleSrc(ctx ModuleContext, paths ...string) ModuleSrcPath {
+	path := validatePath(ctx, paths...)
+	return ModuleSrcPath{basePath{path, ctx.AConfig()}, PathForSource(ctx, ctx.ModuleDir(), path), ctx.ModuleDir()}
+}
+
+// OptionalPathForModuleSrc returns an OptionalPath. The OptionalPath contains a
+// valid path if p is non-nil.
+func OptionalPathForModuleSrc(ctx ModuleContext, p *string) OptionalPath {
+	if p == nil {
+		return OptionalPath{}
+	}
+	return OptionalPathForPath(PathForModuleSrc(ctx, *p))
+}
+
+func (p ModuleSrcPath) String() string {
+	return p.sourcePath.String()
+}
+
+func (p ModuleSrcPath) genPathWithExt(ctx ModuleContext, ext string) ModuleGenPath {
+	return PathForModuleGen(ctx, p.moduleDir, pathtools.ReplaceExtension(p.path, ext))
+}
+
+func (p ModuleSrcPath) objPathWithExt(ctx ModuleContext, subdir, ext string) ModuleObjPath {
+	return PathForModuleObj(ctx, subdir, p.moduleDir, pathtools.ReplaceExtension(p.path, ext))
+}
+
+func (p ModuleSrcPath) resPathWithName(ctx ModuleContext, name string) ModuleResPath {
+	// TODO: Use full directory if the new ctx is not the current ctx?
+	return PathForModuleRes(ctx, p.path, name)
+}
+
+// ModuleOutPath is a Path representing a module's output directory.
+type ModuleOutPath struct {
+	OutputPath
+}
+
+var _ Path = ModuleOutPath{}
+
+// PathForModuleOut returns a Path representing the paths... under the module's
+// output directory.
+func PathForModuleOut(ctx ModuleContext, paths ...string) ModuleOutPath {
+	p := validatePath(ctx, paths...)
+	return ModuleOutPath{PathForOutput(ctx, ".intermediates", ctx.ModuleDir(), ctx.ModuleName(), ctx.ModuleSubDir(), p)}
+}
+
+// ModuleGenPath is a Path representing the 'gen' directory in a module's output
+// directory. Mainly used for generated sources.
+type ModuleGenPath struct {
+	ModuleOutPath
+	path string
+}
+
+var _ Path = ModuleGenPath{}
+var _ genPathProvider = ModuleGenPath{}
+var _ objPathProvider = ModuleGenPath{}
+
+// PathForModuleGen returns a Path representing the paths... under the module's
+// `gen' directory.
+func PathForModuleGen(ctx ModuleContext, paths ...string) ModuleGenPath {
+	p := validatePath(ctx, paths...)
+	return ModuleGenPath{
+		PathForModuleOut(ctx, "gen", p),
+		p,
+	}
+}
+
+func (p ModuleGenPath) genPathWithExt(ctx ModuleContext, ext string) ModuleGenPath {
+	// TODO: make a different path for local vs remote generated files?
+	return PathForModuleGen(ctx, pathtools.ReplaceExtension(p.path, ext))
+}
+
+func (p ModuleGenPath) objPathWithExt(ctx ModuleContext, subdir, ext string) ModuleObjPath {
+	return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
+}
+
+// ModuleObjPath is a Path representing the 'obj' directory in a module's output
+// directory. Used for compiled objects.
+type ModuleObjPath struct {
+	ModuleOutPath
+}
+
+var _ Path = ModuleObjPath{}
+
+// PathForModuleObj returns a Path representing the paths... under the module's
+// 'obj' directory.
+func PathForModuleObj(ctx ModuleContext, paths ...string) ModuleObjPath {
+	p := validatePath(ctx, paths...)
+	return ModuleObjPath{PathForModuleOut(ctx, "obj", p)}
+}
+
+// ModuleResPath is a a Path representing the 'res' directory in a module's
+// output directory.
+type ModuleResPath struct {
+	ModuleOutPath
+}
+
+var _ Path = ModuleResPath{}
+
+// PathForModuleRes returns a Path representing the paths... under the module's
+// 'res' directory.
+func PathForModuleRes(ctx ModuleContext, paths ...string) ModuleResPath {
+	p := validatePath(ctx, paths...)
+	return ModuleResPath{PathForModuleOut(ctx, "res", p)}
+}
+
+// PathForModuleInstall returns a Path representing the install path for the
+// module appended with paths...
+func PathForModuleInstall(ctx ModuleContext, paths ...string) OutputPath {
+	var outPaths []string
+	if ctx.Device() {
+		partition := "system"
+		if ctx.Proprietary() {
+			partition = "vendor"
+		}
+		if ctx.InstallInData() {
+			partition = "data"
+		}
+		outPaths = []string{"target", "product", ctx.AConfig().DeviceName(), partition}
+	} else {
+		outPaths = []string{"host", ctx.HostType().String() + "-x86"}
+	}
+	if ctx.Debug() {
+		outPaths = append([]string{"debug"}, outPaths...)
+	}
+	outPaths = append(outPaths, paths...)
+	return PathForOutput(ctx, outPaths...)
+}
+
+// validateSafePath validates a path that we trust (may contain ninja variables).
+// Ensures that each path component does not attempt to leave its component.
+func validateSafePath(ctx PathContext, paths ...string) string {
+	for _, path := range paths {
+		path := filepath.Clean(path)
+		if path == ".." || strings.HasPrefix(path, "../") || strings.HasPrefix(path, "/") {
+			reportPathError(ctx, "Path is outside directory: %s", path)
+			return ""
+		}
+	}
+	// TODO: filepath.Join isn't necessarily correct with embedded ninja
+	// variables. '..' may remove the entire ninja variable, even if it
+	// will be expanded to multiple nested directories.
+	return filepath.Join(paths...)
+}
+
+// validatePath validates that a path does not include ninja variables, and that
+// each path component does not attempt to leave its component. Returns a joined
+// version of each path component.
+func validatePath(ctx PathContext, paths ...string) string {
+	for _, path := range paths {
+		if strings.Contains(path, "$") {
+			reportPathError(ctx, "Path contains invalid character($): %s", path)
+			return ""
+		}
+	}
+	return validateSafePath(ctx, paths...)
+}
diff --git a/android/paths_test.go b/android/paths_test.go
new file mode 100644
index 0000000..9d69473
--- /dev/null
+++ b/android/paths_test.go
@@ -0,0 +1,182 @@
+// 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 android
+
+import (
+	"errors"
+	"fmt"
+	"reflect"
+	"strings"
+	"testing"
+)
+
+type strsTestCase struct {
+	in  []string
+	out string
+	err []error
+}
+
+var commonValidatePathTestCases = []strsTestCase{
+	{
+		in:  []string{""},
+		out: "",
+	},
+	{
+		in:  []string{"a/b"},
+		out: "a/b",
+	},
+	{
+		in:  []string{"a/b", "c"},
+		out: "a/b/c",
+	},
+	{
+		in:  []string{"a/.."},
+		out: ".",
+	},
+	{
+		in:  []string{"."},
+		out: ".",
+	},
+	{
+		in:  []string{".."},
+		out: "",
+		err: []error{errors.New("Path is outside directory: ..")},
+	},
+	{
+		in:  []string{"../a"},
+		out: "",
+		err: []error{errors.New("Path is outside directory: ../a")},
+	},
+	{
+		in:  []string{"b/../../a"},
+		out: "",
+		err: []error{errors.New("Path is outside directory: ../a")},
+	},
+	{
+		in:  []string{"/a"},
+		out: "",
+		err: []error{errors.New("Path is outside directory: /a")},
+	},
+	{
+		in:  []string{"a", "../b"},
+		out: "",
+		err: []error{errors.New("Path is outside directory: ../b")},
+	},
+	{
+		in:  []string{"a", "b/../../c"},
+		out: "",
+		err: []error{errors.New("Path is outside directory: ../c")},
+	},
+	{
+		in:  []string{"a", "./.."},
+		out: "",
+		err: []error{errors.New("Path is outside directory: ..")},
+	},
+}
+
+var validateSafePathTestCases = append(commonValidatePathTestCases, []strsTestCase{
+	{
+		in:  []string{"$host/../$a"},
+		out: "$a",
+	},
+}...)
+
+var validatePathTestCases = append(commonValidatePathTestCases, []strsTestCase{
+	{
+		in:  []string{"$host/../$a"},
+		out: "",
+		err: []error{errors.New("Path contains invalid character($): $host/../$a")},
+	},
+	{
+		in:  []string{"$host/.."},
+		out: "",
+		err: []error{errors.New("Path contains invalid character($): $host/..")},
+	},
+}...)
+
+func TestValidateSafePath(t *testing.T) {
+	for _, testCase := range validateSafePathTestCases {
+		ctx := &configErrorWrapper{}
+		out := validateSafePath(ctx, testCase.in...)
+		check(t, "validateSafePath", p(testCase.in), out, ctx.errors, testCase.out, testCase.err)
+	}
+}
+
+func TestValidatePath(t *testing.T) {
+	for _, testCase := range validatePathTestCases {
+		ctx := &configErrorWrapper{}
+		out := validatePath(ctx, testCase.in...)
+		check(t, "validatePath", p(testCase.in), out, ctx.errors, testCase.out, testCase.err)
+	}
+}
+
+func TestOptionalPath(t *testing.T) {
+	var path OptionalPath
+	checkInvalidOptionalPath(t, path)
+
+	path = OptionalPathForPath(nil)
+	checkInvalidOptionalPath(t, path)
+}
+
+func checkInvalidOptionalPath(t *testing.T, path OptionalPath) {
+	if path.Valid() {
+		t.Errorf("Uninitialized OptionalPath should not be valid")
+	}
+	if path.String() != "" {
+		t.Errorf("Uninitialized OptionalPath String() should return \"\", not %q", path.String())
+	}
+	defer func() {
+		if r := recover(); r == nil {
+			t.Errorf("Expected a panic when calling Path() on an uninitialized OptionalPath")
+		}
+	}()
+	path.Path()
+}
+
+func check(t *testing.T, testType, testString string,
+	got interface{}, err []error,
+	expected interface{}, expectedErr []error) {
+
+	printedTestCase := false
+	e := func(s string, expected, got interface{}) {
+		if !printedTestCase {
+			t.Errorf("test case %s: %s", testType, testString)
+			printedTestCase = true
+		}
+		t.Errorf("incorrect %s", s)
+		t.Errorf("  expected: %s", p(expected))
+		t.Errorf("       got: %s", p(got))
+	}
+
+	if !reflect.DeepEqual(expectedErr, err) {
+		e("errors:", expectedErr, err)
+	}
+
+	if !reflect.DeepEqual(expected, got) {
+		e("output:", expected, got)
+	}
+}
+
+func p(in interface{}) string {
+	if v, ok := in.([]interface{}); ok {
+		s := make([]string, len(v))
+		for i := range v {
+			s[i] = fmt.Sprintf("%#v", v[i])
+		}
+		return "[" + strings.Join(s, ", ") + "]"
+	} else {
+		return fmt.Sprintf("%#v", in)
+	}
+}
diff --git a/android/util.go b/android/util.go
new file mode 100644
index 0000000..60c8a63
--- /dev/null
+++ b/android/util.go
@@ -0,0 +1,78 @@
+// 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 android
+
+import "sort"
+
+func JoinWithPrefix(strs []string, prefix string) string {
+	if len(strs) == 0 {
+		return ""
+	}
+
+	if len(strs) == 1 {
+		return prefix + strs[0]
+	}
+
+	n := len(" ") * (len(strs) - 1)
+	for _, s := range strs {
+		n += len(prefix) + len(s)
+	}
+
+	ret := make([]byte, 0, n)
+	for i, s := range strs {
+		if i != 0 {
+			ret = append(ret, ' ')
+		}
+		ret = append(ret, prefix...)
+		ret = append(ret, s...)
+	}
+	return string(ret)
+}
+
+func JoinWithPrefixAndQuote(strs []string, prefix string) string {
+	if len(strs) == 0 {
+		return ""
+	}
+
+	if len(strs) == 1 {
+		return prefix + `"` + strs[0] + `"`
+	}
+
+	n := len(" ") * (len(strs) - 1)
+	for _, s := range strs {
+		n += len(prefix) + len(s) + len(`""`)
+	}
+
+	ret := make([]byte, 0, n)
+	for i, s := range strs {
+		if i != 0 {
+			ret = append(ret, ' ')
+		}
+		ret = append(ret, prefix...)
+		ret = append(ret, '"')
+		ret = append(ret, s...)
+		ret = append(ret, '"')
+	}
+	return string(ret)
+}
+
+func sortedKeys(m map[string][]string) []string {
+	s := make([]string, 0, len(m))
+	for k := range m {
+		s = append(s, k)
+	}
+	sort.Strings(s)
+	return s
+}
diff --git a/android/variable.go b/android/variable.go
new file mode 100644
index 0000000..29d7a08
--- /dev/null
+++ b/android/variable.go
@@ -0,0 +1,232 @@
+// 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 android
+
+import (
+	"fmt"
+	"reflect"
+	"runtime"
+	"strings"
+
+	"github.com/google/blueprint/proptools"
+)
+
+func init() {
+	RegisterBottomUpMutator("variable", variableMutator)
+}
+
+type variableProperties struct {
+	Product_variables struct {
+		Platform_sdk_version struct {
+			Asflags []string
+		}
+
+		// unbundled_build is a catch-all property to annotate modules that don't build in one or
+		// more unbundled branches, usually due to dependencies missing from the manifest.
+		Unbundled_build struct {
+			Enabled *bool `android:"arch_variant"`
+		} `android:"arch_variant"`
+
+		Brillo struct {
+			Version_script *string `android:"arch_variant"`
+		} `android:"arch_variant"`
+
+		Malloc_not_svelte struct {
+			Cflags []string
+		}
+
+		Safestack struct {
+			Cflags []string `android:"arch_variant"`
+		} `android:"arch_variant"`
+	} `android:"arch_variant"`
+}
+
+var zeroProductVariables variableProperties
+
+type productVariables struct {
+	// Suffix to add to generated Makefiles
+	Make_suffix *string `json:",omitempty"`
+
+	Platform_sdk_version *int `json:",omitempty"`
+
+	DeviceName        *string   `json:",omitempty"`
+	DeviceArch        *string   `json:",omitempty"`
+	DeviceArchVariant *string   `json:",omitempty"`
+	DeviceCpuVariant  *string   `json:",omitempty"`
+	DeviceAbi         *[]string `json:",omitempty"`
+	DeviceUsesClang   *bool     `json:",omitempty"`
+
+	DeviceSecondaryArch        *string   `json:",omitempty"`
+	DeviceSecondaryArchVariant *string   `json:",omitempty"`
+	DeviceSecondaryCpuVariant  *string   `json:",omitempty"`
+	DeviceSecondaryAbi         *[]string `json:",omitempty"`
+
+	HostArch          *string `json:",omitempty"`
+	HostSecondaryArch *string `json:",omitempty"`
+
+	CrossHost              *string `json:",omitempty"`
+	CrossHostArch          *string `json:",omitempty"`
+	CrossHostSecondaryArch *string `json:",omitempty"`
+
+	Allow_missing_dependencies *bool `json:",omitempty"`
+	Unbundled_build            *bool `json:",omitempty"`
+	Brillo                     *bool `json:",omitempty"`
+	Malloc_not_svelte          *bool `json:",omitempty"`
+	Safestack                  *bool `json:",omitempty"`
+	HostStaticBinaries         *bool `json:",omitempty"`
+
+	SanitizeHost   *[]string `json:",omitempty"`
+	SanitizeDevice *[]string `json:",omitempty"`
+}
+
+func boolPtr(v bool) *bool {
+	return &v
+}
+
+func intPtr(v int) *int {
+	return &v
+}
+
+func stringPtr(v string) *string {
+	return &v
+}
+
+func (v *productVariables) SetDefaultConfig() {
+	*v = productVariables{
+		Platform_sdk_version:       intPtr(22),
+		HostArch:                   stringPtr("x86_64"),
+		HostSecondaryArch:          stringPtr("x86"),
+		DeviceName:                 stringPtr("flounder"),
+		DeviceArch:                 stringPtr("arm64"),
+		DeviceArchVariant:          stringPtr("armv8-a"),
+		DeviceCpuVariant:           stringPtr("denver64"),
+		DeviceAbi:                  &[]string{"arm64-v8a"},
+		DeviceUsesClang:            boolPtr(true),
+		DeviceSecondaryArch:        stringPtr("arm"),
+		DeviceSecondaryArchVariant: stringPtr("armv7-a-neon"),
+		DeviceSecondaryCpuVariant:  stringPtr("denver"),
+		DeviceSecondaryAbi:         &[]string{"armeabi-v7a"},
+		Malloc_not_svelte:          boolPtr(false),
+		Safestack:                  boolPtr(false),
+	}
+
+	if runtime.GOOS == "linux" {
+		v.CrossHost = stringPtr("windows")
+		v.CrossHostArch = stringPtr("x86")
+		v.CrossHostSecondaryArch = stringPtr("x86_64")
+	}
+}
+
+func variableMutator(mctx BottomUpMutatorContext) {
+	var module Module
+	var ok bool
+	if module, ok = mctx.Module().(Module); !ok {
+		return
+	}
+
+	// TODO: depend on config variable, create variants, propagate variants up tree
+	a := module.base()
+	variableValues := reflect.ValueOf(&a.variableProperties.Product_variables).Elem()
+	zeroValues := reflect.ValueOf(zeroProductVariables.Product_variables)
+
+	for i := 0; i < variableValues.NumField(); i++ {
+		variableValue := variableValues.Field(i)
+		zeroValue := zeroValues.Field(i)
+		name := variableValues.Type().Field(i).Name
+		property := "product_variables." + proptools.PropertyNameForField(name)
+
+		// Check that the variable was set for the product
+		val := reflect.ValueOf(mctx.Config().(Config).ProductVariables).FieldByName(name)
+		if !val.IsValid() || val.Kind() != reflect.Ptr || val.IsNil() {
+			continue
+		}
+
+		val = val.Elem()
+
+		// For bools, check that the value is true
+		if val.Kind() == reflect.Bool && val.Bool() == false {
+			continue
+		}
+
+		// Check if any properties were set for the module
+		if reflect.DeepEqual(variableValue.Interface(), zeroValue.Interface()) {
+			continue
+		}
+
+		a.setVariableProperties(mctx, property, variableValue, val.Interface())
+	}
+}
+
+func (a *ModuleBase) setVariableProperties(ctx BottomUpMutatorContext,
+	prefix string, productVariablePropertyValue reflect.Value, variableValue interface{}) {
+
+	printfIntoProperties(productVariablePropertyValue, variableValue)
+
+	err := proptools.AppendMatchingProperties(a.generalProperties,
+		productVariablePropertyValue.Addr().Interface(), nil)
+	if err != nil {
+		if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
+			ctx.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
+		} else {
+			panic(err)
+		}
+	}
+}
+
+func printfIntoProperties(productVariablePropertyValue reflect.Value, variableValue interface{}) {
+	for i := 0; i < productVariablePropertyValue.NumField(); i++ {
+		propertyValue := productVariablePropertyValue.Field(i)
+		kind := propertyValue.Kind()
+		if kind == reflect.Ptr {
+			if propertyValue.IsNil() {
+				continue
+			}
+			propertyValue = propertyValue.Elem()
+		}
+		switch propertyValue.Kind() {
+		case reflect.String:
+			printfIntoProperty(propertyValue, variableValue)
+		case reflect.Slice:
+			for j := 0; j < propertyValue.Len(); j++ {
+				printfIntoProperty(propertyValue.Index(j), variableValue)
+			}
+		case reflect.Bool:
+			// Nothing
+		case reflect.Struct:
+			printfIntoProperties(propertyValue, variableValue)
+		default:
+			panic(fmt.Errorf("unsupported field kind %q", propertyValue.Kind()))
+		}
+	}
+}
+
+func printfIntoProperty(propertyValue reflect.Value, variableValue interface{}) {
+	s := propertyValue.String()
+	// For now, we only support int formats
+	var i int
+	if strings.Contains(s, "%d") {
+		switch v := variableValue.(type) {
+		case int:
+			i = v
+		case bool:
+			if v {
+				i = 1
+			}
+		default:
+			panic(fmt.Errorf("unsupported type %T", variableValue))
+		}
+		propertyValue.Set(reflect.ValueOf(fmt.Sprintf(s, i)))
+	}
+}