Add soong_build primary builder

Initial build logic for building android with soong.  It can build
a variety of C and C++ files for arm/arm64 and host.

Change-Id: I10eb37c2c2a50be6af1bb5fd568c0962b9476bf0
diff --git a/common/arch.go b/common/arch.go
new file mode 100644
index 0000000..8daade0
--- /dev/null
+++ b/common/arch.go
@@ -0,0 +1,545 @@
+// 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 common
+
+import (
+	"blueprint"
+	"blueprint/proptools"
+	"fmt"
+	"reflect"
+	"runtime"
+	"strings"
+)
+
+var (
+	Arm    = newArch32("Arm")
+	Arm64  = newArch64("Arm64")
+	Mips   = newArch32("Mips")
+	Mips64 = newArch64("Mips64")
+	X86    = newArch32("X86")
+	X86_64 = newArch64("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 archProperties struct {
+	Arch struct {
+		Arm    interface{}
+		Arm64  interface{}
+		Mips   interface{}
+		Mips64 interface{}
+		X86    interface{}
+		X86_64 interface{}
+	}
+	Multilib struct {
+		Lib32 interface{}
+		Lib64 interface{}
+	}
+	Target struct {
+		Host        interface{}
+		Android     interface{}
+		Linux       interface{}
+		Darwin      interface{}
+		Windows     interface{}
+		Not_windows interface{}
+	}
+}
+
+// An Arch indicates a single CPU architecture.
+type Arch struct {
+	HostOrDevice HostOrDevice
+	ArchType     ArchType
+	ArchVariant  string
+	CpuVariant   string
+}
+
+func (a Arch) String() string {
+	s := a.HostOrDevice.String() + "_" + a.ArchType.String()
+	if a.ArchVariant != "" {
+		s += "_" + a.ArchVariant
+	}
+	if a.CpuVariant != "" {
+		s += "_" + a.CpuVariant
+	}
+	return s
+}
+
+type ArchType struct {
+	Name          string
+	Field         string
+	Multilib      string
+	MultilibField string
+}
+
+func newArch32(field string) ArchType {
+	return ArchType{
+		Name:          strings.ToLower(field),
+		Field:         field,
+		Multilib:      "lib32",
+		MultilibField: "Lib32",
+	}
+}
+
+func newArch64(field string) ArchType {
+	return ArchType{
+		Name:          strings.ToLower(field),
+		Field:         field,
+		Multilib:      "lib64",
+		MultilibField: "Lib64",
+	}
+}
+
+func (a ArchType) String() string {
+	return a.Name
+}
+
+type HostOrDeviceSupported int
+
+const (
+	_ HostOrDeviceSupported = iota
+	HostSupported
+	DeviceSupported
+	HostAndDeviceSupported
+)
+
+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) FieldLower() string {
+	switch hod {
+	case Device:
+		return "android"
+	case Host:
+		return "host"
+	default:
+		panic(fmt.Sprintf("unexpected HostOrDevice value %d", hod))
+	}
+}
+
+func (hod HostOrDevice) Field() 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",
+}
+
+var (
+	armArch = Arch{
+		HostOrDevice: Device,
+		ArchType:     Arm,
+		ArchVariant:  "armv7-a-neon",
+		CpuVariant:   "cortex-a15",
+	}
+	arm64Arch = Arch{
+		HostOrDevice: Device,
+		ArchType:     Arm64,
+		ArchVariant:  "armv8-a",
+		CpuVariant:   "denver",
+	}
+	hostArch = Arch{
+		HostOrDevice: Host,
+		ArchType:     X86,
+	}
+	host64Arch = Arch{
+		HostOrDevice: Host,
+		ArchType:     X86_64,
+	}
+)
+
+func ArchMutator(mctx blueprint.EarlyMutatorContext) {
+	var module AndroidModule
+	var ok bool
+	if module, ok = mctx.Module().(AndroidModule); !ok {
+		return
+	}
+
+	// TODO: this is all hardcoded for arm64 primary, arm secondary for now
+	// Replace with a configuration file written by lunch or bootstrap
+
+	arches := []Arch{}
+
+	if module.base().HostSupported() {
+		arches = append(arches, host64Arch)
+	}
+
+	if module.base().DeviceSupported() {
+		switch module.base().commonProperties.Compile_multilib {
+		case "both":
+			arches = append(arches, arm64Arch, armArch)
+		case "first", "64":
+			arches = append(arches, arm64Arch)
+		case "32":
+			arches = append(arches, armArch)
+		default:
+			mctx.ModuleErrorf(`compile_multilib must be "both", "first", "32", or "64", found %q`,
+				module.base().commonProperties.Compile_multilib)
+		}
+	}
+
+	archNames := []string{}
+	for _, arch := range arches {
+		archNames = append(archNames, arch.String())
+	}
+
+	modules := mctx.CreateVariations(archNames...)
+
+	for i, m := range modules {
+		m.(AndroidModule).base().SetArch(arches[i])
+		m.(AndroidModule).base().setArchProperties(mctx, arches[i])
+	}
+}
+
+func InitArchModule(m AndroidModule, defaultMultilib string,
+	propertyStructs ...interface{}) (blueprint.Module, []interface{}) {
+
+	base := m.base()
+
+	base.commonProperties.Compile_multilib = defaultMultilib
+
+	base.generalProperties = append(base.generalProperties,
+		&base.commonProperties)
+	base.generalProperties = append(base.generalProperties,
+		propertyStructs...)
+
+	for _, properties := range base.generalProperties {
+		propertiesValue := reflect.ValueOf(properties)
+		if propertiesValue.Kind() != reflect.Ptr {
+			panic("properties must be a pointer to a struct")
+		}
+
+		propertiesValue = propertiesValue.Elem()
+		if propertiesValue.Kind() != reflect.Struct {
+			panic("properties must be a pointer to a struct")
+		}
+
+		archProperties := &archProperties{}
+		forEachInterface(reflect.ValueOf(archProperties), func(v reflect.Value) {
+			newValue := proptools.CloneProperties(propertiesValue)
+			proptools.ZeroProperties(newValue.Elem())
+			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
+}
+
+// Rewrite the module's properties structs to contain arch-specific values.
+func (a *AndroidModuleBase) setArchProperties(ctx blueprint.EarlyMutatorContext, arch Arch) {
+	for i := range a.generalProperties {
+		generalPropsValue := reflect.ValueOf(a.generalProperties[i]).Elem()
+
+		// Handle arch-specific properties in the form:
+		// arch {
+		//     arm64 {
+		//         key: value,
+		//     },
+		// },
+		t := arch.ArchType
+		extendProperties(ctx, "arch", t.Name, generalPropsValue,
+			reflect.ValueOf(a.archProperties[i].Arch).FieldByName(t.Field).Elem().Elem())
+
+		// Handle multilib-specific properties in the form:
+		// multilib {
+		//     lib32 {
+		//         key: value,
+		//     },
+		// },
+		extendProperties(ctx, "multilib", t.Multilib, generalPropsValue,
+			reflect.ValueOf(a.archProperties[i].Multilib).FieldByName(t.MultilibField).Elem().Elem())
+
+		// Handle host-or-device-specific properties in the form:
+		// target {
+		//     host {
+		//         key: value,
+		//     },
+		// },
+		hod := arch.HostOrDevice
+		extendProperties(ctx, "target", hod.FieldLower(), generalPropsValue,
+			reflect.ValueOf(a.archProperties[i].Target).FieldByName(hod.Field()).Elem().Elem())
+
+		// Handle host target properties in the form:
+		// target {
+		//     linux {
+		//         key: value,
+		//     },
+		//     not_windows {
+		//         key: value,
+		//     },
+		// },
+		var osList = []struct {
+			goos  string
+			field string
+		}{
+			{"darwin", "Darwin"},
+			{"linux", "Linux"},
+			{"windows", "Windows"},
+		}
+
+		if hod.Host() {
+			for _, v := range osList {
+				if v.goos == runtime.GOOS {
+					extendProperties(ctx, "target", v.goos, generalPropsValue,
+						reflect.ValueOf(a.archProperties[i].Target).FieldByName(v.field).Elem().Elem())
+				}
+			}
+			extendProperties(ctx, "target", "not_windows", generalPropsValue,
+				reflect.ValueOf(a.archProperties[i].Target).FieldByName("Not_windows").Elem().Elem())
+		}
+
+		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()))
+	}
+}
+
+// TODO: move this to proptools
+func extendProperties(ctx blueprint.EarlyMutatorContext, variationType, variationName string,
+	dstValue, srcValue reflect.Value) {
+	extendPropertiesRecursive(ctx, variationType, variationName, dstValue, srcValue, "")
+}
+
+func extendPropertiesRecursive(ctx blueprint.EarlyMutatorContext, variationType, variationName string,
+	dstValue, srcValue reflect.Value, recursePrefix string) {
+
+	typ := dstValue.Type()
+	if srcValue.Type() != typ {
+		panic(fmt.Errorf("can't extend mismatching types (%s <- %s)",
+			dstValue.Kind(), srcValue.Kind()))
+	}
+
+	for i := 0; i < srcValue.NumField(); i++ {
+		field := typ.Field(i)
+		if field.PkgPath != "" {
+			// The field is not exported so just skip it.
+			continue
+		}
+
+		srcFieldValue := srcValue.Field(i)
+		dstFieldValue := dstValue.Field(i)
+
+		localPropertyName := proptools.PropertyNameForField(field.Name)
+		propertyName := fmt.Sprintf("%s.%s.%s%s", variationType, variationName,
+			recursePrefix, localPropertyName)
+		propertyPresentInVariation := ctx.ContainsProperty(propertyName)
+
+		if !propertyPresentInVariation {
+			continue
+		}
+
+		tag := field.Tag.Get("android")
+		tags := map[string]bool{}
+		for _, entry := range strings.Split(tag, ",") {
+			if entry != "" {
+				tags[entry] = true
+			}
+		}
+
+		if !tags["arch_variant"] {
+			ctx.PropertyErrorf(propertyName, "property %q can't be specific to a build variant",
+				recursePrefix+proptools.PropertyNameForField(field.Name))
+			continue
+		}
+
+		switch srcFieldValue.Kind() {
+		case reflect.Bool:
+			// Replace the original value.
+			dstFieldValue.Set(srcFieldValue)
+		case reflect.String:
+			// Append the extension string.
+			dstFieldValue.SetString(dstFieldValue.String() +
+				srcFieldValue.String())
+		case reflect.Struct:
+			// Recursively extend the struct's fields.
+			newRecursePrefix := fmt.Sprintf("%s%s.", recursePrefix, strings.ToLower(field.Name))
+			extendPropertiesRecursive(ctx, variationType, variationName,
+				dstFieldValue, srcFieldValue,
+				newRecursePrefix)
+		case reflect.Slice:
+			val, err := archCombineSlices(dstFieldValue, srcFieldValue, tags["arch_subtract"])
+			if err != nil {
+				ctx.PropertyErrorf(propertyName, err.Error())
+				continue
+			}
+			dstFieldValue.Set(val)
+		case reflect.Ptr, reflect.Interface:
+			// Recursively extend the pointed-to struct's fields.
+			if dstFieldValue.IsNil() != srcFieldValue.IsNil() {
+				panic(fmt.Errorf("can't extend field %q: nilitude mismatch"))
+			}
+			if dstFieldValue.Type() != srcFieldValue.Type() {
+				panic(fmt.Errorf("can't extend field %q: type mismatch"))
+			}
+			if !dstFieldValue.IsNil() {
+				newRecursePrefix := fmt.Sprintf("%s.%s", recursePrefix, field.Name)
+				extendPropertiesRecursive(ctx, variationType, variationName,
+					dstFieldValue.Elem(), srcFieldValue.Elem(),
+					newRecursePrefix)
+			}
+		default:
+			panic(fmt.Errorf("unexpected kind for property struct field %q: %s",
+				field.Name, srcFieldValue.Kind()))
+		}
+	}
+}
+
+func archCombineSlices(general, arch reflect.Value, canSubtract bool) (reflect.Value, error) {
+	if !canSubtract {
+		// Append the extension slice.
+		return reflect.AppendSlice(general, arch), nil
+	}
+
+	// Support -val in arch list to subtract a value from original list
+	l := general.Interface().([]string)
+	for archIndex := 0; archIndex < arch.Len(); archIndex++ {
+		archString := arch.Index(archIndex).String()
+		if strings.HasPrefix(archString, "-") {
+			generalIndex := findStringInSlice(archString[1:], l)
+			if generalIndex == -1 {
+				return reflect.Value{},
+					fmt.Errorf("can't find %q to subtract from general properties", archString[1:])
+			}
+			l = append(l[:generalIndex], l[generalIndex+1:]...)
+		} else {
+			l = append(l, archString)
+		}
+	}
+
+	return reflect.ValueOf(l), nil
+}
+
+func findStringInSlice(str string, slice []string) int {
+	for i, s := range slice {
+		if s == str {
+			return i
+		}
+	}
+
+	return -1
+}
diff --git a/common/defs.go b/common/defs.go
new file mode 100644
index 0000000..4499216
--- /dev/null
+++ b/common/defs.go
@@ -0,0 +1,61 @@
+// 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 common
+
+import (
+	"blueprint"
+)
+
+var (
+	pctx = blueprint.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")
+)
diff --git a/common/glob.go b/common/glob.go
new file mode 100644
index 0000000..9aada95
--- /dev/null
+++ b/common/glob.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 common
+
+import (
+	"fmt"
+	"path/filepath"
+
+	"blueprint"
+	"blueprint/bootstrap"
+
+	"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 "$glob"`, globCmd),
+			Description: "glob $glob",
+
+			Restat:    true,
+			Generator: true,
+			Deps:      blueprint.DepsGCC,
+			Depfile:   "$out.d",
+		},
+		"glob")
+)
+
+func hasGlob(in []string) bool {
+	for _, s := range in {
+		if glob.IsGlob(s) {
+			return true
+		}
+	}
+
+	return false
+}
+
+func ExpandGlobs(ctx AndroidModuleContext, in []string) []string {
+	if !hasGlob(in) {
+		return in
+	}
+
+	out := make([]string, 0, len(in))
+	for _, s := range in {
+		if glob.IsGlob(s) {
+			out = append(out, Glob(ctx, s)...)
+		} else {
+			out = append(out, s)
+		}
+	}
+
+	return out
+}
+
+func Glob(ctx AndroidModuleContext, globPattern string) []string {
+	fileListFile := filepath.Join(ModuleOutDir(ctx), "glob", globToString(globPattern))
+	depFile := fileListFile + ".d"
+
+	// Get a globbed file list, and write out fileListFile and depFile
+	files, err := glob.GlobWithDepFile(globPattern, fileListFile, depFile)
+	if err != nil {
+		ctx.ModuleErrorf("glob: %s", err.Error())
+		return []string{globPattern}
+	}
+
+	// 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},
+		Implicits: []string{globCmd},
+		Args: map[string]string{
+			"glob": globPattern,
+		},
+	})
+
+	// Phony rule so the cleanup phase doesn't delete the depFile
+	ctx.Build(pctx, blueprint.BuildParams{
+		Rule:    blueprint.Phony,
+		Outputs: []string{depFile},
+	})
+
+	// Make build.ninja depend on the fileListFile
+	ctx.AddNinjaFileDeps(fileListFile)
+
+	return files
+}
+
+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/common/module.go b/common/module.go
new file mode 100644
index 0000000..0cbe4b0
--- /dev/null
+++ b/common/module.go
@@ -0,0 +1,327 @@
+// 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 common
+
+import (
+	"blueprint"
+	"path/filepath"
+)
+
+var (
+	DeviceSharedLibrary = "shared_library"
+	DeviceStaticLibrary = "static_library"
+	DeviceExecutable    = "executable"
+	HostSharedLibrary   = "host_shared_library"
+	HostStaticLibrary   = "host_static_library"
+	HostExecutable      = "host_executable"
+)
+
+type AndroidModuleContext interface {
+	blueprint.ModuleContext
+
+	Arch() Arch
+	InstallFile(installPath, srcPath string)
+	CheckbuildFile(srcPath string)
+}
+
+type AndroidModule interface {
+	blueprint.Module
+
+	GenerateAndroidBuildActions(AndroidModuleContext)
+
+	base() *AndroidModuleBase
+	Disabled() bool
+	HostOrDevice() HostOrDevice
+}
+
+type AndroidDynamicDepender interface {
+	AndroidDynamicDependencies(ctx AndroidDynamicDependerModuleContext) []string
+}
+
+type AndroidDynamicDependerModuleContext interface {
+	blueprint.DynamicDependerModuleContext
+}
+
+type commonProperties struct {
+	Name         string
+	Deps         []string
+	ResourceDirs []string
+
+	// disabled: don't emit any build rules for this module
+	Disabled bool `android:"arch_variant"`
+
+	// multilib: 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
+
+	// Set by ArchMutator
+	CompileArch Arch `blueprint:"mutated"`
+
+	// Set by InitAndroidModule
+	HostOrDeviceSupported HostOrDeviceSupported `blueprint:"mutated"`
+}
+
+type hostAndDeviceProperties struct {
+	Host_supported   bool
+	Device_supported bool
+}
+
+func InitAndroidModule(m AndroidModule, hod HostOrDeviceSupported, defaultMultilib string,
+	propertyStructs ...interface{}) (blueprint.Module, []interface{}) {
+
+	base := m.base()
+	base.module = m
+	base.commonProperties.HostOrDeviceSupported = hod
+
+	if hod == HostAndDeviceSupported {
+		// Default to module to device supported, host not supported, can override in module
+		// properties
+		base.hostAndDeviceProperties.Device_supported = true
+		propertyStructs = append(propertyStructs, &base.hostAndDeviceProperties)
+	}
+
+	return InitArchModule(m, defaultMultilib, 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"
+//         "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 AndroidModuleBase struct {
+	// Putting the curiously recurring thing pointing to the thing that contains
+	// the thing pattern to good use.
+	module AndroidModule
+
+	commonProperties        commonProperties
+	hostAndDeviceProperties hostAndDeviceProperties
+	generalProperties       []interface{}
+	archProperties          []*archProperties
+
+	noAddressSanitizer bool
+	installFiles       []string
+	checkbuildFiles    []string
+}
+
+func (a *AndroidModuleBase) base() *AndroidModuleBase {
+	return a
+}
+
+func (a *AndroidModuleBase) SetArch(arch Arch) {
+	a.commonProperties.CompileArch = arch
+}
+
+func (a *AndroidModuleBase) HostOrDevice() HostOrDevice {
+	return a.commonProperties.CompileArch.HostOrDevice
+}
+
+func (a *AndroidModuleBase) HostSupported() bool {
+	return a.commonProperties.HostOrDeviceSupported == HostSupported ||
+		a.commonProperties.HostOrDeviceSupported == HostAndDeviceSupported &&
+			a.hostAndDeviceProperties.Host_supported
+}
+
+func (a *AndroidModuleBase) DeviceSupported() bool {
+	return a.commonProperties.HostOrDeviceSupported == DeviceSupported ||
+		a.commonProperties.HostOrDeviceSupported == HostAndDeviceSupported &&
+			a.hostAndDeviceProperties.Device_supported
+}
+
+func (a *AndroidModuleBase) Disabled() bool {
+	return a.commonProperties.Disabled
+}
+
+func (a *AndroidModuleBase) computeInstallDeps(
+	ctx blueprint.ModuleContext) []string {
+
+	result := []string{}
+	ctx.VisitDepsDepthFirstIf(isFileInstaller,
+		func(m blueprint.Module) {
+			fileInstaller := m.(fileInstaller)
+			files := fileInstaller.filesToInstall()
+			result = append(result, files...)
+		})
+
+	return result
+}
+
+func (a *AndroidModuleBase) filesToInstall() []string {
+	return a.installFiles
+}
+
+func (p *AndroidModuleBase) NoAddressSanitizer() bool {
+	return p.noAddressSanitizer
+}
+
+func (p *AndroidModuleBase) resourceDirs() []string {
+	return p.commonProperties.ResourceDirs
+}
+
+func (a *AndroidModuleBase) generateModuleTarget(ctx blueprint.ModuleContext) {
+	if a != ctx.FinalModule().(AndroidModule).base() {
+		return
+	}
+
+	allInstalledFiles := []string{}
+	ctx.VisitAllModuleVariants(func(module blueprint.Module) {
+		if androidModule, ok := module.(AndroidModule); ok {
+			files := androidModule.base().installFiles
+			allInstalledFiles = append(allInstalledFiles, files...)
+		}
+	})
+
+	if len(allInstalledFiles) > 0 {
+		ctx.Build(pctx, blueprint.BuildParams{
+			Rule:    blueprint.Phony,
+			Outputs: []string{ctx.ModuleName()},
+			Inputs:  allInstalledFiles,
+		})
+	}
+}
+
+func (a *AndroidModuleBase) DynamicDependencies(ctx blueprint.DynamicDependerModuleContext) []string {
+	actx := &androidDynamicDependerContext{
+		DynamicDependerModuleContext: ctx,
+		module: a,
+	}
+
+	if dynamic, ok := a.module.(AndroidDynamicDepender); ok {
+		return dynamic.AndroidDynamicDependencies(actx)
+	}
+
+	return nil
+}
+
+func (a *AndroidModuleBase) GenerateBuildActions(ctx blueprint.ModuleContext) {
+	androidCtx := &androidModuleContext{
+		ModuleContext: ctx,
+		installDeps:   a.computeInstallDeps(ctx),
+		installFiles:  a.installFiles,
+		arch:          a.commonProperties.CompileArch,
+	}
+
+	if a.commonProperties.Disabled {
+		return
+	}
+
+	a.module.GenerateAndroidBuildActions(androidCtx)
+	if ctx.Failed() {
+		return
+	}
+
+	a.generateModuleTarget(ctx)
+	if ctx.Failed() {
+		return
+	}
+}
+
+type androidModuleContext struct {
+	blueprint.ModuleContext
+	arch            Arch
+	installDeps     []string
+	installFiles    []string
+	checkbuildFiles []string
+}
+
+func (a *androidModuleContext) Build(pctx *blueprint.PackageContext, params blueprint.BuildParams) {
+	params.Optional = true
+	a.ModuleContext.Build(pctx, params)
+}
+
+func (a *androidModuleContext) Arch() Arch {
+	return a.arch
+}
+
+func (a *androidModuleContext) InstallFile(installPath, srcPath string) {
+	var fullInstallPath string
+	if a.arch.HostOrDevice.Device() {
+		// TODO: replace unset with a device name once we have device targeting
+		fullInstallPath = filepath.Join("out/target/product/unset/system", installPath,
+			filepath.Base(srcPath))
+	} else {
+		// TODO: replace unset with a host name
+		fullInstallPath = filepath.Join("out/host/unset/", installPath, filepath.Base(srcPath))
+	}
+
+	a.ModuleContext.Build(pctx, blueprint.BuildParams{
+		Rule:      Cp,
+		Outputs:   []string{fullInstallPath},
+		Inputs:    []string{srcPath},
+		OrderOnly: a.installDeps,
+	})
+
+	a.installFiles = append(a.installFiles, fullInstallPath)
+	a.checkbuildFiles = append(a.checkbuildFiles, srcPath)
+}
+
+func (a *androidModuleContext) CheckbuildFile(srcPath string) {
+	a.checkbuildFiles = append(a.checkbuildFiles, srcPath)
+}
+
+type androidDynamicDependerContext struct {
+	blueprint.DynamicDependerModuleContext
+	module *AndroidModuleBase
+}
+
+type fileInstaller interface {
+	filesToInstall() []string
+}
+
+func isFileInstaller(m blueprint.Module) bool {
+	_, ok := m.(fileInstaller)
+	return ok
+}
+
+func isAndroidModule(m blueprint.Module) bool {
+	_, ok := m.(AndroidModule)
+	return ok
+}
diff --git a/common/paths.go b/common/paths.go
new file mode 100644
index 0000000..91b8f99
--- /dev/null
+++ b/common/paths.go
@@ -0,0 +1,83 @@
+// 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 common
+
+import (
+	"path/filepath"
+
+	"blueprint"
+)
+
+type Config interface {
+	CpPreserveSymlinksFlags() string
+	SrcDir() string
+}
+
+// ModuleOutDir returns the path to the module-specific output directory.
+func ModuleOutDir(ctx AndroidModuleContext) string {
+	return filepath.Join(".intermediates", ctx.ModuleDir(), ctx.ModuleName(), ctx.ModuleSubDir())
+}
+
+// ModuleSrcDir returns the path of the directory that all source file paths are
+// specified relative to.
+func ModuleSrcDir(ctx blueprint.ModuleContext) string {
+	config := ctx.Config().(Config)
+	return filepath.Join(config.SrcDir(), ctx.ModuleDir())
+}
+
+// ModuleBinDir returns the path to the module- and architecture-specific binary
+// output directory.
+func ModuleBinDir(ctx AndroidModuleContext) string {
+	return filepath.Join(ModuleOutDir(ctx), "bin")
+}
+
+// ModuleLibDir returns the path to the module- and architecture-specific
+// library output directory.
+func ModuleLibDir(ctx AndroidModuleContext) string {
+	return filepath.Join(ModuleOutDir(ctx), "lib")
+}
+
+// ModuleGenDir returns the module directory for generated files
+// path.
+func ModuleGenDir(ctx AndroidModuleContext) string {
+	return filepath.Join(ModuleOutDir(ctx), "gen")
+}
+
+// ModuleObjDir returns the module- and architecture-specific object directory
+// path.
+func ModuleObjDir(ctx AndroidModuleContext) string {
+	return filepath.Join(ModuleOutDir(ctx), "obj")
+}
+
+// ModuleGoPackageDir returns the module-specific package root directory path.
+// This directory is where the final package .a files are output and where
+// dependent modules search for this package via -I arguments.
+func ModuleGoPackageDir(ctx AndroidModuleContext) string {
+	return filepath.Join(ModuleOutDir(ctx), "pkg")
+}
+
+// ModuleIncludeDir returns the module-specific public include directory path.
+func ModuleIncludeDir(ctx AndroidModuleContext) string {
+	return filepath.Join(ModuleOutDir(ctx), "include")
+}
+
+// ModuleProtoDir returns the module-specific public proto include directory path.
+func ModuleProtoDir(ctx AndroidModuleContext) string {
+	return filepath.Join(ModuleOutDir(ctx), "proto")
+}
+
+func ModuleJSCompiledDir(ctx AndroidModuleContext) string {
+	return filepath.Join(ModuleOutDir(ctx), "js")
+}