Merge "Add script to make a Go-style workspace for Soong"
diff --git a/Android.bp b/Android.bp
index 23cbad8..2ae993f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -14,6 +14,7 @@
     "androidmk",
     "cmd/*",
     "third_party/zip",
+    "ui/*",
 ]
 
 bootstrap_go_package {
@@ -46,6 +47,7 @@
     ],
     srcs: [
         "android/androidmk.go",
+        "android/api_levels.go",
         "android/arch.go",
         "android/config.go",
         "android/defaults.go",
@@ -94,6 +96,7 @@
 
         "cc/config/x86_darwin_host.go",
         "cc/config/x86_linux_host.go",
+        "cc/config/x86_linux_bionic_host.go",
         "cc/config/x86_windows_host.go",
     ],
     testSrcs: [
@@ -117,6 +120,7 @@
         "cc/builder.go",
         "cc/cc.go",
         "cc/check.go",
+        "cc/coverage.go",
         "cc/gen.go",
         "cc/makevars.go",
         "cc/prebuilt.go",
@@ -128,6 +132,7 @@
         "cc/tidy.go",
         "cc/util.go",
 
+        "cc/cmakelists.go",
         "cc/compiler.go",
         "cc/installer.go",
         "cc/linker.go",
@@ -142,9 +147,12 @@
         "cc/ndk_headers.go",
         "cc/ndk_library.go",
         "cc/ndk_sysroot.go",
+
+        "cc/llndk_library.go",
     ],
     testSrcs: [
         "cc/cc_test.go",
+        "cc/test_data_test.go",
     ],
     pluginFor: ["soong_build"],
 }
@@ -166,6 +174,19 @@
 }
 
 bootstrap_go_package {
+    name: "soong-phony",
+    pkgPath: "android/soong/phony",
+    deps: [
+        "blueprint",
+        "soong-android",
+    ],
+    srcs: [
+        "phony/phony.go",
+    ],
+    pluginFor: ["soong_build"],
+}
+
+bootstrap_go_package {
     name: "soong-java",
     pkgPath: "android/soong/java",
     deps: [
@@ -211,6 +232,7 @@
 toolchain_library {
     name: "libatomic",
     defaults: ["linux_bionic_supported"],
+    vendor_available: true,
     arch: {
         arm: {
             instruction_set: "arm",
@@ -221,6 +243,7 @@
 toolchain_library {
     name: "libgcc",
     defaults: ["linux_bionic_supported"],
+    vendor_available: true,
     arch: {
         arm: {
             instruction_set: "arm",
@@ -229,6 +252,17 @@
 }
 
 toolchain_library {
+    name: "libwinpthread",
+    host_supported: true,
+    enabled: false,
+    target: {
+        windows: {
+            enabled: true
+        },
+    },
+}
+
+toolchain_library {
     name: "libgcov",
     defaults: ["linux_bionic_supported"],
     arch: {
diff --git a/android/androidmk.go b/android/androidmk.go
index aeb0aa5..af6608f 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -43,7 +43,7 @@
 	OutputFile OptionalPath
 	Disabled   bool
 
-	Custom func(w io.Writer, name, prefix string) error
+	Custom func(w io.Writer, name, prefix, moduleDir string) error
 
 	Extra []func(w io.Writer, outputFile Path) error
 }
@@ -164,28 +164,35 @@
 		return err
 	}
 
+	// Make does not understand LinuxBionic
+	if amod.Os() == LinuxBionic {
+		return nil
+	}
+
 	if data.SubName != "" {
 		name += data.SubName
 	}
 
 	if data.Custom != nil {
 		prefix := ""
-		switch amod.Os().Class {
-		case Host:
-			prefix = "HOST_"
-		case HostCross:
-			prefix = "HOST_CROSS_"
-		case Device:
-			prefix = "TARGET_"
+		if amod.ArchSpecific() {
+			switch amod.Os().Class {
+			case Host:
+				prefix = "HOST_"
+			case HostCross:
+				prefix = "HOST_CROSS_"
+			case Device:
+				prefix = "TARGET_"
 
+			}
+
+			config := ctx.Config().(Config)
+			if amod.Arch().ArchType != config.Targets[amod.Os().Class][0].Arch.ArchType {
+				prefix = "2ND_" + prefix
+			}
 		}
 
-		config := ctx.Config().(Config)
-		if amod.Arch().ArchType != config.Targets[amod.Os().Class][0].Arch.ArchType {
-			prefix = "2ND_" + prefix
-		}
-
-		return data.Custom(w, name, prefix)
+		return data.Custom(w, name, prefix, filepath.Dir(ctx.BlueprintFile(mod)))
 	}
 
 	if data.Disabled {
@@ -227,6 +234,12 @@
 		if amod.commonProperties.Proprietary {
 			fmt.Fprintln(w, "LOCAL_PROPRIETARY_MODULE := true")
 		}
+		if amod.commonProperties.Vendor {
+			fmt.Fprintln(w, "LOCAL_VENDOR_MODULE := true")
+		}
+		if amod.commonProperties.Owner != "" {
+			fmt.Fprintln(w, "LOCAL_MODULE_OWNER :=", amod.commonProperties.Owner)
+		}
 	}
 
 	if host {
diff --git a/android/api_levels.go b/android/api_levels.go
new file mode 100644
index 0000000..c77ced1
--- /dev/null
+++ b/android/api_levels.go
@@ -0,0 +1,63 @@
+// Copyright 2017 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+import (
+	"encoding/json"
+
+	"github.com/google/blueprint"
+)
+
+func init() {
+	RegisterSingletonType("api_levels", ApiLevelsSingleton)
+}
+
+func ApiLevelsSingleton() blueprint.Singleton {
+	return &apiLevelsSingleton{}
+}
+
+type apiLevelsSingleton struct{}
+
+func createApiLevelsJson(ctx blueprint.SingletonContext, file string,
+	apiLevelsMap map[string]int) {
+
+	jsonStr, err := json.Marshal(apiLevelsMap)
+	if err != nil {
+		ctx.Errorf(err.Error())
+	}
+
+	ctx.Build(pctx, blueprint.BuildParams{
+		Rule:    WriteFile,
+		Outputs: []string{file},
+		Args: map[string]string{
+			"content": string(jsonStr[:]),
+		},
+	})
+}
+
+func GetApiLevelsJson(ctx PathContext) Path {
+	return PathForOutput(ctx, "api_levels.json")
+}
+
+func (a *apiLevelsSingleton) GenerateBuildActions(ctx blueprint.SingletonContext) {
+	baseApiLevel := 9000
+	apiLevelsMap := map[string]int{}
+	for i, codename := range ctx.Config().(Config).PlatformVersionAllCodenames() {
+		apiLevelsMap[codename] = baseApiLevel + i
+	}
+
+	apiLevelsJson := GetApiLevelsJson(ctx)
+	createApiLevelsJson(ctx, apiLevelsJson.String(), apiLevelsMap)
+}
diff --git a/android/arch.go b/android/arch.go
index df50afa..f9697bc 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -217,7 +217,8 @@
 type OsClass int
 
 const (
-	Device OsClass = iota
+	Generic OsClass = iota
+	Device
 	Host
 	HostCross
 )
@@ -780,6 +781,10 @@
 		addTarget(BuildOs, *variables.HostSecondaryArch, nil, nil, nil)
 	}
 
+	if config.Host_bionic != nil && *config.Host_bionic {
+		addTarget(LinuxBionic, "x86_64", nil, nil, nil)
+	}
+
 	if variables.CrossHost != nil && *variables.CrossHost != "" {
 		crossHostOs := osByName(*variables.CrossHost)
 		if crossHostOs == NoOsType {
@@ -851,8 +856,10 @@
 		{"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"}},
+		{"arm", "armv7-a-neon", "kryo", []string{"armeabi-v7a"}},
 		{"arm64", "armv8-a", "cortex-a53", []string{"arm64-v8a"}},
 		{"arm64", "armv8-a", "denver64", []string{"arm64-v8a"}},
+		{"arm64", "armv8-a", "kryo", []string{"arm64-v8a"}},
 		{"mips", "mips32-fp", "", []string{"mips"}},
 		{"mips", "mips32r2-fp", "", []string{"mips"}},
 		{"mips", "mips32r2-fp-xburst", "", []string{"mips"}},
diff --git a/android/config.go b/android/config.go
index 3603477..3c25485 100644
--- a/android/config.go
+++ b/android/config.go
@@ -40,6 +40,7 @@
 type FileConfigurableOptions struct {
 	Mega_device *bool `json:",omitempty"`
 	Ndk_abis    *bool `json:",omitempty"`
+	Host_bionic *bool `json:",omitempty"`
 }
 
 func (f *FileConfigurableOptions) SetDefaultConfig() {
@@ -177,7 +178,6 @@
 
 		srcDir:   srcDir,
 		buildDir: buildDir,
-		envDeps:  make(map[string]string),
 
 		deviceConfig: &deviceConfig{},
 	}
@@ -281,6 +281,10 @@
 	var val string
 	var exists bool
 	c.envLock.Lock()
+	defer c.envLock.Unlock()
+	if c.envDeps == nil {
+		c.envDeps = make(map[string]string)
+	}
 	if val, exists = c.envDeps[key]; !exists {
 		if c.envFrozen {
 			panic("Cannot access new environment variables after envdeps are frozen")
@@ -288,7 +292,6 @@
 		val = os.Getenv(key)
 		c.envDeps[key] = val
 	}
-	c.envLock.Unlock()
 	return val
 }
 
@@ -312,8 +315,8 @@
 
 func (c *config) EnvDeps() map[string]string {
 	c.envLock.Lock()
+	defer c.envLock.Unlock()
 	c.envFrozen = true
-	c.envLock.Unlock()
 	return c.envDeps
 }
 
@@ -350,6 +353,10 @@
 	return strconv.Itoa(c.PlatformSdkVersionInt())
 }
 
+func (c *config) PlatformVersionAllCodenames() []string {
+	return c.ProductVariables.Platform_version_all_codenames
+}
+
 func (c *config) BuildNumber() string {
 	return "000000"
 }
@@ -436,7 +443,7 @@
 	default:
 		return "0x70000000"
 	case Mips, Mips64:
-		return "0x30000000"
+		return "0x64000000"
 	}
 }
 
@@ -459,21 +466,38 @@
 	return "vendor"
 }
 
-func (c *deviceConfig) VndkVersion() string {
+func (c *deviceConfig) CompileVndk() bool {
 	if c.config.ProductVariables.DeviceVndkVersion == nil {
-		return ""
+		return false
 	}
-	return *c.config.ProductVariables.DeviceVndkVersion
+	return *c.config.ProductVariables.DeviceVndkVersion == "current"
 }
 
 func (c *deviceConfig) BtConfigIncludeDir() string {
 	return String(c.config.ProductVariables.BtConfigIncludeDir)
 }
 
-func (c *deviceConfig) BtHcilpIncluded() string {
-	return String(c.config.ProductVariables.BtHcilpIncluded)
+func (c *deviceConfig) NativeCoverageEnabled() bool {
+	return Bool(c.config.ProductVariables.NativeCoverage)
 }
 
-func (c *deviceConfig) BtHciUseMct() bool {
-	return Bool(c.config.ProductVariables.BtHciUseMct)
+func (c *deviceConfig) CoverageEnabledForPath(path string) bool {
+	coverage := false
+	if c.config.ProductVariables.CoveragePaths != nil {
+		for _, prefix := range *c.config.ProductVariables.CoveragePaths {
+			if strings.HasPrefix(path, prefix) {
+				coverage = true
+				break
+			}
+		}
+	}
+	if coverage && c.config.ProductVariables.CoverageExcludePaths != nil {
+		for _, prefix := range *c.config.ProductVariables.CoverageExcludePaths {
+			if strings.HasPrefix(path, prefix) {
+				coverage = false
+				break
+			}
+		}
+	}
+	return coverage
 }
diff --git a/android/defs.go b/android/defs.go
index d7e2a9f..6f46316 100644
--- a/android/defs.go
+++ b/android/defs.go
@@ -80,6 +80,13 @@
 			Description: "concatenate licenses $out",
 		})
 
+	WriteFile = pctx.AndroidStaticRule("WriteFile",
+		blueprint.RuleParams{
+			Command:     "echo '$content' > $out",
+			Description: "writing file $out",
+		},
+		"content")
+
 	// Used only when USE_GOMA=true is set, to restrict non-goma jobs to the local parallelism value
 	localPool = blueprint.NewBuiltinPool("local_pool")
 )
diff --git a/android/env.go b/android/env.go
index 3b523a2..c7409e8 100644
--- a/android/env.go
+++ b/android/env.go
@@ -27,10 +27,6 @@
 // compare the contents of the environment variables, rewriting the file if necessary to cause
 // a manifest regeneration.
 
-func init() {
-	RegisterSingletonType("env", EnvSingleton)
-}
-
 func EnvSingleton() blueprint.Singleton {
 	return &envSingleton{}
 }
diff --git a/android/module.go b/android/module.go
index 7156e8c..963d611 100644
--- a/android/module.go
+++ b/android/module.go
@@ -57,8 +57,10 @@
 	Host() bool
 	Device() bool
 	Darwin() bool
+	Windows() bool
 	Debug() bool
 	PrimaryArch() bool
+	Vendor() bool
 	AConfig() Config
 	DeviceConfig() DeviceConfig
 }
@@ -77,6 +79,7 @@
 	ModuleBuild(pctx blueprint.PackageContext, params ModuleBuildParams)
 
 	ExpandSources(srcFiles, excludes []string) Paths
+	ExpandSourcesSubDir(srcFiles, excludes []string, subDir string) Paths
 	Glob(globPattern string, excludes []string) Paths
 
 	InstallFile(installPath OutputPath, srcPath Path, deps ...Path) OutputPath
@@ -86,8 +89,10 @@
 
 	AddMissingDependencies(deps []string)
 
-	Proprietary() bool
 	InstallInData() bool
+	InstallInSanitizerDir() bool
+
+	RequiredModuleNames() []string
 }
 
 type Module interface {
@@ -100,6 +105,7 @@
 	Enabled() bool
 	Target() Target
 	InstallInData() bool
+	InstallInSanitizerDir() bool
 	SkipInstall()
 }
 
@@ -134,6 +140,12 @@
 	// whether this is a proprietary vendor module, and should be installed into /vendor
 	Proprietary bool
 
+	// vendor who owns this module
+	Owner string
+
+	// whether this module is device specific and should be installed into /vendor
+	Vendor bool
+
 	// *.logtags files, to combine together in order to generate the /system/etc/event-log-tags
 	// file
 	Logtags []string
@@ -218,17 +230,17 @@
 	return InitArchModule(m, propertyStructs...)
 }
 
-// A AndroidModuleBase object contains the properties that are common to all Android
+// A ModuleBase 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.
+// The ModuleBase 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.
@@ -236,12 +248,12 @@
 // For example:
 //
 //     import (
-//         "android/soong/common"
+//         "android/soong/android"
 //         "github.com/google/blueprint"
 //     )
 //
 //     type myModule struct {
-//         common.AndroidModuleBase
+//         android.ModuleBase
 //         properties struct {
 //             MyProperty string
 //         }
@@ -249,10 +261,10 @@
 //
 //     func NewMyModule() (blueprint.Module, []interface{}) {
 //         m := &myModule{}
-//         return common.InitAndroidModule(m, &m.properties)
+//         return android.InitAndroidModule(m, &m.properties)
 //     }
 //
-//     func (m *myModule) GenerateAndroidBuildActions(ctx common.AndroidModuleContext) {
+//     func (m *myModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 //         // Get the CPU architecture for the current build variant.
 //         variantArch := ctx.Arch()
 //
@@ -393,6 +405,10 @@
 	return false
 }
 
+func (p *ModuleBase) InstallInSanitizerDir() bool {
+	return false
+}
+
 func (a *ModuleBase) generateModuleTarget(ctx blueprint.ModuleContext) {
 	allInstalledFiles := Paths{}
 	allCheckbuildFiles := Paths{}
@@ -449,6 +465,7 @@
 	return androidBaseContextImpl{
 		target:        a.commonProperties.CompileTarget,
 		targetPrimary: a.commonProperties.CompilePrimary,
+		vendor:        a.commonProperties.Proprietary || a.commonProperties.Vendor,
 		config:        ctx.Config().(Config),
 	}
 }
@@ -485,6 +502,7 @@
 	target        Target
 	targetPrimary bool
 	debug         bool
+	vendor        bool
 	config        Config
 }
 
@@ -597,6 +615,10 @@
 	return a.target.Os == Darwin
 }
 
+func (a *androidBaseContextImpl) Windows() bool {
+	return a.target.Os == Windows
+}
+
 func (a *androidBaseContextImpl) Debug() bool {
 	return a.debug
 }
@@ -613,14 +635,18 @@
 	return DeviceConfig{a.config.deviceConfig}
 }
 
-func (a *androidModuleContext) Proprietary() bool {
-	return a.module.base().commonProperties.Proprietary
+func (a *androidBaseContextImpl) Vendor() bool {
+	return a.vendor
 }
 
 func (a *androidModuleContext) InstallInData() bool {
 	return a.module.InstallInData()
 }
 
+func (a *androidModuleContext) InstallInSanitizerDir() bool {
+	return a.module.InstallInSanitizerDir()
+}
+
 func (a *androidModuleContext) InstallFileName(installPath OutputPath, name string, srcPath Path,
 	deps ...Path) OutputPath {
 
@@ -628,7 +654,7 @@
 	a.module.base().hooks.runInstallHooks(a, fullInstallPath, false)
 
 	if !a.module.base().commonProperties.SkipInstall &&
-		(a.Host() || !a.AConfig().SkipDeviceInstall()) {
+		(!a.Device() || !a.AConfig().SkipDeviceInstall()) {
 
 		deps = append(deps, a.installDeps...)
 
@@ -666,7 +692,7 @@
 	a.module.base().hooks.runInstallHooks(a, fullInstallPath, true)
 
 	if !a.module.base().commonProperties.SkipInstall &&
-		(a.Host() || !a.AConfig().SkipDeviceInstall()) {
+		(!a.Device() || !a.AConfig().SkipDeviceInstall()) {
 
 		a.ModuleBuild(pctx, ModuleBuildParams{
 			Rule:      Symlink,
@@ -742,9 +768,14 @@
 }
 
 // Returns a list of paths expanded from globs and modules referenced using ":module" syntax.
-// ExpandSourceDeps must have already been called during the dependency resolution phase.
+// ExtractSourcesDeps must have already been called during the dependency resolution phase.
 func (ctx *androidModuleContext) ExpandSources(srcFiles, excludes []string) Paths {
+	return ctx.ExpandSourcesSubDir(srcFiles, excludes, "")
+}
+
+func (ctx *androidModuleContext) ExpandSourcesSubDir(srcFiles, excludes []string, subDir string) Paths {
 	prefix := PathForModuleSrc(ctx).String()
+
 	for i, e := range excludes {
 		j := findStringInSlice(e, srcFiles)
 		if j != -1 {
@@ -754,23 +785,32 @@
 		excludes[i] = filepath.Join(prefix, e)
 	}
 
-	globbedSrcFiles := make(Paths, 0, len(srcFiles))
+	expandedSrcFiles := make(Paths, 0, len(srcFiles))
 	for _, s := range srcFiles {
 		if m := SrcIsModule(s); m != "" {
 			module := ctx.GetDirectDepWithTag(m, SourceDepTag)
 			if srcProducer, ok := module.(SourceFileProducer); ok {
-				globbedSrcFiles = append(globbedSrcFiles, srcProducer.Srcs()...)
+				expandedSrcFiles = append(expandedSrcFiles, srcProducer.Srcs()...)
 			} else {
 				ctx.ModuleErrorf("srcs dependency %q is not a source file producing module", m)
 			}
 		} else if pathtools.IsGlob(s) {
-			globbedSrcFiles = append(globbedSrcFiles, ctx.Glob(filepath.Join(prefix, s), excludes)...)
+			globbedSrcFiles := ctx.Glob(filepath.Join(prefix, s), excludes)
+			expandedSrcFiles = append(expandedSrcFiles, globbedSrcFiles...)
+			for i, s := range expandedSrcFiles {
+				expandedSrcFiles[i] = s.(ModuleSrcPath).WithSubDir(ctx, subDir)
+			}
 		} else {
-			globbedSrcFiles = append(globbedSrcFiles, PathForModuleSrc(ctx, s))
+			s := PathForModuleSrc(ctx, s).WithSubDir(ctx, subDir)
+			expandedSrcFiles = append(expandedSrcFiles, s)
 		}
 	}
 
-	return globbedSrcFiles
+	return expandedSrcFiles
+}
+
+func (ctx *androidModuleContext) RequiredModuleNames() []string {
+	return ctx.module.base().commonProperties.Required
 }
 
 func (ctx *androidModuleContext) Glob(globPattern string, excludes []string) Paths {
diff --git a/android/mutator.go b/android/mutator.go
index 3420280..940b0ff 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -14,7 +14,11 @@
 
 package android
 
-import "github.com/google/blueprint"
+import (
+	"sync"
+
+	"github.com/google/blueprint"
+)
 
 // Mutator phases:
 //   Pre-arch
@@ -23,36 +27,68 @@
 //   Deps
 //   PostDeps
 
-func registerMutators() {
-	ctx := registerMutatorsContext{}
+var registerMutatorsOnce sync.Once
+var registeredMutators []*mutator
 
-	register := func(funcs []RegisterMutatorFunc) {
-		for _, f := range funcs {
-			f(ctx)
+func registerMutatorsToContext(ctx *blueprint.Context, mutators []*mutator) {
+	for _, t := range mutators {
+		var handle blueprint.MutatorHandle
+		if t.bottomUpMutator != nil {
+			handle = ctx.RegisterBottomUpMutator(t.name, t.bottomUpMutator)
+		} else if t.topDownMutator != nil {
+			handle = ctx.RegisterTopDownMutator(t.name, t.topDownMutator)
+		}
+		if t.parallel {
+			handle.Parallel()
 		}
 	}
-
-	ctx.TopDown("load_hooks", loadHookMutator).Parallel()
-	ctx.BottomUp("prebuilts", prebuiltMutator).Parallel()
-	ctx.BottomUp("defaults_deps", defaultsDepsMutator).Parallel()
-	ctx.TopDown("defaults", defaultsMutator).Parallel()
-
-	register(preArch)
-
-	ctx.BottomUp("arch", archMutator).Parallel()
-	ctx.TopDown("arch_hooks", archHookMutator).Parallel()
-
-	register(preDeps)
-
-	ctx.BottomUp("deps", depsMutator).Parallel()
-
-	ctx.TopDown("prebuilt_select", PrebuiltSelectModuleMutator).Parallel()
-	ctx.BottomUp("prebuilt_replace", PrebuiltReplaceMutator).Parallel()
-
-	register(postDeps)
 }
 
-type registerMutatorsContext struct{}
+func registerMutators(ctx *blueprint.Context) {
+
+	registerMutatorsOnce.Do(func() {
+		ctx := &registerMutatorsContext{}
+
+		register := func(funcs []RegisterMutatorFunc) {
+			for _, f := range funcs {
+				f(ctx)
+			}
+		}
+
+		ctx.TopDown("load_hooks", loadHookMutator).Parallel()
+		ctx.BottomUp("prebuilts", prebuiltMutator).Parallel()
+		ctx.BottomUp("defaults_deps", defaultsDepsMutator).Parallel()
+		ctx.TopDown("defaults", defaultsMutator).Parallel()
+
+		register(preArch)
+
+		ctx.BottomUp("arch", archMutator).Parallel()
+		ctx.TopDown("arch_hooks", archHookMutator).Parallel()
+
+		register(preDeps)
+
+		ctx.BottomUp("deps", depsMutator).Parallel()
+
+		ctx.TopDown("prebuilt_select", PrebuiltSelectModuleMutator).Parallel()
+		ctx.BottomUp("prebuilt_replace", PrebuiltReplaceMutator).Parallel()
+
+		register(postDeps)
+
+		registeredMutators = ctx.mutators
+	})
+
+	registerMutatorsToContext(ctx, registeredMutators)
+}
+
+func RegisterTestMutators(ctx *blueprint.Context) {
+	mutators := registerMutatorsContext{}
+	mutators.BottomUp("deps", depsMutator).Parallel()
+	registerMutatorsToContext(ctx, mutators.mutators)
+}
+
+type registerMutatorsContext struct {
+	mutators []*mutator
+}
 
 type RegisterMutatorsContext interface {
 	TopDown(name string, m AndroidTopDownMutator) MutatorHandle
@@ -99,7 +135,7 @@
 	androidBaseContextImpl
 }
 
-func (registerMutatorsContext) BottomUp(name string, m AndroidBottomUpMutator) MutatorHandle {
+func (x *registerMutatorsContext) BottomUp(name string, m AndroidBottomUpMutator) MutatorHandle {
 	f := func(ctx blueprint.BottomUpMutatorContext) {
 		if a, ok := ctx.Module().(Module); ok {
 			actx := &androidBottomUpMutatorContext{
@@ -110,11 +146,11 @@
 		}
 	}
 	mutator := &mutator{name: name, bottomUpMutator: f}
-	mutators = append(mutators, mutator)
+	x.mutators = append(x.mutators, mutator)
 	return mutator
 }
 
-func (registerMutatorsContext) TopDown(name string, m AndroidTopDownMutator) MutatorHandle {
+func (x *registerMutatorsContext) TopDown(name string, m AndroidTopDownMutator) MutatorHandle {
 	f := func(ctx blueprint.TopDownMutatorContext) {
 		if a, ok := ctx.Module().(Module); ok {
 			actx := &androidTopDownMutatorContext{
@@ -125,7 +161,7 @@
 		}
 	}
 	mutator := &mutator{name: name, topDownMutator: f}
-	mutators = append(mutators, mutator)
+	x.mutators = append(x.mutators, mutator)
 	return mutator
 }
 
diff --git a/android/package_ctx.go b/android/package_ctx.go
index ee826c8..d9bb109 100644
--- a/android/package_ctx.go
+++ b/android/package_ctx.go
@@ -18,6 +18,7 @@
 	"fmt"
 
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/pathtools"
 )
 
 // AndroidPackageContext is a wrapper for blueprint.PackageContext that adds
@@ -55,6 +56,10 @@
 	e.pctx.AddNinjaFileDeps(deps...)
 }
 
+func (e *configErrorWrapper) Fs() pathtools.FileSystem {
+	return nil
+}
+
 // 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
diff --git a/android/paths.go b/android/paths.go
index e76e1fe..969c753 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -16,7 +16,6 @@
 
 import (
 	"fmt"
-	"os"
 	"path/filepath"
 	"reflect"
 	"strings"
@@ -28,6 +27,7 @@
 // PathContext is the subset of a (Module|Singleton)Context required by the
 // Path methods.
 type PathContext interface {
+	Fs() pathtools.FileSystem
 	Config() interface{}
 	AddNinjaFileDeps(deps ...string)
 }
@@ -86,6 +86,11 @@
 
 	// Base returns the last element of the path
 	Base() string
+
+	// Rel returns the portion of the path relative to the directory it was created from.  For
+	// example, Rel on a PathsForModuleSrc would return the path relative to the module source
+	// directory.
+	Rel() string
 }
 
 // WritablePath is a type of path that can be used as an output for build rules.
@@ -283,6 +288,7 @@
 type basePath struct {
 	path   string
 	config Config
+	rel    string
 }
 
 func (p basePath) Ext() string {
@@ -293,6 +299,13 @@
 	return filepath.Base(p.path)
 }
 
+func (p basePath) Rel() string {
+	if p.rel != "" {
+		return p.rel
+	}
+	return p.path
+}
+
 // SourcePath is a Path representing a file path rooted from SrcDir
 type SourcePath struct {
 	basePath
@@ -304,7 +317,7 @@
 // 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)}}
+	ret := SourcePath{basePath{p, pathConfig(ctx), ""}}
 
 	abs, err := filepath.Abs(ret.String())
 	if err != nil {
@@ -330,7 +343,7 @@
 // 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)}}
+	ret := SourcePath{basePath{p, pathConfig(ctx), ""}}
 
 	abs, err := filepath.Abs(ret.String())
 	if err != nil {
@@ -347,12 +360,10 @@
 		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())
-		}
+	if exists, _, err := ctx.Fs().Exists(ret.String()); err != nil {
+		reportPathError(ctx, "%s: %s", ret, err.Error())
+	} else if !exists {
+		reportPathError(ctx, "source path %s does not exist", ret)
 	}
 	return ret
 }
@@ -367,7 +378,7 @@
 	}
 
 	p := validatePath(ctx, paths...)
-	path := SourcePath{basePath{p, pathConfig(ctx)}}
+	path := SourcePath{basePath{p, pathConfig(ctx), ""}}
 
 	abs, err := filepath.Abs(path.String())
 	if err != nil {
@@ -404,7 +415,7 @@
 	} else {
 		// We cannot add build statements in this context, so we fall back to
 		// AddNinjaFileDeps
-		files, dirs, err := pathtools.Glob(path.String())
+		files, dirs, err := pathtools.Glob(path.String(), nil)
 		if err != nil {
 			reportPathError(ctx, "glob: %s", err.Error())
 			return OptionalPath{}
@@ -437,7 +448,7 @@
 func (p SourcePath) OverlayPath(ctx ModuleContext, path Path) OptionalPath {
 	var relDir string
 	if moduleSrcPath, ok := path.(ModuleSrcPath); ok {
-		relDir = moduleSrcPath.sourcePath.path
+		relDir = moduleSrcPath.path
 	} else if srcPath, ok := path.(SourcePath); ok {
 		relDir = srcPath.path
 	} else {
@@ -478,7 +489,7 @@
 // OutputPath, and report a ModuleError.
 func PathForOutput(ctx PathContext, paths ...string) OutputPath {
 	path := validatePath(ctx, paths...)
-	return OutputPath{basePath{path, pathConfig(ctx)}}
+	return OutputPath{basePath{path, pathConfig(ctx), ""}}
 }
 
 func (p OutputPath) writablePath() {}
@@ -507,9 +518,7 @@
 
 // ModuleSrcPath is a Path representing a file rooted from a module's local source dir
 type ModuleSrcPath struct {
-	basePath
-	sourcePath SourcePath
-	moduleDir  string
+	SourcePath
 }
 
 var _ Path = ModuleSrcPath{}
@@ -520,8 +529,10 @@
 // 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()}
+	p := validatePath(ctx, paths...)
+	path := ModuleSrcPath{PathForSource(ctx, ctx.ModuleDir(), p)}
+	path.basePath.rel = p
+	return path
 }
 
 // OptionalPathForModuleSrc returns an OptionalPath. The OptionalPath contains a
@@ -533,16 +544,12 @@
 	return OptionalPathForPath(PathForModuleSrc(ctx, *p))
 }
 
-func (p ModuleSrcPath) String() string {
-	return p.sourcePath.String()
-}
-
 func (p ModuleSrcPath) genPathWithExt(ctx ModuleContext, subdir, ext string) ModuleGenPath {
-	return PathForModuleGen(ctx, subdir, p.moduleDir, pathtools.ReplaceExtension(p.path, ext))
+	return PathForModuleGen(ctx, subdir, 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))
+	return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
 }
 
 func (p ModuleSrcPath) resPathWithName(ctx ModuleContext, name string) ModuleResPath {
@@ -550,6 +557,18 @@
 	return PathForModuleRes(ctx, p.path, name)
 }
 
+func (p ModuleSrcPath) WithSubDir(ctx ModuleContext, subdir string) ModuleSrcPath {
+	subdir = PathForModuleSrc(ctx, subdir).String()
+	var err error
+	rel, err := filepath.Rel(subdir, p.path)
+	if err != nil {
+		ctx.ModuleErrorf("source file %q is not under path %q", p.path, subdir)
+		return p
+	}
+	p.rel = rel
+	return p
+}
+
 // ModuleOutPath is a Path representing a module's output directory.
 type ModuleOutPath struct {
 	OutputPath
@@ -630,10 +649,13 @@
 	var outPaths []string
 	if ctx.Device() {
 		partition := "system"
-		if ctx.Proprietary() {
+		if ctx.Vendor() {
 			partition = ctx.DeviceConfig().VendorPath()
 		}
-		if ctx.InstallInData() {
+
+		if ctx.InstallInSanitizerDir() {
+			partition = "data/asan/" + partition
+		} else if ctx.InstallInData() {
 			partition = "data"
 		}
 		outPaths = []string{"target", "product", ctx.AConfig().DeviceName(), partition}
diff --git a/android/prebuilt.go b/android/prebuilt.go
index 5f9b4b0..772df7a 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -19,7 +19,11 @@
 // This file implements common functionality for handling modules that may exist as prebuilts,
 // source, or both.
 
-var prebuiltDependencyTag blueprint.BaseDependencyTag
+type prebuiltDependencyTag struct {
+	blueprint.BaseDependencyTag
+}
+
+var prebuiltDepTag prebuiltDependencyTag
 
 type Prebuilt struct {
 	Properties struct {
@@ -64,7 +68,7 @@
 		p := m.Prebuilt()
 		name := m.base().BaseModuleName()
 		if ctx.OtherModuleExists(name) {
-			ctx.AddReverseDependency(ctx.Module(), prebuiltDependencyTag, name)
+			ctx.AddReverseDependency(ctx.Module(), prebuiltDepTag, name)
 			p.Properties.SourceExists = true
 		} else {
 			ctx.Rename(name)
@@ -72,12 +76,17 @@
 	}
 }
 
-// PrebuiltSelectModuleMutator marks prebuilts that are overriding source modules, and disables
-// installing the source module.
+// PrebuiltSelectModuleMutator marks prebuilts that are used, either overriding source modules or
+// because the source module doesn't exist.  It also disables installing overridden source modules.
 func PrebuiltSelectModuleMutator(ctx TopDownMutatorContext) {
-	if s, ok := ctx.Module().(Module); ok {
+	if m, ok := ctx.Module().(PrebuiltInterface); ok && m.Prebuilt() != nil {
+		p := m.Prebuilt()
+		if !p.Properties.SourceExists {
+			p.Properties.UsePrebuilt = p.usePrebuilt(ctx, nil)
+		}
+	} else if s, ok := ctx.Module().(Module); ok {
 		ctx.VisitDirectDeps(func(m blueprint.Module) {
-			if ctx.OtherModuleDependencyTag(m) == prebuiltDependencyTag {
+			if ctx.OtherModuleDependencyTag(m) == prebuiltDepTag {
 				p := m.(PrebuiltInterface).Prebuilt()
 				if p.usePrebuilt(ctx, s) {
 					p.Properties.UsePrebuilt = true
@@ -117,5 +126,5 @@
 		return true
 	}
 
-	return !source.Enabled()
+	return source == nil || !source.Enabled()
 }
diff --git a/android/prebuilt_test.go b/android/prebuilt_test.go
index 311f821..d09518b 100644
--- a/android/prebuilt_test.go
+++ b/android/prebuilt_test.go
@@ -144,20 +144,33 @@
 				t.Fatalf("failed to find module foo")
 			}
 
+			var dependsOnSourceModule, dependsOnPrebuiltModule bool
+			ctx.VisitDirectDeps(foo, func(m blueprint.Module) {
+				if _, ok := m.(*sourceModule); ok {
+					dependsOnSourceModule = true
+				}
+				if p, ok := m.(*prebuiltModule); ok {
+					dependsOnPrebuiltModule = true
+					if !p.Prebuilt().Properties.UsePrebuilt {
+						t.Errorf("dependency on prebuilt module not marked used")
+					}
+				}
+			})
+
 			if test.prebuilt {
-				if !foo.(*sourceModule).dependsOnPrebuiltModule {
+				if !dependsOnPrebuiltModule {
 					t.Errorf("doesn't depend on prebuilt module")
 				}
 
-				if foo.(*sourceModule).dependsOnSourceModule {
+				if dependsOnSourceModule {
 					t.Errorf("depends on source module")
 				}
 			} else {
-				if foo.(*sourceModule).dependsOnPrebuiltModule {
+				if dependsOnPrebuiltModule {
 					t.Errorf("depends on prebuilt module")
 				}
 
-				if !foo.(*sourceModule).dependsOnSourceModule {
+				if !dependsOnSourceModule {
 					t.Errorf("doens't depend on source module")
 				}
 			}
@@ -209,14 +222,6 @@
 }
 
 func (s *sourceModule) GenerateAndroidBuildActions(ctx ModuleContext) {
-	ctx.VisitDirectDeps(func(m blueprint.Module) {
-		if _, ok := m.(*sourceModule); ok {
-			s.dependsOnSourceModule = true
-		}
-		if _, ok := m.(*prebuiltModule); ok {
-			s.dependsOnPrebuiltModule = true
-		}
-	})
 }
 
 func findModule(ctx *blueprint.Context, name string) blueprint.Module {
diff --git a/android/register.go b/android/register.go
index 0ad9d30..9396664 100644
--- a/android/register.go
+++ b/android/register.go
@@ -15,8 +15,6 @@
 package android
 
 import (
-	"sync"
-
 	"github.com/google/blueprint"
 )
 
@@ -51,8 +49,6 @@
 	singletons = append(singletons, singleton{name, factory})
 }
 
-var registerMutatorsOnce sync.Once
-
 func NewContext() *blueprint.Context {
 	ctx := blueprint.NewContext()
 
@@ -64,19 +60,9 @@
 		ctx.RegisterSingletonType(t.name, t.factory)
 	}
 
-	registerMutatorsOnce.Do(registerMutators)
+	registerMutators(ctx)
 
-	for _, t := range mutators {
-		var handle blueprint.MutatorHandle
-		if t.bottomUpMutator != nil {
-			handle = ctx.RegisterBottomUpMutator(t.name, t.bottomUpMutator)
-		} else if t.topDownMutator != nil {
-			handle = ctx.RegisterTopDownMutator(t.name, t.topDownMutator)
-		}
-		if t.parallel {
-			handle.Parallel()
-		}
-	}
+	ctx.RegisterSingletonType("env", EnvSingleton)
 
 	return ctx
 }
diff --git a/android/variable.go b/android/variable.go
index bb84be2..b0ab2d0 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -54,14 +54,6 @@
 			Cflags []string `android:"arch_variant"`
 		} `android:"arch_variant"`
 
-		Cpusets struct {
-			Cflags []string
-		}
-
-		Schedboost struct {
-			Cflags []string
-		}
-
 		Binder32bit struct {
 			Cflags []string
 		}
@@ -90,7 +82,8 @@
 	// Suffix to add to generated Makefiles
 	Make_suffix *string `json:",omitempty"`
 
-	Platform_sdk_version *int `json:",omitempty"`
+	Platform_sdk_version           *int     `json:",omitempty"`
+	Platform_version_all_codenames []string `json:",omitempty"`
 
 	DeviceName        *string   `json:",omitempty"`
 	DeviceArch        *string   `json:",omitempty"`
@@ -118,8 +111,6 @@
 	Malloc_not_svelte          *bool `json:",omitempty"`
 	Safestack                  *bool `json:",omitempty"`
 	HostStaticBinaries         *bool `json:",omitempty"`
-	Cpusets                    *bool `json:",omitempty"`
-	Schedboost                 *bool `json:",omitempty"`
 	Binder32bit                *bool `json:",omitempty"`
 	UseGoma                    *bool `json:",omitempty"`
 	Debuggable                 *bool `json:",omitempty"`
@@ -131,6 +122,10 @@
 	ClangTidy  *bool   `json:",omitempty"`
 	TidyChecks *string `json:",omitempty"`
 
+	NativeCoverage       *bool     `json:",omitempty"`
+	CoveragePaths        *[]string `json:",omitempty"`
+	CoverageExcludePaths *[]string `json:",omitempty"`
+
 	DevicePrefer32BitExecutables *bool `json:",omitempty"`
 	HostPrefer32BitExecutables   *bool `json:",omitempty"`
 
@@ -141,8 +136,6 @@
 	ArtUseReadBarrier *bool `json:",omitempty"`
 
 	BtConfigIncludeDir *string `json:",omitempty"`
-	BtHcilpIncluded    *string `json:",omitempty"`
-	BtHciUseMct        *bool   `json:",omitempty"`
 }
 
 func boolPtr(v bool) *bool {
diff --git a/androidmk/cmd/androidmk/android.go b/androidmk/cmd/androidmk/android.go
index 72ececb..bd9d8ee 100644
--- a/androidmk/cmd/androidmk/android.go
+++ b/androidmk/cmd/androidmk/android.go
@@ -12,87 +12,120 @@
 	clear_vars = "__android_mk_clear_vars"
 )
 
-var standardProperties = map[string]struct {
-	string
-	bpparser.Type
-}{
-	// String properties
-	"LOCAL_MODULE":               {"name", bpparser.StringType},
-	"LOCAL_MODULE_CLASS":         {"class", bpparser.StringType},
-	"LOCAL_CXX_STL":              {"stl", bpparser.StringType},
-	"LOCAL_STRIP_MODULE":         {"strip", bpparser.StringType},
-	"LOCAL_MULTILIB":             {"compile_multilib", bpparser.StringType},
-	"LOCAL_ARM_MODE_HACK":        {"instruction_set", bpparser.StringType},
-	"LOCAL_SDK_VERSION":          {"sdk_version", bpparser.StringType},
-	"LOCAL_NDK_STL_VARIANT":      {"stl", bpparser.StringType},
-	"LOCAL_JAR_MANIFEST":         {"manifest", bpparser.StringType},
-	"LOCAL_JARJAR_RULES":         {"jarjar_rules", bpparser.StringType},
-	"LOCAL_CERTIFICATE":          {"certificate", bpparser.StringType},
-	"LOCAL_PACKAGE_NAME":         {"name", bpparser.StringType},
-	"LOCAL_MODULE_RELATIVE_PATH": {"relative_install_path", bpparser.StringType},
-	"LOCAL_PROTOC_OPTIMIZE_TYPE": {"proto.type", bpparser.StringType},
-
-	// List properties
-	"LOCAL_SRC_FILES_EXCLUDE":             {"exclude_srcs", bpparser.ListType},
-	"LOCAL_SHARED_LIBRARIES":              {"shared_libs", bpparser.ListType},
-	"LOCAL_STATIC_LIBRARIES":              {"static_libs", bpparser.ListType},
-	"LOCAL_WHOLE_STATIC_LIBRARIES":        {"whole_static_libs", bpparser.ListType},
-	"LOCAL_SYSTEM_SHARED_LIBRARIES":       {"system_shared_libs", bpparser.ListType},
-	"LOCAL_ASFLAGS":                       {"asflags", bpparser.ListType},
-	"LOCAL_CLANG_ASFLAGS":                 {"clang_asflags", bpparser.ListType},
-	"LOCAL_CFLAGS":                        {"cflags", bpparser.ListType},
-	"LOCAL_CONLYFLAGS":                    {"conlyflags", bpparser.ListType},
-	"LOCAL_CPPFLAGS":                      {"cppflags", bpparser.ListType},
-	"LOCAL_REQUIRED_MODULES":              {"required", bpparser.ListType},
-	"LOCAL_MODULE_TAGS":                   {"tags", bpparser.ListType},
-	"LOCAL_LDLIBS":                        {"host_ldlibs", bpparser.ListType},
-	"LOCAL_CLANG_CFLAGS":                  {"clang_cflags", bpparser.ListType},
-	"LOCAL_YACCFLAGS":                     {"yaccflags", bpparser.ListType},
-	"LOCAL_SANITIZE_RECOVER":              {"sanitize.recover", bpparser.ListType},
-	"LOCAL_LOGTAGS_FILES":                 {"logtags", bpparser.ListType},
-	"LOCAL_EXPORT_SHARED_LIBRARY_HEADERS": {"export_shared_lib_headers", bpparser.ListType},
-	"LOCAL_EXPORT_STATIC_LIBRARY_HEADERS": {"export_static_lib_headers", bpparser.ListType},
-	"LOCAL_INIT_RC":                       {"init_rc", bpparser.ListType},
-	"LOCAL_TIDY_FLAGS":                    {"tidy_flags", bpparser.ListType},
-	// TODO: This is comma-seperated, not space-separated
-	"LOCAL_TIDY_CHECKS": {"tidy_checks", bpparser.ListType},
-
-	"LOCAL_JAVA_RESOURCE_DIRS":    {"java_resource_dirs", bpparser.ListType},
-	"LOCAL_JAVACFLAGS":            {"javacflags", bpparser.ListType},
-	"LOCAL_DX_FLAGS":              {"dxflags", bpparser.ListType},
-	"LOCAL_JAVA_LIBRARIES":        {"java_libs", bpparser.ListType},
-	"LOCAL_STATIC_JAVA_LIBRARIES": {"java_static_libs", bpparser.ListType},
-	"LOCAL_AIDL_INCLUDES":         {"aidl_includes", bpparser.ListType},
-	"LOCAL_AAPT_FLAGS":            {"aaptflags", bpparser.ListType},
-	"LOCAL_PACKAGE_SPLITS":        {"package_splits", bpparser.ListType},
-
-	// Bool properties
-	"LOCAL_IS_HOST_MODULE":          {"host", bpparser.BoolType},
-	"LOCAL_CLANG":                   {"clang", bpparser.BoolType},
-	"LOCAL_FORCE_STATIC_EXECUTABLE": {"static_executable", bpparser.BoolType},
-	"LOCAL_NATIVE_COVERAGE":         {"native_coverage", bpparser.BoolType},
-	"LOCAL_NO_CRT":                  {"nocrt", bpparser.BoolType},
-	"LOCAL_ALLOW_UNDEFINED_SYMBOLS": {"allow_undefined_symbols", bpparser.BoolType},
-	"LOCAL_RTTI_FLAG":               {"rtti", bpparser.BoolType},
-	"LOCAL_NO_STANDARD_LIBRARIES":   {"no_standard_libraries", bpparser.BoolType},
-	"LOCAL_PACK_MODULE_RELOCATIONS": {"pack_relocations", bpparser.BoolType},
-	"LOCAL_TIDY":                    {"tidy", bpparser.BoolType},
-	"LOCAL_USE_VNDK":                {"use_vndk", bpparser.BoolType},
-	"LOCAL_PROPRIETARY_MODULE":      {"proprietary", bpparser.BoolType},
-
-	"LOCAL_EXPORT_PACKAGE_RESOURCES": {"export_package_resources", bpparser.BoolType},
+type bpVariable struct {
+	name         string
+	variableType bpparser.Type
 }
 
-var rewriteProperties = map[string]struct {
-	f func(file *bpFile, prefix string, value *mkparser.MakeString, append bool) error
-}{
-	"LOCAL_C_INCLUDES":            {localIncludeDirs},
-	"LOCAL_EXPORT_C_INCLUDE_DIRS": {exportIncludeDirs},
-	"LOCAL_MODULE_STEM":           {stem},
-	"LOCAL_MODULE_HOST_OS":        {hostOs},
-	"LOCAL_SRC_FILES":             {srcFiles},
-	"LOCAL_SANITIZE":              {sanitize},
-	"LOCAL_LDFLAGS":               {ldflags},
+type variableAssignmentContext struct {
+	file    *bpFile
+	prefix  string
+	mkvalue *mkparser.MakeString
+	append  bool
+}
+
+var rewriteProperties = map[string](func(variableAssignmentContext) error){
+	// custom functions
+	"LOCAL_C_INCLUDES":            localIncludeDirs,
+	"LOCAL_EXPORT_C_INCLUDE_DIRS": exportIncludeDirs,
+	"LOCAL_LDFLAGS":               ldflags,
+	"LOCAL_MODULE_CLASS":          prebuiltClass,
+	"LOCAL_MODULE_STEM":           stem,
+	"LOCAL_MODULE_HOST_OS":        hostOs,
+	"LOCAL_SRC_FILES":             srcFiles,
+	"LOCAL_SANITIZE":              sanitize,
+
+	// composite functions
+	"LOCAL_MODULE_TAGS": includeVariableIf(bpVariable{"tags", bpparser.ListType}, not(valueDumpEquals("optional"))),
+
+	// skip functions
+	"LOCAL_ADDITIONAL_DEPENDENCIES": skip, // TODO: check for only .mk files?
+	"LOCAL_CPP_EXTENSION":           skip,
+	"LOCAL_MODULE_SUFFIX":           skip, // TODO
+	"LOCAL_PATH":                    skip, // Nothing to do, except maybe avoid the "./" in paths?
+	"LOCAL_PRELINK_MODULE":          skip, // Already phased out
+}
+
+// adds a group of properties all having the same type
+func addStandardProperties(propertyType bpparser.Type, properties map[string]string) {
+	for key, val := range properties {
+		rewriteProperties[key] = includeVariable(bpVariable{val, propertyType})
+	}
+}
+
+func init() {
+	addStandardProperties(bpparser.StringType,
+		map[string]string{
+			"LOCAL_MODULE":               "name",
+			"LOCAL_CXX_STL":              "stl",
+			"LOCAL_STRIP_MODULE":         "strip",
+			"LOCAL_MULTILIB":             "compile_multilib",
+			"LOCAL_ARM_MODE_HACK":        "instruction_set",
+			"LOCAL_SDK_VERSION":          "sdk_version",
+			"LOCAL_NDK_STL_VARIANT":      "stl",
+			"LOCAL_JAR_MANIFEST":         "manifest",
+			"LOCAL_JARJAR_RULES":         "jarjar_rules",
+			"LOCAL_CERTIFICATE":          "certificate",
+			"LOCAL_PACKAGE_NAME":         "name",
+			"LOCAL_MODULE_RELATIVE_PATH": "relative_install_path",
+			"LOCAL_PROTOC_OPTIMIZE_TYPE": "proto.type",
+			"LOCAL_MODULE_OWNER":         "owner",
+		})
+	addStandardProperties(bpparser.ListType,
+		map[string]string{
+			"LOCAL_SRC_FILES_EXCLUDE":             "exclude_srcs",
+			"LOCAL_HEADER_LIBRARIES":              "header_libs",
+			"LOCAL_SHARED_LIBRARIES":              "shared_libs",
+			"LOCAL_STATIC_LIBRARIES":              "static_libs",
+			"LOCAL_WHOLE_STATIC_LIBRARIES":        "whole_static_libs",
+			"LOCAL_SYSTEM_SHARED_LIBRARIES":       "system_shared_libs",
+			"LOCAL_ASFLAGS":                       "asflags",
+			"LOCAL_CLANG_ASFLAGS":                 "clang_asflags",
+			"LOCAL_CFLAGS":                        "cflags",
+			"LOCAL_CONLYFLAGS":                    "conlyflags",
+			"LOCAL_CPPFLAGS":                      "cppflags",
+			"LOCAL_REQUIRED_MODULES":              "required",
+			"LOCAL_LDLIBS":                        "host_ldlibs",
+			"LOCAL_CLANG_CFLAGS":                  "clang_cflags",
+			"LOCAL_YACCFLAGS":                     "yaccflags",
+			"LOCAL_SANITIZE_RECOVER":              "sanitize.recover",
+			"LOCAL_LOGTAGS_FILES":                 "logtags",
+			"LOCAL_EXPORT_HEADER_LIBRARY_HEADERS": "export_header_lib_headers",
+			"LOCAL_EXPORT_SHARED_LIBRARY_HEADERS": "export_shared_lib_headers",
+			"LOCAL_EXPORT_STATIC_LIBRARY_HEADERS": "export_static_lib_headers",
+			"LOCAL_INIT_RC":                       "init_rc",
+			"LOCAL_TIDY_FLAGS":                    "tidy_flags",
+			// TODO: This is comma-separated, not space-separated
+			"LOCAL_TIDY_CHECKS": "tidy_checks",
+
+			"LOCAL_JAVA_RESOURCE_DIRS":    "java_resource_dirs",
+			"LOCAL_JAVACFLAGS":            "javacflags",
+			"LOCAL_DX_FLAGS":              "dxflags",
+			"LOCAL_JAVA_LIBRARIES":        "java_libs",
+			"LOCAL_STATIC_JAVA_LIBRARIES": "java_static_libs",
+			"LOCAL_AIDL_INCLUDES":         "aidl_includes",
+			"LOCAL_AAPT_FLAGS":            "aaptflags",
+			"LOCAL_PACKAGE_SPLITS":        "package_splits",
+			"LOCAL_COMPATIBILITY_SUITE":   "test_suites",
+		})
+	addStandardProperties(bpparser.BoolType,
+		map[string]string{
+			// Bool properties
+			"LOCAL_IS_HOST_MODULE":          "host",
+			"LOCAL_CLANG":                   "clang",
+			"LOCAL_FORCE_STATIC_EXECUTABLE": "static_executable",
+			"LOCAL_NATIVE_COVERAGE":         "native_coverage",
+			"LOCAL_NO_CRT":                  "nocrt",
+			"LOCAL_ALLOW_UNDEFINED_SYMBOLS": "allow_undefined_symbols",
+			"LOCAL_RTTI_FLAG":               "rtti",
+			"LOCAL_NO_STANDARD_LIBRARIES":   "no_standard_libraries",
+			"LOCAL_PACK_MODULE_RELOCATIONS": "pack_relocations",
+			"LOCAL_TIDY":                    "tidy",
+			"LOCAL_PROPRIETARY_MODULE":      "proprietary",
+			"LOCAL_VENDOR_MODULE":           "vendor",
+
+			"LOCAL_EXPORT_PACKAGE_RESOURCES": "export_package_resources",
+		})
 }
 
 type listSplitFunc func(bpparser.Expression) (string, bpparser.Expression, error)
@@ -219,8 +252,8 @@
 	}
 }
 
-func localIncludeDirs(file *bpFile, prefix string, value *mkparser.MakeString, appendVariable bool) error {
-	val, err := makeVariableToBlueprint(file, value, bpparser.ListType)
+func localIncludeDirs(ctx variableAssignmentContext) error {
+	val, err := makeVariableToBlueprint(ctx.file, ctx.mkvalue, bpparser.ListType)
 	if err != nil {
 		return err
 	}
@@ -231,14 +264,14 @@
 	}
 
 	if global, ok := lists["global"]; ok && !emptyList(global) {
-		err = setVariable(file, appendVariable, prefix, "include_dirs", global, true)
+		err = setVariable(ctx.file, ctx.append, ctx.prefix, "include_dirs", global, true)
 		if err != nil {
 			return err
 		}
 	}
 
 	if local, ok := lists["local"]; ok && !emptyList(local) {
-		err = setVariable(file, appendVariable, prefix, "local_include_dirs", local, true)
+		err = setVariable(ctx.file, ctx.append, ctx.prefix, "local_include_dirs", local, true)
 		if err != nil {
 			return err
 		}
@@ -247,8 +280,8 @@
 	return nil
 }
 
-func exportIncludeDirs(file *bpFile, prefix string, value *mkparser.MakeString, appendVariable bool) error {
-	val, err := makeVariableToBlueprint(file, value, bpparser.ListType)
+func exportIncludeDirs(ctx variableAssignmentContext) error {
+	val, err := makeVariableToBlueprint(ctx.file, ctx.mkvalue, bpparser.ListType)
 	if err != nil {
 		return err
 	}
@@ -259,17 +292,17 @@
 	}
 
 	if local, ok := lists["local"]; ok && !emptyList(local) {
-		err = setVariable(file, appendVariable, prefix, "export_include_dirs", local, true)
+		err = setVariable(ctx.file, ctx.append, ctx.prefix, "export_include_dirs", local, true)
 		if err != nil {
 			return err
 		}
-		appendVariable = true
+		ctx.append = true
 	}
 
 	// Add any paths that could not be converted to local relative paths to export_include_dirs
 	// anyways, they will cause an error if they don't exist and can be fixed manually.
 	if global, ok := lists["global"]; ok && !emptyList(global) {
-		err = setVariable(file, appendVariable, prefix, "export_include_dirs", global, true)
+		err = setVariable(ctx.file, ctx.append, ctx.prefix, "export_include_dirs", global, true)
 		if err != nil {
 			return err
 		}
@@ -278,8 +311,8 @@
 	return nil
 }
 
-func stem(file *bpFile, prefix string, value *mkparser.MakeString, appendVariable bool) error {
-	val, err := makeVariableToBlueprint(file, value, bpparser.StringType)
+func stem(ctx variableAssignmentContext) error {
+	val, err := makeVariableToBlueprint(ctx.file, ctx.mkvalue, bpparser.StringType)
 	if err != nil {
 		return err
 	}
@@ -292,11 +325,11 @@
 		}
 	}
 
-	return setVariable(file, appendVariable, prefix, varName, val, true)
+	return setVariable(ctx.file, ctx.append, ctx.prefix, varName, val, true)
 }
 
-func hostOs(file *bpFile, prefix string, value *mkparser.MakeString, appendVariable bool) error {
-	val, err := makeVariableToBlueprint(file, value, bpparser.ListType)
+func hostOs(ctx variableAssignmentContext) error {
+	val, err := makeVariableToBlueprint(ctx.file, ctx.mkvalue, bpparser.ListType)
 	if err != nil {
 		return err
 	}
@@ -319,15 +352,15 @@
 	}
 
 	if inList("windows") {
-		err = setVariable(file, appendVariable, "target.windows", "enabled", trueValue, true)
+		err = setVariable(ctx.file, ctx.append, "target.windows", "enabled", trueValue, true)
 	}
 
 	if !inList("linux") && err == nil {
-		err = setVariable(file, appendVariable, "target.linux", "enabled", falseValue, true)
+		err = setVariable(ctx.file, ctx.append, "target.linux", "enabled", falseValue, true)
 	}
 
 	if !inList("darwin") && err == nil {
-		err = setVariable(file, appendVariable, "target.darwin", "enabled", falseValue, true)
+		err = setVariable(ctx.file, ctx.append, "target.darwin", "enabled", falseValue, true)
 	}
 
 	return err
@@ -352,8 +385,8 @@
 
 }
 
-func srcFiles(file *bpFile, prefix string, value *mkparser.MakeString, appendVariable bool) error {
-	val, err := makeVariableToBlueprint(file, value, bpparser.ListType)
+func srcFiles(ctx variableAssignmentContext) error {
+	val, err := makeVariableToBlueprint(ctx.file, ctx.mkvalue, bpparser.ListType)
 	if err != nil {
 		return err
 	}
@@ -361,14 +394,14 @@
 	lists, err := splitBpList(val, splitSrcsLogtags)
 
 	if srcs, ok := lists["srcs"]; ok && !emptyList(srcs) {
-		err = setVariable(file, appendVariable, prefix, "srcs", srcs, true)
+		err = setVariable(ctx.file, ctx.append, ctx.prefix, "srcs", srcs, true)
 		if err != nil {
 			return err
 		}
 	}
 
 	if logtags, ok := lists["logtags"]; ok && !emptyList(logtags) {
-		err = setVariable(file, true, prefix, "logtags", logtags, true)
+		err = setVariable(ctx.file, true, ctx.prefix, "logtags", logtags, true)
 		if err != nil {
 			return err
 		}
@@ -377,8 +410,8 @@
 	return nil
 }
 
-func sanitize(file *bpFile, prefix string, mkvalue *mkparser.MakeString, appendVariable bool) error {
-	val, err := makeVariableToBlueprint(file, mkvalue, bpparser.ListType)
+func sanitize(ctx variableAssignmentContext) error {
+	val, err := makeVariableToBlueprint(ctx.file, ctx.mkvalue, bpparser.ListType)
 	if err != nil {
 		return err
 	}
@@ -388,7 +421,7 @@
 		case *bpparser.Variable:
 			return "vars", value, nil
 		case *bpparser.Operator:
-			file.errorf(mkvalue, "unknown sanitize expression")
+			ctx.file.errorf(ctx.mkvalue, "unknown sanitize expression")
 			return "unknown", value, nil
 		case *bpparser.String:
 			switch v.Value {
@@ -398,7 +431,7 @@
 				}
 				return v.Value, bpTrue, nil
 			default:
-				file.errorf(mkvalue, "unknown sanitize argument: %s", v.Value)
+				ctx.file.errorf(ctx.mkvalue, "unknown sanitize argument: %s", v.Value)
 				return "unknown", value, nil
 			}
 		default:
@@ -416,13 +449,13 @@
 
 		switch k {
 		case "never", "address", "coverage", "integer", "thread", "undefined":
-			err = setVariable(file, false, prefix, "sanitize."+k, lists[k].(*bpparser.List).Values[0], true)
+			err = setVariable(ctx.file, false, ctx.prefix, "sanitize."+k, lists[k].(*bpparser.List).Values[0], true)
 		case "unknown":
-			// Nothing, we already added the error above
+		// Nothing, we already added the error above
 		case "vars":
 			fallthrough
 		default:
-			err = setVariable(file, true, prefix, "sanitize", v, true)
+			err = setVariable(ctx.file, true, ctx.prefix, "sanitize", v, true)
 		}
 
 		if err != nil {
@@ -433,8 +466,19 @@
 	return err
 }
 
-func ldflags(file *bpFile, prefix string, mkvalue *mkparser.MakeString, appendVariable bool) error {
-	val, err := makeVariableToBlueprint(file, mkvalue, bpparser.ListType)
+func prebuiltClass(ctx variableAssignmentContext) error {
+	class := ctx.mkvalue.Value(nil)
+	if v, ok := prebuiltTypes[class]; ok {
+		ctx.file.scope.Set("BUILD_PREBUILT", v)
+	} else {
+		// reset to default
+		ctx.file.scope.Set("BUILD_PREBUILT", "prebuilt")
+	}
+	return nil
+}
+
+func ldflags(ctx variableAssignmentContext) error {
+	val, err := makeVariableToBlueprint(ctx.file, ctx.mkvalue, bpparser.ListType)
 	if err != nil {
 		return err
 	}
@@ -456,13 +500,13 @@
 		}
 
 		if v, ok := exp2.Args[1].(*bpparser.Variable); !ok || v.Name != "LOCAL_PATH" {
-			file.errorf(mkvalue, "Unrecognized version-script")
+			ctx.file.errorf(ctx.mkvalue, "Unrecognized version-script")
 			return "ldflags", value, nil
 		}
 
 		s, ok := exp1.Args[1].(*bpparser.String)
 		if !ok {
-			file.errorf(mkvalue, "Unrecognized version-script")
+			ctx.file.errorf(ctx.mkvalue, "Unrecognized version-script")
 			return "ldflags", value, nil
 		}
 
@@ -475,7 +519,7 @@
 	}
 
 	if ldflags, ok := lists["ldflags"]; ok && !emptyList(ldflags) {
-		err = setVariable(file, appendVariable, prefix, "ldflags", ldflags, true)
+		err = setVariable(ctx.file, ctx.append, ctx.prefix, "ldflags", ldflags, true)
 		if err != nil {
 			return err
 		}
@@ -483,9 +527,9 @@
 
 	if version_script, ok := lists["version"]; ok && !emptyList(version_script) {
 		if len(version_script.(*bpparser.List).Values) > 1 {
-			file.errorf(mkvalue, "multiple version scripts found?")
+			ctx.file.errorf(ctx.mkvalue, "multiple version scripts found?")
 		}
-		err = setVariable(file, false, prefix, "version_script", version_script.(*bpparser.List).Values[0], true)
+		err = setVariable(ctx.file, false, ctx.prefix, "version_script", version_script.(*bpparser.List).Values[0], true)
 		if err != nil {
 			return err
 		}
@@ -494,8 +538,52 @@
 	return nil
 }
 
-var deleteProperties = map[string]struct{}{
-	"LOCAL_CPP_EXTENSION": struct{}{},
+// given a conditional, returns a function that will insert a variable assignment or not, based on the conditional
+func includeVariableIf(bpVar bpVariable, conditional func(ctx variableAssignmentContext) bool) func(ctx variableAssignmentContext) error {
+	return func(ctx variableAssignmentContext) error {
+		var err error
+		if conditional(ctx) {
+			err = includeVariableNow(bpVar, ctx)
+		}
+		return err
+	}
+}
+
+// given a variable, returns a function that will always insert a variable assignment
+func includeVariable(bpVar bpVariable) func(ctx variableAssignmentContext) error {
+	return includeVariableIf(bpVar, always)
+}
+
+func includeVariableNow(bpVar bpVariable, ctx variableAssignmentContext) error {
+	var val bpparser.Expression
+	var err error
+	val, err = makeVariableToBlueprint(ctx.file, ctx.mkvalue, bpVar.variableType)
+	if err == nil {
+		err = setVariable(ctx.file, ctx.append, ctx.prefix, bpVar.name, val, true)
+	}
+	return err
+}
+
+// given a function that returns a bool, returns a function that returns the opposite
+func not(conditional func(ctx variableAssignmentContext) bool) func(ctx variableAssignmentContext) bool {
+	return func(ctx variableAssignmentContext) bool {
+		return !conditional(ctx)
+	}
+}
+
+// returns a function that tells whether mkvalue.Dump equals the given query string
+func valueDumpEquals(textToMatch string) func(ctx variableAssignmentContext) bool {
+	return func(ctx variableAssignmentContext) bool {
+		return (ctx.mkvalue.Dump() == textToMatch)
+	}
+}
+
+func always(ctx variableAssignmentContext) bool {
+	return true
+}
+
+func skip(ctx variableAssignmentContext) error {
+	return nil
 }
 
 // Shorter suffixes of other suffixes must be at the end of the list
@@ -572,6 +660,7 @@
 	"BUILD_STATIC_LIBRARY":        "cc_library_static",
 	"BUILD_HOST_SHARED_LIBRARY":   "cc_library_host_shared",
 	"BUILD_HOST_STATIC_LIBRARY":   "cc_library_host_static",
+	"BUILD_HEADER_LIBRARY":        "cc_library_headers",
 	"BUILD_EXECUTABLE":            "cc_binary",
 	"BUILD_HOST_EXECUTABLE":       "cc_binary_host",
 	"BUILD_NATIVE_TEST":           "cc_test",
@@ -584,8 +673,13 @@
 	"BUILD_HOST_JAVA_LIBRARY":        "java_library_host",
 	"BUILD_HOST_DALVIK_JAVA_LIBRARY": "java_library_host_dalvik",
 	"BUILD_PACKAGE":                  "android_app",
+}
 
-	"BUILD_PREBUILT": "prebuilt",
+var prebuiltTypes = map[string]string{
+	"SHARED_LIBRARIES": "cc_prebuilt_library_shared",
+	"STATIC_LIBRARIES": "cc_prebuilt_library_static",
+	"EXECUTABLES":      "cc_prebuilt_binary",
+	"JAVA_LIBRARIES":   "prebuilt_java_library",
 }
 
 var soongModuleTypes = map[string]bool{}
@@ -601,6 +695,9 @@
 		globalScope.Set(k, v)
 		soongModuleTypes[v] = true
 	}
+	for _, v := range prebuiltTypes {
+		soongModuleTypes[v] = true
+	}
 
 	return globalScope
 }
diff --git a/androidmk/cmd/androidmk/androidmk.go b/androidmk/cmd/androidmk/androidmk.go
index 729e4f2..1d94b65 100644
--- a/androidmk/cmd/androidmk/androidmk.go
+++ b/androidmk/cmd/androidmk/androidmk.go
@@ -233,28 +233,16 @@
 	appendVariable := assignment.Type == "+="
 
 	var err error
-	if prop, ok := standardProperties[name]; ok {
-		var val bpparser.Expression
-		val, err = makeVariableToBlueprint(file, assignment.Value, prop.Type)
-		if err == nil {
-			err = setVariable(file, appendVariable, prefix, prop.string, val, true)
-		}
-	} else if prop, ok := rewriteProperties[name]; ok {
-		err = prop.f(file, prefix, assignment.Value, appendVariable)
-	} else if _, ok := deleteProperties[name]; ok {
-		return
+	if prop, ok := rewriteProperties[name]; ok {
+		err = prop(variableAssignmentContext{file, prefix, assignment.Value, appendVariable})
 	} else {
 		switch {
-		case name == "LOCAL_PATH":
-			// Nothing to do, except maybe avoid the "./" in paths?
 		case name == "LOCAL_ARM_MODE":
 			// This is a hack to get the LOCAL_ARM_MODE value inside
 			// of an arch: { arm: {} } block.
 			armModeAssign := assignment
 			armModeAssign.Name = mkparser.SimpleMakeString("LOCAL_ARM_MODE_HACK_arm", assignment.Name.Pos())
 			handleAssignment(file, armModeAssign, c)
-		case name == "LOCAL_ADDITIONAL_DEPENDENCIES":
-			// TODO: check for only .mk files?
 		case strings.HasPrefix(name, "LOCAL_"):
 			file.errorf(assignment, "unsupported assignment to %s", name)
 			return
diff --git a/androidmk/cmd/androidmk/androidmk_test.go b/androidmk/cmd/androidmk/androidmk_test.go
index 2f4daf7..0c44ea7 100644
--- a/androidmk/cmd/androidmk/androidmk_test.go
+++ b/androidmk/cmd/androidmk/androidmk_test.go
@@ -357,6 +357,34 @@
 }
 `,
 	},
+	{
+		desc: "Remove LOCAL_MODULE_TAGS optional",
+		in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_SHARED_LIBRARY)
+`,
+
+		expected: `
+cc_library_shared {
+
+}
+`,
+	},
+	{
+		desc: "Keep LOCAL_MODULE_TAGS non-optional",
+		in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := debug
+include $(BUILD_SHARED_LIBRARY)
+`,
+
+		expected: `
+cc_library_shared {
+	tags: ["debug"],
+}
+`,
+	},
 }
 
 func reformatBlueprint(input string) string {
diff --git a/build.ninja.in b/build.ninja.in
index a717672..9449d73 100644
--- a/build.ninja.in
+++ b/build.ninja.in
@@ -70,7 +70,6 @@
 build $
         ${g.bootstrap.buildDir}/.bootstrap/blueprint/test/github.com/google/blueprint.a $
         : g.bootstrap.compile ${g.bootstrap.srcDir}/build/blueprint/context.go $
-        ${g.bootstrap.srcDir}/build/blueprint/fs.go $
         ${g.bootstrap.srcDir}/build/blueprint/glob.go $
         ${g.bootstrap.srcDir}/build/blueprint/live_tracker.go $
         ${g.bootstrap.srcDir}/build/blueprint/mangle.go $
@@ -139,7 +138,6 @@
 build $
         ${g.bootstrap.buildDir}/.bootstrap/blueprint/pkg/github.com/google/blueprint.a $
         : g.bootstrap.compile ${g.bootstrap.srcDir}/build/blueprint/context.go $
-        ${g.bootstrap.srcDir}/build/blueprint/fs.go $
         ${g.bootstrap.srcDir}/build/blueprint/glob.go $
         ${g.bootstrap.srcDir}/build/blueprint/live_tracker.go $
         ${g.bootstrap.srcDir}/build/blueprint/mangle.go $
@@ -217,7 +215,7 @@
 # Variant:
 # Type:    bootstrap_go_package
 # Factory: github.com/google/blueprint/bootstrap.newGoPackageModuleFactory.func1
-# Defined: build/blueprint/Blueprints:50:1
+# Defined: build/blueprint/Blueprints:49:1
 
 build $
         ${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg/github.com/google/blueprint/deptools.a $
@@ -233,7 +231,7 @@
 # Variant:
 # Type:    bootstrap_go_package
 # Factory: github.com/google/blueprint/bootstrap.newGoPackageModuleFactory.func1
-# Defined: build/blueprint/Blueprints:34:1
+# Defined: build/blueprint/Blueprints:33:1
 
 build $
         ${g.bootstrap.buildDir}/.bootstrap/blueprint-parser/test/github.com/google/blueprint/parser.a $
@@ -300,12 +298,13 @@
 # Variant:
 # Type:    bootstrap_go_package
 # Factory: github.com/google/blueprint/bootstrap.newGoPackageModuleFactory.func1
-# Defined: build/blueprint/Blueprints:56:1
+# Defined: build/blueprint/Blueprints:55:1
 
 build $
         ${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/test/github.com/google/blueprint/pathtools.a $
         : g.bootstrap.compile $
         ${g.bootstrap.srcDir}/build/blueprint/pathtools/lists.go $
+        ${g.bootstrap.srcDir}/build/blueprint/pathtools/fs.go $
         ${g.bootstrap.srcDir}/build/blueprint/pathtools/glob.go $
         ${g.bootstrap.srcDir}/build/blueprint/pathtools/glob_test.go | $
         ${g.bootstrap.compileCmd} $
@@ -351,6 +350,7 @@
         ${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/pkg/github.com/google/blueprint/pathtools.a $
         : g.bootstrap.compile $
         ${g.bootstrap.srcDir}/build/blueprint/pathtools/lists.go $
+        ${g.bootstrap.srcDir}/build/blueprint/pathtools/fs.go $
         ${g.bootstrap.srcDir}/build/blueprint/pathtools/glob.go | $
         ${g.bootstrap.compileCmd} $
         ${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg/github.com/google/blueprint/deptools.a
diff --git a/cc/androidmk.go b/cc/androidmk.go
index c0be111..59a1db8 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -26,6 +26,7 @@
 type AndroidMkContext interface {
 	Target() android.Target
 	subAndroidMk(*android.AndroidMkData, interface{})
+	vndk() bool
 }
 
 type subAndroidMkProvider interface {
@@ -56,16 +57,16 @@
 		if len(c.Properties.AndroidMkSharedLibs) > 0 {
 			fmt.Fprintln(w, "LOCAL_SHARED_LIBRARIES := "+strings.Join(c.Properties.AndroidMkSharedLibs, " "))
 		}
-		if c.Target().Os == android.Android && c.Properties.Sdk_version != "" {
+		if c.Target().Os == android.Android && c.Properties.Sdk_version != "" && !c.vndk() {
 			fmt.Fprintln(w, "LOCAL_SDK_VERSION := "+c.Properties.Sdk_version)
 			fmt.Fprintln(w, "LOCAL_NDK_STL_VARIANT := none")
-		} else if c.Target().Os == android.Android && c.Properties.Use_vndk {
-			fmt.Fprintln(w, "LOCAL_USE_VNDK := true")
-			fmt.Fprintln(w, "LOCAL_NDK_STL_VARIANT := none")
 		} else {
 			// These are already included in LOCAL_SHARED_LIBRARIES
 			fmt.Fprintln(w, "LOCAL_CXX_STL := none")
 		}
+		if c.vndk() {
+			fmt.Fprintln(w, "LOCAL_USE_VNDK := true")
+		}
 		return nil
 	})
 
@@ -77,50 +78,88 @@
 	c.subAndroidMk(&ret, c.linker)
 	c.subAndroidMk(&ret, c.installer)
 
+	if c.vndk() {
+		ret.SubName += ".vendor"
+	}
+
 	return ret, nil
 }
 
-func (library *libraryDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
-	if !library.static() {
-		ctx.subAndroidMk(ret, &library.stripper)
-		ctx.subAndroidMk(ret, &library.relocationPacker)
+func (library *libraryDecorator) androidMkWriteExportedFlags(w io.Writer) {
+	exportedFlags := library.exportedFlags()
+	if len(exportedFlags) > 0 {
+		fmt.Fprintln(w, "LOCAL_EXPORT_CFLAGS :=", strings.Join(exportedFlags, " "))
 	}
+	exportedFlagsDeps := library.exportedFlagsDeps()
+	if len(exportedFlagsDeps) > 0 {
+		fmt.Fprintln(w, "LOCAL_EXPORT_C_INCLUDE_DEPS :=", strings.Join(exportedFlagsDeps.Strings(), " "))
+	}
+}
 
+func (library *libraryDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
 	if library.static() {
 		ret.Class = "STATIC_LIBRARIES"
-	} else {
+	} else if library.shared() {
+		ctx.subAndroidMk(ret, &library.stripper)
+		ctx.subAndroidMk(ret, &library.relocationPacker)
+
 		ret.Class = "SHARED_LIBRARIES"
+	} else if library.header() {
+		ret.Custom = func(w io.Writer, name, prefix, moduleDir string) error {
+			fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
+			fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
+			fmt.Fprintln(w, "LOCAL_MODULE :=", name)
+
+			archStr := ctx.Target().Arch.ArchType.String()
+			var host bool
+			switch ctx.Target().Os.Class {
+			case android.Host:
+				fmt.Fprintln(w, "LOCAL_MODULE_HOST_ARCH := ", archStr)
+				host = true
+			case android.HostCross:
+				fmt.Fprintln(w, "LOCAL_MODULE_HOST_CROSS_ARCH := ", archStr)
+				host = true
+			case android.Device:
+				fmt.Fprintln(w, "LOCAL_MODULE_TARGET_ARCH := ", archStr)
+			}
+
+			if host {
+				fmt.Fprintln(w, "LOCAL_MODULE_HOST_OS :=", ctx.Target().Os.String())
+				fmt.Fprintln(w, "LOCAL_IS_HOST_MODULE := true")
+			} else if ctx.vndk() {
+				fmt.Fprintln(w, "LOCAL_USE_VNDK := true")
+			}
+
+			library.androidMkWriteExportedFlags(w)
+			fmt.Fprintln(w, "include $(BUILD_HEADER_LIBRARY)")
+
+			return nil
+		}
+
+		return
 	}
 
 	ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) error {
-		var exportedIncludes []string
-		for _, flag := range library.exportedFlags() {
-			if strings.HasPrefix(flag, "-I") {
-				exportedIncludes = append(exportedIncludes, strings.TrimPrefix(flag, "-I"))
-			}
-		}
-		if len(exportedIncludes) > 0 {
-			fmt.Fprintln(w, "LOCAL_EXPORT_C_INCLUDE_DIRS :=", strings.Join(exportedIncludes, " "))
-		}
-		exportedIncludeDeps := library.exportedFlagsDeps()
-		if len(exportedIncludeDeps) > 0 {
-			fmt.Fprintln(w, "LOCAL_EXPORT_C_INCLUDE_DEPS :=", strings.Join(exportedIncludeDeps.Strings(), " "))
-		}
+		library.androidMkWriteExportedFlags(w)
 
 		fmt.Fprintln(w, "LOCAL_BUILT_MODULE_STEM := $(LOCAL_MODULE)"+outputFile.Ext())
 
 		fmt.Fprintln(w, "LOCAL_SYSTEM_SHARED_LIBRARIES :=")
 
+		if library.coverageOutputFile.Valid() {
+			fmt.Fprintln(w, "LOCAL_PREBUILT_COVERAGE_ARCHIVE :=", library.coverageOutputFile.String())
+		}
+
 		return nil
 	})
 
-	if !library.static() {
+	if library.shared() {
 		ctx.subAndroidMk(ret, library.baseInstaller)
 	}
 }
 
 func (object *objectLinker) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
-	ret.Custom = func(w io.Writer, name, prefix string) error {
+	ret.Custom = func(w io.Writer, name, prefix, moduleDir string) error {
 		out := ret.OutputFile.Path()
 
 		fmt.Fprintln(w, "\n$("+prefix+"OUT_INTERMEDIATE_LIBRARIES)/"+name+objectExtension+":", out.String())
@@ -145,6 +184,9 @@
 			fmt.Fprintln(w, "LOCAL_MODULE_SYMLINKS := "+strings.Join(binary.symlinks, " "))
 		}
 
+		if binary.coverageOutputFile.Valid() {
+			fmt.Fprintln(w, "LOCAL_PREBUILT_COVERAGE_ARCHIVE :=", binary.coverageOutputFile.String())
+		}
 		return nil
 	})
 }
@@ -159,6 +201,31 @@
 	if Bool(test.Properties.Test_per_src) {
 		ret.SubName = "_" + test.binaryDecorator.Properties.Stem
 	}
+
+	ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) error {
+		if len(test.Properties.Test_suites) > 0 {
+			fmt.Fprintln(w, "LOCAL_COMPATIBILITY_SUITE :=",
+				strings.Join(test.Properties.Test_suites, " "))
+		}
+		return nil
+	})
+
+	var testFiles []string
+	for _, d := range test.data {
+		rel := d.Rel()
+		path := d.String()
+		if !strings.HasSuffix(path, rel) {
+			panic(fmt.Errorf("path %q does not end with %q", path, rel))
+		}
+		path = strings.TrimSuffix(path, rel)
+		testFiles = append(testFiles, path+":"+rel)
+	}
+	if len(testFiles) > 0 {
+		ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) error {
+			fmt.Fprintln(w, "LOCAL_TEST_DATA := "+strings.Join(testFiles, " "))
+			return nil
+		})
+	}
 }
 
 func (test *testLibrary) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
@@ -222,7 +289,7 @@
 }
 
 func (c *stubDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
-	ret.SubName = "." + c.properties.ApiLevel
+	ret.SubName = ndkLibrarySuffix + "." + c.properties.ApiLevel
 	ret.Class = "SHARED_LIBRARIES"
 
 	ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) error {
@@ -232,6 +299,7 @@
 		fmt.Fprintln(w, "LOCAL_MODULE_SUFFIX := "+outputFile.Ext())
 		fmt.Fprintln(w, "LOCAL_MODULE_PATH := "+path)
 		fmt.Fprintln(w, "LOCAL_MODULE_STEM := "+stem)
+		fmt.Fprintln(w, "LOCAL_NO_NOTICE_FILE := true")
 
 		// Prevent make from installing the libraries to obj/lib (since we have
 		// dozens of libraries with the same name, they'll clobber each other
@@ -240,3 +308,21 @@
 		return nil
 	})
 }
+
+func (c *llndkStubDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
+	ret.Class = "SHARED_LIBRARIES"
+	ret.SubName = ".vendor"
+
+	ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) error {
+		c.libraryDecorator.androidMkWriteExportedFlags(w)
+
+		fmt.Fprintln(w, "LOCAL_BUILT_MODULE_STEM := $(LOCAL_MODULE)"+outputFile.Ext())
+		fmt.Fprintln(w, "LOCAL_STRIP_MODULE := false")
+		fmt.Fprintln(w, "LOCAL_SYSTEM_SHARED_LIBRARIES :=")
+		fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE := true")
+		fmt.Fprintln(w, "LOCAL_NO_NOTICE_FILE := true")
+		fmt.Fprintln(w, "LOCAL_USE_VNDK := true")
+
+		return nil
+	})
+}
diff --git a/cc/binary.go b/cc/binary.go
index afc8a99..b4610ed 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -15,6 +15,8 @@
 package cc
 
 import (
+	"path/filepath"
+
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 
@@ -79,6 +81,9 @@
 
 	// Names of symlinks to be installed for use in LOCAL_MODULE_SYMLINKS
 	symlinks []string
+
+	// Output archive of gcno coverage information
+	coverageOutputFile android.OptionalPath
 }
 
 var _ linker = (*binaryDecorator)(nil)
@@ -103,7 +108,7 @@
 	deps = binary.baseLinker.linkerDeps(ctx, deps)
 	if ctx.toolchain().Bionic() {
 		if !Bool(binary.baseLinker.Properties.Nocrt) {
-			if !ctx.sdk() && !ctx.vndk() {
+			if !ctx.sdk() {
 				if binary.static() {
 					deps.CrtBegin = "crtbegin_static"
 				} else {
@@ -199,7 +204,7 @@
 	if ctx.Host() && !binary.static() {
 		if !ctx.AConfig().IsEnvTrue("DISABLE_HOST_PIE") {
 			flags.LdFlags = append(flags.LdFlags, "-pie")
-			if ctx.Os() == android.Windows {
+			if ctx.Windows() {
 				flags.LdFlags = append(flags.LdFlags, "-Wl,-e_mainCRTStartup")
 			}
 		}
@@ -208,7 +213,7 @@
 	// MinGW spits out warnings about -fPIC even for -fpie?!) being ignored because
 	// all code is position independent, and then those warnings get promoted to
 	// errors.
-	if ctx.Os() != android.Windows {
+	if !ctx.Windows() {
 		flags.CFlags = append(flags.CFlags, "-fpie")
 	}
 
@@ -233,7 +238,22 @@
 				if binary.Properties.DynamicLinker != "" {
 					flags.DynamicLinker = binary.Properties.DynamicLinker
 				} else {
-					flags.DynamicLinker = "/system/bin/linker"
+					switch ctx.Os() {
+					case android.Android:
+						flags.DynamicLinker = "/system/bin/linker"
+					case android.LinuxBionic:
+						// The linux kernel expects the linker to be an
+						// absolute path
+						path := android.PathForOutput(ctx,
+							"host", "linux_bionic-x86", "bin", "linker")
+						if p, err := filepath.Abs(path.String()); err == nil {
+							flags.DynamicLinker = p
+						} else {
+							ctx.ModuleErrorf("can't find path to dynamic linker: %q", err)
+						}
+					default:
+						ctx.ModuleErrorf("unknown dynamic linker")
+					}
 					if flags.Toolchain.Is64Bit() {
 						flags.DynamicLinker += "64"
 					}
@@ -299,6 +319,10 @@
 		deps.LateStaticLibs, deps.WholeStaticLibs, linkerDeps, deps.CrtBegin, deps.CrtEnd, true,
 		builderFlags, outputFile)
 
+	objs.coverageFiles = append(objs.coverageFiles, deps.StaticLibObjs.coverageFiles...)
+	objs.coverageFiles = append(objs.coverageFiles, deps.WholeStaticLibObjs.coverageFiles...)
+	binary.coverageOutputFile = TransformCoverageFilesToLib(ctx, objs, builderFlags, binary.getStem(ctx))
+
 	return ret
 }
 
diff --git a/cc/builder.go b/cc/builder.go
index c9a6722..0694cb7 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -109,7 +109,7 @@
 		},
 		"objcopyCmd", "prefix")
 
-	stripPath = pctx.SourcePathVariable("stripPath", "build/soong/scripts/strip.sh")
+	_ = pctx.SourcePathVariable("stripPath", "build/soong/scripts/strip.sh")
 
 	strip = pctx.AndroidStaticRule("strip",
 		blueprint.RuleParams{
@@ -127,7 +127,7 @@
 			Description: "empty file $out",
 		})
 
-	copyGccLibPath = pctx.SourcePathVariable("copyGccLibPath", "build/soong/scripts/copygcclib.sh")
+	_ = pctx.SourcePathVariable("copyGccLibPath", "build/soong/scripts/copygcclib.sh")
 
 	copyGccLib = pctx.AndroidStaticRule("copyGccLib",
 		blueprint.RuleParams{
@@ -139,7 +139,7 @@
 		},
 		"ccCmd", "cFlags", "libName")
 
-	tocPath = pctx.SourcePathVariable("tocPath", "build/soong/scripts/toc.sh")
+	_ = pctx.SourcePathVariable("tocPath", "build/soong/scripts/toc.sh")
 
 	toc = pctx.AndroidStaticRule("toc",
 		blueprint.RuleParams{
@@ -159,7 +159,7 @@
 		},
 		"cFlags", "tidyFlags")
 
-	yasmCmd = pctx.SourcePathVariable("yasmCmd", "prebuilts/misc/${config.HostPrebuiltTag}/yasm/yasm")
+	_ = pctx.SourcePathVariable("yasmCmd", "prebuilts/misc/${config.HostPrebuiltTag}/yasm/yasm")
 
 	yasm = pctx.AndroidStaticRule("yasm",
 		blueprint.RuleParams{
@@ -184,6 +184,7 @@
 
 type builderFlags struct {
 	globalFlags string
+	arFlags     string
 	asFlags     string
 	cFlags      string
 	conlyFlags  string
@@ -198,6 +199,9 @@
 	toolchain   config.Toolchain
 	clang       bool
 	tidy        bool
+	coverage    bool
+
+	systemIncludeFlags string
 
 	groupStaticLibs bool
 
@@ -207,21 +211,24 @@
 }
 
 type Objects struct {
-	objFiles  android.Paths
-	tidyFiles android.Paths
+	objFiles      android.Paths
+	tidyFiles     android.Paths
+	coverageFiles android.Paths
 }
 
 func (a Objects) Copy() Objects {
 	return Objects{
-		objFiles:  append(android.Paths{}, a.objFiles...),
-		tidyFiles: append(android.Paths{}, a.tidyFiles...),
+		objFiles:      append(android.Paths{}, a.objFiles...),
+		tidyFiles:     append(android.Paths{}, a.tidyFiles...),
+		coverageFiles: append(android.Paths{}, a.coverageFiles...),
 	}
 }
 
 func (a Objects) Append(b Objects) Objects {
 	return Objects{
-		objFiles:  append(a.objFiles, b.objFiles...),
-		tidyFiles: append(a.tidyFiles, b.tidyFiles...),
+		objFiles:      append(a.objFiles, b.objFiles...),
+		tidyFiles:     append(a.tidyFiles, b.tidyFiles...),
+		coverageFiles: append(a.coverageFiles, b.coverageFiles...),
 	}
 }
 
@@ -234,10 +241,30 @@
 	if flags.tidy && flags.clang {
 		tidyFiles = make(android.Paths, 0, len(srcFiles))
 	}
+	var coverageFiles android.Paths
+	if flags.coverage {
+		coverageFiles = make(android.Paths, 0, len(srcFiles))
+	}
 
-	cflags := flags.globalFlags + " " + flags.cFlags + " " + flags.conlyFlags
-	cppflags := flags.globalFlags + " " + flags.cFlags + " " + flags.cppFlags
-	asflags := flags.globalFlags + " " + flags.asFlags
+	cflags := strings.Join([]string{
+		flags.globalFlags,
+		flags.systemIncludeFlags,
+		flags.cFlags,
+		flags.conlyFlags,
+	}, " ")
+
+	cppflags := strings.Join([]string{
+		flags.globalFlags,
+		flags.systemIncludeFlags,
+		flags.cFlags,
+		flags.cppFlags,
+	}, " ")
+
+	asflags := strings.Join([]string{
+		flags.globalFlags,
+		flags.systemIncludeFlags,
+		flags.asFlags,
+	}, " ")
 
 	if flags.clang {
 		cflags += " ${config.NoOverrideClangGlobalCflags}"
@@ -268,12 +295,14 @@
 		var moduleCflags string
 		var ccCmd string
 		tidy := flags.tidy && flags.clang
+		coverage := flags.coverage
 
 		switch srcFile.Ext() {
 		case ".S", ".s":
 			ccCmd = "gcc"
 			moduleCflags = asflags
 			tidy = false
+			coverage = false
 		case ".c":
 			ccCmd = "gcc"
 			moduleCflags = cflags
@@ -300,11 +329,19 @@
 			ccCmd = gccCmd(flags.toolchain, ccCmd)
 		}
 
+		var implicitOutputs android.WritablePaths
+		if coverage {
+			gcnoFile := android.ObjPathWithExt(ctx, subdir, srcFile, "gcno")
+			implicitOutputs = append(implicitOutputs, gcnoFile)
+			coverageFiles = append(coverageFiles, gcnoFile)
+		}
+
 		ctx.ModuleBuild(pctx, android.ModuleBuildParams{
-			Rule:      cc,
-			Output:    objFile,
-			Input:     srcFile,
-			OrderOnly: deps,
+			Rule:            cc,
+			Output:          objFile,
+			ImplicitOutputs: implicitOutputs,
+			Input:           srcFile,
+			OrderOnly:       deps,
 			Args: map[string]string{
 				"cFlags": moduleCflags,
 				"ccCmd":  ccCmd,
@@ -332,8 +369,9 @@
 	}
 
 	return Objects{
-		objFiles:  objFiles,
-		tidyFiles: tidyFiles,
+		objFiles:      objFiles,
+		tidyFiles:     tidyFiles,
+		coverageFiles: coverageFiles,
 	}
 }
 
@@ -341,8 +379,16 @@
 func TransformObjToStaticLib(ctx android.ModuleContext, objFiles android.Paths,
 	flags builderFlags, outputFile android.ModuleOutPath, deps android.Paths) {
 
+	if ctx.Darwin() {
+		transformDarwinObjToStaticLib(ctx, objFiles, flags, outputFile, deps)
+		return
+	}
+
 	arCmd := gccCmd(flags.toolchain, "ar")
 	arFlags := "crsPD"
+	if flags.arFlags != "" {
+		arFlags += " " + flags.arFlags
+	}
 
 	ctx.ModuleBuild(pctx, android.ModuleBuildParams{
 		Rule:      ar,
@@ -360,7 +406,7 @@
 // darwin.  The darwin ar tool doesn't support @file for list files, and has a
 // very small command line length limit, so we have to split the ar into multiple
 // steps, each appending to the previous one.
-func TransformDarwinObjToStaticLib(ctx android.ModuleContext, objFiles android.Paths,
+func transformDarwinObjToStaticLib(ctx android.ModuleContext, objFiles android.Paths,
 	flags builderFlags, outputPath android.ModuleOutPath, deps android.Paths) {
 
 	arFlags := "cqs"
@@ -599,6 +645,20 @@
 	})
 }
 
+func TransformCoverageFilesToLib(ctx android.ModuleContext,
+	inputs Objects, flags builderFlags, baseName string) android.OptionalPath {
+
+	if len(inputs.coverageFiles) > 0 {
+		outputFile := android.PathForModuleOut(ctx, baseName+".gcnodir")
+
+		TransformObjToStaticLib(ctx, inputs.coverageFiles, flags, outputFile, nil)
+
+		return android.OptionalPathForPath(outputFile)
+	}
+
+	return android.OptionalPath{}
+}
+
 func CopyGccLib(ctx android.ModuleContext, libName string,
 	flags builderFlags, outputFile android.WritablePath) {
 
diff --git a/cc/cc.go b/cc/cc.go
index 8bf1467..63caf3a 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -35,6 +35,7 @@
 
 	android.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
 		ctx.BottomUp("link", linkageMutator).Parallel()
+		ctx.BottomUp("image", vendorMutator).Parallel()
 		ctx.BottomUp("ndk_api", ndkApiMutator).Parallel()
 		ctx.BottomUp("test_per_src", testPerSrcMutator).Parallel()
 		ctx.BottomUp("begin", beginMutator).Parallel()
@@ -46,6 +47,8 @@
 
 		ctx.TopDown("tsan_deps", sanitizerDepsMutator(tsan))
 		ctx.BottomUp("tsan", sanitizerMutator(tsan)).Parallel()
+
+		ctx.BottomUp("coverage", coverageLinkingMutator).Parallel()
 	})
 
 	pctx.Import("android/soong/cc/config")
@@ -78,6 +81,7 @@
 
 	// Paths to .o files
 	Objs               Objects
+	StaticLibObjs      Objects
 	WholeStaticLibObjs Objects
 
 	// Paths to generated source files
@@ -93,6 +97,7 @@
 
 type Flags struct {
 	GlobalFlags []string // Flags that apply to C, C++, and assembly source files
+	ArFlags     []string // Flags that apply to ar
 	AsFlags     []string // Flags that apply to assembly source files
 	CFlags      []string // Flags that apply to C and C++ source files
 	ConlyFlags  []string // Flags that apply to C source files
@@ -105,9 +110,14 @@
 	TidyFlags   []string // Flags that apply to clang-tidy
 	YasmFlags   []string // Flags that apply to yasm assembly source files
 
+	// Global include flags that apply to C, C++, and assembly source files
+	// These must be after any module include flags, which will be in GlobalFlags.
+	SystemIncludeFlags []string
+
 	Toolchain config.Toolchain
 	Clang     bool
 	Tidy      bool
+	Coverage  bool
 
 	RequiredInstructionSet string
 	DynamicLinker          string
@@ -130,22 +140,32 @@
 	// Minimum sdk version supported when compiling against the ndk
 	Sdk_version string
 
-	// Whether to compile against the VNDK
-	Use_vndk bool
-
 	// don't insert default compiler flags into asflags, cflags,
 	// cppflags, conlyflags, ldflags, or include_dirs
 	No_default_compiler_flags *bool
 
+	// whether this module should be allowed to install onto /vendor as
+	// well as /system. The two variants will be built separately, one
+	// like normal, and the other limited to the set of libraries and
+	// headers that are exposed to /vendor modules.
+	//
+	// The vendor variant may be used with a different (newer) /system,
+	// so it shouldn't have any unversioned runtime dependencies, or
+	// make assumptions about the system that may not be true in the
+	// future.
+	//
+	// Nothing happens if BOARD_VNDK_VERSION isn't set in the BoardConfig.mk
+	Vendor_available *bool
+
 	AndroidMkSharedLibs []string `blueprint:"mutated"`
 	HideFromMake        bool     `blueprint:"mutated"`
 	PreventInstall      bool     `blueprint:"mutated"`
-	Vndk_version        string   `blueprint:"mutated"`
+
+	UseVndk bool `blueprint:"mutated"`
 }
 
 type UnusedProperties struct {
-	Native_coverage *bool
-	Tags            []string
+	Tags []string
 }
 
 type ModuleContextIntf interface {
@@ -208,6 +228,7 @@
 	installerProps() []interface{}
 	install(ctx ModuleContext, path android.Path)
 	inData() bool
+	inSanitizerDir() bool
 	hostToolPath() android.OptionalPath
 }
 
@@ -261,6 +282,7 @@
 	installer installer
 	stl       *stl
 	sanitize  *sanitize
+	coverage  *coverage
 
 	androidMkSharedLibDeps []string
 
@@ -269,6 +291,9 @@
 	cachedToolchain config.Toolchain
 
 	subAndroidMkOnce map[subAndroidMkProvider]bool
+
+	// Flags used to compile this module
+	flags Flags
 }
 
 func (c *Module) Init() (blueprint.Module, []interface{}) {
@@ -288,6 +313,9 @@
 	if c.sanitize != nil {
 		props = append(props, c.sanitize.props()...)
 	}
+	if c.coverage != nil {
+		props = append(props, c.coverage.props()...)
+	}
 	for _, feature := range c.features {
 		props = append(props, feature.props()...)
 	}
@@ -308,6 +336,10 @@
 	return false
 }
 
+func (c *Module) vndk() bool {
+	return c.Properties.UseVndk
+}
+
 type baseModuleContext struct {
 	android.BaseContext
 	moduleContextImpl
@@ -323,6 +355,12 @@
 	moduleContextImpl
 }
 
+// Vendor returns true for vendor modules so that they get installed onto the
+// correct partition
+func (ctx *moduleContext) Vendor() bool {
+	return ctx.ModuleContext.Vendor() || ctx.moduleContextImpl.mod.Properties.UseVndk
+}
+
 type moduleContextImpl struct {
 	mod *Module
 	ctx BaseModuleContext
@@ -359,7 +397,7 @@
 }
 
 func (ctx *moduleContextImpl) sdk() bool {
-	if ctx.ctx.Device() {
+	if ctx.ctx.Device() && !ctx.vndk() {
 		return ctx.mod.Properties.Sdk_version != ""
 	}
 	return false
@@ -367,8 +405,8 @@
 
 func (ctx *moduleContextImpl) sdkVersion() string {
 	if ctx.ctx.Device() {
-		if ctx.mod.Properties.Use_vndk {
-			return ctx.mod.Properties.Vndk_version
+		if ctx.vndk() {
+			return "current"
 		} else {
 			return ctx.mod.Properties.Sdk_version
 		}
@@ -377,10 +415,7 @@
 }
 
 func (ctx *moduleContextImpl) vndk() bool {
-	if ctx.ctx.Device() {
-		return ctx.mod.Properties.Use_vndk
-	}
-	return false
+	return ctx.mod.vndk()
 }
 
 func (ctx *moduleContextImpl) selectedStl() string {
@@ -408,6 +443,7 @@
 	}
 	module.stl = &stl{}
 	module.sanitize = &sanitize{}
+	module.coverage = &coverage{}
 	return module
 }
 
@@ -420,7 +456,9 @@
 
 func (c *Module) Name() string {
 	name := c.ModuleBase.Name()
-	if p, ok := c.linker.(prebuiltLinkerInterface); ok {
+	if p, ok := c.linker.(interface {
+		Name(string) string
+	}); ok {
 		name = p.Name(name)
 	}
 	return name
@@ -451,6 +489,9 @@
 	if c.sanitize != nil {
 		flags = c.sanitize.flags(ctx, flags)
 	}
+	if c.coverage != nil {
+		flags = c.coverage.flags(ctx, flags)
+	}
 	for _, feature := range c.features {
 		flags = feature.flags(ctx, flags)
 	}
@@ -462,6 +503,13 @@
 	flags.CppFlags, _ = filterList(flags.CppFlags, config.IllegalFlags)
 	flags.ConlyFlags, _ = filterList(flags.ConlyFlags, config.IllegalFlags)
 
+	deps := c.depsToPaths(ctx)
+	if ctx.Failed() {
+		return
+	}
+	flags.GlobalFlags = append(flags.GlobalFlags, deps.Flags...)
+	c.flags = flags
+
 	// Optimization to reduce size of build.ninja
 	// Replace the long list of flags for each file with a module-local variable
 	ctx.Variable(pctx, "cflags", strings.Join(flags.CFlags, " "))
@@ -471,13 +519,6 @@
 	flags.CppFlags = []string{"$cppflags"}
 	flags.AsFlags = []string{"$asflags"}
 
-	deps := c.depsToPaths(ctx)
-	if ctx.Failed() {
-		return
-	}
-
-	flags.GlobalFlags = append(flags.GlobalFlags, deps.Flags...)
-
 	var objs Objects
 	if c.compiler != nil {
 		objs = c.compiler.compile(ctx, flags, deps)
@@ -522,26 +563,18 @@
 	if c.sanitize != nil {
 		c.sanitize.begin(ctx)
 	}
+	if c.coverage != nil {
+		c.coverage.begin(ctx)
+	}
 	for _, feature := range c.features {
 		feature.begin(ctx)
 	}
 	if ctx.sdk() {
-		if ctx.vndk() {
-			ctx.PropertyErrorf("use_vndk",
-				"sdk_version and use_vndk cannot be used at the same time")
-		}
-
 		version, err := normalizeNdkApiLevel(ctx.sdkVersion(), ctx.Arch())
 		if err != nil {
 			ctx.PropertyErrorf("sdk_version", err.Error())
 		}
 		c.Properties.Sdk_version = version
-	} else if ctx.vndk() {
-		version, err := normalizeNdkApiLevel(ctx.DeviceConfig().VndkVersion(), ctx.Arch())
-		if err != nil {
-			ctx.ModuleErrorf("Bad BOARD_VNDK_VERSION: %s", err.Error())
-		}
-		c.Properties.Vndk_version = version
 	}
 }
 
@@ -560,6 +593,9 @@
 	if c.sanitize != nil {
 		deps = c.sanitize.deps(ctx, deps)
 	}
+	if c.coverage != nil {
+		deps = c.coverage.deps(ctx, deps)
+	}
 	for _, feature := range c.features {
 		deps = feature.deps(ctx, deps)
 	}
@@ -630,7 +666,7 @@
 
 	variantNdkLibs := []string{}
 	variantLateNdkLibs := []string{}
-	if ctx.sdk() || ctx.vndk() {
+	if ctx.Os() == android.Android {
 		version := ctx.sdkVersion()
 
 		// Rewrites the names of shared libraries into the names of the NDK
@@ -647,14 +683,16 @@
 			variantLibs := []string{}
 			nonvariantLibs := []string{}
 			for _, entry := range list {
-				if inList(entry, ndkPrebuiltSharedLibraries) {
+				if ctx.sdk() && inList(entry, ndkPrebuiltSharedLibraries) {
 					if !inList(entry, ndkMigratedLibs) {
 						nonvariantLibs = append(nonvariantLibs, entry+".ndk."+version)
 					} else {
 						variantLibs = append(variantLibs, entry+ndkLibrarySuffix)
 					}
+				} else if ctx.vndk() && inList(entry, config.LLndkLibraries()) {
+					nonvariantLibs = append(nonvariantLibs, entry+llndkLibrarySuffix)
 				} else {
-					nonvariantLibs = append(variantLibs, entry)
+					nonvariantLibs = append(nonvariantLibs, entry)
 				}
 			}
 			return nonvariantLibs, variantLibs
@@ -760,6 +798,10 @@
 			// Host code is not restricted
 			return
 		}
+		if from.Properties.UseVndk {
+			// Vendor code is already limited by the vendor mutator
+			return
+		}
 		if from.Properties.Sdk_version == "" {
 			// Platform code can link to anything
 			return
@@ -948,6 +990,20 @@
 			depPaths.CrtEnd = linkFile
 		}
 
+		switch tag {
+		case staticDepTag, staticExportDepTag, lateStaticDepTag:
+			staticLib, ok := cc.linker.(libraryInterface)
+			if !ok || !staticLib.static() {
+				ctx.ModuleErrorf("module %q not a static library", name)
+				return
+			}
+
+			// When combining coverage files for shared libraries and executables, coverage files
+			// in static libraries act as if they were whole static libraries.
+			depPaths.StaticLibObjs.coverageFiles = append(depPaths.StaticLibObjs.coverageFiles,
+				staticLib.objs().coverageFiles...)
+		}
+
 		if ptr != nil {
 			if !linkFile.Valid() {
 				ctx.ModuleErrorf("module %q missing output file", name)
@@ -972,10 +1028,17 @@
 	if c.installer == nil {
 		return false
 	}
-	if c.sanitize != nil && c.sanitize.inData() {
+	return c.installer.inData()
+}
+
+func (c *Module) InstallInSanitizerDir() bool {
+	if c.installer == nil {
+		return false
+	}
+	if c.sanitize != nil && c.sanitize.inSanitizerDir() {
 		return true
 	}
-	return c.installer.inData()
+	return c.installer.inSanitizerDir()
 }
 
 func (c *Module) HostToolPath() android.OptionalPath {
@@ -1021,11 +1084,63 @@
 		&StripProperties{},
 		&InstallerProperties{},
 		&TidyProperties{},
+		&CoverageProperties{},
 	)
 
 	return android.InitDefaultsModule(module, module, props...)
 }
 
+const (
+	// coreMode is the variant used for framework-private libraries, or
+	// SDK libraries. (which framework-private libraries can use)
+	coreMode = "core"
+
+	// vendorMode is the variant used for /vendor code that compiles
+	// against the VNDK.
+	vendorMode = "vendor"
+)
+
+func vendorMutator(mctx android.BottomUpMutatorContext) {
+	if mctx.Os() != android.Android {
+		return
+	}
+
+	m, ok := mctx.Module().(*Module)
+	if !ok {
+		return
+	}
+
+	// Sanity check
+	if Bool(m.Properties.Vendor_available) && mctx.Vendor() {
+		mctx.PropertyErrorf("vendor_available",
+			"doesn't make sense at the same time as `vendor: true` or `proprietary: true`")
+		return
+	}
+
+	if !mctx.DeviceConfig().CompileVndk() {
+		// If the device isn't compiling against the VNDK, we always
+		// use the core mode.
+		mctx.CreateVariations(coreMode)
+	} else if _, ok := m.linker.(*llndkStubDecorator); ok {
+		// LL-NDK stubs only exist in the vendor variant, since the
+		// real libraries will be used in the core variant.
+		mctx.CreateVariations(vendorMode)
+	} else if Bool(m.Properties.Vendor_available) {
+		// This will be available in both /system and /vendor
+		mod := mctx.CreateVariations(coreMode, vendorMode)
+		mod[1].(*Module).Properties.UseVndk = true
+	} else if mctx.Vendor() && m.Properties.Sdk_version == "" {
+		// This will be available in /vendor only
+		mod := mctx.CreateVariations(vendorMode)
+		mod[0].(*Module).Properties.UseVndk = true
+	} else {
+		// This is either in /system (or similar: /data), or is a
+		// modules built with the NDK. Modules built with the NDK
+		// will be restricted using the existing link type checks.
+		mctx.CreateVariations(coreMode)
+	}
+}
+
 // lastUniqueElements returns all unique elements of a slice, keeping the last copy of each
 // modifies the slice contents in place, and returns a subslice of the original slice
 func lastUniqueElements(list []string) []string {
diff --git a/cc/check.go b/cc/check.go
index 340464e..d04b145 100644
--- a/cc/check.go
+++ b/cc/check.go
@@ -36,6 +36,8 @@
 			ctx.PropertyErrorf(prop, "Bad flag `%s`, use local_include_dirs or include_dirs instead", flag)
 		} else if inList(flag, config.IllegalFlags) {
 			ctx.PropertyErrorf(prop, "Illegal flag `%s`", flag)
+		} else if flag == "--coverage" {
+			ctx.PropertyErrorf(prop, "Bad flag: `%s`, use native_coverage instead", flag)
 		} else if strings.Contains(flag, " ") {
 			args := strings.Split(flag, " ")
 			if args[0] == "-include" {
@@ -73,6 +75,8 @@
 			ctx.PropertyErrorf(prop, "Bad flag: `%s` is not allowed", flag)
 		} else if strings.HasPrefix(flag, "-Wl,--version-script") {
 			ctx.PropertyErrorf(prop, "Bad flag: `%s`, use version_script instead", flag)
+		} else if flag == "--coverage" {
+			ctx.PropertyErrorf(prop, "Bad flag: `%s`, use native_coverage instead", flag)
 		} else if strings.Contains(flag, " ") {
 			args := strings.Split(flag, " ")
 			if args[0] == "-z" {
diff --git a/cc/cmakelists.go b/cc/cmakelists.go
new file mode 100644
index 0000000..4df9ece
--- /dev/null
+++ b/cc/cmakelists.go
@@ -0,0 +1,398 @@
+package cc
+
+import (
+	"fmt"
+
+	"android/soong/android"
+	"android/soong/cc/config"
+	"os"
+	"path"
+	"path/filepath"
+	"strings"
+
+	"github.com/google/blueprint"
+)
+
+// This singleton generates CMakeLists.txt files. It does so for each blueprint Android.bp resulting in a cc.Module
+// when either make, mm, mma, mmm or mmma is called. CMakeLists.txt files are generated in a separate folder
+// structure (see variable CLionOutputProjectsDirectory for root).
+
+func init() {
+	android.RegisterSingletonType("cmakelists_generator", cMakeListsGeneratorSingleton)
+}
+
+func cMakeListsGeneratorSingleton() blueprint.Singleton {
+	return &cmakelistsGeneratorSingleton{}
+}
+
+type cmakelistsGeneratorSingleton struct{}
+
+const (
+	cMakeListsFilename              = "CMakeLists.txt"
+	cLionAggregateProjectsDirectory = "development" + string(os.PathSeparator) + "ide" + string(os.PathSeparator) + "clion"
+	cLionOutputProjectsDirectory    = "out" + string(os.PathSeparator) + cLionAggregateProjectsDirectory
+	minimumCMakeVersionSupported    = "3.5"
+
+	// Environment variables used to modify behavior of this singleton.
+	envVariableGenerateCMakeLists = "SOONG_GEN_CMAKEFILES"
+	envVariableGenerateDebugInfo  = "SOONG_GEN_CMAKEFILES_DEBUG"
+	envVariableTrue               = "1"
+)
+
+// Instruct generator to trace how header include path and flags were generated.
+// This is done to ease investigating bug reports.
+var outputDebugInfo = false
+
+func (c *cmakelistsGeneratorSingleton) GenerateBuildActions(ctx blueprint.SingletonContext) {
+	if getEnvVariable(envVariableGenerateCMakeLists, ctx) != envVariableTrue {
+		return
+	}
+
+	outputDebugInfo = (getEnvVariable(envVariableGenerateDebugInfo, ctx) == envVariableTrue)
+
+	ctx.VisitAllModules(func(module blueprint.Module) {
+		if ccModule, ok := module.(*Module); ok {
+			if compiledModule, ok := ccModule.compiler.(CompiledInterface); ok {
+				generateCLionProject(compiledModule, ctx, ccModule)
+			}
+		}
+	})
+
+	// Link all handmade CMakeLists.txt aggregate from
+	//     BASE/development/ide/clion to
+	// BASE/out/development/ide/clion.
+	dir := filepath.Join(getAndroidSrcRootDirectory(ctx), cLionAggregateProjectsDirectory)
+	filepath.Walk(dir, linkAggregateCMakeListsFiles)
+
+	return
+}
+
+func getEnvVariable(name string, ctx blueprint.SingletonContext) string {
+	// Using android.Config.Getenv instead of os.getEnv to guarantee soong will
+	// re-run in case this environment variable changes.
+	return ctx.Config().(android.Config).Getenv(name)
+}
+
+func exists(path string) bool {
+	_, err := os.Stat(path)
+	if err == nil {
+		return true
+	}
+	if os.IsNotExist(err) {
+		return false
+	}
+	return true
+}
+
+func linkAggregateCMakeListsFiles(path string, info os.FileInfo, err error) error {
+
+	if info == nil {
+		return nil
+	}
+
+	dst := strings.Replace(path, cLionAggregateProjectsDirectory, cLionOutputProjectsDirectory, 1)
+	if info.IsDir() {
+		// This is a directory to create
+		os.MkdirAll(dst, os.ModePerm)
+	} else {
+		// This is a file to link
+		os.Remove(dst)
+		os.Symlink(path, dst)
+	}
+	return nil
+}
+
+func generateCLionProject(compiledModule CompiledInterface, ctx blueprint.SingletonContext, ccModule *Module) {
+	srcs := compiledModule.Srcs()
+	if len(srcs) == 0 {
+		return
+	}
+
+	// Ensure the directory hosting the cmakelists.txt exists
+	clionproject_location := getCMakeListsForModule(ccModule, ctx)
+	projectDir := path.Dir(clionproject_location)
+	os.MkdirAll(projectDir, os.ModePerm)
+
+	// Create cmakelists.txt
+	f, _ := os.Create(filepath.Join(projectDir, cMakeListsFilename))
+	defer f.Close()
+
+	// Header.
+	f.WriteString("# THIS FILE WAS AUTOMATICALY GENERATED!\n")
+	f.WriteString("# ANY MODIFICATION WILL BE OVERWRITTEN!\n\n")
+	f.WriteString("# To improve project view in Clion    :\n")
+	f.WriteString("# Tools > CMake > Change Project Root  \n\n")
+	f.WriteString(fmt.Sprintf("cmake_minimum_required(VERSION %s)\n", minimumCMakeVersionSupported))
+	f.WriteString(fmt.Sprintf("project(%s)\n", ccModule.ModuleBase.Name()))
+	f.WriteString(fmt.Sprintf("set(ANDROID_ROOT %s)\n\n", getAndroidSrcRootDirectory(ctx)))
+
+	if ccModule.flags.Clang {
+		pathToCC, _ := evalVariable(ctx, "${config.ClangBin}/")
+		f.WriteString(fmt.Sprintf("set(CMAKE_C_COMPILER \"%s%s\")\n", buildCMakePath(pathToCC), "clang"))
+		f.WriteString(fmt.Sprintf("set(CMAKE_CXX_COMPILER \"%s%s\")\n", buildCMakePath(pathToCC), "clang++"))
+	} else {
+		toolchain := config.FindToolchain(ccModule.Os(), ccModule.Arch())
+		root, _ := evalVariable(ctx, toolchain.GccRoot())
+		triple, _ := evalVariable(ctx, toolchain.GccTriple())
+		pathToCC := filepath.Join(root, "bin", triple+"-")
+		f.WriteString(fmt.Sprintf("set(CMAKE_C_COMPILER \"%s%s\")\n", buildCMakePath(pathToCC), "gcc"))
+		f.WriteString(fmt.Sprintf("set(CMAKE_CXX_COMPILER \"%s%s\")\n", buildCMakePath(pathToCC), "g++"))
+	}
+	// Add all sources to the project.
+	f.WriteString("list(APPEND\n")
+	f.WriteString("     SOURCE_FILES\n")
+	for _, src := range srcs {
+		f.WriteString(fmt.Sprintf("    ${ANDROID_ROOT}/%s\n", src.String()))
+	}
+	f.WriteString(")\n")
+
+	// Add all header search path and compiler parameters (-D, -W, -f, -XXXX)
+	f.WriteString("\n# GLOBAL FLAGS:\n")
+	globalParameters := parseCompilerParameters(ccModule.flags.GlobalFlags, ctx, f)
+	translateToCMake(globalParameters, f, true, true)
+
+	f.WriteString("\n# CFLAGS:\n")
+	cParameters := parseCompilerParameters(ccModule.flags.CFlags, ctx, f)
+	translateToCMake(cParameters, f, true, true)
+
+	f.WriteString("\n# C ONLY FLAGS:\n")
+	cOnlyParameters := parseCompilerParameters(ccModule.flags.ConlyFlags, ctx, f)
+	translateToCMake(cOnlyParameters, f, true, false)
+
+	f.WriteString("\n# CPP FLAGS:\n")
+	cppParameters := parseCompilerParameters(ccModule.flags.CppFlags, ctx, f)
+	translateToCMake(cppParameters, f, false, true)
+
+	f.WriteString("\n# SYSTEM INCLUDE FLAGS:\n")
+	includeParameters := parseCompilerParameters(ccModule.flags.SystemIncludeFlags, ctx, f)
+	translateToCMake(includeParameters, f, true, true)
+
+	// Add project executable.
+	f.WriteString(fmt.Sprintf("\nadd_executable(%s ${SOURCE_FILES})\n",
+		cleanExecutableName(ccModule.ModuleBase.Name())))
+}
+
+func cleanExecutableName(s string) string {
+	return strings.Replace(s, "@", "-", -1)
+}
+
+func translateToCMake(c compilerParameters, f *os.File, cflags bool, cppflags bool) {
+	writeAllIncludeDirectories(c.systemHeaderSearchPath, f, true)
+	writeAllIncludeDirectories(c.headerSearchPath, f, false)
+	if cflags {
+		writeAllFlags(c.flags, f, "CMAKE_C_FLAGS")
+	}
+
+	if cppflags {
+		writeAllFlags(c.flags, f, "CMAKE_CXX_FLAGS")
+	}
+	if c.sysroot != "" {
+		f.WriteString(fmt.Sprintf("include_directories(SYSTEM \"%s\")\n", buildCMakePath(path.Join(c.sysroot, "usr", "include"))))
+	}
+
+}
+
+func buildCMakePath(p string) string {
+	if path.IsAbs(p) {
+		return p
+	}
+	return fmt.Sprintf("${ANDROID_ROOT}/%s", p)
+}
+
+func writeAllIncludeDirectories(includes []string, f *os.File, isSystem bool) {
+	if len(includes) == 0 {
+		return
+	}
+
+	system := ""
+        if isSystem {
+		system = "SYSTEM"
+	}
+
+	f.WriteString(fmt.Sprintf("include_directories(%s \n", system))
+
+	for _, include := range includes {
+		f.WriteString(fmt.Sprintf("    \"%s\"\n", buildCMakePath(include)))
+	}
+	f.WriteString(")\n\n")
+
+	// Also add all headers to source files.
+	f.WriteString("file (GLOB_RECURSE TMP_HEADERS\n");
+	for _, include := range includes {
+		f.WriteString(fmt.Sprintf("    \"%s/**/*.h\"\n", buildCMakePath(include)))
+	}
+	f.WriteString(")\n")
+	f.WriteString("list (APPEND SOURCE_FILES ${TMP_HEADERS})\n\n");
+}
+
+func writeAllFlags(flags []string, f *os.File, tag string) {
+	for _, flag := range flags {
+		f.WriteString(fmt.Sprintf("set(%s \"${%s} %s\")\n", tag, tag, flag))
+	}
+}
+
+type parameterType int
+
+const (
+	headerSearchPath parameterType = iota
+	variable
+	systemHeaderSearchPath
+	flag
+	systemRoot
+)
+
+type compilerParameters struct {
+	headerSearchPath       []string
+	systemHeaderSearchPath []string
+	flags                  []string
+	sysroot                string
+}
+
+func makeCompilerParameters() compilerParameters {
+	return compilerParameters{
+		sysroot: "",
+	}
+}
+
+func categorizeParameter(parameter string) parameterType {
+	if strings.HasPrefix(parameter, "-I") {
+		return headerSearchPath
+	}
+	if strings.HasPrefix(parameter, "$") {
+		return variable
+	}
+	if strings.HasPrefix(parameter, "-isystem") {
+		return systemHeaderSearchPath
+	}
+	if strings.HasPrefix(parameter, "-isysroot") {
+		return systemRoot
+	}
+	if strings.HasPrefix(parameter, "--sysroot") {
+		return systemRoot
+	}
+	return flag
+}
+
+func parseCompilerParameters(params []string, ctx blueprint.SingletonContext, f *os.File) compilerParameters {
+	var compilerParameters = makeCompilerParameters()
+
+	for i, str := range params {
+		f.WriteString(fmt.Sprintf("# Raw param [%d] = '%s'\n", i, str))
+	}
+
+	for i := 0; i < len(params); i++ {
+		param := params[i]
+		if param == "" {
+			continue
+		}
+
+		switch categorizeParameter(param) {
+		case headerSearchPath:
+			compilerParameters.headerSearchPath =
+				append(compilerParameters.headerSearchPath, strings.TrimPrefix(param, "-I"))
+		case variable:
+			if evaluated, error := evalVariable(ctx, param); error == nil {
+				if outputDebugInfo {
+					f.WriteString(fmt.Sprintf("# variable %s = '%s'\n", param, evaluated))
+				}
+
+				paramsFromVar := parseCompilerParameters(strings.Split(evaluated, " "), ctx, f)
+				concatenateParams(&compilerParameters, paramsFromVar)
+
+			} else {
+				if outputDebugInfo {
+					f.WriteString(fmt.Sprintf("# variable %s could NOT BE RESOLVED\n", param))
+				}
+			}
+		case systemHeaderSearchPath:
+			if i < len(params)-1 {
+				compilerParameters.systemHeaderSearchPath =
+					append(compilerParameters.systemHeaderSearchPath, params[i+1])
+			} else if outputDebugInfo {
+				f.WriteString("# Found a header search path marker with no path")
+			}
+			i = i + 1
+		case flag:
+			c := cleanupParameter(param)
+			f.WriteString(fmt.Sprintf("# FLAG '%s' became %s\n", param, c))
+			compilerParameters.flags = append(compilerParameters.flags, c)
+		case systemRoot:
+			if i < len(params)-1 {
+				compilerParameters.sysroot = params[i+1]
+			} else if outputDebugInfo {
+				f.WriteString("# Found a system root path marker with no path")
+			}
+			i = i + 1
+		}
+	}
+	return compilerParameters
+}
+
+func cleanupParameter(p string) string {
+	// In the blueprint, c flags can be passed as:
+	//  cflags: [ "-DLOG_TAG=\"libEGL\"", ]
+	// which becomes:
+	// '-DLOG_TAG="libEGL"' in soong.
+	// In order to be injected in CMakelists.txt we need to:
+	// - Remove the wrapping ' character
+	// - Double escape all special \ and " characters.
+	// For a end result like:
+	// -DLOG_TAG=\\\"libEGL\\\"
+	if !strings.HasPrefix(p, "'") || !strings.HasSuffix(p, "'") || len(p) < 3 {
+		return p
+	}
+
+	// Reverse wrapper quotes and escaping that may have happened in NinjaAndShellEscape
+	// TODO:  It is ok to reverse here for now but if NinjaAndShellEscape becomes more complex,
+	// we should create a method NinjaAndShellUnescape in escape.go and use that instead.
+	p = p[1 : len(p)-1]
+	p = strings.Replace(p, `'\''`, `'`, -1)
+	p = strings.Replace(p, `$$`, `$`, -1)
+
+	p = doubleEscape(p)
+	return p
+}
+
+func escape(s string) string {
+	s = strings.Replace(s, `\`, `\\`, -1)
+	s = strings.Replace(s, `"`, `\"`, -1)
+	return s
+}
+
+func doubleEscape(s string) string {
+	s = escape(s)
+	s = escape(s)
+	return s
+}
+
+func concatenateParams(c1 *compilerParameters, c2 compilerParameters) {
+	c1.headerSearchPath = append(c1.headerSearchPath, c2.headerSearchPath...)
+	c1.systemHeaderSearchPath = append(c1.systemHeaderSearchPath, c2.systemHeaderSearchPath...)
+	if c2.sysroot != "" {
+		c1.sysroot = c2.sysroot
+	}
+	c1.flags = append(c1.flags, c2.flags...)
+}
+
+func evalVariable(ctx blueprint.SingletonContext, str string) (string, error) {
+	evaluated, err := ctx.Eval(pctx, str)
+	if err == nil {
+		return evaluated, nil
+	}
+	return "", err
+}
+
+func getCMakeListsForModule(module *Module, ctx blueprint.SingletonContext) string {
+	return filepath.Join(getAndroidSrcRootDirectory(ctx),
+		cLionOutputProjectsDirectory,
+		path.Dir(ctx.BlueprintFile(module)),
+		module.ModuleBase.Name()+"-"+
+			module.ModuleBase.Arch().ArchType.Name+"-"+
+			module.ModuleBase.Os().Name,
+		cMakeListsFilename)
+}
+
+func getAndroidSrcRootDirectory(ctx blueprint.SingletonContext) string {
+	srcPath, _ := filepath.Abs(android.PathForSource(ctx).String())
+	return srcPath
+}
diff --git a/cc/compiler.go b/cc/compiler.go
index d53e799..aed4480 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -86,6 +86,16 @@
 	// pass -frtti instead of -fno-rtti
 	Rtti *bool
 
+	// C standard version to use. Can be a specific version (such as "gnu11"),
+	// "experimental" (which will use draft versions like C1x when available),
+	// or the empty string (which will use the default).
+	C_std string
+
+	// C++ standard version to use. Can be a specific version (such as
+	// "gnu++11"), "experimental" (which will use draft versions like C++1z when
+	// available), or the empty string (which will use the default).
+	Cpp_std string
+
 	// if set to false, use -std=c++* instead of -std=gnu++*
 	Gnu_extensions *bool
 
@@ -103,6 +113,18 @@
 		// release builds
 		Cflags []string `android:"arch_variant"`
 	} `android:"arch_variant"`
+
+	Target struct {
+		Vendor struct {
+			// list of source files that should only be used in the
+			// vendor variant of the C/C++ module.
+			Srcs []string
+
+			// list of source files that should not be used to
+			// build the vendor variant of the C/C++ module.
+			Exclude_srcs []string
+		}
+	}
 }
 
 func NewBaseCompiler() *baseCompiler {
@@ -113,10 +135,20 @@
 	Properties BaseCompilerProperties
 	Proto      ProtoProperties
 	deps       android.Paths
+	srcs       android.Paths
+	flags      builderFlags
 }
 
 var _ compiler = (*baseCompiler)(nil)
 
+type CompiledInterface interface {
+	Srcs() android.Paths
+}
+
+func (compiler *baseCompiler) Srcs() android.Paths {
+	return compiler.srcs
+}
+
 func (compiler *baseCompiler) appendCflags(flags []string) {
 	compiler.Properties.Cflags = append(compiler.Properties.Cflags, flags...)
 }
@@ -174,23 +206,23 @@
 	}
 
 	if !ctx.noDefaultCompilerFlags() {
+		flags.GlobalFlags = append(flags.GlobalFlags, "-I"+android.PathForModuleSrc(ctx).String())
+
 		if !(ctx.sdk() || ctx.vndk()) || ctx.Host() {
-			flags.GlobalFlags = append(flags.GlobalFlags,
+			flags.SystemIncludeFlags = append(flags.SystemIncludeFlags,
 				"${config.CommonGlobalIncludes}",
 				"${config.CommonGlobalSystemIncludes}",
 				tc.IncludeFlags(),
 				"${config.CommonNativehelperInclude}")
 		}
-
-		flags.GlobalFlags = append(flags.GlobalFlags, "-I"+android.PathForModuleSrc(ctx).String())
 	}
 
-	if ctx.sdk() || ctx.vndk() {
+	if ctx.sdk() {
 		// The NDK headers are installed to a common sysroot. While a more
 		// typical Soong approach would be to only make the headers for the
 		// library you're using available, we're trying to emulate the NDK
 		// behavior here, and the NDK always has all the NDK headers available.
-		flags.GlobalFlags = append(flags.GlobalFlags,
+		flags.SystemIncludeFlags = append(flags.SystemIncludeFlags,
 			"-isystem "+getCurrentIncludePath(ctx).String(),
 			"-isystem "+getCurrentIncludePath(ctx).Join(ctx, tc.ClangTriple()).String())
 
@@ -210,7 +242,12 @@
 		legacyIncludes := fmt.Sprintf(
 			"prebuilts/ndk/current/platforms/android-%s/arch-%s/usr/include",
 			ctx.sdkVersion(), ctx.Arch().ArchType.String())
-		flags.GlobalFlags = append(flags.GlobalFlags, "-isystem "+legacyIncludes)
+		flags.SystemIncludeFlags = append(flags.SystemIncludeFlags, "-isystem "+legacyIncludes)
+	}
+
+	if ctx.vndk() {
+		flags.GlobalFlags = append(flags.GlobalFlags,
+			"-D__ANDROID_API__=__ANDROID_API_FUTURE__", "-D__ANDROID_VNDK__")
 	}
 
 	instructionSet := compiler.Properties.Instruction_set
@@ -242,10 +279,7 @@
 		flags.LdFlags = config.ClangFilterUnknownCflags(flags.LdFlags)
 
 		target := "-target " + tc.ClangTriple()
-		var gccPrefix string
-		if !ctx.Darwin() {
-			gccPrefix = "-B" + filepath.Join(tc.GccRoot(), tc.GccTriple(), "bin")
-		}
+		gccPrefix := "-B" + config.ToolPath(tc)
 
 		flags.CFlags = append(flags.CFlags, target, gccPrefix)
 		flags.AsFlags = append(flags.AsFlags, target, gccPrefix)
@@ -307,7 +341,18 @@
 
 	if !ctx.sdk() {
 		cStd := config.CStdVersion
+		if compiler.Properties.C_std == "experimental" {
+			cStd = config.ExperimentalCStdVersion
+		} else if compiler.Properties.C_std != "" {
+			cStd = compiler.Properties.C_std
+		}
+
 		cppStd := config.CppStdVersion
+		if compiler.Properties.Cpp_std == "experimental" {
+			cppStd = config.ExperimentalCppStdVersion
+		} else if compiler.Properties.Cpp_std != "" {
+			cppStd = compiler.Properties.Cpp_std
+		}
 
 		if !flags.Clang {
 			// GCC uses an invalid C++14 ABI (emits calls to
@@ -384,7 +429,7 @@
 var gnuToCReplacer = strings.NewReplacer("gnu", "c")
 
 func ndkPathDeps(ctx ModuleContext) android.Paths {
-	if ctx.sdk() || ctx.vndk() {
+	if ctx.sdk() {
 		// The NDK sysroot timestamp file depends on all the NDK sysroot files
 		// (headers and libraries).
 		return android.Paths{getNdkSysrootTimestampFile(ctx)}
@@ -396,6 +441,14 @@
 	pathDeps := deps.GeneratedHeaders
 	pathDeps = append(pathDeps, ndkPathDeps(ctx)...)
 
+	if ctx.vndk() {
+		compiler.Properties.Srcs = append(compiler.Properties.Srcs,
+			compiler.Properties.Target.Vendor.Srcs...)
+
+		compiler.Properties.Exclude_srcs = append(compiler.Properties.Exclude_srcs,
+			compiler.Properties.Target.Vendor.Exclude_srcs...)
+	}
+
 	srcs := ctx.ExpandSources(compiler.Properties.Srcs, compiler.Properties.Exclude_srcs)
 	srcs = append(srcs, deps.GeneratedSources...)
 
@@ -408,6 +461,9 @@
 
 	compiler.deps = pathDeps
 
+	// Save src, buildFlags and context
+	compiler.srcs = srcs
+
 	// Compile files listed in c.Properties.Srcs into objects
 	objs := compileObjs(ctx, buildFlags, "", srcs, compiler.deps)
 
diff --git a/cc/config/arm64_device.go b/cc/config/arm64_device.go
index fe47ddf..f387ddf 100644
--- a/cc/config/arm64_device.go
+++ b/cc/config/arm64_device.go
@@ -66,9 +66,6 @@
 		"-fuse-ld=gold",
 		"-Wl,--icf=safe",
 		"-Wl,--no-undefined-version",
-
-		// Disable transitive dependency library symbol resolving.
-		"-Wl,--allow-shlib-undefined",
 	}
 
 	arm64Cppflags = []string{
@@ -79,6 +76,11 @@
 		"cortex-a53": []string{
 			"-mcpu=cortex-a53",
 		},
+		"kryo": []string{
+			// Use the cortex-a57 cpu since some compilers
+			// don't support a Kryo specific target yet.
+			"-mcpu=cortex-a57",
+		},
 	}
 
 	arm64ClangCpuVariantCflags = copyVariantFlags(arm64CpuVariantCflags)
@@ -92,8 +94,12 @@
 	android.RegisterArchVariants(android.Arm64,
 		"armv8_a",
 		"cortex_a53",
+		"kryo",
 		"denver64")
 
+	// Clang supports specific Kryo targeting
+	replaceFirst(arm64ClangCpuVariantCflags["kryo"], "-mcpu=cortex-a57", "-mcpu=kryo")
+
 	pctx.StaticVariable("arm64GccVersion", arm64GccVersion)
 
 	pctx.SourcePathVariable("Arm64GccRoot",
@@ -112,17 +118,24 @@
 		strings.Join(arm64CpuVariantCflags["cortex-a53"], " "))
 	pctx.StaticVariable("Arm64ClangCortexA53Cflags",
 		strings.Join(arm64ClangCpuVariantCflags["cortex-a53"], " "))
+
+	pctx.StaticVariable("Arm64KryoCflags",
+		strings.Join(arm64CpuVariantCflags["kryo"], " "))
+	pctx.StaticVariable("Arm64ClangKryoCflags",
+		strings.Join(arm64ClangCpuVariantCflags["kryo"], " "))
 }
 
 var (
 	arm64CpuVariantCflagsVar = map[string]string{
 		"":           "",
 		"cortex-a53": "${config.Arm64CortexA53Cflags}",
+		"kryo":       "${config.Arm64KryoCflags}",
 	}
 
 	arm64ClangCpuVariantCflagsVar = map[string]string{
 		"":           "",
 		"cortex-a53": "${config.Arm64ClangCortexA53Cflags}",
+		"kryo":       "${config.Arm64ClangKryoCflags}",
 	}
 )
 
diff --git a/cc/config/arm_device.go b/cc/config/arm_device.go
index 1b60eae..9058de2 100644
--- a/cc/config/arm_device.go
+++ b/cc/config/arm_device.go
@@ -128,6 +128,16 @@
 		},
 		"krait": []string{
 			"-mcpu=cortex-a15",
+			"-mfpu=neon-vfpv4",
+			// Fake an ARM compiler flag as these processors support LPAE which GCC/clang
+			// don't advertise.
+			// TODO This is a hack and we need to add it for each processor that supports LPAE until some
+			// better solution comes around. See Bug 27340895
+			"-D__ARM_FEATURE_LPAE=1",
+		},
+		"kryo": []string{
+			"-mcpu=cortex-a15",
+			"-mfpu=neon-fp-armv8",
 			// Fake an ARM compiler flag as these processors support LPAE which GCC/clang
 			// don't advertise.
 			// TODO This is a hack and we need to add it for each processor that supports LPAE until some
@@ -156,18 +166,13 @@
 		"cortex_a53",
 		"cortex_a53_a57",
 		"krait",
+		"kryo",
 		"denver")
 
-	replaceFirst := func(slice []string, from, to string) {
-		if slice[0] != from {
-			panic(fmt.Errorf("Expected %q, found %q", from, to))
-		}
-
-		slice[0] = to
-	}
-
+	// Krait and Kryo targets are not supported by GCC, but are supported by Clang,
+	// so override the definitions when building modules with Clang.
 	replaceFirst(armClangCpuVariantCflags["krait"], "-mcpu=cortex-a15", "-mcpu=krait")
-	armClangCpuVariantCflags["krait"] = append(armClangCpuVariantCflags["krait"], "-mfpu=neon-vfpv4")
+	replaceFirst(armClangCpuVariantCflags["kryo"], "-mcpu=cortex-a15", "-mcpu=krait")
 
 	pctx.StaticVariable("armGccVersion", armGccVersion)
 
@@ -197,6 +202,7 @@
 	pctx.StaticVariable("ArmCortexA8Cflags", strings.Join(armCpuVariantCflags["cortex-a8"], " "))
 	pctx.StaticVariable("ArmCortexA15Cflags", strings.Join(armCpuVariantCflags["cortex-a15"], " "))
 	pctx.StaticVariable("ArmKraitCflags", strings.Join(armCpuVariantCflags["krait"], " "))
+	pctx.StaticVariable("ArmKryoCflags", strings.Join(armCpuVariantCflags["kryo"], " "))
 
 	// Clang cflags
 	pctx.StaticVariable("ArmToolchainClangCflags", strings.Join(ClangFilterUnknownCflags(armToolchainCflags), " "))
@@ -227,6 +233,8 @@
 		strings.Join(armClangCpuVariantCflags["cortex-a15"], " "))
 	pctx.StaticVariable("ArmClangKraitCflags",
 		strings.Join(armClangCpuVariantCflags["krait"], " "))
+	pctx.StaticVariable("ArmClangKryoCflags",
+		strings.Join(armClangCpuVariantCflags["kryo"], " "))
 }
 
 var (
@@ -244,6 +252,7 @@
 		"cortex-a53":     "${config.ArmCortexA7Cflags}",
 		"cortex-a53.a57": "${config.ArmCortexA7Cflags}",
 		"krait":          "${config.ArmKraitCflags}",
+		"kryo":           "${config.ArmKryoCflags}",
 		"denver":         "${config.ArmCortexA15Cflags}",
 	}
 
@@ -261,6 +270,7 @@
 		"cortex-a53":     "${config.ArmClangCortexA7Cflags}",
 		"cortex-a53.a57": "${config.ArmClangCortexA7Cflags}",
 		"krait":          "${config.ArmClangKraitCflags}",
+		"kryo":           "${config.ArmClangKryoCflags}",
 		"denver":         "${config.ArmClangCortexA15Cflags}",
 	}
 )
diff --git a/cc/config/clang.go b/cc/config/clang.go
index 10f4cea..30ab1c6 100644
--- a/cc/config/clang.go
+++ b/cc/config/clang.go
@@ -91,6 +91,10 @@
 
 		// http://b/29823425 Disable -Wexpansion-to-defined for Clang update to r271374
 		"-Wno-expansion-to-defined",
+
+		// http://b/36463318 Clang executes with an absolute path, so clang-provided
+		// headers are now absolute.
+		"-fdebug-prefix-map=$$PWD/=",
 	}, " "))
 
 	pctx.StaticVariable("ClangExtraCppflags", strings.Join([]string{
diff --git a/cc/config/global.go b/cc/config/global.go
index e248040..774f3f7 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -15,6 +15,7 @@
 package config
 
 import (
+	"fmt"
 	"strings"
 
 	"android/soong/android"
@@ -65,9 +66,11 @@
 		"-w",
 	}
 
-	CStdVersion      = "gnu99"
-	CppStdVersion    = "gnu++14"
-	GccCppStdVersion = "gnu++11"
+	CStdVersion               = "gnu99"
+	CppStdVersion             = "gnu++14"
+	GccCppStdVersion          = "gnu++11"
+	ExperimentalCStdVersion   = "gnu11"
+	ExperimentalCppStdVersion = "gnu++1z"
 )
 
 var pctx = android.NewPackageContext("android/soong/cc/config")
@@ -130,7 +133,7 @@
 		if override := config.(android.Config).Getenv("LLVM_PREBUILTS_VERSION"); override != "" {
 			return override, nil
 		}
-		return "clang-3688880", nil
+		return "clang-3859424", nil
 	})
 	pctx.StaticVariable("ClangPath", "${ClangBase}/${HostPrebuiltTag}/${ClangVersion}")
 	pctx.StaticVariable("ClangBin", "${ClangPath}/bin")
@@ -143,6 +146,14 @@
 	})
 	pctx.StaticVariable("ClangAsanLibDir", "${ClangPath}/lib64/clang/${ClangShortVersion}/lib/linux")
 
+	// These are tied to the version of LLVM directly in external/llvm, so they might trail the host prebuilts
+	// being used for the rest of the build process.
+	pctx.SourcePathVariable("RSClangBase", "prebuilts/clang/host")
+	pctx.SourcePathVariable("RSClangVersion", "clang-3289846")
+	pctx.SourcePathVariable("RSReleaseVersion", "3.8")
+	pctx.StaticVariable("RSLLVMPrebuiltsPath", "${RSClangBase}/${HostPrebuiltTag}/${RSClangVersion}/bin")
+	pctx.StaticVariable("RSIncludePath", "${RSLLVMPrebuiltsPath}/../lib64/clang/${RSReleaseVersion}/include")
+
 	pctx.VariableFunc("CcWrapper", func(config interface{}) (string, error) {
 		if override := config.(android.Config).Getenv("CC_WRAPPER"); override != "" {
 			return override + " ", nil
@@ -166,3 +177,17 @@
 func VndkLibraries() []string {
 	return []string{}
 }
+
+// This needs to be kept up to date with the list in system/core/rootdir/etc/ld.config.txt:
+// [vendor]
+// namespace.default.link.system.shared_libs
+func LLndkLibraries() []string {
+	return []string{"libc", "libm", "libdl", "liblog", "ld-android"}
+}
+
+func replaceFirst(slice []string, from, to string) {
+	if slice[0] != from {
+		panic(fmt.Errorf("Expected %q, found %q", from, to))
+	}
+	slice[0] = to
+}
diff --git a/cc/config/toolchain.go b/cc/config/toolchain.go
index 995c8c6..5270437 100644
--- a/cc/config/toolchain.go
+++ b/cc/config/toolchain.go
@@ -16,6 +16,7 @@
 
 import (
 	"fmt"
+	"path/filepath"
 
 	"android/soong/android"
 )
@@ -47,6 +48,7 @@
 	GccTriple() string
 	// GccVersion should return a real value, not a ninja reference
 	GccVersion() string
+	ToolPath() string
 
 	ToolchainCflags() string
 	ToolchainLdflags() string
@@ -145,6 +147,10 @@
 	return true
 }
 
+func (t toolchainBase) ToolPath() string {
+	return ""
+}
+
 type toolchain64Bit struct {
 	toolchainBase
 }
@@ -201,18 +207,29 @@
 	return indexList(s, list) != -1
 }
 
-func AddressSanitizerRuntimeLibrary(t Toolchain) string {
+func SanitizerRuntimeLibrary(t Toolchain, sanitizer string) string {
 	arch := t.SanitizerRuntimeLibraryArch()
 	if arch == "" {
 		return ""
 	}
-	return "libclang_rt.asan-" + arch + "-android.so"
+	return "libclang_rt." + sanitizer + "-" + arch + "-android.so"
+}
+
+func AddressSanitizerRuntimeLibrary(t Toolchain) string {
+	return SanitizerRuntimeLibrary(t, "asan")
 }
 
 func UndefinedBehaviorSanitizerRuntimeLibrary(t Toolchain) string {
-	arch := t.SanitizerRuntimeLibraryArch()
-	if arch == "" {
-		return ""
+	return SanitizerRuntimeLibrary(t, "ubsan_standalone")
+}
+
+func ThreadSanitizerRuntimeLibrary(t Toolchain) string {
+	return SanitizerRuntimeLibrary(t, "tsan")
+}
+
+func ToolPath(t Toolchain) string {
+	if p := t.ToolPath(); p != "" {
+		return p
 	}
-	return "libclang_rt.ubsan_standalone-" + arch + "-android.so"
+	return filepath.Join(t.GccRoot(), t.GccTriple(), "bin")
 }
diff --git a/cc/config/x86_darwin_host.go b/cc/config/x86_darwin_host.go
index 18acef8..b6b08fe 100644
--- a/cc/config/x86_darwin_host.go
+++ b/cc/config/x86_darwin_host.go
@@ -17,6 +17,7 @@
 import (
 	"fmt"
 	"os/exec"
+	"path/filepath"
 	"strings"
 
 	"android/soong/android"
@@ -131,6 +132,11 @@
 		return strings.TrimSpace(string(bytes)), err
 	})
 
+	pctx.VariableFunc("MacToolPath", func(config interface{}) (string, error) {
+		bytes, err := exec.Command("xcrun", "--find", "ld").Output()
+		return filepath.Dir(strings.TrimSpace(string(bytes))), err
+	})
+
 	pctx.StaticVariable("DarwinGccVersion", darwinGccVersion)
 	pctx.SourcePathVariable("DarwinGccRoot",
 		"prebuilts/gcc/${HostPrebuiltTag}/host/i686-apple-darwin-${DarwinGccVersion}")
@@ -276,6 +282,10 @@
 	return false
 }
 
+func (t *toolchainDarwin) ToolPath() string {
+	return "${config.MacToolPath}"
+}
+
 var toolchainDarwinX86Singleton Toolchain = &toolchainDarwinX86{}
 var toolchainDarwinX8664Singleton Toolchain = &toolchainDarwinX8664{}
 
diff --git a/cc/config/x86_linux_bionic_host.go b/cc/config/x86_linux_bionic_host.go
new file mode 100644
index 0000000..bd6cd0e
--- /dev/null
+++ b/cc/config/x86_linux_bionic_host.go
@@ -0,0 +1,168 @@
+// 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 config
+
+import (
+	"strings"
+
+	"android/soong/android"
+)
+
+var (
+	linuxBionicCflags = ClangFilterUnknownCflags([]string{
+		"-fno-exceptions", // from build/core/combo/select.mk
+		"-Wno-multichar",  // from build/core/combo/select.mk
+
+		"-fdiagnostics-color",
+
+		"-Wa,--noexecstack",
+
+		"-fPIC",
+		"-no-canonical-prefixes",
+
+		"-U_FORTIFY_SOURCE",
+		"-D_FORTIFY_SOURCE=2",
+		"-fstack-protector-strong",
+
+		// From x86_64_device
+		"-ffunction-sections",
+		"-finline-functions",
+		"-finline-limit=300",
+		"-fno-short-enums",
+		"-funswitch-loops",
+		"-funwind-tables",
+		"-no-canonical-prefixes",
+		"-fno-canonical-system-headers",
+
+		// HOST_RELEASE_CFLAGS
+		"-O2", // from build/core/combo/select.mk
+		"-g",  // from build/core/combo/select.mk
+		"-fno-strict-aliasing", // from build/core/combo/select.mk
+
+		// Tell clang where the gcc toolchain is
+		"--gcc-toolchain=${LinuxBionicGccRoot}",
+
+		// TODO: We're not really android, but we don't have a triple yet b/31393676
+		"-U__ANDROID__",
+		"-fno-emulated-tls",
+
+		// This is normally in ClangExtraTargetCflags, but this is considered host
+		"-nostdlibinc",
+	})
+
+	linuxBionicLdflags = ClangFilterUnknownCflags([]string{
+		"-Wl,-z,noexecstack",
+		"-Wl,-z,relro",
+		"-Wl,-z,now",
+		"-Wl,--build-id=md5",
+		"-Wl,--warn-shared-textrel",
+		"-Wl,--fatal-warnings",
+		"-Wl,--gc-sections",
+		"-Wl,--hash-style=gnu",
+		"-Wl,--no-undefined-version",
+
+		// Use the device gcc toolchain
+		"--gcc-toolchain=${LinuxBionicGccRoot}",
+	})
+)
+
+func init() {
+	pctx.StaticVariable("LinuxBionicCflags", strings.Join(linuxBionicCflags, " "))
+	pctx.StaticVariable("LinuxBionicLdflags", strings.Join(linuxBionicLdflags, " "))
+
+	pctx.StaticVariable("LinuxBionicIncludeFlags", bionicHeaders("x86_64", "x86"))
+
+	// Use the device gcc toolchain for now
+	pctx.StaticVariable("LinuxBionicGccRoot", "${X86_64GccRoot}")
+}
+
+type toolchainLinuxBionic struct {
+	toolchain64Bit
+}
+
+func (t *toolchainLinuxBionic) Name() string {
+	return "x86_64"
+}
+
+func (t *toolchainLinuxBionic) GccRoot() string {
+	return "${config.LinuxBionicGccRoot}"
+}
+
+func (t *toolchainLinuxBionic) GccTriple() string {
+	return "x86_64-linux-android"
+}
+
+func (t *toolchainLinuxBionic) GccVersion() string {
+	return "4.9"
+}
+
+func (t *toolchainLinuxBionic) Cflags() string {
+	return ""
+}
+
+func (t *toolchainLinuxBionic) Cppflags() string {
+	return ""
+}
+
+func (t *toolchainLinuxBionic) Ldflags() string {
+	return ""
+}
+
+func (t *toolchainLinuxBionic) IncludeFlags() string {
+	return "${config.LinuxBionicIncludeFlags}"
+}
+
+func (t *toolchainLinuxBionic) ClangTriple() string {
+	// TODO: we don't have a triple yet b/31393676
+	return "x86_64-linux-android"
+}
+
+func (t *toolchainLinuxBionic) ClangCflags() string {
+	return "${config.LinuxBionicCflags}"
+}
+
+func (t *toolchainLinuxBionic) ClangCppflags() string {
+	return ""
+}
+
+func (t *toolchainLinuxBionic) ClangLdflags() string {
+	return "${config.LinuxBionicLdflags}"
+}
+
+func (t *toolchainLinuxBionic) ToolchainClangCflags() string {
+	return "-m64 -march=x86-64"
+}
+
+func (t *toolchainLinuxBionic) ToolchainClangLdflags() string {
+	return "-m64"
+}
+
+func (t *toolchainLinuxBionic) AvailableLibraries() []string {
+	return nil
+}
+
+func (t *toolchainLinuxBionic) Bionic() bool {
+	return true
+}
+
+var toolchainLinuxBionicSingleton Toolchain = &toolchainLinuxBionic{}
+
+func linuxBionicToolchainFactory(arch android.Arch) Toolchain {
+	return toolchainLinuxBionicSingleton
+}
+
+func init() {
+	registerToolchainFactory(android.LinuxBionic, android.X86_64, linuxBionicToolchainFactory)
+}
diff --git a/cc/config/x86_windows_host.go b/cc/config/x86_windows_host.go
index 4aad9c8..4709823 100644
--- a/cc/config/x86_windows_host.go
+++ b/cc/config/x86_windows_host.go
@@ -69,6 +69,7 @@
 
 	windowsX86Ldflags = []string{
 		"-m32",
+		"-Wl,--large-address-aware",
 		"-L${WindowsGccRoot}/${WindowsGccTriple}/lib32",
 	}
 
diff --git a/cc/coverage.go b/cc/coverage.go
new file mode 100644
index 0000000..b1c8783
--- /dev/null
+++ b/cc/coverage.go
@@ -0,0 +1,124 @@
+// Copyright 2017 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package cc
+
+import (
+	"android/soong/android"
+	"github.com/google/blueprint"
+)
+
+type CoverageProperties struct {
+	Native_coverage *bool
+
+	CoverageEnabled bool `blueprint:"mutated"`
+}
+
+type coverage struct {
+	Properties CoverageProperties
+
+	// Whether binaries containing this module need --coverage added to their ldflags
+	linkCoverage bool
+}
+
+func (cov *coverage) props() []interface{} {
+	return []interface{}{&cov.Properties}
+}
+
+func (cov *coverage) begin(ctx BaseModuleContext) {}
+
+func (cov *coverage) deps(ctx BaseModuleContext, deps Deps) Deps {
+	return deps
+}
+
+func (cov *coverage) flags(ctx ModuleContext, flags Flags) Flags {
+	if !ctx.DeviceConfig().NativeCoverageEnabled() {
+		return flags
+	}
+
+	if cov.Properties.CoverageEnabled {
+		flags.Coverage = true
+		flags.GlobalFlags = append(flags.GlobalFlags, "--coverage", "-O0")
+		cov.linkCoverage = true
+	}
+
+	// Even if we don't have coverage enabled, if any of our object files were compiled
+	// with coverage, then we need to add --coverage to our ldflags.
+	if !cov.linkCoverage {
+		if ctx.static() && !ctx.staticBinary() {
+			// For static libraries, the only thing that changes our object files
+			// are included whole static libraries, so check to see if any of
+			// those have coverage enabled.
+			ctx.VisitDirectDeps(func(m blueprint.Module) {
+				if ctx.OtherModuleDependencyTag(m) != wholeStaticDepTag {
+					return
+				}
+
+				if cc, ok := m.(*Module); ok && cc.coverage != nil {
+					if cc.coverage.linkCoverage {
+						cov.linkCoverage = true
+					}
+				}
+			})
+		} else {
+			// For executables and shared libraries, we need to check all of
+			// our static dependencies.
+			ctx.VisitDirectDeps(func(m blueprint.Module) {
+				cc, ok := m.(*Module)
+				if !ok || cc.coverage == nil {
+					return
+				}
+
+				if static, ok := cc.linker.(libraryInterface); !ok || !static.static() {
+					return
+				}
+
+				if cc.coverage.linkCoverage {
+					cov.linkCoverage = true
+				}
+			})
+		}
+	}
+
+	if cov.linkCoverage {
+		flags.LdFlags = append(flags.LdFlags, "--coverage")
+	}
+
+	return flags
+}
+
+func coverageLinkingMutator(mctx android.BottomUpMutatorContext) {
+	if c, ok := mctx.Module().(*Module); ok && c.coverage != nil {
+		var enabled bool
+
+		if !mctx.DeviceConfig().NativeCoverageEnabled() {
+			// Coverage is disabled globally
+		} else if mctx.Host() {
+			// TODO(dwillemsen): because of -nodefaultlibs, we must depend on libclang_rt.profile-*.a
+			// Just turn off for now.
+		} else if c.coverage.Properties.Native_coverage != nil {
+			enabled = *c.coverage.Properties.Native_coverage
+		} else {
+			enabled = mctx.DeviceConfig().CoverageEnabledForPath(mctx.ModuleDir())
+		}
+
+		if enabled {
+			// Create a variation so that we don't need to recompile objects
+			// when turning on or off coverage. We'll still relink the necessary
+			// binaries, since we don't know which ones those are until later.
+			m := mctx.CreateLocalVariations("cov")
+			m[0].(*Module).coverage.Properties.CoverageEnabled = true
+		}
+	}
+}
diff --git a/cc/gen_stub_libs.py b/cc/gen_stub_libs.py
index d6cd973..7c70406 100755
--- a/cc/gen_stub_libs.py
+++ b/cc/gen_stub_libs.py
@@ -79,7 +79,7 @@
     return version.endswith('_PRIVATE') or version.endswith('_PLATFORM')
 
 
-def should_omit_version(name, tags, arch, api):
+def should_omit_version(name, tags, arch, api, vndk):
     """Returns True if the version section should be ommitted.
 
     We want to omit any sections that do not have any symbols we'll have in the
@@ -90,6 +90,8 @@
         return True
     if 'platform-only' in tags:
         return True
+    if 'vndk' in tags and not vndk:
+        return True
     if not symbol_in_arch(tags, arch):
         return True
     if not symbol_in_api(tags, arch, api):
@@ -271,11 +273,12 @@
 
 class Generator(object):
     """Output generator that writes stub source files and version scripts."""
-    def __init__(self, src_file, version_script, arch, api):
+    def __init__(self, src_file, version_script, arch, api, vndk):
         self.src_file = src_file
         self.version_script = version_script
         self.arch = arch
         self.api = api
+        self.vndk = vndk
 
     def write(self, versions):
         """Writes all symbol data to the output files."""
@@ -286,13 +289,15 @@
         """Writes a single version block's data to the output files."""
         name = version.name
         tags = version.tags
-        if should_omit_version(name, tags, self.arch, self.api):
+        if should_omit_version(name, tags, self.arch, self.api, self.vndk):
             return
 
         section_versioned = symbol_versioned_in_api(tags, self.api)
         version_empty = True
         pruned_symbols = []
         for symbol in version.symbols:
+            if not self.vndk and 'vndk' in symbol.tags:
+                continue
             if not symbol_in_arch(symbol.tags, self.arch):
                 continue
             if not symbol_in_api(symbol.tags, self.arch, self.api):
@@ -333,6 +338,8 @@
     parser.add_argument(
         '--arch', choices=ALL_ARCHITECTURES, required=True,
         help='Architecture being targeted.')
+    parser.add_argument(
+        '--vndk', action='store_true', help='Use the VNDK variant.')
 
     parser.add_argument(
         'symbol_file', type=os.path.realpath, help='Path to symbol file.')
@@ -361,7 +368,8 @@
 
     with open(args.stub_src, 'w') as src_file:
         with open(args.version_script, 'w') as version_file:
-            generator = Generator(src_file, version_file, args.arch, args.api)
+            generator = Generator(src_file, version_file, args.arch, args.api,
+                                  args.vndk)
             generator.write(versions)
 
 
diff --git a/cc/installer.go b/cc/installer.go
index 64f87d9..112a7ea 100644
--- a/cc/installer.go
+++ b/cc/installer.go
@@ -30,8 +30,9 @@
 type installLocation int
 
 const (
-	InstallInSystem installLocation = 0
-	InstallInData                   = iota
+	InstallInSystem       installLocation = 0
+	InstallInData                         = iota
+	InstallInSanitizerDir                 = iota
 )
 
 func NewBaseInstaller(dir, dir64 string, location installLocation) *baseInstaller {
@@ -67,6 +68,9 @@
 	if !ctx.Host() && !ctx.Arch().Native {
 		subDir = filepath.Join(subDir, ctx.Arch().ArchType.String())
 	}
+	if installer.location == InstallInData && ctx.vndk() {
+		subDir = filepath.Join(subDir, "vendor")
+	}
 	return android.PathForModuleInstall(ctx, subDir, installer.Properties.Relative_install_path, installer.relative)
 }
 
@@ -78,6 +82,10 @@
 	return installer.location == InstallInData
 }
 
+func (installer *baseInstaller) inSanitizerDir() bool {
+	return installer.location == InstallInSanitizerDir
+}
+
 func (installer *baseInstaller) hostToolPath() android.OptionalPath {
 	return android.OptionalPath{}
 }
diff --git a/cc/library.go b/cc/library.go
index 8474f96..0ba7088 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -64,7 +64,9 @@
 		// export headers generated from .proto sources
 		Export_proto_headers bool
 	}
+}
 
+type LibraryMutatedProperties struct {
 	VariantName string `blueprint:"mutated"`
 
 	// Build a static variant
@@ -82,6 +84,16 @@
 	// be added to the include path (using -I) for this module and any module that links
 	// against this module
 	Export_include_dirs []string `android:"arch_variant"`
+
+	Target struct {
+		Vendor struct {
+			// list of exported include directories, like
+			// export_include_dirs, that will be applied to the
+			// vendor variant of this library. This will overwrite
+			// any other declarations.
+			Export_include_dirs []string
+		}
+	}
 }
 
 func init() {
@@ -142,8 +154,16 @@
 	flagsDeps android.Paths
 }
 
+func (f *flagExporter) exportedIncludes(ctx ModuleContext) android.Paths {
+	if ctx.Vendor() && f.Properties.Target.Vendor.Export_include_dirs != nil {
+		return android.PathsForModuleSrc(ctx, f.Properties.Target.Vendor.Export_include_dirs)
+	} else {
+		return android.PathsForModuleSrc(ctx, f.Properties.Export_include_dirs)
+	}
+}
+
 func (f *flagExporter) exportIncludes(ctx ModuleContext, inc string) {
-	includeDirs := android.PathsForModuleSrc(ctx, f.Properties.Export_include_dirs)
+	includeDirs := f.exportedIncludes(ctx)
 	for _, dir := range includeDirs.Strings() {
 		f.flags = append(f.flags, inc+dir)
 	}
@@ -175,7 +195,8 @@
 // libraryDecorator wraps baseCompiler, baseLinker and baseInstaller to provide library-specific
 // functionality: static vs. shared linkage, reusing object files for shared libraries
 type libraryDecorator struct {
-	Properties LibraryProperties
+	Properties        LibraryProperties
+	MutatedProperties LibraryMutatedProperties
 
 	// For reusing static library objects for shared library
 	reuseObjects Objects
@@ -199,6 +220,9 @@
 
 	sanitize *sanitize
 
+	// Output archive of gcno coverage information files
+	coverageOutputFile android.OptionalPath
+
 	// Decorated interafaces
 	*baseCompiler
 	*baseLinker
@@ -210,6 +234,7 @@
 	props = append(props, library.baseLinker.linkerProps()...)
 	return append(props,
 		&library.Properties,
+		&library.MutatedProperties,
 		&library.flagExporter.Properties,
 		&library.stripper.StripProperties,
 		&library.relocationPacker.Properties)
@@ -221,17 +246,17 @@
 	// MinGW spits out warnings about -fPIC even for -fpie?!) being ignored because
 	// all code is position independent, and then those warnings get promoted to
 	// errors.
-	if ctx.Os() != android.Windows {
+	if !ctx.Windows() {
 		flags.CFlags = append(flags.CFlags, "-fPIC")
 	}
 
 	if library.static() {
 		flags.CFlags = append(flags.CFlags, library.Properties.Static.Cflags...)
-	} else {
+	} else if library.shared() {
 		flags.CFlags = append(flags.CFlags, library.Properties.Shared.Cflags...)
 	}
 
-	if !library.static() {
+	if library.shared() {
 		libName := library.getLibName(ctx)
 		// GCC for Android assumes that -shared means -Bsymbolic, use -Wl,-shared instead
 		sharedFlag := "-Wl,-shared"
@@ -239,7 +264,7 @@
 			sharedFlag = "-shared"
 		}
 		var f []string
-		if ctx.Device() {
+		if ctx.toolchain().Bionic() {
 			f = append(f,
 				"-nostdlib",
 				"-Wl,--gc-sections",
@@ -270,7 +295,7 @@
 }
 
 func (library *libraryDecorator) compilerFlags(ctx ModuleContext, flags Flags) Flags {
-	exportIncludeDirs := android.PathsForModuleSrc(ctx, library.flagExporter.Properties.Export_include_dirs)
+	exportIncludeDirs := library.flagExporter.exportedIncludes(ctx)
 	if len(exportIncludeDirs) > 0 {
 		flags.GlobalFlags = append(flags.GlobalFlags, includeDirsToFlags(exportIncludeDirs))
 	}
@@ -300,7 +325,7 @@
 		srcs := android.PathsForModuleSrc(ctx, library.Properties.Static.Srcs)
 		objs = objs.Append(compileObjs(ctx, buildFlags, android.DeviceStaticLibrary,
 			srcs, library.baseCompiler.deps))
-	} else {
+	} else if library.shared() {
 		srcs := android.PathsForModuleSrc(ctx, library.Properties.Shared.Srcs)
 		objs = objs.Append(compileObjs(ctx, buildFlags, android.DeviceSharedLibrary,
 			srcs, library.baseCompiler.deps))
@@ -321,7 +346,8 @@
 	buildShared() bool
 
 	// Sets whether a specific variant is static or shared
-	setStatic(bool)
+	setStatic()
+	setShared()
 }
 
 func (library *libraryDecorator) getLibName(ctx ModuleContext) string {
@@ -336,13 +362,13 @@
 		}
 	}
 
-	return name + library.Properties.VariantName
+	return name + library.MutatedProperties.VariantName
 }
 
 func (library *libraryDecorator) linkerInit(ctx BaseModuleContext) {
 	location := InstallInSystem
-	if library.sanitize.inData() {
-		location = InstallInData
+	if library.sanitize.inSanitizerDir() {
+		location = InstallInSanitizerDir
 	}
 	library.baseInstaller.location = location
 
@@ -359,9 +385,9 @@
 			library.Properties.Static.Whole_static_libs...)
 		deps.StaticLibs = append(deps.StaticLibs, library.Properties.Static.Static_libs...)
 		deps.SharedLibs = append(deps.SharedLibs, library.Properties.Static.Shared_libs...)
-	} else {
+	} else if library.shared() {
 		if ctx.toolchain().Bionic() && !Bool(library.baseLinker.Properties.Nocrt) {
-			if !ctx.sdk() && !ctx.vndk() {
+			if !ctx.sdk() {
 				deps.CrtBegin = "crtbegin_so"
 				deps.CrtEnd = "crtend_so"
 			} else {
@@ -392,13 +418,13 @@
 	library.objects = library.objects.Append(objs)
 
 	outputFile := android.PathForModuleOut(ctx,
-		ctx.ModuleName()+library.Properties.VariantName+staticLibraryExtension)
+		ctx.ModuleName()+library.MutatedProperties.VariantName+staticLibraryExtension)
+	builderFlags := flagsToBuilderFlags(flags)
 
-	if ctx.Darwin() {
-		TransformDarwinObjToStaticLib(ctx, library.objects.objFiles, flagsToBuilderFlags(flags), outputFile, objs.tidyFiles)
-	} else {
-		TransformObjToStaticLib(ctx, library.objects.objFiles, flagsToBuilderFlags(flags), outputFile, objs.tidyFiles)
-	}
+	TransformObjToStaticLib(ctx, library.objects.objFiles, builderFlags, outputFile, objs.tidyFiles)
+
+	library.coverageOutputFile = TransformCoverageFilesToLib(ctx, library.objects, builderFlags,
+		ctx.ModuleName()+library.MutatedProperties.VariantName)
 
 	library.wholeStaticMissingDeps = ctx.GetMissingDependencies()
 
@@ -454,7 +480,7 @@
 
 	builderFlags := flagsToBuilderFlags(flags)
 
-	if !ctx.Darwin() {
+	if !ctx.Darwin() && !ctx.Windows() {
 		// Optimize out relinking against shared libraries whose interface hasn't changed by
 		// depending on a table of contents file instead of the library itself.
 		tocPath := outputFile.RelPathString()
@@ -506,6 +532,10 @@
 		deps.StaticLibs, deps.LateStaticLibs, deps.WholeStaticLibs,
 		linkerDeps, deps.CrtBegin, deps.CrtEnd, false, builderFlags, outputFile)
 
+	objs.coverageFiles = append(objs.coverageFiles, deps.StaticLibObjs.coverageFiles...)
+	objs.coverageFiles = append(objs.coverageFiles, deps.WholeStaticLibObjs.coverageFiles...)
+	library.coverageOutputFile = TransformCoverageFilesToLib(ctx, objs, builderFlags, library.getLibName(ctx))
+
 	return ret
 }
 
@@ -515,7 +545,7 @@
 	objs = objs.Append(deps.Objs)
 
 	var out android.Path
-	if library.static() {
+	if library.static() || library.header() {
 		out = library.linkStatic(ctx, flags, deps, objs)
 	} else {
 		out = library.linkShared(ctx, flags, deps, objs)
@@ -548,12 +578,12 @@
 }
 
 func (library *libraryDecorator) buildStatic() bool {
-	return library.Properties.BuildStatic &&
+	return library.MutatedProperties.BuildStatic &&
 		(library.Properties.Static.Enabled == nil || *library.Properties.Static.Enabled)
 }
 
 func (library *libraryDecorator) buildShared() bool {
-	return library.Properties.BuildShared &&
+	return library.MutatedProperties.BuildShared &&
 		(library.Properties.Shared.Enabled == nil || *library.Properties.Shared.Enabled)
 }
 
@@ -580,31 +610,45 @@
 }
 
 func (library *libraryDecorator) static() bool {
-	return library.Properties.VariantIsStatic
+	return library.MutatedProperties.VariantIsStatic
 }
 
-func (library *libraryDecorator) setStatic(static bool) {
-	library.Properties.VariantIsStatic = static
+func (library *libraryDecorator) shared() bool {
+	return library.MutatedProperties.VariantIsShared
+}
+
+func (library *libraryDecorator) header() bool {
+	return !library.static() && !library.shared()
+}
+
+func (library *libraryDecorator) setStatic() {
+	library.MutatedProperties.VariantIsStatic = true
+	library.MutatedProperties.VariantIsShared = false
+}
+
+func (library *libraryDecorator) setShared() {
+	library.MutatedProperties.VariantIsStatic = false
+	library.MutatedProperties.VariantIsShared = true
 }
 
 func (library *libraryDecorator) BuildOnlyStatic() {
-	library.Properties.BuildShared = false
+	library.MutatedProperties.BuildShared = false
 }
 
 func (library *libraryDecorator) BuildOnlyShared() {
-	library.Properties.BuildStatic = false
+	library.MutatedProperties.BuildStatic = false
 }
 
 func (library *libraryDecorator) HeaderOnly() {
-	library.Properties.BuildShared = false
-	library.Properties.BuildStatic = false
+	library.MutatedProperties.BuildShared = false
+	library.MutatedProperties.BuildStatic = false
 }
 
 func NewLibrary(hod android.HostOrDeviceSupported) (*Module, *libraryDecorator) {
 	module := newModule(hod, android.MultilibBoth)
 
 	library := &libraryDecorator{
-		Properties: LibraryProperties{
+		MutatedProperties: LibraryMutatedProperties{
 			BuildShared: true,
 			BuildStatic: true,
 		},
@@ -630,8 +674,8 @@
 				static := modules[0].(*Module)
 				shared := modules[1].(*Module)
 
-				static.linker.(libraryInterface).setStatic(true)
-				shared.linker.(libraryInterface).setStatic(false)
+				static.linker.(libraryInterface).setStatic()
+				shared.linker.(libraryInterface).setShared()
 
 				if staticCompiler, ok := static.compiler.(*libraryDecorator); ok {
 					sharedCompiler := shared.compiler.(*libraryDecorator)
@@ -645,10 +689,10 @@
 				}
 			} else if library.buildStatic() {
 				modules = mctx.CreateLocalVariations("static")
-				modules[0].(*Module).linker.(libraryInterface).setStatic(true)
+				modules[0].(*Module).linker.(libraryInterface).setStatic()
 			} else if library.buildShared() {
 				modules = mctx.CreateLocalVariations("shared")
-				modules[0].(*Module).linker.(libraryInterface).setStatic(false)
+				modules[0].(*Module).linker.(libraryInterface).setShared()
 			}
 		}
 	}
diff --git a/cc/linker.go b/cc/linker.go
index 4619926..5a3b478 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -143,7 +143,7 @@
 			if linker.Properties.System_shared_libs != nil {
 				deps.LateSharedLibs = append(deps.LateSharedLibs,
 					linker.Properties.System_shared_libs...)
-			} else if !ctx.sdk() {
+			} else if !ctx.sdk() && !ctx.vndk() {
 				deps.LateSharedLibs = append(deps.LateSharedLibs, "libc", "libm")
 			}
 		}
@@ -154,6 +154,13 @@
 				"libm",
 			)
 		}
+		if ctx.vndk() {
+			deps.LateSharedLibs = append(deps.LateSharedLibs, "libc", "libm")
+		}
+	}
+
+	if ctx.Windows() {
+		deps.LateStaticLibs = append(deps.LateStaticLibs, "libwinpthread")
 	}
 
 	return deps
diff --git a/cc/llndk_library.go b/cc/llndk_library.go
new file mode 100644
index 0000000..cde1bc7
--- /dev/null
+++ b/cc/llndk_library.go
@@ -0,0 +1,164 @@
+// Copyright 2017 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package cc
+
+import (
+	"path/filepath"
+	"strings"
+
+	"github.com/google/blueprint"
+
+	"android/soong/android"
+)
+
+var (
+	llndkLibrarySuffix = ".llndk"
+)
+
+// Creates a stub shared library based on the provided version file.
+//
+// Example:
+//
+// llndk_library {
+//     name: "libfoo",
+//     symbol_file: "libfoo.map.txt",
+//     export_include_dirs: ["include_vndk"],
+// }
+//
+type llndkLibraryProperties struct {
+	// Relative path to the symbol map.
+	// An example file can be seen here: TODO(danalbert): Make an example.
+	Symbol_file string
+
+	// Whether to export any headers as -isystem instead of -I. Mainly for use by
+	// bionic/libc.
+	Export_headers_as_system bool
+
+	// Which headers to process with versioner. This really only handles
+	// bionic/libc/include right now.
+	Export_preprocessed_headers []string
+
+	// Whether the system library uses symbol versions.
+	Unversioned bool
+}
+
+type llndkStubDecorator struct {
+	*libraryDecorator
+
+	Properties llndkLibraryProperties
+
+	exportHeadersTimestamp android.OptionalPath
+	versionScriptPath      android.ModuleGenPath
+}
+
+func (stub *llndkStubDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) Objects {
+	objs, versionScript := compileStubLibrary(ctx, flags, stub.Properties.Symbol_file, "current", "--vndk")
+	stub.versionScriptPath = versionScript
+	return objs
+}
+
+func (stub *llndkStubDecorator) linkerDeps(ctx DepsContext, deps Deps) Deps {
+	return Deps{}
+}
+
+func (stub *llndkStubDecorator) Name(name string) string {
+	return name + llndkLibrarySuffix
+}
+
+func (stub *llndkStubDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags {
+	stub.libraryDecorator.libName = strings.TrimSuffix(ctx.ModuleName(),
+		llndkLibrarySuffix)
+	return stub.libraryDecorator.linkerFlags(ctx, flags)
+}
+
+func (stub *llndkStubDecorator) processHeaders(ctx ModuleContext, srcHeaderDir string, outDir android.ModuleGenPath) android.Path {
+	srcDir := android.PathForModuleSrc(ctx, srcHeaderDir)
+	srcFiles := ctx.Glob(filepath.Join(srcDir.String(), "**/*.h"), nil)
+
+	var installPaths []android.WritablePath
+	for _, header := range srcFiles {
+		headerDir := filepath.Dir(header.String())
+		relHeaderDir, err := filepath.Rel(srcDir.String(), headerDir)
+		if err != nil {
+			ctx.ModuleErrorf("filepath.Rel(%q, %q) failed: %s",
+				srcDir.String(), headerDir, err)
+			continue
+		}
+
+		installPaths = append(installPaths, outDir.Join(ctx, relHeaderDir, header.Base()))
+	}
+
+	return processHeadersWithVersioner(ctx, srcDir, outDir, srcFiles, installPaths)
+}
+
+func (stub *llndkStubDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps,
+	objs Objects) android.Path {
+
+	if !stub.Properties.Unversioned {
+		linkerScriptFlag := "-Wl,--version-script," + stub.versionScriptPath.String()
+		flags.LdFlags = append(flags.LdFlags, linkerScriptFlag)
+	}
+
+	if len(stub.Properties.Export_preprocessed_headers) > 0 {
+		genHeaderOutDir := android.PathForModuleGen(ctx, "include")
+
+		var timestampFiles android.Paths
+		for _, dir := range stub.Properties.Export_preprocessed_headers {
+			timestampFiles = append(timestampFiles, stub.processHeaders(ctx, dir, genHeaderOutDir))
+		}
+
+		includePrefix := "-I "
+		if stub.Properties.Export_headers_as_system {
+			includePrefix = "-isystem "
+		}
+
+		stub.reexportFlags([]string{includePrefix + " " + genHeaderOutDir.String()})
+		stub.reexportDeps(timestampFiles)
+	}
+
+	if stub.Properties.Export_headers_as_system {
+		stub.exportIncludes(ctx, "-isystem")
+		stub.libraryDecorator.flagExporter.Properties.Export_include_dirs = []string{}
+	}
+
+	return stub.libraryDecorator.link(ctx, flags, deps, objs)
+}
+
+func newLLndkStubLibrary() (*Module, []interface{}) {
+	module, library := NewLibrary(android.DeviceSupported)
+	library.BuildOnlyShared()
+	module.stl = nil
+	module.sanitize = nil
+	library.StripProperties.Strip.None = true
+
+	stub := &llndkStubDecorator{
+		libraryDecorator: library,
+	}
+	module.compiler = stub
+	module.linker = stub
+	module.installer = nil
+
+	return module, []interface{}{&stub.Properties, &library.MutatedProperties, &library.flagExporter.Properties}
+}
+
+func llndkLibraryFactory() (blueprint.Module, []interface{}) {
+	module, properties := newLLndkStubLibrary()
+	return android.InitAndroidArchModule(module, android.DeviceSupported,
+		android.MultilibBoth, properties...)
+}
+
+func init() {
+	android.RegisterModuleType("llndk_library", llndkLibraryFactory)
+}
diff --git a/cc/makevars.go b/cc/makevars.go
index 2e64e3c..ce2ac5a 100644
--- a/cc/makevars.go
+++ b/cc/makevars.go
@@ -16,7 +16,6 @@
 
 import (
 	"fmt"
-	"path/filepath"
 	"sort"
 	"strings"
 
@@ -40,6 +39,13 @@
 	ctx.Strict("PATH_TO_CLANG_TIDY", "${config.ClangBin}/clang-tidy")
 	ctx.StrictSorted("CLANG_CONFIG_UNKNOWN_CFLAGS", strings.Join(config.ClangUnknownCflags, " "))
 
+	ctx.Strict("RS_LLVM_PREBUILTS_VERSION", "${config.RSClangVersion}")
+	ctx.Strict("RS_LLVM_PREBUILTS_BASE", "${config.RSClangBase}")
+	ctx.Strict("RS_LLVM_PREBUILTS_PATH", "${config.RSLLVMPrebuiltsPath}")
+	ctx.Strict("RS_CLANG", "${config.RSLLVMPrebuiltsPath}/clang")
+	ctx.Strict("RS_LLVM_AS", "${config.RSLLVMPrebuiltsPath}/llvm-as")
+	ctx.Strict("RS_LLVM_LINK", "${config.RSLLVMPrebuiltsPath}/llvm-link")
+
 	ctx.Strict("GLOBAL_CFLAGS_NO_OVERRIDE", "${config.NoOverrideGlobalCflags}")
 	ctx.Strict("GLOBAL_CLANG_CFLAGS_NO_OVERRIDE", "${config.ClangExtraNoOverrideCflags}")
 	ctx.Strict("GLOBAL_CPPFLAGS_NO_OVERRIDE", "")
@@ -51,15 +57,19 @@
 	} else {
 		ctx.Strict("BOARD_VNDK_VERSION", "")
 	}
-	ctx.Strict("VNDK_LIBRARIES", strings.Join(config.VndkLibraries(), " "))
 
 	ctx.Strict("ADDRESS_SANITIZER_CONFIG_EXTRA_CFLAGS", asanCflags)
 	ctx.Strict("ADDRESS_SANITIZER_CONFIG_EXTRA_LDFLAGS", asanLdflags)
 	ctx.Strict("ADDRESS_SANITIZER_CONFIG_EXTRA_STATIC_LIBRARIES", asanLibs)
 
+	ctx.Strict("CFI_EXTRA_CFLAGS", cfiCflags)
+	ctx.Strict("CFI_EXTRA_LDFLAGS", cfiLdflags)
+
 	ctx.Strict("DEFAULT_C_STD_VERSION", config.CStdVersion)
 	ctx.Strict("DEFAULT_CPP_STD_VERSION", config.CppStdVersion)
 	ctx.Strict("DEFAULT_GCC_CPP_STD_VERSION", config.GccCppStdVersion)
+	ctx.Strict("EXPERIMENTAL_C_STD_VERSION", config.ExperimentalCStdVersion)
+	ctx.Strict("EXPERIMENTAL_CPP_STD_VERSION", config.ExperimentalCppStdVersion)
 
 	ctx.Strict("DEFAULT_GLOBAL_TIDY_CHECKS", "${config.TidyDefaultGlobalChecks}")
 	ctx.Strict("DEFAULT_LOCAL_TIDY_CHECKS", joinLocalTidyChecks(config.DefaultLocalTidyChecks))
@@ -174,9 +184,7 @@
 	if toolchain.ClangSupported() {
 		clangPrefix := secondPrefix + "CLANG_" + typePrefix
 		clangExtras := "-target " + toolchain.ClangTriple()
-		if target.Os != android.Darwin {
-			clangExtras += " -B" + filepath.Join(toolchain.GccRoot(), toolchain.GccTriple(), "bin")
-		}
+		clangExtras += " -B" + config.ToolPath(toolchain)
 
 		ctx.Strict(clangPrefix+"GLOBAL_CFLAGS", strings.Join([]string{
 			toolchain.ClangCflags(),
@@ -200,6 +208,7 @@
 		if target.Os.Class == android.Device {
 			ctx.Strict(secondPrefix+"ADDRESS_SANITIZER_RUNTIME_LIBRARY", strings.TrimSuffix(config.AddressSanitizerRuntimeLibrary(toolchain), ".so"))
 			ctx.Strict(secondPrefix+"UBSAN_RUNTIME_LIBRARY", strings.TrimSuffix(config.UndefinedBehaviorSanitizerRuntimeLibrary(toolchain), ".so"))
+			ctx.Strict(secondPrefix+"TSAN_RUNTIME_LIBRARY", strings.TrimSuffix(config.ThreadSanitizerRuntimeLibrary(toolchain), ".so"))
 		}
 
 		// This is used by external/gentoo/...
diff --git a/cc/ndk_headers.go b/cc/ndk_headers.go
index 9cc3417..deb735a 100644
--- a/cc/ndk_headers.go
+++ b/cc/ndk_headers.go
@@ -15,6 +15,8 @@
 package cc
 
 import (
+	"fmt"
+	"os"
 	"path/filepath"
 
 	"github.com/google/blueprint"
@@ -22,6 +24,22 @@
 	"android/soong/android"
 )
 
+var (
+	preprocessBionicHeaders = pctx.AndroidStaticRule("preprocessBionicHeaders",
+		blueprint.RuleParams{
+			// The `&& touch $out` isn't really necessary, but Blueprint won't
+			// let us have only implicit outputs.
+			Command:     "$versionerCmd -o $outDir $srcDir $depsPath && touch $out",
+			CommandDeps: []string{"$versionerCmd"},
+			Description: "versioner preprocess $in",
+		},
+		"depsPath", "srcDir", "outDir")
+)
+
+func init() {
+	pctx.HostBinToolVariable("versionerCmd", "versioner")
+}
+
 // Returns the NDK base include path for use with sdk_version current. Usable with -I.
 func getCurrentIncludePath(ctx android.ModuleContext) android.OutputPath {
 	return getNdkSysrootBase(ctx).Join(ctx, "usr/include")
@@ -63,6 +81,38 @@
 func (m *headerModule) DepsMutator(ctx android.BottomUpMutatorContext) {
 }
 
+func getHeaderInstallDir(ctx android.ModuleContext, header android.Path, from string,
+	to string) android.OutputPath {
+	// Output path is the sysroot base + "usr/include" + to directory + directory component
+	// of the file without the leading from directory stripped.
+	//
+	// Given:
+	// sysroot base = "ndk/sysroot"
+	// from = "include/foo"
+	// to = "bar"
+	// header = "include/foo/woodly/doodly.h"
+	// output path = "ndk/sysroot/usr/include/bar/woodly/doodly.h"
+
+	// full/platform/path/to/include/foo
+	fullFromPath := android.PathForModuleSrc(ctx, from)
+
+	// full/platform/path/to/include/foo/woodly
+	headerDir := filepath.Dir(header.String())
+
+	// woodly
+	strippedHeaderDir, err := filepath.Rel(fullFromPath.String(), headerDir)
+	if err != nil {
+		ctx.ModuleErrorf("filepath.Rel(%q, %q) failed: %s", headerDir,
+			fullFromPath.String(), err)
+	}
+
+	// full/platform/path/to/sysroot/usr/include/bar/woodly
+	installDir := getCurrentIncludePath(ctx).Join(ctx, to, strippedHeaderDir)
+
+	// full/platform/path/to/sysroot/usr/include/bar/woodly/doodly.h
+	return installDir
+}
+
 func (m *headerModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	if m.properties.License == "" {
 		ctx.PropertyErrorf("license", "field is required")
@@ -72,34 +122,14 @@
 
 	srcFiles := ctx.ExpandSources(m.properties.Srcs, nil)
 	for _, header := range srcFiles {
-		// Output path is the sysroot base + "usr/include" + to directory + directory component
-		// of the file without the leading from directory stripped.
-		//
-		// Given:
-		// sysroot base = "ndk/sysroot"
-		// from = "include/foo"
-		// to = "bar"
-		// header = "include/foo/woodly/doodly.h"
-		// output path = "ndk/sysroot/usr/include/bar/woodly/doodly.h"
-
-		// full/platform/path/to/include/foo
-		fullFromPath := android.PathForModuleSrc(ctx, m.properties.From)
-
-		// full/platform/path/to/include/foo/woodly
-		headerDir := filepath.Dir(header.String())
-
-		// woodly
-		strippedHeaderDir, err := filepath.Rel(fullFromPath.String(), headerDir)
-		if err != nil {
-			ctx.ModuleErrorf("filepath.Rel(%q, %q) failed: %s", headerDir,
-				fullFromPath.String(), err)
+		installDir := getHeaderInstallDir(ctx, header, m.properties.From, m.properties.To)
+		installedPath := ctx.InstallFile(installDir, header)
+		installPath := installDir.Join(ctx, header.Base())
+		if installPath != installedPath {
+			panic(fmt.Sprintf(
+				"expected header install path (%q) not equal to actual install path %q",
+				installPath, installedPath))
 		}
-
-		// full/platform/path/to/sysroot/usr/include/bar/woodly
-		installDir := getCurrentIncludePath(ctx).Join(ctx, m.properties.To, strippedHeaderDir)
-
-		// full/platform/path/to/sysroot/usr/include/bar/woodly/doodly.h
-		installPath := ctx.InstallFile(installDir, header)
 		m.installPaths = append(m.installPaths, installPath.String())
 	}
 
@@ -110,6 +140,119 @@
 
 func ndkHeadersFactory() (blueprint.Module, []interface{}) {
 	module := &headerModule{}
+	return android.InitAndroidModule(module, &module.properties)
+}
+
+type preprocessedHeaderProperies struct {
+	// Base directory of the headers being installed. As an example:
+	//
+	// preprocessed_ndk_headers {
+	//     name: "foo",
+	//     from: "include",
+	//     to: "",
+	// }
+	//
+	// Will install $SYSROOT/usr/include/foo/bar/baz.h. If `from` were instead
+	// "include/foo", it would have installed $SYSROOT/usr/include/bar/baz.h.
+	From string
+
+	// Install path within the sysroot. This is relative to usr/include.
+	To string
+
+	// Path to the NOTICE file associated with the headers.
+	License string
+}
+
+// Like ndk_headers, but preprocesses the headers with the bionic versioner:
+// https://android.googlesource.com/platform/bionic/+/master/tools/versioner/README.md.
+//
+// Unlike ndk_headers, we don't operate on a list of sources but rather a whole directory, the
+// module does not have the srcs property, and operates on a full directory (the `from` property).
+//
+// Note that this is really only built to handle bionic/libc/include.
+type preprocessedHeaderModule struct {
+	android.ModuleBase
+
+	properties preprocessedHeaderProperies
+
+	installPaths []string
+	licensePath  android.ModuleSrcPath
+}
+
+func (m *preprocessedHeaderModule) DepsMutator(ctx android.BottomUpMutatorContext) {
+}
+
+func (m *preprocessedHeaderModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	if m.properties.License == "" {
+		ctx.PropertyErrorf("license", "field is required")
+	}
+
+	m.licensePath = android.PathForModuleSrc(ctx, m.properties.License)
+
+	fromSrcPath := android.PathForModuleSrc(ctx, m.properties.From)
+	toOutputPath := getCurrentIncludePath(ctx).Join(ctx, m.properties.To)
+	srcFiles := ctx.Glob(filepath.Join(fromSrcPath.String(), "**/*.h"), nil)
+	var installPaths []android.WritablePath
+	for _, header := range srcFiles {
+		installDir := getHeaderInstallDir(ctx, header, m.properties.From, m.properties.To)
+		installPath := installDir.Join(ctx, header.Base())
+		installPaths = append(installPaths, installPath)
+		m.installPaths = append(m.installPaths, installPath.String())
+	}
+
+	if len(m.installPaths) == 0 {
+		ctx.ModuleErrorf("glob %q matched zero files", m.properties.From)
+	}
+
+	processHeadersWithVersioner(ctx, fromSrcPath, toOutputPath, srcFiles, installPaths)
+}
+
+func processHeadersWithVersioner(ctx android.ModuleContext, srcDir, outDir android.Path, srcFiles android.Paths, installPaths []android.WritablePath) android.Path {
+	// The versioner depends on a dependencies directory to simplify determining include paths
+	// when parsing headers. This directory contains architecture specific directories as well
+	// as a common directory, each of which contains symlinks to the actually directories to
+	// be included.
+	//
+	// ctx.Glob doesn't follow symlinks, so we need to do this ourselves so we correctly
+	// depend on these headers.
+	// TODO(http://b/35673191): Update the versioner to use a --sysroot.
+	depsPath := android.PathForSource(ctx, "bionic/libc/versioner-dependencies")
+	depsGlob := ctx.Glob(filepath.Join(depsPath.String(), "**/*"), nil)
+	for i, path := range depsGlob {
+		fileInfo, err := os.Lstat(path.String())
+		if err != nil {
+			ctx.ModuleErrorf("os.Lstat(%q) failed: %s", path.String, err)
+		}
+		if fileInfo.Mode()&os.ModeSymlink == os.ModeSymlink {
+			dest, err := os.Readlink(path.String())
+			if err != nil {
+				ctx.ModuleErrorf("os.Readlink(%q) failed: %s",
+					path.String, err)
+			}
+			// Additional .. to account for the symlink itself.
+			depsGlob[i] = android.PathForSource(
+				ctx, filepath.Clean(filepath.Join(path.String(), "..", dest)))
+		}
+	}
+
+	timestampFile := android.PathForModuleOut(ctx, "versioner.timestamp")
+	ctx.ModuleBuild(pctx, android.ModuleBuildParams{
+		Rule:            preprocessBionicHeaders,
+		Output:          timestampFile,
+		Implicits:       append(srcFiles, depsGlob...),
+		ImplicitOutputs: installPaths,
+		Args: map[string]string{
+			"depsPath": depsPath.String(),
+			"srcDir":   srcDir.String(),
+			"outDir":   outDir.String(),
+		},
+	})
+
+	return timestampFile
+}
+
+func preprocessedNdkHeadersFactory() (blueprint.Module, []interface{}) {
+	module := &preprocessedHeaderModule{}
 	// Host module rather than device module because device module install steps
 	// do not get run when embedded in make. We're not any of the existing
 	// module types that can be exposed via the Android.mk exporter, so just use
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index da3d5c7..f5499dd 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -30,10 +30,10 @@
 
 	genStubSrc = pctx.AndroidStaticRule("genStubSrc",
 		blueprint.RuleParams{
-			Command:     "$toolPath --arch $arch --api $apiLevel $in $out",
+			Command:     "$toolPath --arch $arch --api $apiLevel $vndk $in $out",
 			Description: "genStubSrc $out",
 			CommandDeps: []string{"$toolPath"},
-		}, "arch", "apiLevel")
+		}, "arch", "apiLevel", "vndk")
 
 	ndkLibrarySuffix = ".ndk"
 
@@ -65,15 +65,10 @@
 
 // Creates a stub shared library based on the provided version file.
 //
-// The name of the generated file will be based on the module name by stripping
-// the ".ndk" suffix from the module name. Module names must end with ".ndk"
-// (as a convention to allow soong to guess the NDK name of a dependency when
-// needed). "libfoo.ndk" will generate "libfoo.so.
-//
 // Example:
 //
 // ndk_library {
-//     name: "libfoo.ndk",
+//     name: "libfoo",
 //     symbol_file: "libfoo.map.txt",
 //     first_version: "9",
 // }
@@ -130,6 +125,16 @@
 		"x86_64": 21,
 	}
 
+	archStr := arch.ArchType.String()
+	firstArchVersion, ok := firstArchVersions[archStr]
+	if !ok {
+		panic(fmt.Errorf("Arch %q not found in firstArchVersions", archStr))
+	}
+
+	if apiLevel == "minimum" {
+		return strconv.Itoa(firstArchVersion), nil
+	}
+
 	// If the NDK drops support for a platform version, we don't want to have to
 	// fix up every module that was using it as its SDK version. Clip to the
 	// supported version here instead.
@@ -139,12 +144,6 @@
 	}
 	version = intMax(version, minVersion)
 
-	archStr := arch.ArchType.String()
-	firstArchVersion, ok := firstArchVersions[archStr]
-	if !ok {
-		panic(fmt.Errorf("Arch %q not found in firstArchVersions", archStr))
-	}
-
 	return strconv.Itoa(intMax(version, firstArchVersion)), nil
 }
 
@@ -226,7 +225,11 @@
 func (c *stubDecorator) compilerInit(ctx BaseModuleContext) {
 	c.baseCompiler.compilerInit(ctx)
 
-	name := strings.TrimSuffix(ctx.ModuleName(), ".ndk")
+	name := ctx.baseModuleName()
+	if strings.HasSuffix(name, ndkLibrarySuffix) {
+		ctx.PropertyErrorf("name", "Do not append %q manually, just use the base name", ndkLibrarySuffix)
+	}
+
 	ndkMigratedLibsLock.Lock()
 	defer ndkMigratedLibsLock.Unlock()
 	for _, lib := range ndkMigratedLibs {
@@ -237,28 +240,20 @@
 	ndkMigratedLibs = append(ndkMigratedLibs, name)
 }
 
-func (c *stubDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) Objects {
+func compileStubLibrary(ctx ModuleContext, flags Flags, symbolFile, apiLevel, vndk string) (Objects, android.ModuleGenPath) {
 	arch := ctx.Arch().ArchType.String()
 
-	if !strings.HasSuffix(ctx.ModuleName(), ndkLibrarySuffix) {
-		ctx.ModuleErrorf("ndk_library modules names must be suffixed with %q\n",
-			ndkLibrarySuffix)
-	}
-	libName := strings.TrimSuffix(ctx.ModuleName(), ndkLibrarySuffix)
-	fileBase := fmt.Sprintf("%s.%s.%s", libName, arch, c.properties.ApiLevel)
-	stubSrcName := fileBase + ".c"
-	stubSrcPath := android.PathForModuleGen(ctx, stubSrcName)
-	versionScriptName := fileBase + ".map"
-	versionScriptPath := android.PathForModuleGen(ctx, versionScriptName)
-	c.versionScriptPath = versionScriptPath
-	symbolFilePath := android.PathForModuleSrc(ctx, c.properties.Symbol_file)
+	stubSrcPath := android.PathForModuleGen(ctx, "stub.c")
+	versionScriptPath := android.PathForModuleGen(ctx, "stub.map")
+	symbolFilePath := android.PathForModuleSrc(ctx, symbolFile)
 	ctx.ModuleBuild(pctx, android.ModuleBuildParams{
 		Rule:    genStubSrc,
 		Outputs: []android.WritablePath{stubSrcPath, versionScriptPath},
 		Input:   symbolFilePath,
 		Args: map[string]string{
 			"arch":     arch,
-			"apiLevel": c.properties.ApiLevel,
+			"apiLevel": apiLevel,
+			"vndk":     vndk,
 		},
 	})
 
@@ -276,16 +271,25 @@
 
 	subdir := ""
 	srcs := []android.Path{stubSrcPath}
-	return compileObjs(ctx, flagsToBuilderFlags(flags), subdir, srcs, nil)
+	return compileObjs(ctx, flagsToBuilderFlags(flags), subdir, srcs, nil), versionScriptPath
+}
+
+func (c *stubDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) Objects {
+	objs, versionScript := compileStubLibrary(ctx, flags, c.properties.Symbol_file, c.properties.ApiLevel, "")
+	c.versionScriptPath = versionScript
+	return objs
 }
 
 func (linker *stubDecorator) linkerDeps(ctx DepsContext, deps Deps) Deps {
 	return Deps{}
 }
 
+func (linker *stubDecorator) Name(name string) string {
+	return name + ndkLibrarySuffix
+}
+
 func (stub *stubDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags {
-	stub.libraryDecorator.libName = strings.TrimSuffix(ctx.ModuleName(),
-		ndkLibrarySuffix)
+	stub.libraryDecorator.libName = ctx.baseModuleName()
 	return stub.libraryDecorator.linkerFlags(ctx, flags)
 }
 
@@ -335,7 +339,7 @@
 	module.linker = stub
 	module.installer = stub
 
-	return module, []interface{}{&stub.properties}
+	return module, []interface{}{&stub.properties, &library.MutatedProperties}
 }
 
 func ndkLibraryFactory() (blueprint.Module, []interface{}) {
diff --git a/cc/ndk_prebuilt.go b/cc/ndk_prebuilt.go
index 676d63d..f1bd3b5 100644
--- a/cc/ndk_prebuilt.go
+++ b/cc/ndk_prebuilt.go
@@ -194,7 +194,7 @@
 
 	libName := strings.TrimPrefix(ctx.ModuleName(), "ndk_")
 	libExt := flags.Toolchain.ShlibSuffix()
-	if ndk.Properties.BuildStatic {
+	if ndk.static() {
 		libExt = staticLibraryExtension
 	}
 
diff --git a/cc/ndk_sysroot.go b/cc/ndk_sysroot.go
index e82f12b..47dda27 100644
--- a/cc/ndk_sysroot.go
+++ b/cc/ndk_sysroot.go
@@ -61,6 +61,7 @@
 func init() {
 	android.RegisterModuleType("ndk_headers", ndkHeadersFactory)
 	android.RegisterModuleType("ndk_library", ndkLibraryFactory)
+	android.RegisterModuleType("preprocessed_ndk_headers", preprocessedNdkHeadersFactory)
 	android.RegisterSingletonType("ndk", NdkSingleton)
 
 	pctx.Import("android/soong/common")
@@ -93,9 +94,12 @@
 			installPaths = append(installPaths, m.installPaths...)
 			licensePaths = append(licensePaths, m.licensePath.String())
 		}
-	})
 
-	ctx.VisitAllModules(func(module blueprint.Module) {
+		if m, ok := module.(*preprocessedHeaderModule); ok {
+			installPaths = append(installPaths, m.installPaths...)
+			licensePaths = append(licensePaths, m.licensePath.String())
+		}
+
 		if m, ok := module.(*Module); ok {
 			if installer, ok := m.installer.(*stubDecorator); ok {
 				installPaths = append(installPaths, installer.installPath)
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index abe03b9..08ba145 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -21,7 +21,9 @@
 )
 
 func init() {
-	android.RegisterModuleType("cc_prebuilt_shared_library", prebuiltSharedLibraryFactory)
+	android.RegisterModuleType("cc_prebuilt_library_shared", prebuiltSharedLibraryFactory)
+	android.RegisterModuleType("cc_prebuilt_library_static", prebuiltStaticLibraryFactory)
+	android.RegisterModuleType("cc_prebuilt_binary", prebuiltBinaryFactory)
 }
 
 type prebuiltLinkerInterface interface {
@@ -29,17 +31,21 @@
 	prebuilt() *android.Prebuilt
 }
 
-type prebuiltLibraryLinker struct {
-	*libraryDecorator
+type prebuiltLinker struct {
 	android.Prebuilt
 }
 
-var _ prebuiltLinkerInterface = (*prebuiltLibraryLinker)(nil)
-
-func (p *prebuiltLibraryLinker) prebuilt() *android.Prebuilt {
+func (p *prebuiltLinker) prebuilt() *android.Prebuilt {
 	return &p.Prebuilt
 }
 
+type prebuiltLibraryLinker struct {
+	*libraryDecorator
+	prebuiltLinker
+}
+
+var _ prebuiltLinkerInterface = (*prebuiltLibraryLinker)(nil)
+
 func (p *prebuiltLibraryLinker) linkerProps() []interface{} {
 	props := p.libraryDecorator.linkerProps()
 	return append(props, &p.Prebuilt.Properties)
@@ -61,13 +67,61 @@
 
 func prebuiltSharedLibraryFactory() (blueprint.Module, []interface{}) {
 	module, library := NewLibrary(android.HostAndDeviceSupported)
+	library.BuildOnlyShared()
 	module.compiler = nil
 
 	prebuilt := &prebuiltLibraryLinker{
 		libraryDecorator: library,
 	}
 	module.linker = prebuilt
-	module.installer = prebuilt
+
+	return module.Init()
+}
+
+func prebuiltStaticLibraryFactory() (blueprint.Module, []interface{}) {
+	module, library := NewLibrary(android.HostAndDeviceSupported)
+	library.BuildOnlyStatic()
+	module.compiler = nil
+
+	prebuilt := &prebuiltLibraryLinker{
+		libraryDecorator: library,
+	}
+	module.linker = prebuilt
+
+	return module.Init()
+}
+
+type prebuiltBinaryLinker struct {
+	*binaryDecorator
+	prebuiltLinker
+}
+
+var _ prebuiltLinkerInterface = (*prebuiltBinaryLinker)(nil)
+
+func (p *prebuiltBinaryLinker) linkerProps() []interface{} {
+	props := p.binaryDecorator.linkerProps()
+	return append(props, &p.Prebuilt.Properties)
+}
+
+func (p *prebuiltBinaryLinker) link(ctx ModuleContext,
+	flags Flags, deps PathDeps, objs Objects) android.Path {
+	// TODO(ccross): verify shared library dependencies
+	if len(p.Prebuilt.Properties.Srcs) > 0 {
+		// TODO(ccross): .toc optimization, stripping, packing
+		return p.Prebuilt.Path(ctx)
+	}
+
+	return nil
+}
+
+func prebuiltBinaryFactory() (blueprint.Module, []interface{}) {
+	module, binary := NewBinary(android.HostAndDeviceSupported)
+	module.compiler = nil
+
+	prebuilt := &prebuiltBinaryLinker{
+		binaryDecorator: binary,
+	}
+	module.linker = prebuilt
 
 	return module.Init()
 }
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 79c86aa..18d6c16 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -28,6 +28,13 @@
 	asanCflags  = "-fno-omit-frame-pointer"
 	asanLdflags = "-Wl,-u,__asan_preinit"
 	asanLibs    = "libasan"
+
+	cfiCflags = "-flto -fsanitize-cfi-cross-dso -fvisibility=default " +
+		"-fsanitize-blacklist=external/compiler-rt/lib/cfi/cfi_blacklist.txt"
+	// FIXME: revert the __cfi_check flag when clang is updated to r280031.
+	cfiLdflags = "-flto -fsanitize-cfi-cross-dso -fsanitize=cfi " +
+		"-Wl,-plugin-opt,O1 -Wl,-export-dynamic-symbol=__cfi_check"
+	cfiArflags = "--plugin ${config.ClangBin}/../lib64/LLVMgold.so"
 )
 
 type sanitizerType int
@@ -90,7 +97,7 @@
 
 	SanitizerEnabled bool `blueprint:"mutated"`
 	SanitizeDep      bool `blueprint:"mutated"`
-	InData           bool `blueprint:"mutated"`
+	InSanitizerDir   bool `blueprint:"mutated"`
 }
 
 type sanitize struct {
@@ -167,7 +174,14 @@
 		}
 	}
 
-	if !ctx.AConfig().EnableCFI() {
+	// CFI needs gold linker, and mips toolchain does not have one.
+	if !ctx.AConfig().EnableCFI() || ctx.Arch().ArchType == android.Mips || ctx.Arch().ArchType == android.Mips64 {
+		s.Cfi = nil
+		s.Diag.Cfi = nil
+	}
+
+	// Also disable CFI for arm32 until b/35157333 is fixed.
+	if ctx.Arch().ArchType == android.Arm {
 		s.Cfi = nil
 		s.Diag.Cfi = nil
 	}
@@ -280,11 +294,6 @@
 		flags.CFlags = append(flags.CFlags, asanCflags)
 		flags.LdFlags = append(flags.LdFlags, asanLdflags)
 
-		// ASan runtime library must be the first in the link order.
-		runtimeLibrary := config.AddressSanitizerRuntimeLibrary(ctx.toolchain())
-		if runtimeLibrary != "" {
-			flags.libFlags = append([]string{"${config.ClangAsanLibDir}/" + runtimeLibrary}, flags.libFlags...)
-		}
 		if ctx.Host() {
 			// -nodefaultlibs (provided with libc++) prevents the driver from linking
 			// libraries needed with -fsanitize=address. http://b/18650275 (WAI)
@@ -317,14 +326,14 @@
 			// __cfi_check needs to be built as Thumb (see the code in linker_cfi.cpp). LLVM is not set up
 			// to do this on a function basis, so force Thumb on the entire module.
 			flags.RequiredInstructionSet = "thumb"
+			// Workaround for b/33678192. CFI jumptables need Thumb2 codegen.  Revert when
+			// Clang is updated past r290384.
+			flags.LdFlags = append(flags.LdFlags, "-march=armv7-a")
 		}
 		sanitizers = append(sanitizers, "cfi")
-		cfiFlags := []string{"-flto", "-fsanitize=cfi", "-fsanitize-cfi-cross-dso"}
-		flags.CFlags = append(flags.CFlags, cfiFlags...)
-		flags.CFlags = append(flags.CFlags, "-fvisibility=default")
-		flags.LdFlags = append(flags.LdFlags, cfiFlags...)
-		// FIXME: revert the __cfi_check flag when clang is updated to r280031.
-		flags.LdFlags = append(flags.LdFlags, "-Wl,-plugin-opt,O1", "-Wl,-export-dynamic-symbol=__cfi_check")
+		flags.CFlags = append(flags.CFlags, cfiCflags)
+		flags.LdFlags = append(flags.LdFlags, cfiLdflags)
+		flags.ArFlags = append(flags.ArFlags, cfiArflags)
 		if Bool(sanitize.Properties.Sanitize.Diag.Cfi) {
 			diagSanitizers = append(diagSanitizers, "cfi")
 		}
@@ -374,8 +383,8 @@
 	return flags
 }
 
-func (sanitize *sanitize) inData() bool {
-	return sanitize.Properties.InData
+func (sanitize *sanitize) inSanitizerDir() bool {
+	return sanitize.Properties.InSanitizerDir
 }
 
 func (sanitize *sanitize) Sanitizer(t sanitizerType) bool {
@@ -438,7 +447,7 @@
 				modules[0].(*Module).sanitize.Properties.SanitizeDep = false
 				modules[1].(*Module).sanitize.Properties.SanitizeDep = false
 				if mctx.Device() {
-					modules[1].(*Module).sanitize.Properties.InData = true
+					modules[1].(*Module).sanitize.Properties.InSanitizerDir = true
 				} else {
 					modules[0].(*Module).Properties.PreventInstall = true
 				}
diff --git a/cc/stl.go b/cc/stl.go
index 43776f7..9e67145 100644
--- a/cc/stl.go
+++ b/cc/stl.go
@@ -60,7 +60,7 @@
 				ctx.ModuleErrorf("stl: %q is not a supported STL with sdk_version set", s)
 				return ""
 			}
-		} else if ctx.Os() == android.Windows {
+		} else if ctx.Windows() {
 			switch s {
 			case "libc++", "libc++_static", "libstdc++", "":
 				// libc++ is not supported on mingw
diff --git a/cc/test.go b/cc/test.go
index f60996c..145b5b0 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -19,9 +19,9 @@
 	"runtime"
 	"strings"
 
-	"github.com/google/blueprint"
-
 	"android/soong/android"
+
+	"github.com/google/blueprint"
 )
 
 type TestProperties struct {
@@ -38,6 +38,14 @@
 	// relative_install_path. Useful if several tests need to be in the same
 	// directory, but test_per_src doesn't work.
 	No_named_install_directory *bool
+
+	// list of files or filegroup modules that provide data that should be installed alongside
+	// the test
+	Data []string
+
+	// list of compatibility suites (for example "cts", "vts") that the module should be
+	// installed into.
+	Test_suites []string
 }
 
 func init() {
@@ -191,6 +199,7 @@
 	*binaryDecorator
 	*baseCompiler
 	Properties TestBinaryProperties
+	data       android.Paths
 }
 
 func (test *testBinary) linkerProps() []interface{} {
@@ -205,6 +214,8 @@
 }
 
 func (test *testBinary) linkerDeps(ctx DepsContext, deps Deps) Deps {
+	android.ExtractSourcesDeps(ctx, test.Properties.Data)
+
 	deps = test.testDecorator.linkerDeps(ctx, deps)
 	deps = test.binaryDecorator.linkerDeps(ctx, deps)
 	return deps
@@ -217,6 +228,8 @@
 }
 
 func (test *testBinary) install(ctx ModuleContext, file android.Path) {
+	test.data = ctx.ExpandSources(test.Properties.Data, nil)
+
 	test.binaryDecorator.baseInstaller.dir = "nativetest"
 	test.binaryDecorator.baseInstaller.dir64 = "nativetest64"
 
diff --git a/cc/test_data_test.go b/cc/test_data_test.go
new file mode 100644
index 0000000..9b5a85a
--- /dev/null
+++ b/cc/test_data_test.go
@@ -0,0 +1,208 @@
+// Copyright 2017 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package cc
+
+import (
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"testing"
+
+	"android/soong/android"
+	"android/soong/genrule"
+
+	"github.com/google/blueprint"
+)
+
+type dataFile struct {
+	path string
+	file string
+}
+
+var testDataTests = []struct {
+	name    string
+	modules string
+	data    []dataFile
+}{
+	{
+		name: "data files",
+		modules: `
+			test {
+				name: "foo",
+				data: [
+					"baz",
+					"bar/baz",
+				],
+			}`,
+		data: []dataFile{
+			{"dir", "baz"},
+			{"dir", "bar/baz"},
+		},
+	},
+	{
+		name: "filegroup",
+		modules: `
+			filegroup {
+				name: "fg",
+				srcs: [
+					"baz",
+					"bar/baz",
+				],
+			}
+
+			test {
+				name: "foo",
+				data: [":fg"],
+			}`,
+		data: []dataFile{
+			{"dir", "baz"},
+			{"dir", "bar/baz"},
+		},
+	},
+	{
+		name: "relative filegroup",
+		modules: `
+			filegroup {
+				name: "fg",
+				srcs: [
+					"bar/baz",
+				],
+				path: "bar",
+			}
+
+			test {
+				name: "foo",
+				data: [":fg"],
+			}`,
+		data: []dataFile{
+			{"dir/bar", "baz"},
+		},
+	},
+	{
+		name: "relative filegroup trailing slash",
+		modules: `
+			filegroup {
+				name: "fg",
+				srcs: [
+					"bar/baz",
+				],
+				path: "bar/",
+			}
+
+			test {
+				name: "foo",
+				data: [":fg"],
+			}`,
+		data: []dataFile{
+			{"dir/bar", "baz"},
+		},
+	},
+}
+
+func TestDataTests(t *testing.T) {
+	buildDir, err := ioutil.TempDir("", "soong_test_test")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer os.RemoveAll(buildDir)
+
+	config := android.TestConfig(buildDir)
+
+	for _, test := range testDataTests {
+		t.Run(test.name, func(t *testing.T) {
+			ctx := blueprint.NewContext()
+			android.RegisterTestMutators(ctx)
+			ctx.MockFileSystem(map[string][]byte{
+				"Blueprints":     []byte(`subdirs = ["dir"]`),
+				"dir/Blueprints": []byte(test.modules),
+				"dir/baz":        nil,
+				"dir/bar/baz":    nil,
+			})
+			ctx.RegisterModuleType("filegroup", genrule.FileGroupFactory)
+			ctx.RegisterModuleType("test", newTest)
+
+			_, errs := ctx.ParseBlueprintsFiles("Blueprints")
+			fail(t, errs)
+			_, errs = ctx.PrepareBuildActions(config)
+			fail(t, errs)
+
+			foo := findModule(ctx, "foo")
+			if foo == nil {
+				t.Fatalf("failed to find module foo")
+			}
+
+			got := foo.(*testDataTest).data
+			if len(got) != len(test.data) {
+				t.Errorf("expected %d data files, got %d",
+					len(test.data), len(got))
+			}
+
+			for i := range got {
+				if i >= len(test.data) {
+					break
+				}
+
+				path := filepath.Join(test.data[i].path, test.data[i].file)
+				if test.data[i].file != got[i].Rel() ||
+					path != got[i].String() {
+					fmt.Errorf("expected %s:%s got %s:%s",
+						path, test.data[i].file,
+						got[i].String(), got[i].Rel())
+				}
+			}
+		})
+	}
+}
+
+type testDataTest struct {
+	android.ModuleBase
+	data       android.Paths
+	Properties struct {
+		Data []string
+	}
+}
+
+func newTest() (blueprint.Module, []interface{}) {
+	m := &testDataTest{}
+	return android.InitAndroidModule(m, &m.Properties)
+}
+
+func (test *testDataTest) DepsMutator(ctx android.BottomUpMutatorContext) {
+	android.ExtractSourcesDeps(ctx, test.Properties.Data)
+}
+
+func (test *testDataTest) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	test.data = ctx.ExpandSources(test.Properties.Data, nil)
+}
+
+func findModule(ctx *blueprint.Context, name string) blueprint.Module {
+	var ret blueprint.Module
+	ctx.VisitAllModules(func(m blueprint.Module) {
+		if ctx.ModuleName(m) == name {
+			ret = m
+		}
+	})
+	return ret
+}
+
+func fail(t *testing.T, errs []error) {
+	if len(errs) > 0 {
+		for _, err := range errs {
+			t.Error(err)
+		}
+		t.FailNow()
+	}
+}
diff --git a/cc/test_gen_stub_libs.py b/cc/test_gen_stub_libs.py
index 8683d31..8611ef3 100755
--- a/cc/test_gen_stub_libs.py
+++ b/cc/test_gen_stub_libs.py
@@ -107,27 +107,39 @@
 
 class OmitVersionTest(unittest.TestCase):
     def test_omit_private(self):
-        self.assertFalse(gsl.should_omit_version('foo', [], 'arm', 9))
-
-        self.assertTrue(gsl.should_omit_version('foo_PRIVATE', [], 'arm', 9))
-        self.assertTrue(gsl.should_omit_version('foo_PLATFORM', [], 'arm', 9))
+        self.assertFalse(gsl.should_omit_version('foo', [], 'arm', 9, False))
 
         self.assertTrue(gsl.should_omit_version(
-            'foo', ['platform-only'], 'arm', 9))
+            'foo_PRIVATE', [], 'arm', 9, False))
+        self.assertTrue(gsl.should_omit_version(
+            'foo_PLATFORM', [], 'arm', 9, False))
+
+        self.assertTrue(gsl.should_omit_version(
+            'foo', ['platform-only'], 'arm', 9, False))
+
+    def test_omit_vndk(self):
+        self.assertTrue(gsl.should_omit_version(
+            'foo', ['vndk'], 'arm', 9, False))
+
+        self.assertFalse(gsl.should_omit_version('foo', [], 'arm', 9, True))
+        self.assertFalse(gsl.should_omit_version(
+            'foo', ['vndk'], 'arm', 9, True))
 
     def test_omit_arch(self):
-        self.assertFalse(gsl.should_omit_version('foo', [], 'arm', 9))
-        self.assertFalse(gsl.should_omit_version('foo', ['arm'], 'arm', 9))
+        self.assertFalse(gsl.should_omit_version('foo', [], 'arm', 9, False))
+        self.assertFalse(gsl.should_omit_version(
+            'foo', ['arm'], 'arm', 9, False))
 
-        self.assertTrue(gsl.should_omit_version('foo', ['x86'], 'arm', 9))
+        self.assertTrue(gsl.should_omit_version(
+            'foo', ['x86'], 'arm', 9, False))
 
     def test_omit_api(self):
-        self.assertFalse(gsl.should_omit_version('foo', [], 'arm', 9))
+        self.assertFalse(gsl.should_omit_version('foo', [], 'arm', 9, False))
         self.assertFalse(
-            gsl.should_omit_version('foo', ['introduced=9'], 'arm', 9))
+            gsl.should_omit_version('foo', ['introduced=9'], 'arm', 9, False))
 
         self.assertTrue(
-            gsl.should_omit_version('foo', ['introduced=14'], 'arm', 9))
+            gsl.should_omit_version('foo', ['introduced=14'], 'arm', 9, False))
 
 
 class SymbolFileParseTest(unittest.TestCase):
@@ -302,7 +314,7 @@
         # OmitVersionTest, PrivateVersionTest, and SymbolPresenceTest.
         src_file = cStringIO.StringIO()
         version_file = cStringIO.StringIO()
-        generator = gsl.Generator(src_file, version_file, 'arm', 9)
+        generator = gsl.Generator(src_file, version_file, 'arm', 9, False)
 
         version = gsl.Version('VERSION_PRIVATE', None, [], [
             gsl.Symbol('foo', []),
@@ -330,7 +342,7 @@
         # SymbolPresenceTest.
         src_file = cStringIO.StringIO()
         version_file = cStringIO.StringIO()
-        generator = gsl.Generator(src_file, version_file, 'arm', 9)
+        generator = gsl.Generator(src_file, version_file, 'arm', 9, False)
 
         version = gsl.Version('VERSION_1', None, [], [
             gsl.Symbol('foo', ['x86']),
@@ -346,10 +358,17 @@
         self.assertEqual('', src_file.getvalue())
         self.assertEqual('', version_file.getvalue())
 
+        version = gsl.Version('VERSION_1', None, [], [
+            gsl.Symbol('foo', ['vndk']),
+        ])
+        generator.write_version(version)
+        self.assertEqual('', src_file.getvalue())
+        self.assertEqual('', version_file.getvalue())
+
     def test_write(self):
         src_file = cStringIO.StringIO()
         version_file = cStringIO.StringIO()
-        generator = gsl.Generator(src_file, version_file, 'arm', 9)
+        generator = gsl.Generator(src_file, version_file, 'arm', 9, False)
 
         versions = [
             gsl.Version('VERSION_1', None, [], [
@@ -410,6 +429,7 @@
 
             VERSION_4 { # versioned=9
                 wibble;
+                wizzes; # vndk
             } VERSION_2;
 
             VERSION_5 { # versioned=14
@@ -421,7 +441,7 @@
 
         src_file = cStringIO.StringIO()
         version_file = cStringIO.StringIO()
-        generator = gsl.Generator(src_file, version_file, 'arm', 9)
+        generator = gsl.Generator(src_file, version_file, 'arm', 9, False)
         generator.write(versions)
 
         expected_src = textwrap.dedent("""\
diff --git a/cc/util.go b/cc/util.go
index 466266c..36d8dd2 100644
--- a/cc/util.go
+++ b/cc/util.go
@@ -88,6 +88,7 @@
 func flagsToBuilderFlags(in Flags) builderFlags {
 	return builderFlags{
 		globalFlags: strings.Join(in.GlobalFlags, " "),
+		arFlags:     strings.Join(in.ArFlags, " "),
 		asFlags:     strings.Join(in.AsFlags, " "),
 		cFlags:      strings.Join(in.CFlags, " "),
 		conlyFlags:  strings.Join(in.ConlyFlags, " "),
@@ -101,8 +102,11 @@
 		yasmFlags:   strings.Join(in.YasmFlags, " "),
 		toolchain:   in.Toolchain,
 		clang:       in.Clang,
+		coverage:    in.Coverage,
 		tidy:        in.Tidy,
 
+		systemIncludeFlags: strings.Join(in.SystemIncludeFlags, " "),
+
 		groupStaticLibs: in.GroupStaticLibs,
 	}
 }
diff --git a/cmd/fileslist/Android.bp b/cmd/fileslist/Android.bp
new file mode 100644
index 0000000..cbf939a
--- /dev/null
+++ b/cmd/fileslist/Android.bp
@@ -0,0 +1,20 @@
+// Copyright 2017 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+blueprint_go_binary {
+    name: "fileslist",
+    srcs: [
+        "fileslist.go",
+    ],
+}
diff --git a/cmd/fileslist/fileslist.go b/cmd/fileslist/fileslist.go
new file mode 100755
index 0000000..56ea66d
--- /dev/null
+++ b/cmd/fileslist/fileslist.go
@@ -0,0 +1,173 @@
+// Copyright 2017 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// fileslist.py replacement written in GO, which utilizes multi-cores.
+
+package main
+
+import (
+	"crypto/sha256"
+	"encoding/json"
+	"flag"
+	"fmt"
+	"io"
+	"os"
+	"path/filepath"
+	"runtime"
+	"sort"
+	"strings"
+	"sync"
+)
+
+const (
+	MAX_DEFAULT_PARA = 24
+)
+
+func defaultPara() int {
+	ret := runtime.NumCPU()
+	if ret > MAX_DEFAULT_PARA {
+		return MAX_DEFAULT_PARA
+	}
+	return ret
+}
+
+var (
+	para = flag.Int("para", defaultPara(), "Number of goroutines")
+)
+
+// Represents each file.
+type Node struct {
+	SHA256 string
+	Name   string // device side path.
+	Size   int64
+	path   string // host side path.
+	stat   os.FileInfo
+}
+
+func newNode(hostPath string, devicePath string, stat os.FileInfo) Node {
+	return Node{Name: devicePath, path: hostPath, stat: stat}
+}
+
+// Scan a Node and returns true if it should be added to the result.
+func (n *Node) scan() bool {
+	n.Size = n.stat.Size()
+
+	// Calculate SHA256.
+	h := sha256.New()
+	if n.stat.Mode()&os.ModeSymlink == 0 {
+		f, err := os.Open(n.path)
+		if err != nil {
+			panic(err)
+		}
+		defer f.Close()
+
+		if _, err := io.Copy(h, f); err != nil {
+			panic(err)
+		}
+	} else {
+		// Hash the content of symlink, not the file it points to.
+		s, err := os.Readlink(n.path)
+		if err != nil {
+			panic(err)
+		}
+		if _, err := io.WriteString(h, s); err != nil {
+			panic(err)
+		}
+	}
+	n.SHA256 = fmt.Sprintf("%x", h.Sum(nil))
+	return true
+}
+
+func main() {
+	flag.Parse()
+
+	allOutput := make([]Node, 0, 1024) // Store all outputs.
+	mutex := &sync.Mutex{}             // Guard allOutput
+
+	ch := make(chan Node) // Pass nodes to goroutines.
+
+	var wg sync.WaitGroup // To wait for all goroutines.
+	wg.Add(*para)
+
+	// Scan files in multiple goroutines.
+	for i := 0; i < *para; i++ {
+		go func() {
+			defer wg.Done()
+
+			output := make([]Node, 0, 1024) // Local output list.
+			for node := range ch {
+				if node.scan() {
+					output = append(output, node)
+				}
+			}
+			// Add to the global output list.
+			mutex.Lock()
+			allOutput = append(allOutput, output...)
+			mutex.Unlock()
+		}()
+	}
+
+	// Walk the directories and find files to scan.
+	for _, dir := range flag.Args() {
+		absDir, err := filepath.Abs(dir)
+		if err != nil {
+			panic(err)
+		}
+		deviceRoot := filepath.Clean(absDir + "/..")
+		err = filepath.Walk(dir, func(path string, stat os.FileInfo, err error) error {
+			if err != nil {
+				panic(err)
+			}
+			if stat.IsDir() {
+				return nil
+			}
+			absPath, err := filepath.Abs(path)
+			if err != nil {
+				panic(err)
+			}
+			devicePath, err := filepath.Rel(deviceRoot, absPath)
+			if err != nil {
+				panic(err)
+			}
+			devicePath = "/" + devicePath
+			ch <- newNode(absPath, devicePath, stat)
+			return nil
+		})
+		if err != nil {
+			panic(err)
+		}
+	}
+
+	// Wait until all the goroutines finish.
+	close(ch)
+	wg.Wait()
+
+	// Sort the entries and dump as json.
+	sort.Slice(allOutput, func(i, j int) bool {
+		if allOutput[i].Size > allOutput[j].Size {
+			return true
+		}
+		if allOutput[i].Size == allOutput[j].Size && strings.Compare(allOutput[i].Name, allOutput[j].Name) > 0 {
+			return true
+		}
+		return false
+	})
+
+	j, err := json.MarshalIndent(allOutput, "", "  ")
+	if err != nil {
+		panic(nil)
+	}
+
+	fmt.Printf("%s\n", j)
+}
diff --git a/cmd/javac_filter/Android.bp b/cmd/javac_filter/Android.bp
new file mode 100644
index 0000000..cbdabb9
--- /dev/null
+++ b/cmd/javac_filter/Android.bp
@@ -0,0 +1,23 @@
+// Copyright 2017 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+blueprint_go_binary {
+    name: "soong_javac_filter",
+    srcs: [
+        "javac_filter.go",
+    ],
+    testSrcs: [
+        "javac_filter_test.go",
+    ],
+}
diff --git a/cmd/javac_filter/javac_filter.go b/cmd/javac_filter/javac_filter.go
new file mode 100644
index 0000000..a089acd
--- /dev/null
+++ b/cmd/javac_filter/javac_filter.go
@@ -0,0 +1,109 @@
+// Copyright 2017 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// soong_javac_filter expects the output of javac on stdin, and produces
+// an ANSI colorized version of the output on stdout.
+//
+// It also hides the unhelpful and unhideable "warning there is a warning"
+// messages.
+package main
+
+import (
+	"bufio"
+	"fmt"
+	"io"
+	"os"
+	"regexp"
+)
+
+// Regular expressions are based on
+// https://chromium.googlesource.com/chromium/src/+/master/build/android/gyp/javac.py
+// Colors are based on clang's output
+var (
+	filelinePrefix = `^[-.\w/\\]+.java:[0-9]+:`
+	warningRe      = regexp.MustCompile(filelinePrefix + ` (warning:) .*$`)
+	errorRe        = regexp.MustCompile(filelinePrefix + ` (.*?:) .*$`)
+	markerRe       = regexp.MustCompile(`\s*(\^)\s*$`)
+
+	escape  = "\x1b"
+	reset   = escape + "[0m"
+	bold    = escape + "[1m"
+	red     = escape + "[31m"
+	green   = escape + "[32m"
+	magenta = escape + "[35m"
+)
+
+func main() {
+	err := process(bufio.NewReader(os.Stdin), os.Stdout)
+	if err != nil {
+		fmt.Fprintln(os.Stderr, "reading standard input:", err)
+		os.Exit(-1)
+	}
+}
+
+func process(r io.Reader, w io.Writer) error {
+	scanner := bufio.NewScanner(r)
+	// Some javac wrappers output the entire list of java files being
+	// compiled on a single line, which can be very large, set the maximum
+	// buffer size to 2MB.
+	scanner.Buffer(nil, 2*1024*1024)
+	for scanner.Scan() {
+		processLine(w, scanner.Text())
+	}
+	return scanner.Err()
+}
+
+func processLine(w io.Writer, line string) {
+	for _, f := range filters {
+		if f.MatchString(line) {
+			return
+		}
+	}
+	for _, p := range colorPatterns {
+		var matched bool
+		if line, matched = applyColor(line, p.color, p.re); matched {
+			break
+		}
+	}
+	fmt.Fprintln(w, line)
+}
+
+// If line matches re, make it bold and apply color to the first submatch
+// Returns line, modified if it matched, and true if it matched.
+func applyColor(line, color string, re *regexp.Regexp) (string, bool) {
+	if m := re.FindStringSubmatchIndex(line); m != nil {
+		tagStart, tagEnd := m[2], m[3]
+		line = bold + line[:tagStart] +
+			color + line[tagStart:tagEnd] + reset + bold +
+			line[tagEnd:] + reset
+		return line, true
+	}
+	return line, false
+}
+
+var colorPatterns = []struct {
+	re    *regexp.Regexp
+	color string
+}{
+	{warningRe, magenta},
+	{errorRe, red},
+	{markerRe, green},
+}
+
+var filters = []*regexp.Regexp{
+	regexp.MustCompile(`Note: (Some input files|.*\.java) uses? or overrides? a deprecated API.`),
+	regexp.MustCompile(`Note: Recompile with -Xlint:deprecation for details.`),
+	regexp.MustCompile(`Note: (Some input files|.*\.java) uses? unchecked or unsafe operations.`),
+	regexp.MustCompile(`Note: Recompile with -Xlint:unchecked for details.`),
+}
diff --git a/cmd/javac_filter/javac_filter_test.go b/cmd/javac_filter/javac_filter_test.go
new file mode 100644
index 0000000..43381ce
--- /dev/null
+++ b/cmd/javac_filter/javac_filter_test.go
@@ -0,0 +1,74 @@
+// Copyright 2017 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+	"bytes"
+	"testing"
+)
+
+var testCases = []struct {
+	in, out string
+}{
+	{
+		in:  "File.java:40: error: cannot find symbol\n",
+		out: "\x1b[1mFile.java:40: \x1b[31merror:\x1b[0m\x1b[1m cannot find symbol\x1b[0m\n",
+	},
+	{
+		in:  "import static com.blah.SYMBOL;\n",
+		out: "import static com.blah.SYMBOL;\n",
+	},
+	{
+		in:  "          ^           \n",
+		out: "\x1b[1m          \x1b[32m^\x1b[0m\x1b[1m           \x1b[0m\n",
+	},
+	{
+		in:  "File.java:398: warning: [RectIntersectReturnValueIgnored] Return value of com.blah.function() must be checked\n",
+		out: "\x1b[1mFile.java:398: \x1b[35mwarning:\x1b[0m\x1b[1m [RectIntersectReturnValueIgnored] Return value of com.blah.function() must be checked\x1b[0m\n",
+	},
+	{
+		in:  "    (see http://go/errorprone/bugpattern/RectIntersectReturnValueIgnored.md)\n",
+		out: "    (see http://go/errorprone/bugpattern/RectIntersectReturnValueIgnored.md)\n",
+	},
+	{
+		in: `
+Note: Some input files use or override a deprecated API.
+Note: Recompile with -Xlint:deprecation for details.
+Note: Some input files use unchecked or unsafe operations.
+Note: Recompile with -Xlint:unchecked for details.
+Note: dir/file.java uses or overrides a deprecated API.
+Note: dir/file.java uses unchecked or unsafe operations.
+`,
+		out: "\n",
+	},
+	{
+		in:  "\n",
+		out: "\n",
+	},
+}
+
+func TestJavacColorize(t *testing.T) {
+	for _, test := range testCases {
+		buf := new(bytes.Buffer)
+		err := process(bytes.NewReader([]byte(test.in)), buf)
+		if err != nil {
+			t.Errorf("error: %q", err)
+		}
+		got := string(buf.Bytes())
+		if got != test.out {
+			t.Errorf("expected %q got %q", test.out, got)
+		}
+	}
+}
diff --git a/cmd/microfactory/Android.bp b/cmd/microfactory/Android.bp
new file mode 100644
index 0000000..a457f43
--- /dev/null
+++ b/cmd/microfactory/Android.bp
@@ -0,0 +1,23 @@
+// Copyright 2017 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+blueprint_go_binary {
+    name: "microfactory",
+    srcs: [
+        "microfactory.go",
+    ],
+    testSrcs: [
+        "microfactory_test.go",
+    ],
+}
diff --git a/cmd/microfactory/microfactory.go b/cmd/microfactory/microfactory.go
new file mode 100644
index 0000000..6c062e2
--- /dev/null
+++ b/cmd/microfactory/microfactory.go
@@ -0,0 +1,572 @@
+// Copyright 2017 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Microfactory is a tool to incrementally compile a go program. It's similar
+// to `go install`, but doesn't require a GOPATH. A package->path mapping can
+// be specified as command line options:
+//
+//   -pkg-path android/soong=build/soong
+//   -pkg-path github.com/google/blueprint=build/blueprint
+//
+// The paths can be relative to the current working directory, or an absolute
+// path. Both packages and paths are compared with full directory names, so the
+// android/soong-test package wouldn't be mapped in the above case.
+//
+// Microfactory will ignore *_test.go files, and limits *_darwin.go and
+// *_linux.go files to MacOS and Linux respectively. It does not support build
+// tags or any other suffixes.
+//
+// Builds are incremental by package. All input files are hashed, and if the
+// hash of an input or dependency changes, the package is rebuilt.
+//
+// It also exposes the -trimpath option from go's compiler so that embedded
+// path names (such as in log.Llongfile) are relative paths instead of absolute
+// paths.
+//
+// If you don't have a previously built version of Microfactory, when used with
+// -s <microfactory_src_dir> -b <microfactory_bin_file>, Microfactory can
+// rebuild itself as necessary. Combined with a shell script like soong_ui.bash
+// that uses `go run` to run Microfactory for the first time, go programs can be
+// quickly bootstrapped entirely from source (and a standard go distribution).
+package main
+
+import (
+	"bytes"
+	"crypto/sha1"
+	"flag"
+	"fmt"
+	"go/ast"
+	"go/parser"
+	"go/token"
+	"io"
+	"io/ioutil"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"runtime"
+	"sort"
+	"strconv"
+	"strings"
+	"sync"
+	"syscall"
+)
+
+var (
+	race    = false
+	verbose = false
+
+	goToolDir = filepath.Join(runtime.GOROOT(), "pkg", "tool", runtime.GOOS+"_"+runtime.GOARCH)
+	goVersion = findGoVersion()
+)
+
+func findGoVersion() string {
+	if version, err := ioutil.ReadFile(filepath.Join(runtime.GOROOT(), "VERSION")); err == nil {
+		return string(version)
+	}
+
+	cmd := exec.Command(filepath.Join(runtime.GOROOT(), "bin", "go"), "version")
+	if version, err := cmd.Output(); err == nil {
+		return string(version)
+	} else {
+		panic(fmt.Sprintf("Unable to discover go version: %v", err))
+	}
+}
+
+type GoPackage struct {
+	Name string
+
+	// Inputs
+	directDeps []*GoPackage // specified directly by the module
+	allDeps    []*GoPackage // direct dependencies and transitive dependencies
+	files      []string
+
+	// Outputs
+	pkgDir     string
+	output     string
+	hashResult []byte
+
+	// Status
+	mutex    sync.Mutex
+	compiled bool
+	failed   error
+	rebuilt  bool
+}
+
+// LinkedHashMap<string, GoPackage>
+type linkedDepSet struct {
+	packageSet  map[string](*GoPackage)
+	packageList []*GoPackage
+}
+
+func newDepSet() *linkedDepSet {
+	return &linkedDepSet{packageSet: make(map[string]*GoPackage)}
+}
+func (s *linkedDepSet) tryGetByName(name string) (*GoPackage, bool) {
+	pkg, contained := s.packageSet[name]
+	return pkg, contained
+}
+func (s *linkedDepSet) getByName(name string) *GoPackage {
+	pkg, _ := s.tryGetByName(name)
+	return pkg
+}
+func (s *linkedDepSet) add(name string, goPackage *GoPackage) {
+	s.packageSet[name] = goPackage
+	s.packageList = append(s.packageList, goPackage)
+}
+func (s *linkedDepSet) ignore(name string) {
+	s.packageSet[name] = nil
+}
+
+// FindDeps searches all applicable go files in `path`, parses all of them
+// for import dependencies that exist in pkgMap, then recursively does the
+// same for all of those dependencies.
+func (p *GoPackage) FindDeps(path string, pkgMap *pkgPathMapping) error {
+	depSet := newDepSet()
+	err := p.findDeps(path, pkgMap, depSet)
+	if err != nil {
+		return err
+	}
+	p.allDeps = depSet.packageList
+	return nil
+}
+
+// findDeps is the recursive version of FindDeps. allPackages is the map of
+// all locally defined packages so that the same dependency of two different
+// packages is only resolved once.
+func (p *GoPackage) findDeps(path string, pkgMap *pkgPathMapping, allPackages *linkedDepSet) error {
+	// If this ever becomes too slow, we can look at reading the files once instead of twice
+	// But that just complicates things today, and we're already really fast.
+	foundPkgs, err := parser.ParseDir(token.NewFileSet(), path, func(fi os.FileInfo) bool {
+		name := fi.Name()
+		if fi.IsDir() || strings.HasSuffix(name, "_test.go") || name[0] == '.' || name[0] == '_' {
+			return false
+		}
+		if runtime.GOOS != "darwin" && strings.HasSuffix(name, "_darwin.go") {
+			return false
+		}
+		if runtime.GOOS != "linux" && strings.HasSuffix(name, "_linux.go") {
+			return false
+		}
+		return true
+	}, parser.ImportsOnly)
+	if err != nil {
+		return fmt.Errorf("Error parsing directory %q: %v", path, err)
+	}
+
+	var foundPkg *ast.Package
+	// foundPkgs is a map[string]*ast.Package, but we only want one package
+	if len(foundPkgs) != 1 {
+		return fmt.Errorf("Expected one package in %q, got %d", path, len(foundPkgs))
+	}
+	// Extract the first (and only) entry from the map.
+	for _, pkg := range foundPkgs {
+		foundPkg = pkg
+	}
+
+	var deps []string
+	localDeps := make(map[string]bool)
+
+	for filename, astFile := range foundPkg.Files {
+		p.files = append(p.files, filename)
+
+		for _, importSpec := range astFile.Imports {
+			name, err := strconv.Unquote(importSpec.Path.Value)
+			if err != nil {
+				return fmt.Errorf("%s: invalid quoted string: <%s> %v", filename, importSpec.Path.Value, err)
+			}
+
+			if pkg, ok := allPackages.tryGetByName(name); ok {
+				if pkg != nil {
+					if _, ok := localDeps[name]; !ok {
+						deps = append(deps, name)
+						localDeps[name] = true
+					}
+				}
+				continue
+			}
+
+			var pkgPath string
+			if path, ok, err := pkgMap.Path(name); err != nil {
+				return err
+			} else if !ok {
+				// Probably in the stdlib, but if not, then the compiler will fail with a reasonable error message
+				// Mark it as such so that we don't try to decode its path again.
+				allPackages.ignore(name)
+				continue
+			} else {
+				pkgPath = path
+			}
+
+			pkg := &GoPackage{
+				Name: name,
+			}
+			deps = append(deps, name)
+			allPackages.add(name, pkg)
+			localDeps[name] = true
+
+			if err := pkg.findDeps(pkgPath, pkgMap, allPackages); err != nil {
+				return err
+			}
+		}
+	}
+
+	sort.Strings(p.files)
+
+	if verbose {
+		fmt.Fprintf(os.Stderr, "Package %q depends on %v\n", p.Name, deps)
+	}
+
+	for _, dep := range deps {
+		p.directDeps = append(p.directDeps, allPackages.getByName(dep))
+	}
+
+	return nil
+}
+
+func (p *GoPackage) Compile(outDir, trimPath string) error {
+	p.mutex.Lock()
+	defer p.mutex.Unlock()
+	if p.compiled {
+		return p.failed
+	}
+	p.compiled = true
+
+	// Build all dependencies in parallel, then fail if any of them failed.
+	var wg sync.WaitGroup
+	for _, dep := range p.directDeps {
+		wg.Add(1)
+		go func(dep *GoPackage) {
+			defer wg.Done()
+			dep.Compile(outDir, trimPath)
+		}(dep)
+	}
+	wg.Wait()
+	for _, dep := range p.directDeps {
+		if dep.failed != nil {
+			p.failed = dep.failed
+			return p.failed
+		}
+	}
+
+	p.pkgDir = filepath.Join(outDir, p.Name)
+	p.output = filepath.Join(p.pkgDir, p.Name) + ".a"
+	shaFile := p.output + ".hash"
+
+	hash := sha1.New()
+	fmt.Fprintln(hash, runtime.GOOS, runtime.GOARCH, goVersion)
+
+	cmd := exec.Command(filepath.Join(goToolDir, "compile"),
+		"-o", p.output,
+		"-p", p.Name,
+		"-complete", "-pack", "-nolocalimports")
+	if race {
+		cmd.Args = append(cmd.Args, "-race")
+		fmt.Fprintln(hash, "-race")
+	}
+	if trimPath != "" {
+		cmd.Args = append(cmd.Args, "-trimpath", trimPath)
+		fmt.Fprintln(hash, trimPath)
+	}
+	for _, dep := range p.directDeps {
+		cmd.Args = append(cmd.Args, "-I", dep.pkgDir)
+		hash.Write(dep.hashResult)
+	}
+	for _, filename := range p.files {
+		cmd.Args = append(cmd.Args, filename)
+		fmt.Fprintln(hash, filename)
+
+		// Hash the contents of the input files
+		f, err := os.Open(filename)
+		if err != nil {
+			f.Close()
+			err = fmt.Errorf("%s: %v", filename, err)
+			p.failed = err
+			return err
+		}
+		_, err = io.Copy(hash, f)
+		if err != nil {
+			f.Close()
+			err = fmt.Errorf("%s: %v", filename, err)
+			p.failed = err
+			return err
+		}
+		f.Close()
+	}
+	p.hashResult = hash.Sum(nil)
+
+	var rebuild bool
+	if _, err := os.Stat(p.output); err != nil {
+		rebuild = true
+	}
+	if !rebuild {
+		if oldSha, err := ioutil.ReadFile(shaFile); err == nil {
+			rebuild = !bytes.Equal(oldSha, p.hashResult)
+		} else {
+			rebuild = true
+		}
+	}
+
+	if !rebuild {
+		return nil
+	}
+
+	err := os.RemoveAll(p.pkgDir)
+	if err != nil {
+		err = fmt.Errorf("%s: %v", p.Name, err)
+		p.failed = err
+		return err
+	}
+
+	err = os.MkdirAll(filepath.Dir(p.output), 0777)
+	if err != nil {
+		err = fmt.Errorf("%s: %v", p.Name, err)
+		p.failed = err
+		return err
+	}
+
+	cmd.Stdin = nil
+	cmd.Stdout = os.Stdout
+	cmd.Stderr = os.Stderr
+	if verbose {
+		fmt.Fprintln(os.Stderr, cmd.Args)
+	}
+	err = cmd.Run()
+	if err != nil {
+		err = fmt.Errorf("%s: %v", p.Name, err)
+		p.failed = err
+		return err
+	}
+
+	err = ioutil.WriteFile(shaFile, p.hashResult, 0666)
+	if err != nil {
+		err = fmt.Errorf("%s: %v", p.Name, err)
+		p.failed = err
+		return err
+	}
+
+	p.rebuilt = true
+
+	return nil
+}
+
+func (p *GoPackage) Link(out string) error {
+	if p.Name != "main" {
+		return fmt.Errorf("Can only link main package")
+	}
+
+	shaFile := filepath.Join(filepath.Dir(out), "."+filepath.Base(out)+"_hash")
+
+	if !p.rebuilt {
+		if _, err := os.Stat(out); err != nil {
+			p.rebuilt = true
+		} else if oldSha, err := ioutil.ReadFile(shaFile); err != nil {
+			p.rebuilt = true
+		} else {
+			p.rebuilt = !bytes.Equal(oldSha, p.hashResult)
+		}
+	}
+	if !p.rebuilt {
+		return nil
+	}
+
+	err := os.Remove(shaFile)
+	if err != nil && !os.IsNotExist(err) {
+		return err
+	}
+	err = os.Remove(out)
+	if err != nil && !os.IsNotExist(err) {
+		return err
+	}
+
+	cmd := exec.Command(filepath.Join(goToolDir, "link"), "-o", out)
+	if race {
+		cmd.Args = append(cmd.Args, "-race")
+	}
+	for _, dep := range p.allDeps {
+		cmd.Args = append(cmd.Args, "-L", dep.pkgDir)
+	}
+	cmd.Args = append(cmd.Args, p.output)
+	cmd.Stdin = nil
+	cmd.Stdout = os.Stdout
+	cmd.Stderr = os.Stderr
+	if verbose {
+		fmt.Fprintln(os.Stderr, cmd.Args)
+	}
+	err = cmd.Run()
+	if err != nil {
+		return fmt.Errorf("command %s failed with error %v", cmd.Args, err)
+	}
+
+	return ioutil.WriteFile(shaFile, p.hashResult, 0666)
+}
+
+// rebuildMicrofactory checks to see if microfactory itself needs to be rebuilt,
+// and if does, it will launch a new copy instead of returning.
+func rebuildMicrofactory(mybin, mysrc string, pkgMap *pkgPathMapping) {
+	intermediates := filepath.Join(filepath.Dir(mybin), "."+filepath.Base(mybin)+"_intermediates")
+
+	err := os.MkdirAll(intermediates, 0777)
+	if err != nil {
+		fmt.Fprintln(os.Stderr, "Failed to create intermediates directory: %v", err)
+		os.Exit(1)
+	}
+
+	pkg := &GoPackage{
+		Name: "main",
+	}
+
+	if err := pkg.FindDeps(mysrc, pkgMap); err != nil {
+		fmt.Fprintln(os.Stderr, err)
+		os.Exit(1)
+	}
+
+	if err := pkg.Compile(intermediates, mysrc); err != nil {
+		fmt.Fprintln(os.Stderr, err)
+		os.Exit(1)
+	}
+
+	if err := pkg.Link(mybin); err != nil {
+		fmt.Fprintln(os.Stderr, err)
+		os.Exit(1)
+	}
+
+	if !pkg.rebuilt {
+		return
+	}
+
+	cmd := exec.Command(mybin, os.Args[1:]...)
+	cmd.Stdin = os.Stdin
+	cmd.Stdout = os.Stdout
+	cmd.Stderr = os.Stderr
+	if err := cmd.Run(); err == nil {
+		os.Exit(0)
+	} else if e, ok := err.(*exec.ExitError); ok {
+		os.Exit(e.ProcessState.Sys().(syscall.WaitStatus).ExitStatus())
+	}
+	os.Exit(1)
+}
+
+func main() {
+	var output, mysrc, mybin, trimPath string
+	var pkgMap pkgPathMapping
+
+	flags := flag.NewFlagSet("", flag.ExitOnError)
+	flags.BoolVar(&race, "race", false, "enable data race detection.")
+	flags.BoolVar(&verbose, "v", false, "Verbose")
+	flags.StringVar(&output, "o", "", "Output file")
+	flags.StringVar(&mysrc, "s", "", "Microfactory source directory (for rebuilding microfactory if necessary)")
+	flags.StringVar(&mybin, "b", "", "Microfactory binary location")
+	flags.StringVar(&trimPath, "trimpath", "", "remove prefix from recorded source file paths")
+	flags.Var(&pkgMap, "pkg-path", "Mapping of package prefixes to file paths")
+	err := flags.Parse(os.Args[1:])
+
+	if err == flag.ErrHelp || flags.NArg() != 1 || output == "" {
+		fmt.Fprintln(os.Stderr, "Usage:", os.Args[0], "-o out/binary <main-package>")
+		flags.PrintDefaults()
+		os.Exit(1)
+	}
+
+	if mybin != "" && mysrc != "" {
+		rebuildMicrofactory(mybin, mysrc, &pkgMap)
+	}
+
+	mainPackage := &GoPackage{
+		Name: "main",
+	}
+
+	if path, ok, err := pkgMap.Path(flags.Arg(0)); err != nil {
+		fmt.Fprintln(os.Stderr, "Error finding main path:", err)
+		os.Exit(1)
+	} else if !ok {
+		fmt.Fprintln(os.Stderr, "Cannot find path for", flags.Arg(0))
+	} else {
+		if err := mainPackage.FindDeps(path, &pkgMap); err != nil {
+			fmt.Fprintln(os.Stderr, err)
+			os.Exit(1)
+		}
+	}
+
+	intermediates := filepath.Join(filepath.Dir(output), "."+filepath.Base(output)+"_intermediates")
+
+	err = os.MkdirAll(intermediates, 0777)
+	if err != nil {
+		fmt.Fprintln(os.Stderr, "Failed to create intermediates directory: %ve", err)
+		os.Exit(1)
+	}
+
+	err = mainPackage.Compile(intermediates, trimPath)
+	if err != nil {
+		fmt.Fprintln(os.Stderr, "Failed to compile:", err)
+		os.Exit(1)
+	}
+
+	err = mainPackage.Link(output)
+	if err != nil {
+		fmt.Fprintln(os.Stderr, "microfactory.go failed to link:", err)
+		os.Exit(1)
+	}
+}
+
+// pkgPathMapping can be used with flag.Var to parse -pkg-path arguments of
+// <package-prefix>=<path-prefix> mappings.
+type pkgPathMapping struct {
+	pkgs []string
+
+	paths map[string]string
+}
+
+func (pkgPathMapping) String() string {
+	return "<package-prefix>=<path-prefix>"
+}
+
+func (p *pkgPathMapping) Set(value string) error {
+	equalPos := strings.Index(value, "=")
+	if equalPos == -1 {
+		return fmt.Errorf("Argument must be in the form of: %q", p.String())
+	}
+
+	pkgPrefix := strings.TrimSuffix(value[:equalPos], "/")
+	pathPrefix := strings.TrimSuffix(value[equalPos+1:], "/")
+
+	if p.paths == nil {
+		p.paths = make(map[string]string)
+	}
+	if _, ok := p.paths[pkgPrefix]; ok {
+		return fmt.Errorf("Duplicate package prefix: %q", pkgPrefix)
+	}
+
+	p.pkgs = append(p.pkgs, pkgPrefix)
+	p.paths[pkgPrefix] = pathPrefix
+
+	return nil
+}
+
+// Path takes a package name, applies the path mappings and returns the resulting path.
+//
+// If the package isn't mapped, we'll return false to prevent compilation attempts.
+func (p *pkgPathMapping) Path(pkg string) (string, bool, error) {
+	if p.paths == nil {
+		return "", false, fmt.Errorf("No package mappings")
+	}
+
+	for _, pkgPrefix := range p.pkgs {
+		if pkg == pkgPrefix {
+			return p.paths[pkgPrefix], true, nil
+		} else if strings.HasPrefix(pkg, pkgPrefix+"/") {
+			return filepath.Join(p.paths[pkgPrefix], strings.TrimPrefix(pkg, pkgPrefix+"/")), true, nil
+		}
+	}
+
+	return "", false, nil
+}
diff --git a/cmd/microfactory/microfactory_test.go b/cmd/microfactory/microfactory_test.go
new file mode 100644
index 0000000..8c02bcf
--- /dev/null
+++ b/cmd/microfactory/microfactory_test.go
@@ -0,0 +1,422 @@
+// Copyright 2017 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+	"flag"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"reflect"
+	"runtime"
+	"testing"
+	"time"
+)
+
+func TestSimplePackagePathMap(t *testing.T) {
+	t.Parallel()
+
+	var pkgMap pkgPathMapping
+	flags := flag.NewFlagSet("", flag.ContinueOnError)
+	flags.Var(&pkgMap, "m", "")
+	err := flags.Parse([]string{
+		"-m", "android/soong=build/soong/",
+		"-m", "github.com/google/blueprint/=build/blueprint",
+	})
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	compare := func(got, want interface{}) {
+		if !reflect.DeepEqual(got, want) {
+			t.Errorf("Unexpected values in .pkgs:\nwant: %v\n got: %v",
+				want, got)
+		}
+	}
+
+	wantPkgs := []string{"android/soong", "github.com/google/blueprint"}
+	compare(pkgMap.pkgs, wantPkgs)
+	compare(pkgMap.paths[wantPkgs[0]], "build/soong")
+	compare(pkgMap.paths[wantPkgs[1]], "build/blueprint")
+
+	got, ok, err := pkgMap.Path("android/soong/ui/test")
+	if err != nil {
+		t.Error("Unexpected error in pkgMap.Path(soong):", err)
+	} else if !ok {
+		t.Error("Expected a result from pkgMap.Path(soong)")
+	} else {
+		compare(got, "build/soong/ui/test")
+	}
+
+	got, ok, err = pkgMap.Path("github.com/google/blueprint")
+	if err != nil {
+		t.Error("Unexpected error in pkgMap.Path(blueprint):", err)
+	} else if !ok {
+		t.Error("Expected a result from pkgMap.Path(blueprint)")
+	} else {
+		compare(got, "build/blueprint")
+	}
+}
+
+func TestBadPackagePathMap(t *testing.T) {
+	t.Parallel()
+
+	var pkgMap pkgPathMapping
+	if _, _, err := pkgMap.Path("testing"); err == nil {
+		t.Error("Expected error if no maps are specified")
+	}
+	if err := pkgMap.Set(""); err == nil {
+		t.Error("Expected error with blank argument, but none returned")
+	}
+	if err := pkgMap.Set("a=a"); err != nil {
+		t.Error("Unexpected error: %v", err)
+	}
+	if err := pkgMap.Set("a=b"); err == nil {
+		t.Error("Expected error with duplicate package prefix, but none returned")
+	}
+	if _, ok, err := pkgMap.Path("testing"); err != nil {
+		t.Error("Unexpected error: %v", err)
+	} else if ok {
+		t.Error("Expected testing to be consider in the stdlib")
+	}
+}
+
+// TestSingleBuild ensures that just a basic build works.
+func TestSingleBuild(t *testing.T) {
+	t.Parallel()
+
+	setupDir(t, func(dir string, loadPkg loadPkgFunc) {
+		// The output binary
+		out := filepath.Join(dir, "out", "test")
+
+		pkg := loadPkg()
+
+		if err := pkg.Compile(filepath.Join(dir, "out"), ""); err != nil {
+			t.Fatalf("Got error when compiling:", err)
+		}
+
+		if err := pkg.Link(out); err != nil {
+			t.Fatal("Got error when linking:", err)
+		}
+
+		if _, err := os.Stat(out); err != nil {
+			t.Error("Cannot stat output:", err)
+		}
+	})
+}
+
+// testBuildAgain triggers two builds, running the modify function in between
+// each build. It verifies that the second build did or did not actually need
+// to rebuild anything based on the shouldRebuild argument.
+func testBuildAgain(t *testing.T,
+	shouldRecompile, shouldRelink bool,
+	modify func(dir string, loadPkg loadPkgFunc),
+	after func(pkg *GoPackage)) {
+
+	t.Parallel()
+
+	setupDir(t, func(dir string, loadPkg loadPkgFunc) {
+		// The output binary
+		out := filepath.Join(dir, "out", "test")
+
+		pkg := loadPkg()
+
+		if err := pkg.Compile(filepath.Join(dir, "out"), ""); err != nil {
+			t.Fatal("Got error when compiling:", err)
+		}
+
+		if err := pkg.Link(out); err != nil {
+			t.Fatal("Got error when linking:", err)
+		}
+
+		var firstTime time.Time
+		if stat, err := os.Stat(out); err == nil {
+			firstTime = stat.ModTime()
+		} else {
+			t.Fatal("Failed to stat output file:", err)
+		}
+
+		// mtime on HFS+ (the filesystem on darwin) are stored with 1
+		// second granularity, so the timestamp checks will fail unless
+		// we wait at least a second. Sleeping 1.1s to be safe.
+		if runtime.GOOS == "darwin" {
+			time.Sleep(1100 * time.Millisecond)
+		}
+
+		modify(dir, loadPkg)
+
+		pkg = loadPkg()
+
+		if err := pkg.Compile(filepath.Join(dir, "out"), ""); err != nil {
+			t.Fatal("Got error when compiling:", err)
+		}
+		if shouldRecompile {
+			if !pkg.rebuilt {
+				t.Fatal("Package should have recompiled, but was not recompiled.")
+			}
+		} else {
+			if pkg.rebuilt {
+				t.Fatal("Package should not have needed to be recompiled, but was recompiled.")
+			}
+		}
+
+		if err := pkg.Link(out); err != nil {
+			t.Fatal("Got error while linking:", err)
+		}
+		if shouldRelink {
+			if !pkg.rebuilt {
+				t.Error("Package should have relinked, but was not relinked.")
+			}
+		} else {
+			if pkg.rebuilt {
+				t.Error("Package should not have needed to be relinked, but was relinked.")
+			}
+		}
+
+		if stat, err := os.Stat(out); err == nil {
+			if shouldRelink {
+				if stat.ModTime() == firstTime {
+					t.Error("Output timestamp should be different, but both were", firstTime)
+				}
+			} else {
+				if stat.ModTime() != firstTime {
+					t.Error("Output timestamp should be the same.")
+					t.Error(" first:", firstTime)
+					t.Error("second:", stat.ModTime())
+				}
+			}
+		} else {
+			t.Fatal("Failed to stat output file:", err)
+		}
+
+		after(pkg)
+	})
+}
+
+// TestRebuildAfterNoChanges ensures that we don't rebuild if nothing
+// changes
+func TestRebuildAfterNoChanges(t *testing.T) {
+	testBuildAgain(t, false, false, func(dir string, loadPkg loadPkgFunc) {}, func(pkg *GoPackage) {})
+}
+
+// TestRebuildAfterTimestamp ensures that we don't rebuild because
+// timestamps of important files have changed. We should only rebuild if the
+// content hashes are different.
+func TestRebuildAfterTimestampChange(t *testing.T) {
+	testBuildAgain(t, false, false, func(dir string, loadPkg loadPkgFunc) {
+		// Ensure that we've spent some amount of time asleep
+		time.Sleep(100 * time.Millisecond)
+
+		newTime := time.Now().Local()
+		os.Chtimes(filepath.Join(dir, "test.fact"), newTime, newTime)
+		os.Chtimes(filepath.Join(dir, "main/main.go"), newTime, newTime)
+		os.Chtimes(filepath.Join(dir, "a/a.go"), newTime, newTime)
+		os.Chtimes(filepath.Join(dir, "a/b.go"), newTime, newTime)
+		os.Chtimes(filepath.Join(dir, "b/a.go"), newTime, newTime)
+	}, func(pkg *GoPackage) {})
+}
+
+// TestRebuildAfterGoChange ensures that we rebuild after a content change
+// to a package's go file.
+func TestRebuildAfterGoChange(t *testing.T) {
+	testBuildAgain(t, true, true, func(dir string, loadPkg loadPkgFunc) {
+		if err := ioutil.WriteFile(filepath.Join(dir, "a", "a.go"), []byte(go_a_a+"\n"), 0666); err != nil {
+			t.Fatal("Error writing a/a.go:", err)
+		}
+	}, func(pkg *GoPackage) {
+		if !pkg.directDeps[0].rebuilt {
+			t.Fatal("android/soong/a should have rebuilt")
+		}
+		if !pkg.directDeps[1].rebuilt {
+			t.Fatal("android/soong/b should have rebuilt")
+		}
+	})
+}
+
+// TestRebuildAfterMainChange ensures that we don't rebuild any dependencies
+// if only the main package's go files are touched.
+func TestRebuildAfterMainChange(t *testing.T) {
+	testBuildAgain(t, true, true, func(dir string, loadPkg loadPkgFunc) {
+		if err := ioutil.WriteFile(filepath.Join(dir, "main", "main.go"), []byte(go_main_main+"\n"), 0666); err != nil {
+			t.Fatal("Error writing main/main.go:", err)
+		}
+	}, func(pkg *GoPackage) {
+		if pkg.directDeps[0].rebuilt {
+			t.Fatal("android/soong/a should not have rebuilt")
+		}
+		if pkg.directDeps[1].rebuilt {
+			t.Fatal("android/soong/b should not have rebuilt")
+		}
+	})
+}
+
+// TestRebuildAfterRemoveOut ensures that we rebuild if the output file is
+// missing, even if everything else doesn't need rebuilding.
+func TestRebuildAfterRemoveOut(t *testing.T) {
+	testBuildAgain(t, false, true, func(dir string, loadPkg loadPkgFunc) {
+		if err := os.Remove(filepath.Join(dir, "out", "test")); err != nil {
+			t.Fatal("Failed to remove output:", err)
+		}
+	}, func(pkg *GoPackage) {})
+}
+
+// TestRebuildAfterPartialBuild ensures that even if the build was interrupted
+// between the recompile and relink stages, we'll still relink when we run again.
+func TestRebuildAfterPartialBuild(t *testing.T) {
+	testBuildAgain(t, false, true, func(dir string, loadPkg loadPkgFunc) {
+		if err := ioutil.WriteFile(filepath.Join(dir, "main", "main.go"), []byte(go_main_main+"\n"), 0666); err != nil {
+			t.Fatal("Error writing main/main.go:", err)
+		}
+
+		pkg := loadPkg()
+
+		if err := pkg.Compile(filepath.Join(dir, "out"), ""); err != nil {
+			t.Fatal("Got error when compiling:", err)
+		}
+		if !pkg.rebuilt {
+			t.Fatal("Package should have recompiled, but was not recompiled.")
+		}
+	}, func(pkg *GoPackage) {})
+}
+
+// BenchmarkInitialBuild computes how long a clean build takes (for tiny test
+// inputs).
+func BenchmarkInitialBuild(b *testing.B) {
+	for i := 0; i < b.N; i++ {
+		setupDir(b, func(dir string, loadPkg loadPkgFunc) {
+			pkg := loadPkg()
+			if err := pkg.Compile(filepath.Join(dir, "out"), ""); err != nil {
+				b.Fatal("Got error when compiling:", err)
+			}
+
+			if err := pkg.Link(filepath.Join(dir, "out", "test")); err != nil {
+				b.Fatal("Got error when linking:", err)
+			}
+		})
+	}
+}
+
+// BenchmarkMinIncrementalBuild computes how long an incremental build that
+// doesn't actually need to build anything takes.
+func BenchmarkMinIncrementalBuild(b *testing.B) {
+	setupDir(b, func(dir string, loadPkg loadPkgFunc) {
+		pkg := loadPkg()
+
+		if err := pkg.Compile(filepath.Join(dir, "out"), ""); err != nil {
+			b.Fatal("Got error when compiling:", err)
+		}
+
+		if err := pkg.Link(filepath.Join(dir, "out", "test")); err != nil {
+			b.Fatal("Got error when linking:", err)
+		}
+
+		b.ResetTimer()
+
+		for i := 0; i < b.N; i++ {
+			pkg := loadPkg()
+
+			if err := pkg.Compile(filepath.Join(dir, "out"), ""); err != nil {
+				b.Fatal("Got error when compiling:", err)
+			}
+
+			if err := pkg.Link(filepath.Join(dir, "out", "test")); err != nil {
+				b.Fatal("Got error when linking:", err)
+			}
+
+			if pkg.rebuilt {
+				b.Fatal("Should not have rebuilt anything")
+			}
+		}
+	})
+}
+
+///////////////////////////////////////////////////////
+// Templates used to create fake compilable packages //
+///////////////////////////////////////////////////////
+
+const go_main_main = `
+package main
+import (
+	"fmt"
+	"android/soong/a"
+	"android/soong/b"
+)
+func main() {
+	fmt.Println(a.Stdout, b.Stdout)
+}
+`
+
+const go_a_a = `
+package a
+import "os"
+var Stdout = os.Stdout
+`
+
+const go_a_b = `
+package a
+`
+
+const go_b_a = `
+package b
+import "android/soong/a"
+var Stdout = a.Stdout
+`
+
+type T interface {
+	Fatal(args ...interface{})
+	Fatalf(format string, args ...interface{})
+}
+
+type loadPkgFunc func() *GoPackage
+
+func setupDir(t T, test func(dir string, loadPkg loadPkgFunc)) {
+	dir, err := ioutil.TempDir("", "test")
+	if err != nil {
+		t.Fatalf("Error creating temporary directory: %#v", err)
+	}
+	defer os.RemoveAll(dir)
+
+	writeFile := func(name, contents string) {
+		if err := ioutil.WriteFile(filepath.Join(dir, name), []byte(contents), 0666); err != nil {
+			t.Fatalf("Error writing %q: %#v", name, err)
+		}
+	}
+	mkdir := func(name string) {
+		if err := os.Mkdir(filepath.Join(dir, name), 0777); err != nil {
+			t.Fatalf("Error creating %q directory: %#v", name, err)
+		}
+	}
+	mkdir("main")
+	mkdir("a")
+	mkdir("b")
+	writeFile("main/main.go", go_main_main)
+	writeFile("a/a.go", go_a_a)
+	writeFile("a/b.go", go_a_b)
+	writeFile("b/a.go", go_b_a)
+
+	loadPkg := func() *GoPackage {
+		pkg := &GoPackage{
+			Name: "main",
+		}
+		pkgMap := &pkgPathMapping{}
+		pkgMap.Set("android/soong=" + dir)
+		if err := pkg.FindDeps(filepath.Join(dir, "main"), pkgMap); err != nil {
+			t.Fatalf("Error finding deps: %v", err)
+		}
+		return pkg
+	}
+
+	test(dir, loadPkg)
+}
diff --git a/cmd/multiproduct_kati/Android.bp b/cmd/multiproduct_kati/Android.bp
new file mode 100644
index 0000000..b264c35
--- /dev/null
+++ b/cmd/multiproduct_kati/Android.bp
@@ -0,0 +1,25 @@
+// Copyright 2017 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+blueprint_go_binary {
+    name: "multiproduct_kati",
+    deps: [
+        "soong-ui-build",
+        "soong-ui-logger",
+        "soong-ui-tracer",
+    ],
+    srcs: [
+        "main.go",
+    ],
+}
diff --git a/cmd/multiproduct_kati/main.go b/cmd/multiproduct_kati/main.go
new file mode 100644
index 0000000..3aa5a87
--- /dev/null
+++ b/cmd/multiproduct_kati/main.go
@@ -0,0 +1,206 @@
+// Copyright 2017 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+	"bytes"
+	"context"
+	"flag"
+	"fmt"
+	"os"
+	"path/filepath"
+	"runtime"
+	"strings"
+	"sync"
+	"time"
+
+	"android/soong/ui/build"
+	"android/soong/ui/logger"
+	"android/soong/ui/tracer"
+)
+
+// We default to number of cpus / 4, which seems to be the sweet spot for my
+// system. I suspect this is mostly due to memory or disk bandwidth though, and
+// may depend on the size ofthe source tree, so this probably isn't a great
+// default.
+func detectNumJobs() int {
+	if runtime.NumCPU() < 4 {
+		return 1
+	}
+	return runtime.NumCPU() / 4
+}
+
+var numJobs = flag.Int("j", detectNumJobs(), "number of parallel kati jobs")
+
+var keep = flag.Bool("keep", false, "keep successful output files")
+
+var outDir = flag.String("out", "", "path to store output directories (defaults to tmpdir under $OUT when empty)")
+
+var onlyConfig = flag.Bool("only-config", false, "Only run product config (not Soong or Kati)")
+var onlySoong = flag.Bool("only-soong", false, "Only run product config and Soong (not Kati)")
+
+type Product struct {
+	ctx    build.Context
+	config build.Config
+}
+
+func main() {
+	log := logger.New(os.Stderr)
+	defer log.Cleanup()
+
+	flag.Parse()
+
+	ctx, cancel := context.WithCancel(context.Background())
+	defer cancel()
+
+	trace := tracer.New(log)
+	defer trace.Close()
+
+	build.SetupSignals(log, cancel, func() {
+		trace.Close()
+		log.Cleanup()
+	})
+
+	buildCtx := build.Context{&build.ContextImpl{
+		Context:        ctx,
+		Logger:         log,
+		Tracer:         trace,
+		StdioInterface: build.StdioImpl{},
+	}}
+
+	failed := false
+
+	config := build.NewConfig(buildCtx)
+	if *outDir == "" {
+		name := "multiproduct-" + time.Now().Format("20060102150405")
+
+		*outDir = filepath.Join(config.OutDir(), name)
+
+		if err := os.MkdirAll(*outDir, 0777); err != nil {
+			log.Fatalf("Failed to create tempdir: %v", err)
+		}
+
+		if !*keep {
+			defer func() {
+				if !failed {
+					os.RemoveAll(*outDir)
+				}
+			}()
+		}
+	}
+	config.Environment().Set("OUT_DIR", *outDir)
+	log.Println("Output directory:", *outDir)
+
+	build.SetupOutDir(buildCtx, config)
+	log.SetOutput(filepath.Join(config.OutDir(), "soong.log"))
+	trace.SetOutput(filepath.Join(config.OutDir(), "build.trace"))
+
+	vars, err := build.DumpMakeVars(buildCtx, config, nil, nil, []string{"all_named_products"})
+	if err != nil {
+		log.Fatal(err)
+	}
+	products := strings.Fields(vars["all_named_products"])
+	log.Verbose("Got product list:", products)
+
+	var wg sync.WaitGroup
+	errs := make(chan error, len(products))
+	productConfigs := make(chan Product, len(products))
+
+	// Run the product config for every product in parallel
+	for _, product := range products {
+		wg.Add(1)
+		go func(product string) {
+			defer wg.Done()
+			defer logger.Recover(func(err error) {
+				errs <- fmt.Errorf("Error building %s: %v", product, err)
+			})
+
+			productOutDir := filepath.Join(config.OutDir(), product)
+
+			if err := os.MkdirAll(productOutDir, 0777); err != nil {
+				log.Fatalf("Error creating out directory: %v", err)
+			}
+
+			f, err := os.Create(filepath.Join(productOutDir, "std.log"))
+			if err != nil {
+				log.Fatalf("Error creating std.log: %v", err)
+			}
+
+			productLog := logger.New(&bytes.Buffer{})
+			productLog.SetOutput(filepath.Join(productOutDir, "soong.log"))
+
+			productCtx := build.Context{&build.ContextImpl{
+				Context:        ctx,
+				Logger:         productLog,
+				Tracer:         trace,
+				StdioInterface: build.NewCustomStdio(nil, f, f),
+				Thread:         trace.NewThread(product),
+			}}
+
+			productConfig := build.NewConfig(productCtx)
+			productConfig.Environment().Set("OUT_DIR", productOutDir)
+			productConfig.Lunch(productCtx, product, "eng")
+
+			build.Build(productCtx, productConfig, build.BuildProductConfig)
+			productConfigs <- Product{productCtx, productConfig}
+		}(product)
+	}
+	go func() {
+		defer close(productConfigs)
+		wg.Wait()
+	}()
+
+	var wg2 sync.WaitGroup
+	// Then run up to numJobs worth of Soong and Kati
+	for i := 0; i < *numJobs; i++ {
+		wg2.Add(1)
+		go func() {
+			defer wg2.Done()
+			for product := range productConfigs {
+				func() {
+					defer logger.Recover(func(err error) {
+						errs <- fmt.Errorf("Error building %s: %v", product.config.TargetProduct(), err)
+					})
+
+					buildWhat := 0
+					if !*onlyConfig {
+						buildWhat |= build.BuildSoong
+						if !*onlySoong {
+							buildWhat |= build.BuildKati
+						}
+					}
+					build.Build(product.ctx, product.config, buildWhat)
+					if !*keep {
+						os.RemoveAll(product.config.OutDir())
+					}
+					log.Println("Finished running for", product.config.TargetProduct())
+				}()
+			}
+		}()
+	}
+	go func() {
+		wg2.Wait()
+		close(errs)
+	}()
+
+	for err := range errs {
+		failed = true
+		log.Print(err)
+	}
+
+	if failed {
+		log.Fatalln("Failed")
+	}
+}
diff --git a/cmd/soong_ui/Android.bp b/cmd/soong_ui/Android.bp
new file mode 100644
index 0000000..f09e42e
--- /dev/null
+++ b/cmd/soong_ui/Android.bp
@@ -0,0 +1,25 @@
+// Copyright 2017 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+blueprint_go_binary {
+    name: "soong_ui",
+    deps: [
+        "soong-ui-build",
+        "soong-ui-logger",
+        "soong-ui-tracer",
+    ],
+    srcs: [
+        "main.go",
+    ],
+}
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
new file mode 100644
index 0000000..26887ae
--- /dev/null
+++ b/cmd/soong_ui/main.go
@@ -0,0 +1,95 @@
+// Copyright 2017 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+	"context"
+	"os"
+	"path/filepath"
+	"strconv"
+	"strings"
+	"time"
+
+	"android/soong/ui/build"
+	"android/soong/ui/logger"
+	"android/soong/ui/tracer"
+)
+
+func indexList(s string, list []string) int {
+	for i, l := range list {
+		if l == s {
+			return i
+		}
+	}
+
+	return -1
+}
+
+func inList(s string, list []string) bool {
+	return indexList(s, list) != -1
+}
+
+func main() {
+	log := logger.New(os.Stderr)
+	defer log.Cleanup()
+
+	if len(os.Args) < 2 || !inList("--make-mode", os.Args) {
+		log.Fatalln("The `soong` native UI is not yet available.")
+	}
+
+	ctx, cancel := context.WithCancel(context.Background())
+	defer cancel()
+
+	trace := tracer.New(log)
+	defer trace.Close()
+
+	build.SetupSignals(log, cancel, func() {
+		trace.Close()
+		log.Cleanup()
+	})
+
+	buildCtx := build.Context{&build.ContextImpl{
+		Context:        ctx,
+		Logger:         log,
+		Tracer:         trace,
+		StdioInterface: build.StdioImpl{},
+	}}
+	config := build.NewConfig(buildCtx, os.Args[1:]...)
+
+	log.SetVerbose(config.IsVerbose())
+	build.SetupOutDir(buildCtx, config)
+
+	if config.Dist() {
+		logsDir := filepath.Join(config.DistDir(), "logs")
+		os.MkdirAll(logsDir, 0777)
+		log.SetOutput(filepath.Join(logsDir, "soong.log"))
+		trace.SetOutput(filepath.Join(logsDir, "build.trace"))
+	} else {
+		log.SetOutput(filepath.Join(config.OutDir(), "soong.log"))
+		trace.SetOutput(filepath.Join(config.OutDir(), "build.trace"))
+	}
+
+	if start, ok := os.LookupEnv("TRACE_BEGIN_SOONG"); ok {
+		if !strings.HasSuffix(start, "N") {
+			if start_time, err := strconv.ParseUint(start, 10, 64); err == nil {
+				log.Verbosef("Took %dms to start up.",
+					time.Since(time.Unix(0, int64(start_time))).Nanoseconds()/time.Millisecond.Nanoseconds())
+				buildCtx.CompleteTrace("startup", start_time, uint64(time.Now().UnixNano()))
+			}
+		}
+	}
+
+	build.Build(buildCtx, config, build.BuildAll)
+}
diff --git a/cmd/soong_zip/soong_zip.go b/cmd/soong_zip/soong_zip.go
index b0f3daa..3e62016 100644
--- a/cmd/soong_zip/soong_zip.go
+++ b/cmd/soong_zip/soong_zip.go
@@ -22,6 +22,7 @@
 	"hash/crc32"
 	"io"
 	"io/ioutil"
+	"log"
 	"os"
 	"path/filepath"
 	"runtime"
@@ -54,7 +55,11 @@
 }
 
 type fileArg struct {
-	relativeRoot, file string
+	rootPrefix, relativeRoot, file string
+}
+
+type pathMapping struct {
+	dest, src string
 }
 
 type fileArgs []fileArg
@@ -65,10 +70,13 @@
 
 func (l *fileArgs) Set(s string) error {
 	if *relativeRoot == "" {
-		return fmt.Errorf("must pass -C before -f")
+		return fmt.Errorf("must pass -C before -f or -l")
 	}
 
-	*l = append(*l, fileArg{filepath.Clean(*relativeRoot), s})
+	*l = append(*l,
+		fileArg{rootPrefix: filepath.Clean(*rootPrefix),
+			relativeRoot: filepath.Clean(*relativeRoot),
+			file:         s})
 	return nil
 }
 
@@ -80,11 +88,13 @@
 	out          = flag.String("o", "", "file to write zip file to")
 	manifest     = flag.String("m", "", "input jar manifest file name")
 	directories  = flag.Bool("d", false, "include directories in zip")
+	rootPrefix   = flag.String("P", "", "path prefix within the zip at which to place files")
 	relativeRoot = flag.String("C", "", "path to use as relative root of files in next -f or -l argument")
 	parallelJobs = flag.Int("j", runtime.NumCPU(), "number of parallel threads to use")
 	compLevel    = flag.Int("L", 5, "deflate compression level (0-9)")
-	listFiles    fileArgs
-	files        fileArgs
+
+	listFiles fileArgs
+	files     fileArgs
 
 	cpuProfile = flag.String("cpuprofile", "", "write cpu profile to file")
 	traceFile  = flag.String("trace", "", "write trace to file")
@@ -163,14 +173,66 @@
 		compLevel:   *compLevel,
 	}
 
-	err := w.write(*out, listFiles, *manifest)
+	pathMappings := []pathMapping{}
+	set := make(map[string]string)
+
+	// load listFiles, which specify other files to include.
+	for _, l := range listFiles {
+		list, err := ioutil.ReadFile(l.file)
+		if err != nil {
+			fmt.Fprintln(os.Stderr, err.Error())
+			os.Exit(1)
+		}
+		srcs := strings.Split(string(list), "\n")
+		for _, src := range srcs {
+			if err := fillPathPairs(l.rootPrefix, l.relativeRoot, src,
+				set, &pathMappings); err != nil {
+				log.Fatal(err)
+			}
+		}
+	}
+
+	// also include the usual files that are to be added directly.
+	for _, f := range files {
+		if err := fillPathPairs(f.rootPrefix, f.relativeRoot,
+			f.file, set, &pathMappings); err != nil {
+			log.Fatal(err)
+		}
+	}
+
+	err := w.write(*out, pathMappings, *manifest)
 	if err != nil {
 		fmt.Fprintln(os.Stderr, err.Error())
 		os.Exit(1)
 	}
 }
 
-func (z *zipWriter) write(out string, listFiles fileArgs, manifest string) error {
+func fillPathPairs(prefix, rel, src string, set map[string]string, pathMappings *[]pathMapping) error {
+	src = strings.TrimSpace(src)
+	if src == "" {
+		return nil
+	}
+	src = filepath.Clean(src)
+	dest, err := filepath.Rel(rel, src)
+	if err != nil {
+		return err
+	}
+	dest = filepath.Join(prefix, dest)
+
+	if _, found := set[dest]; found {
+		return fmt.Errorf("found two file paths to be copied into dest path: %q,"+
+			" both [%q]%q and [%q]%q!",
+			dest, dest, src, dest, set[dest])
+	} else {
+		set[dest] = src
+	}
+
+	*pathMappings = append(*pathMappings, pathMapping{dest: dest, src: src})
+
+	return nil
+}
+
+func (z *zipWriter) write(out string, pathMappings []pathMapping, manifest string) error {
 	f, err := os.Create(out)
 	if err != nil {
 		return err
@@ -206,16 +268,8 @@
 		var err error
 		defer close(z.writeOps)
 
-		for _, listFile := range listFiles {
-			err = z.writeListFile(listFile)
-			if err != nil {
-				z.errors <- err
-				return
-			}
-		}
-
-		for _, file := range files {
-			err = z.writeRelFile(file.relativeRoot, file.file)
+		for _, ele := range pathMappings {
+			err = z.writeFile(ele.dest, ele.src)
 			if err != nil {
 				z.errors <- err
 				return
@@ -317,64 +371,28 @@
 	}
 }
 
-func (z *zipWriter) writeListFile(listFile fileArg) error {
-	list, err := ioutil.ReadFile(listFile.file)
-	if err != nil {
-		return err
-	}
-
-	files := strings.Split(string(list), "\n")
-
-	for _, file := range files {
-		file = strings.TrimSpace(file)
-		if file == "" {
-			continue
-		}
-		err = z.writeRelFile(listFile.relativeRoot, file)
-		if err != nil {
-			return err
-		}
-	}
-
-	return nil
-}
-
-func (z *zipWriter) writeRelFile(root, file string) error {
-	file = filepath.Clean(file)
-
-	rel, err := filepath.Rel(root, file)
-	if err != nil {
-		return err
-	}
-
-	err = z.writeFile(rel, file)
-	if err != nil {
-		return err
-	}
-
-	return nil
-}
-
-func (z *zipWriter) writeFile(rel, file string) error {
+func (z *zipWriter) writeFile(dest, src string) error {
 	var fileSize int64
+	var executable bool
 
-	if s, err := os.Lstat(file); err != nil {
+	if s, err := os.Lstat(src); err != nil {
 		return err
 	} else if s.IsDir() {
 		if z.directories {
-			return z.writeDirectory(rel)
+			return z.writeDirectory(dest)
 		}
 		return nil
 	} else if s.Mode()&os.ModeSymlink != 0 {
-		return z.writeSymlink(rel, file)
+		return z.writeSymlink(dest, src)
 	} else if !s.Mode().IsRegular() {
-		return fmt.Errorf("%s is not a file, directory, or symlink", file)
+		return fmt.Errorf("%s is not a file, directory, or symlink", src)
 	} else {
 		fileSize = s.Size()
+		executable = s.Mode()&0100 != 0
 	}
 
 	if z.directories {
-		dir, _ := filepath.Split(rel)
+		dir, _ := filepath.Split(dest)
 		err := z.writeDirectory(dir)
 		if err != nil {
 			return err
@@ -388,15 +406,18 @@
 	// we're sure about the Method and CRC.
 	ze := &zipEntry{
 		fh: &zip.FileHeader{
-			Name:   rel,
+			Name:   dest,
 			Method: zip.Deflate,
 
 			UncompressedSize64: uint64(fileSize),
 		},
 	}
 	ze.fh.SetModTime(z.time)
+	if executable {
+		ze.fh.SetMode(0700)
+	}
 
-	r, err := os.Open(file)
+	r, err := os.Open(src)
 	if err != nil {
 		return err
 	}
@@ -445,7 +466,7 @@
 			f.Close()
 		}(wg, r)
 	} else {
-		go z.compressWholeFile(rel, r, exec, compressChan)
+		go z.compressWholeFile(ze, r, exec, compressChan)
 	}
 
 	return nil
@@ -514,26 +535,19 @@
 	return buf, nil
 }
 
-func (z *zipWriter) compressWholeFile(rel string, r *os.File, exec Execution, compressChan chan *zipEntry) {
+func (z *zipWriter) compressWholeFile(ze *zipEntry, r *os.File, exec Execution, compressChan chan *zipEntry) {
 	var bufSize int
 
 	defer r.Close()
 
-	fileHeader := &zip.FileHeader{
-		Name:   rel,
-		Method: zip.Deflate,
-	}
-	fileHeader.SetModTime(z.time)
-
 	crc := crc32.NewIEEE()
-	count, err := io.Copy(crc, r)
+	_, err := io.Copy(crc, r)
 	if err != nil {
 		z.errors <- err
 		return
 	}
 
-	fileHeader.CRC32 = crc.Sum32()
-	fileHeader.UncompressedSize64 = uint64(count)
+	ze.fh.CRC32 = crc.Sum32()
 
 	_, err = r.Seek(0, 0)
 	if err != nil {
@@ -543,10 +557,7 @@
 
 	compressed, err := z.compressBlock(r, nil, true)
 
-	ze := &zipEntry{
-		fh:            fileHeader,
-		futureReaders: make(chan chan io.Reader, 1),
-	}
+	ze.futureReaders = make(chan chan io.Reader, 1)
 	futureReader := make(chan io.Reader, 1)
 	ze.futureReaders <- futureReader
 	close(ze.futureReaders)
diff --git a/docs/after.png b/docs/after.png
new file mode 100644
index 0000000..fdd14d2
--- /dev/null
+++ b/docs/after.png
Binary files differ
diff --git a/docs/before.png b/docs/before.png
new file mode 100644
index 0000000..a6a1424
--- /dev/null
+++ b/docs/before.png
Binary files differ
diff --git a/docs/clion.md b/docs/clion.md
new file mode 100644
index 0000000..d6ae19a
--- /dev/null
+++ b/docs/clion.md
@@ -0,0 +1,72 @@
+# CLion project generator
+
+Soong can generate CLion projects. This is intended for source code editing
+only. Build should still be done via make/m/mm(a)/mmm(a).
+
+CMakeLists.txt project file generation is enabled via environment variable:
+
+```bash
+$ export SOONG_GEN_CMAKEFILES=1
+$ export SOONG_GEN_CMAKEFILES_DEBUG=1
+```
+
+You can then trigger a full build:
+
+```bash
+$ make -j64
+```
+or build only the project you are interested in:
+
+```bash
+$ make frameworks/native/service/libs/ui
+```
+
+Projects are generated in the ``out`` directory. In the case of libui, the path would
+be:
+
+```bash
+out/development/ide/clion/frameworks/native/libs/ui/libui-arm64-android/CMakeLists.txt
+```
+Note: The generator creates one folder per targetname-architecture-os combination.
+In the case of libui you endup with two projects:
+
+```bash
+$ ls out/development/ide/clion/frameworks/native/libs/ui
+libui-arm64-android libui-arm-android
+```
+
+### Edit multiple projects at once
+To combine several projects into one, you can create super projects
+and place them in:
+
+```bash
+development/ide/clion/[PATH_YOU_WANT]/..
+```
+
+These file will be symbolicaly linked in ``out/development/ide/clion``. All folders
+will also be created there.
+
+An example of a super project for surfaceflinger (using libui and libgui)
+located in development/ide/clion/frameworks/native:
+
+```
+cmake_minimum_required(VERSION 3.6)
+project(native)
+add_subdirectory(services/surfaceflinger)
+add_subdirectory(libs/ui/libui-arm64-android)
+add_subdirectory(libs/gui/libgui-arm64-android)
+```
+
+
+### Flattened filesystem
+
+Upon opening a project it looks like all the folder structure has been
+flattened:
+
+![Flattened View](before.png "")
+
+This is because you need to change the project root. Via Tools > CMake >
+Change Project Root:
+
+![Unflattened View](after.png "")
+
diff --git a/genrule/filegroup.go b/genrule/filegroup.go
index 9b53c9f..71c5439 100644
--- a/genrule/filegroup.go
+++ b/genrule/filegroup.go
@@ -21,7 +21,7 @@
 )
 
 func init() {
-	android.RegisterModuleType("filegroup", fileGroupFactory)
+	android.RegisterModuleType("filegroup", FileGroupFactory)
 }
 
 type fileGroupProperties struct {
@@ -29,6 +29,12 @@
 	Srcs []string
 
 	Exclude_srcs []string
+
+	// The base path to the files.  May be used by other modules to determine which portion
+	// of the path to use.  For example, when a filegroup is used as data in a cc_test rule,
+	// the base path is stripped off the path and the remaining path is used as the
+	// installation directory.
+	Path string
 }
 
 type fileGroup struct {
@@ -42,7 +48,7 @@
 // filegroup modules contain a list of files, and can be used to export files across package
 // boundaries.  filegroups (and genrules) can be referenced from srcs properties of other modules
 // using the syntax ":module".
-func fileGroupFactory() (blueprint.Module, []interface{}) {
+func FileGroupFactory() (blueprint.Module, []interface{}) {
 	module := &fileGroup{}
 
 	return android.InitAndroidModule(module, &module.properties)
@@ -53,7 +59,7 @@
 }
 
 func (fg *fileGroup) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	fg.srcs = ctx.ExpandSources(fg.properties.Srcs, fg.properties.Exclude_srcs)
+	fg.srcs = ctx.ExpandSourcesSubDir(fg.properties.Srcs, fg.properties.Exclude_srcs, fg.properties.Path)
 }
 
 func (fg *fileGroup) Srcs() android.Paths {
diff --git a/genrule/genrule.go b/genrule/genrule.go
index 5c71742..ee4c503 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -150,8 +150,6 @@
 				} else {
 					ctx.ModuleErrorf("host tool %q missing output file", ctx.OtherModuleName(module))
 				}
-			} else {
-				ctx.ModuleErrorf("unknown dependency %q", ctx.OtherModuleName(module))
 			}
 		})
 	}
diff --git a/phony/phony.go b/phony/phony.go
new file mode 100644
index 0000000..c405af8
--- /dev/null
+++ b/phony/phony.go
@@ -0,0 +1,63 @@
+// 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 phony
+
+import (
+	"fmt"
+	"io"
+	"strings"
+
+	"github.com/google/blueprint"
+
+	"android/soong/android"
+)
+
+func init() {
+	android.RegisterModuleType("phony", phonyFactory)
+}
+
+type phony struct {
+	android.ModuleBase
+	requiredModuleNames []string
+}
+
+func phonyFactory() (blueprint.Module, []interface{}) {
+	module := &phony{}
+
+	return android.InitAndroidModule(module)
+}
+
+func (p *phony) DepsMutator(ctx android.BottomUpMutatorContext) {
+}
+
+func (p *phony) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	p.requiredModuleNames = ctx.RequiredModuleNames()
+	if len(p.requiredModuleNames) == 0 {
+		ctx.PropertyErrorf("required", "phony must not have empty required dependencies in order to be useful(and therefore permitted).")
+	}
+}
+
+func (p *phony) AndroidMk() (ret android.AndroidMkData, err error) {
+	ret.Custom = func(w io.Writer, name, prefix, moduleDir string) error {
+		fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
+		fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
+		fmt.Fprintln(w, "LOCAL_MODULE :=", name)
+		fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES := "+strings.Join(p.requiredModuleNames, " "))
+		fmt.Fprintln(w, "include $(BUILD_PHONY_PACKAGE)")
+
+		return nil
+	}
+	return
+}
diff --git a/root.bp b/root.bp
index a1b1f6b..f14b047 100644
--- a/root.bp
+++ b/root.bp
@@ -15,10 +15,12 @@
     "build/tools/*",
     "dalvik",
     "development/*",
+    "device/*/*",
     "external/*",
     "frameworks/av",
     "frameworks/base",
     "frameworks/compile/*",
+    "frameworks/hardware/interfaces",
     "frameworks/native",
     "frameworks/rs",
     "frameworks/wilhelm",
@@ -26,9 +28,11 @@
     "libcore",
     "libnativehelper",
     "packages/apps/HTMLViewer",
+    "prebuilts/clang/host/linux-x86",
     "prebuilts/ndk",
     "prebuilts/sdk",
     "system/*",
+    "system/hardware/interfaces",
     "system/tools/*",
     "test/vts",
     "test/vts-testcase/*",
diff --git a/soong_ui.bash b/soong_ui.bash
new file mode 100755
index 0000000..e3997cf
--- /dev/null
+++ b/soong_ui.bash
@@ -0,0 +1,109 @@
+#!/bin/bash -eu
+#
+# Copyright 2017 Google Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# To track how long we took to startup. %N isn't supported on Darwin, but
+# that's detected in the Go code, and skip calculating the startup time.
+export TRACE_BEGIN_SOONG=$(date +%s%N)
+
+# Function to find top of the source tree (if $TOP isn't set) by walking up the
+# tree.
+function gettop
+{
+    local TOPFILE=build/soong/root.bp
+    if [ -z "${TOP-}" -a -f "${TOP-}/${TOPFILE}" ] ; then
+        # The following circumlocution ensures we remove symlinks from TOP.
+        (cd $TOP; PWD= /bin/pwd)
+    else
+        if [ -f $TOPFILE ] ; then
+            # The following circumlocution (repeated below as well) ensures
+            # that we record the true directory name and not one that is
+            # faked up with symlink names.
+            PWD= /bin/pwd
+        else
+            local HERE=$PWD
+            T=
+            while [ \( ! \( -f $TOPFILE \) \) -a \( $PWD != "/" \) ]; do
+                \cd ..
+                T=`PWD= /bin/pwd -P`
+            done
+            \cd $HERE
+            if [ -f "$T/$TOPFILE" ]; then
+                echo $T
+            fi
+        fi
+    fi
+}
+
+# Bootstrap microfactory from source if necessary and use it to build the
+# soong_ui binary, then run soong_ui.
+function run_go
+{
+    # Increment when microfactory changes enough that it cannot rebuild itself.
+    # For example, if we use a new command line argument that doesn't work on older versions.
+    local mf_version=2
+
+    local mf_src="${TOP}/build/soong/cmd/microfactory"
+
+    local out_dir="${OUT_DIR-}"
+    if [ -z "${out_dir}" ]; then
+        if [ "${OUT_DIR_COMMON_BASE-}" ]; then
+            out_dir="${OUT_DIR_COMMON_BASE}/$(basename ${TOP})"
+        else
+            out_dir="${TOP}/out"
+        fi
+    fi
+
+    local mf_bin="${out_dir}/microfactory_$(uname)"
+    local mf_version_file="${out_dir}/.microfactory_$(uname)_version"
+    local soong_ui_bin="${out_dir}/soong_ui"
+    local from_src=1
+
+    if [ -f "${mf_bin}" ] && [ -f "${mf_version_file}" ]; then
+        if [ "${mf_version}" -eq "$(cat "${mf_version_file}")" ]; then
+            from_src=0
+        fi
+    fi
+
+    local mf_cmd
+    if [ $from_src -eq 1 ]; then
+        mf_cmd="${GOROOT}/bin/go run ${mf_src}/microfactory.go"
+    else
+        mf_cmd="${mf_bin}"
+    fi
+
+    ${mf_cmd} -s "${mf_src}" -b "${mf_bin}" \
+            -pkg-path "android/soong=${TOP}/build/soong" -trimpath "${TOP}/build/soong" \
+            -o "${soong_ui_bin}" android/soong/cmd/soong_ui
+
+    if [ $from_src -eq 1 ]; then
+        echo "${mf_version}" >"${mf_version_file}"
+    fi
+
+    exec "${out_dir}/soong_ui" "$@"
+}
+
+export TOP=$(gettop)
+case $(uname) in
+    Linux)
+        export GOROOT="${TOP}/prebuilts/go/linux-x86/"
+        ;;
+    Darwin)
+        export GOROOT="${TOP}/prebuilts/go/darwin-x86/"
+        ;;
+    *) echo "unknown OS:" $(uname) >&2 && exit 1;;
+esac
+
+run_go "$@"
diff --git a/third_party/zip/Android.bp b/third_party/zip/Android.bp
index 044e6f8..ec89c0c 100644
--- a/third_party/zip/Android.bp
+++ b/third_party/zip/Android.bp
@@ -24,6 +24,7 @@
         "android.go",
     ],
     testSrcs: [
+        "android_test.go",
         "reader_test.go",
         "writer_test.go",
         "zip_test.go",
diff --git a/third_party/zip/android.go b/third_party/zip/android.go
index e2e46ff..f3b6055 100644
--- a/third_party/zip/android.go
+++ b/third_party/zip/android.go
@@ -32,6 +32,12 @@
 	fh := &fileHeader
 	fh.Flags |= 0x8
 
+	// The zip64 extras change between the Central Directory and Local File Header, while we use
+	// the same structure for both. The Local File Haeder is taken care of by us writing a data
+	// descriptor with the zip64 values. The Central Directory Entry is written by Close(), where
+	// the zip64 extra is automatically created and appended when necessary.
+	fh.Extra = stripZip64Extras(fh.Extra)
+
 	h := &header{
 		FileHeader: fh,
 		offset:     uint64(w.cw.count),
@@ -70,6 +76,29 @@
 	return err
 }
 
+// Strip any Zip64 extra fields
+func stripZip64Extras(input []byte) []byte {
+	ret := []byte{}
+
+	for len(input) >= 4 {
+		r := readBuf(input)
+		tag := r.uint16()
+		size := r.uint16()
+		if int(size) > len(r) {
+			break
+		}
+		if tag != zip64ExtraId {
+			ret = append(ret, input[:4+size]...)
+		}
+		input = input[4+size:]
+	}
+
+	// Keep any trailing data
+	ret = append(ret, input...)
+
+	return ret
+}
+
 // CreateCompressedHeader adds a file to the zip file using the provied
 // FileHeader for the file metadata.
 // It returns a Writer to which the already compressed file contents
diff --git a/third_party/zip/android_test.go b/third_party/zip/android_test.go
new file mode 100644
index 0000000..cdf66ff
--- /dev/null
+++ b/third_party/zip/android_test.go
@@ -0,0 +1,71 @@
+// Copyright 2017 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package zip
+
+import (
+	"bytes"
+	"testing"
+)
+
+var stripZip64Testcases = []struct {
+	name string
+	in   []byte
+	out  []byte
+}{
+	{
+		name: "empty",
+		in:   []byte{},
+		out:  []byte{},
+	},
+	{
+		name: "trailing data",
+		in:   []byte{1, 2, 3},
+		out:  []byte{1, 2, 3},
+	},
+	{
+		name: "valid non-zip64 extra",
+		in:   []byte{2, 0, 2, 0, 1, 2},
+		out:  []byte{2, 0, 2, 0, 1, 2},
+	},
+	{
+		name: "two valid non-zip64 extras",
+		in:   []byte{2, 0, 2, 0, 1, 2, 2, 0, 0, 0},
+		out:  []byte{2, 0, 2, 0, 1, 2, 2, 0, 0, 0},
+	},
+	{
+		name: "simple zip64 extra",
+		in:   []byte{1, 0, 8, 0, 1, 2, 3, 4, 5, 6, 7, 8},
+		out:  []byte{},
+	},
+	{
+		name: "zip64 extra and valid non-zip64 extra",
+		in:   []byte{1, 0, 8, 0, 1, 2, 3, 4, 5, 6, 7, 8, 2, 0, 0, 0},
+		out:  []byte{2, 0, 0, 0},
+	},
+	{
+		name: "invalid extra",
+		in:   []byte{0, 0, 8, 0, 0, 0},
+		out:  []byte{0, 0, 8, 0, 0, 0},
+	},
+}
+
+func TestStripZip64Extras(t *testing.T) {
+	for _, testcase := range stripZip64Testcases {
+		got := stripZip64Extras(testcase.in)
+		if !bytes.Equal(got, testcase.out) {
+			t.Errorf("Failed testcase %s\ninput: %v\n want: %v\n  got: %v\n", testcase.name, testcase.in, testcase.out, got)
+		}
+	}
+}
diff --git a/third_party/zip/zip_test.go b/third_party/zip/zip_test.go
index 3a3c915..7373660 100644
--- a/third_party/zip/zip_test.go
+++ b/third_party/zip/zip_test.go
@@ -10,7 +10,6 @@
 	"bytes"
 	"fmt"
 	"hash"
-	"internal/testenv"
 	"io"
 	"io/ioutil"
 	"sort"
@@ -20,7 +19,7 @@
 )
 
 func TestOver65kFiles(t *testing.T) {
-	if testing.Short() && testenv.Builder() == "" {
+	if testing.Short() {
 		t.Skip("skipping in short mode")
 	}
 	buf := new(bytes.Buffer)
diff --git a/ui/build/Android.bp b/ui/build/Android.bp
new file mode 100644
index 0000000..51aed2c
--- /dev/null
+++ b/ui/build/Android.bp
@@ -0,0 +1,44 @@
+// Copyright 2017 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+bootstrap_go_package {
+    name: "soong-ui-build",
+    pkgPath: "android/soong/ui/build",
+    deps: [
+        "soong-ui-logger",
+        "soong-ui-tracer",
+    ],
+    srcs: [
+        "build.go",
+        "config.go",
+        "context.go",
+        "environment.go",
+        "kati.go",
+        "make.go",
+        "ninja.go",
+        "signal.go",
+        "soong.go",
+        "util.go",
+    ],
+    testSrcs: [
+        "environment_test.go",
+        "util_test.go",
+    ],
+    darwin: {
+        srcs: ["util_darwin.go"],
+    },
+    linux: {
+        srcs: ["util_linux.go"],
+    },
+}
diff --git a/ui/build/build.go b/ui/build/build.go
new file mode 100644
index 0000000..506ff51
--- /dev/null
+++ b/ui/build/build.go
@@ -0,0 +1,105 @@
+// Copyright 2017 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package build
+
+import (
+	"os"
+	"os/exec"
+	"path/filepath"
+	"text/template"
+)
+
+// Ensures the out directory exists, and has the proper files to prevent kati
+// from recursing into it.
+func SetupOutDir(ctx Context, config Config) {
+	ensureEmptyFileExists(ctx, filepath.Join(config.OutDir(), "Android.mk"))
+	ensureEmptyFileExists(ctx, filepath.Join(config.OutDir(), "CleanSpec.mk"))
+	ensureEmptyFileExists(ctx, filepath.Join(config.SoongOutDir(), ".soong.in_make"))
+	// The ninja_build file is used by our buildbots to understand that the output
+	// can be parsed as ninja output.
+	ensureEmptyFileExists(ctx, filepath.Join(config.OutDir(), "ninja_build"))
+}
+
+var combinedBuildNinjaTemplate = template.Must(template.New("combined").Parse(`
+builddir = {{.OutDir}}
+include {{.KatiNinjaFile}}
+include {{.SoongNinjaFile}}
+build {{.CombinedNinjaFile}}: phony {{.SoongNinjaFile}}
+`))
+
+func createCombinedBuildNinjaFile(ctx Context, config Config) {
+	file, err := os.Create(config.CombinedNinjaFile())
+	if err != nil {
+		ctx.Fatalln("Failed to create combined ninja file:", err)
+	}
+	defer file.Close()
+
+	if err := combinedBuildNinjaTemplate.Execute(file, config); err != nil {
+		ctx.Fatalln("Failed to write combined ninja file:", err)
+	}
+}
+
+const (
+	BuildNone          = iota
+	BuildProductConfig = 1 << iota
+	BuildSoong         = 1 << iota
+	BuildKati          = 1 << iota
+	BuildNinja         = 1 << iota
+	BuildAll           = BuildProductConfig | BuildSoong | BuildKati | BuildNinja
+)
+
+// Build the tree. The 'what' argument can be used to chose which components of
+// the build to run.
+func Build(ctx Context, config Config, what int) {
+	ctx.Verboseln("Starting build with args:", config.Arguments())
+	ctx.Verboseln("Environment:", config.Environment().Environ())
+
+	if inList("help", config.Arguments()) {
+		cmd := exec.CommandContext(ctx.Context, "make", "-f", "build/core/help.mk")
+		cmd.Env = config.Environment().Environ()
+		cmd.Stdout = ctx.Stdout()
+		cmd.Stderr = ctx.Stderr()
+		if err := cmd.Run(); err != nil {
+			ctx.Fatalln("Failed to run make:", err)
+		}
+		return
+	}
+
+	SetupOutDir(ctx, config)
+
+	if what&BuildProductConfig != 0 {
+		// Run make for product config
+		runMakeProductConfig(ctx, config)
+	}
+
+	if what&BuildSoong != 0 {
+		// Run Soong
+		runSoongBootstrap(ctx, config)
+		runSoong(ctx, config)
+	}
+
+	if what&BuildKati != 0 {
+		// Run ckati
+		runKati(ctx, config)
+	}
+
+	if what&BuildNinja != 0 {
+		// Write combined ninja file
+		createCombinedBuildNinjaFile(ctx, config)
+
+		// Run ninja
+		runNinja(ctx, config)
+	}
+}
diff --git a/ui/build/config.go b/ui/build/config.go
new file mode 100644
index 0000000..9a8a3fb
--- /dev/null
+++ b/ui/build/config.go
@@ -0,0 +1,316 @@
+// Copyright 2017 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package build
+
+import (
+	"log"
+	"os"
+	"path/filepath"
+	"runtime"
+	"strconv"
+	"strings"
+)
+
+type Config struct{ *configImpl }
+
+type configImpl struct {
+	// From the environment
+	arguments []string
+	goma      bool
+	environ   *Environment
+
+	// From the arguments
+	parallel  int
+	keepGoing int
+	verbose   bool
+	dist      bool
+
+	// From the product config
+	katiArgs   []string
+	ninjaArgs  []string
+	katiSuffix string
+}
+
+const srcDirFileCheck = "build/soong/root.bp"
+
+func NewConfig(ctx Context, args ...string) Config {
+	ret := &configImpl{
+		environ: OsEnvironment(),
+	}
+
+	// Make sure OUT_DIR is set appropriately
+	if _, ok := ret.environ.Get("OUT_DIR"); !ok {
+		outDir := "out"
+		if baseDir, ok := ret.environ.Get("OUT_DIR_COMMON_BASE"); ok {
+			if wd, err := os.Getwd(); err != nil {
+				ctx.Fatalln("Failed to get working directory:", err)
+			} else {
+				outDir = filepath.Join(baseDir, filepath.Base(wd))
+			}
+		}
+		ret.environ.Set("OUT_DIR", outDir)
+	}
+
+	ret.environ.Unset(
+		// We're already using it
+		"USE_SOONG_UI",
+
+		// We should never use GOROOT/GOPATH from the shell environment
+		"GOROOT",
+		"GOPATH",
+
+		// These should only come from Soong, not the environment.
+		"CLANG",
+		"CLANG_CXX",
+		"CCC_CC",
+		"CCC_CXX",
+
+		// Used by the goma compiler wrapper, but should only be set by
+		// gomacc
+		"GOMACC_PATH",
+
+		// We handle this above
+		"OUT_DIR_COMMON_BASE",
+	)
+
+	// Tell python not to spam the source tree with .pyc files.
+	ret.environ.Set("PYTHONDONTWRITEBYTECODE", "1")
+
+	// Sane default matching ninja
+	ret.parallel = runtime.NumCPU() + 2
+	ret.keepGoing = 1
+
+	// Precondition: the current directory is the top of the source tree
+	if _, err := os.Stat(srcDirFileCheck); err != nil {
+		if os.IsNotExist(err) {
+			log.Fatalf("Current working directory must be the source tree. %q not found", srcDirFileCheck)
+		}
+		log.Fatalln("Error verifying tree state:", err)
+	}
+
+	for _, arg := range args {
+		arg = strings.TrimSpace(arg)
+		if arg == "--make-mode" {
+			continue
+		} else if arg == "showcommands" {
+			ret.verbose = true
+			continue
+		} else if arg == "dist" {
+			ret.dist = true
+		}
+		if arg[0] == '-' {
+			var err error
+			if arg[1] == 'j' {
+				// TODO: handle space between j and number
+				// Unnecessary if used with makeparallel
+				ret.parallel, err = strconv.Atoi(arg[2:])
+			} else if arg[1] == 'k' {
+				// TODO: handle space between k and number
+				// Unnecessary if used with makeparallel
+				ret.keepGoing, err = strconv.Atoi(arg[2:])
+			} else {
+				ctx.Fatalln("Unknown option:", arg)
+			}
+			if err != nil {
+				ctx.Fatalln("Argument error:", err, arg)
+			}
+		} else {
+			ret.arguments = append(ret.arguments, arg)
+		}
+	}
+
+	return Config{ret}
+}
+
+// Lunch configures the environment for a specific product similarly to the
+// `lunch` bash function.
+func (c *configImpl) Lunch(ctx Context, product, variant string) {
+	if variant != "eng" && variant != "userdebug" && variant != "user" {
+		ctx.Fatalf("Invalid variant %q. Must be one of 'user', 'userdebug' or 'eng'", variant)
+	}
+
+	c.environ.Set("TARGET_PRODUCT", product)
+	c.environ.Set("TARGET_BUILD_VARIANT", variant)
+	c.environ.Set("TARGET_BUILD_TYPE", "release")
+	c.environ.Unset("TARGET_BUILD_APPS")
+}
+
+// Tapas configures the environment to build one or more unbundled apps,
+// similarly to the `tapas` bash function.
+func (c *configImpl) Tapas(ctx Context, apps []string, arch, variant string) {
+	if len(apps) == 0 {
+		apps = []string{"all"}
+	}
+	if variant == "" {
+		variant = "eng"
+	}
+
+	if variant != "eng" && variant != "userdebug" && variant != "user" {
+		ctx.Fatalf("Invalid variant %q. Must be one of 'user', 'userdebug' or 'eng'", variant)
+	}
+
+	var product string
+	switch arch {
+	case "armv5":
+		product = "generic_armv5"
+	case "arm", "":
+		product = "aosp_arm"
+	case "arm64":
+		product = "aosm_arm64"
+	case "mips":
+		product = "aosp_mips"
+	case "mips64":
+		product = "aosp_mips64"
+	case "x86":
+		product = "aosp_x86"
+	case "x86_64":
+		product = "aosp_x86_64"
+	default:
+		ctx.Fatalf("Invalid architecture: %q", arch)
+	}
+
+	c.environ.Set("TARGET_PRODUCT", product)
+	c.environ.Set("TARGET_BUILD_VARIANT", variant)
+	c.environ.Set("TARGET_BUILD_TYPE", "release")
+	c.environ.Set("TARGET_BUILD_APPS", strings.Join(apps, " "))
+}
+
+func (c *configImpl) Environment() *Environment {
+	return c.environ
+}
+
+func (c *configImpl) Arguments() []string {
+	return c.arguments
+}
+
+func (c *configImpl) OutDir() string {
+	if outDir, ok := c.environ.Get("OUT_DIR"); ok {
+		return outDir
+	}
+	return "out"
+}
+
+func (c *configImpl) DistDir() string {
+	if distDir, ok := c.environ.Get("DIST_DIR"); ok {
+		return distDir
+	}
+	return filepath.Join(c.OutDir(), "dist")
+}
+
+func (c *configImpl) NinjaArgs() []string {
+	return c.ninjaArgs
+}
+
+func (c *configImpl) SoongOutDir() string {
+	return filepath.Join(c.OutDir(), "soong")
+}
+
+func (c *configImpl) KatiSuffix() string {
+	if c.katiSuffix != "" {
+		return c.katiSuffix
+	}
+	panic("SetKatiSuffix has not been called")
+}
+
+func (c *configImpl) Dist() bool {
+	return c.dist
+}
+
+func (c *configImpl) IsVerbose() bool {
+	return c.verbose
+}
+
+func (c *configImpl) TargetProduct() string {
+	if v, ok := c.environ.Get("TARGET_PRODUCT"); ok {
+		return v
+	}
+	panic("TARGET_PRODUCT is not defined")
+}
+
+func (c *configImpl) KatiArgs() []string {
+	return c.katiArgs
+}
+
+func (c *configImpl) Parallel() int {
+	return c.parallel
+}
+
+func (c *configImpl) UseGoma() bool {
+	if v, ok := c.environ.Get("USE_GOMA"); ok {
+		v = strings.TrimSpace(v)
+		if v != "" && v != "false" {
+			return true
+		}
+	}
+	return false
+}
+
+// RemoteParallel controls how many remote jobs (i.e., commands which contain
+// gomacc) are run in parallel.  Note the paralleism of all other jobs is
+// still limited by Parallel()
+func (c *configImpl) RemoteParallel() int {
+	if v, ok := c.environ.Get("NINJA_REMOTE_NUM_JOBS"); ok {
+		if i, err := strconv.Atoi(v); err == nil {
+			return i
+		}
+	}
+	return 500
+}
+
+func (c *configImpl) SetKatiArgs(args []string) {
+	c.katiArgs = args
+}
+
+func (c *configImpl) SetNinjaArgs(args []string) {
+	c.ninjaArgs = args
+}
+
+func (c *configImpl) SetKatiSuffix(suffix string) {
+	c.katiSuffix = suffix
+}
+
+func (c *configImpl) KatiEnvFile() string {
+	return filepath.Join(c.OutDir(), "env"+c.KatiSuffix()+".sh")
+}
+
+func (c *configImpl) KatiNinjaFile() string {
+	return filepath.Join(c.OutDir(), "build"+c.KatiSuffix()+".ninja")
+}
+
+func (c *configImpl) SoongNinjaFile() string {
+	return filepath.Join(c.SoongOutDir(), "build.ninja")
+}
+
+func (c *configImpl) CombinedNinjaFile() string {
+	return filepath.Join(c.OutDir(), "combined"+c.KatiSuffix()+".ninja")
+}
+
+func (c *configImpl) SoongAndroidMk() string {
+	return filepath.Join(c.SoongOutDir(), "Android-"+c.TargetProduct()+".mk")
+}
+
+func (c *configImpl) SoongMakeVarsMk() string {
+	return filepath.Join(c.SoongOutDir(), "make_vars-"+c.TargetProduct()+".mk")
+}
+
+func (c *configImpl) HostPrebuiltTag() string {
+	if runtime.GOOS == "linux" {
+		return "linux-x86"
+	} else if runtime.GOOS == "darwin" {
+		return "darwin-x86"
+	} else {
+		panic("Unsupported OS")
+	}
+}
diff --git a/ui/build/context.go b/ui/build/context.go
new file mode 100644
index 0000000..f85bb6c
--- /dev/null
+++ b/ui/build/context.go
@@ -0,0 +1,104 @@
+// Copyright 2017 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package build
+
+import (
+	"context"
+	"io"
+	"os"
+	"time"
+
+	"android/soong/ui/logger"
+	"android/soong/ui/tracer"
+)
+
+type StdioInterface interface {
+	Stdin() io.Reader
+	Stdout() io.Writer
+	Stderr() io.Writer
+}
+
+type StdioImpl struct{}
+
+func (StdioImpl) Stdin() io.Reader  { return os.Stdin }
+func (StdioImpl) Stdout() io.Writer { return os.Stdout }
+func (StdioImpl) Stderr() io.Writer { return os.Stderr }
+
+var _ StdioInterface = StdioImpl{}
+
+type customStdio struct {
+	stdin  io.Reader
+	stdout io.Writer
+	stderr io.Writer
+}
+
+func NewCustomStdio(stdin io.Reader, stdout, stderr io.Writer) StdioInterface {
+	return customStdio{stdin, stdout, stderr}
+}
+
+func (c customStdio) Stdin() io.Reader  { return c.stdin }
+func (c customStdio) Stdout() io.Writer { return c.stdout }
+func (c customStdio) Stderr() io.Writer { return c.stderr }
+
+var _ StdioInterface = customStdio{}
+
+// Context combines a context.Context, logger.Logger, and StdIO redirection.
+// These all are agnostic of the current build, and may be used for multiple
+// builds, while the Config objects contain per-build information.
+type Context struct{ *ContextImpl }
+type ContextImpl struct {
+	context.Context
+	logger.Logger
+
+	StdioInterface
+
+	Thread tracer.Thread
+	Tracer tracer.Tracer
+}
+
+// BeginTrace starts a new Duration Event.
+func (c ContextImpl) BeginTrace(name string) {
+	if c.Tracer != nil {
+		c.Tracer.Begin(name, c.Thread)
+	}
+}
+
+// EndTrace finishes the last Duration Event.
+func (c ContextImpl) EndTrace() {
+	if c.Tracer != nil {
+		c.Tracer.End(c.Thread)
+	}
+}
+
+// CompleteTrace writes a trace with a beginning and end times.
+func (c ContextImpl) CompleteTrace(name string, begin, end uint64) {
+	if c.Tracer != nil {
+		c.Tracer.Complete(name, c.Thread, begin, end)
+	}
+}
+
+// ImportNinjaLog imports a .ninja_log file into the tracer.
+func (c ContextImpl) ImportNinjaLog(filename string, startOffset time.Time) {
+	if c.Tracer != nil {
+		c.Tracer.ImportNinjaLog(c.Thread, filename, startOffset)
+	}
+}
+
+func (c ContextImpl) IsTerminal() bool {
+	if term, ok := os.LookupEnv("TERM"); ok {
+		return term != "dumb" && isTerminal(c.Stdout()) && isTerminal(c.Stderr())
+	}
+	return false
+}
diff --git a/ui/build/environment.go b/ui/build/environment.go
new file mode 100644
index 0000000..baab101
--- /dev/null
+++ b/ui/build/environment.go
@@ -0,0 +1,152 @@
+// Copyright 2017 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package build
+
+import (
+	"bufio"
+	"fmt"
+	"io"
+	"os"
+	"strings"
+)
+
+// Environment adds a number of useful manipulation functions to the list of
+// strings returned by os.Environ() and used in exec.Cmd.Env.
+type Environment []string
+
+// OsEnvironment wraps the current environment returned by os.Environ()
+func OsEnvironment() *Environment {
+	env := Environment(os.Environ())
+	return &env
+}
+
+// Get returns the value associated with the key, and whether it exists.
+// It's equivalent to the os.LookupEnv function, but with this copy of the
+// Environment.
+func (e *Environment) Get(key string) (string, bool) {
+	for _, env := range *e {
+		if k, v, ok := decodeKeyValue(env); ok && k == key {
+			return v, true
+		}
+	}
+	return "", false
+}
+
+// Set sets the value associated with the key, overwriting the current value
+// if it exists.
+func (e *Environment) Set(key, value string) {
+	e.Unset(key)
+	*e = append(*e, key+"="+value)
+}
+
+// Unset removes the specified keys from the Environment.
+func (e *Environment) Unset(keys ...string) {
+	out := (*e)[:0]
+	for _, env := range *e {
+		if key, _, ok := decodeKeyValue(env); ok && inList(key, keys) {
+			continue
+		}
+		out = append(out, env)
+	}
+	*e = out
+}
+
+// Environ returns the []string required for exec.Cmd.Env
+func (e *Environment) Environ() []string {
+	return []string(*e)
+}
+
+// Copy returns a copy of the Environment so that independent changes may be made.
+func (e *Environment) Copy() *Environment {
+	ret := Environment(make([]string, len(*e)))
+	for i, v := range *e {
+		ret[i] = v
+	}
+	return &ret
+}
+
+// IsTrue returns whether an environment variable is set to a positive value (1,y,yes,on,true)
+func (e *Environment) IsEnvTrue(key string) bool {
+	if value, ok := e.Get(key); ok {
+		return value == "1" || value == "y" || value == "yes" || value == "on" || value == "true"
+	}
+	return false
+}
+
+// IsFalse returns whether an environment variable is set to a negative value (0,n,no,off,false)
+func (e *Environment) IsFalse(key string) bool {
+	if value, ok := e.Get(key); ok {
+		return value == "0" || value == "n" || value == "no" || value == "off" || value == "false"
+	}
+	return false
+}
+
+// AppendFromKati reads a shell script written by Kati that exports or unsets
+// environment variables, and applies those to the local Environment.
+func (e *Environment) AppendFromKati(filename string) error {
+	file, err := os.Open(filename)
+	if err != nil {
+		return err
+	}
+	defer file.Close()
+
+	return e.appendFromKati(file)
+}
+
+func (e *Environment) appendFromKati(reader io.Reader) error {
+	scanner := bufio.NewScanner(reader)
+	for scanner.Scan() {
+		text := strings.TrimSpace(scanner.Text())
+
+		if len(text) == 0 || text[0] == '#' {
+			continue
+		}
+
+		cmd := strings.SplitN(text, " ", 2)
+		if len(cmd) != 2 {
+			return fmt.Errorf("Unknown kati environment line: %q", text)
+		}
+
+		if cmd[0] == "unset" {
+			str, ok := singleUnquote(cmd[1])
+			if !ok {
+				fmt.Errorf("Failed to unquote kati line: %q", text)
+			}
+			e.Unset(str)
+		} else if cmd[0] == "export" {
+			key, value, ok := decodeKeyValue(cmd[1])
+			if !ok {
+				return fmt.Errorf("Failed to parse export: %v", cmd)
+			}
+
+			key, ok = singleUnquote(key)
+			if !ok {
+				return fmt.Errorf("Failed to unquote kati line: %q", text)
+			}
+			value, ok = singleUnquote(value)
+			if !ok {
+				return fmt.Errorf("Failed to unquote kati line: %q", text)
+			}
+
+			e.Set(key, value)
+		} else {
+			return fmt.Errorf("Unknown kati environment command: %q", text)
+		}
+	}
+	if err := scanner.Err(); err != nil {
+		return err
+	}
+	return nil
+}
diff --git a/ui/build/environment_test.go b/ui/build/environment_test.go
new file mode 100644
index 0000000..0294dac
--- /dev/null
+++ b/ui/build/environment_test.go
@@ -0,0 +1,80 @@
+// Copyright 2017 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package build
+
+import (
+	"reflect"
+	"strings"
+	"testing"
+)
+
+func TestEnvUnset(t *testing.T) {
+	initial := &Environment{"TEST=1", "TEST2=0"}
+	initial.Unset("TEST")
+	got := initial.Environ()
+	if len(got) != 1 || got[0] != "TEST2=0" {
+		t.Errorf("Expected [TEST2=0], got: %v", got)
+	}
+}
+
+func TestEnvUnsetMissing(t *testing.T) {
+	initial := &Environment{"TEST2=0"}
+	initial.Unset("TEST")
+	got := initial.Environ()
+	if len(got) != 1 || got[0] != "TEST2=0" {
+		t.Errorf("Expected [TEST2=0], got: %v", got)
+	}
+}
+
+func TestEnvSet(t *testing.T) {
+	initial := &Environment{}
+	initial.Set("TEST", "0")
+	got := initial.Environ()
+	if len(got) != 1 || got[0] != "TEST=0" {
+		t.Errorf("Expected [TEST=0], got: %v", got)
+	}
+}
+
+func TestEnvSetDup(t *testing.T) {
+	initial := &Environment{"TEST=1"}
+	initial.Set("TEST", "0")
+	got := initial.Environ()
+	if len(got) != 1 || got[0] != "TEST=0" {
+		t.Errorf("Expected [TEST=0], got: %v", got)
+	}
+}
+
+const testKatiEnvFileContents = `#!/bin/sh
+# Generated by kati unknown
+
+unset 'CLANG'
+export 'BUILD_ID'='NYC'
+`
+
+func TestEnvAppendFromKati(t *testing.T) {
+	initial := &Environment{"CLANG=/usr/bin/clang", "TEST=0"}
+	err := initial.appendFromKati(strings.NewReader(testKatiEnvFileContents))
+	if err != nil {
+		t.Fatalf("Unexpected error from %v", err)
+	}
+
+	got := initial.Environ()
+	expected := []string{"TEST=0", "BUILD_ID=NYC"}
+	if !reflect.DeepEqual(got, expected) {
+		t.Errorf("Environment list does not match")
+		t.Errorf("expected: %v", expected)
+		t.Errorf("     got: %v", got)
+	}
+}
diff --git a/ui/build/kati.go b/ui/build/kati.go
new file mode 100644
index 0000000..f990767
--- /dev/null
+++ b/ui/build/kati.go
@@ -0,0 +1,172 @@
+// Copyright 2017 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package build
+
+import (
+	"bufio"
+	"crypto/md5"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"os/exec"
+	"path/filepath"
+	"regexp"
+	"strconv"
+	"strings"
+)
+
+var spaceSlashReplacer = strings.NewReplacer("/", "_", " ", "_")
+
+// genKatiSuffix creates a suffix for kati-generated files so that we can cache
+// them based on their inputs. So this should encode all common changes to Kati
+// inputs. Currently that includes the TARGET_PRODUCT, kati-processed command
+// line arguments, and the directories specified by mm/mmm.
+func genKatiSuffix(ctx Context, config Config) {
+	katiSuffix := "-" + config.TargetProduct()
+	if args := config.KatiArgs(); len(args) > 0 {
+		katiSuffix += "-" + spaceSlashReplacer.Replace(strings.Join(args, "_"))
+	}
+	if oneShot, ok := config.Environment().Get("ONE_SHOT_MAKEFILE"); ok {
+		katiSuffix += "-" + spaceSlashReplacer.Replace(oneShot)
+	}
+
+	// If the suffix is too long, replace it with a md5 hash and write a
+	// file that contains the original suffix.
+	if len(katiSuffix) > 64 {
+		shortSuffix := "-" + fmt.Sprintf("%x", md5.Sum([]byte(katiSuffix)))
+		config.SetKatiSuffix(shortSuffix)
+
+		ctx.Verbosef("Kati ninja suffix too long: %q", katiSuffix)
+		ctx.Verbosef("Replacing with: %q", shortSuffix)
+
+		if err := ioutil.WriteFile(strings.TrimSuffix(config.KatiNinjaFile(), "ninja")+"suf", []byte(katiSuffix), 0777); err != nil {
+			ctx.Println("Error writing suffix file:", err)
+		}
+	} else {
+		config.SetKatiSuffix(katiSuffix)
+	}
+}
+
+func runKati(ctx Context, config Config) {
+	ctx.BeginTrace("kati")
+	defer ctx.EndTrace()
+
+	genKatiSuffix(ctx, config)
+
+	executable := "prebuilts/build-tools/" + config.HostPrebuiltTag() + "/bin/ckati"
+	args := []string{
+		"--ninja",
+		"--ninja_dir=" + config.OutDir(),
+		"--ninja_suffix=" + config.KatiSuffix(),
+		"--regen",
+		"--ignore_optional_include=" + filepath.Join(config.OutDir(), "%.P"),
+		"--detect_android_echo",
+		"--color_warnings",
+		"--gen_all_targets",
+		"-f", "build/core/main.mk",
+	}
+
+	if !config.Environment().IsFalse("KATI_EMULATE_FIND") {
+		args = append(args, "--use_find_emulator")
+	}
+
+	args = append(args, config.KatiArgs()...)
+
+	args = append(args,
+		"BUILDING_WITH_NINJA=true",
+		"SOONG_ANDROID_MK="+config.SoongAndroidMk(),
+		"SOONG_MAKEVARS_MK="+config.SoongMakeVarsMk())
+
+	if config.UseGoma() {
+		args = append(args, "-j"+strconv.Itoa(config.Parallel()))
+	}
+
+	cmd := exec.CommandContext(ctx.Context, executable, args...)
+	cmd.Env = config.Environment().Environ()
+	pipe, err := cmd.StdoutPipe()
+	if err != nil {
+		ctx.Fatalln("Error getting output pipe for ckati:", err)
+	}
+	cmd.Stderr = cmd.Stdout
+
+	ctx.Verboseln(cmd.Path, cmd.Args)
+	if err := cmd.Start(); err != nil {
+		ctx.Fatalln("Failed to run ckati:", err)
+	}
+
+	katiRewriteOutput(ctx, pipe)
+
+	if err := cmd.Wait(); err != nil {
+		if e, ok := err.(*exec.ExitError); ok {
+			ctx.Fatalln("ckati failed with:", e.ProcessState.String())
+		} else {
+			ctx.Fatalln("Failed to run ckati:", err)
+		}
+	}
+}
+
+var katiIncludeRe = regexp.MustCompile(`^(\[\d+/\d+] )?including [^ ]+ ...$`)
+
+func katiRewriteOutput(ctx Context, pipe io.ReadCloser) {
+	haveBlankLine := true
+	smartTerminal := ctx.IsTerminal()
+
+	scanner := bufio.NewScanner(pipe)
+	for scanner.Scan() {
+		line := scanner.Text()
+		verbose := katiIncludeRe.MatchString(line)
+
+		// For verbose lines, write them on the current line without a newline,
+		// then overwrite them if the next thing we're printing is another
+		// verbose line.
+		if smartTerminal && verbose {
+			// Limit line width to the terminal width, otherwise we'll wrap onto
+			// another line and we won't delete the previous line.
+			//
+			// Run this on every line in case the window has been resized while
+			// we're printing. This could be optimized to only re-run when we
+			// get SIGWINCH if it ever becomes too time consuming.
+			if max, ok := termWidth(ctx.Stdout()); ok {
+				if len(line) > max {
+					// Just do a max. Ninja elides the middle, but that's
+					// more complicated and these lines aren't that important.
+					line = line[:max]
+				}
+			}
+
+			// Move to the beginning on the line, print the output, then clear
+			// the rest of the line.
+			fmt.Fprint(ctx.Stdout(), "\r", line, "\x1b[K")
+			haveBlankLine = false
+			continue
+		} else if smartTerminal && !haveBlankLine {
+			// If we've previously written a verbose message, send a newline to save
+			// that message instead of overwriting it.
+			fmt.Fprintln(ctx.Stdout())
+			haveBlankLine = true
+		} else if !smartTerminal {
+			// Most editors display these as garbage, so strip them out.
+			line = string(stripAnsiEscapes([]byte(line)))
+		}
+
+		// Assume that non-verbose lines are important enough for stderr
+		fmt.Fprintln(ctx.Stderr(), line)
+	}
+
+	// Save our last verbose line.
+	if !haveBlankLine {
+		fmt.Fprintln(ctx.Stdout())
+	}
+}
diff --git a/ui/build/make.go b/ui/build/make.go
new file mode 100644
index 0000000..a8d4483
--- /dev/null
+++ b/ui/build/make.go
@@ -0,0 +1,164 @@
+// Copyright 2017 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package build
+
+import (
+	"fmt"
+	"os/exec"
+	"path/filepath"
+	"strings"
+)
+
+// DumpMakeVars can be used to extract the values of Make variables after the
+// product configurations are loaded. This is roughly equivalent to the
+// `get_build_var` bash function.
+//
+// goals can be used to set MAKECMDGOALS, which emulates passing arguments to
+// Make without actually building them. So all the variables based on
+// MAKECMDGOALS can be read.
+//
+// extra_targets adds real arguments to the make command, in case other targets
+// actually need to be run (like the Soong config generator).
+//
+// vars is the list of variables to read. The values will be put in the
+// returned map.
+func DumpMakeVars(ctx Context, config Config, goals, extra_targets, vars []string) (map[string]string, error) {
+	ctx.BeginTrace("dumpvars")
+	defer ctx.EndTrace()
+
+	cmd := exec.CommandContext(ctx.Context,
+		"make",
+		"--no-print-directory",
+		"-f", "build/core/config.mk",
+		"dump-many-vars",
+		"CALLED_FROM_SETUP=true",
+		"BUILD_SYSTEM=build/core",
+		"MAKECMDGOALS="+strings.Join(goals, " "),
+		"DUMP_MANY_VARS="+strings.Join(vars, " "),
+		"OUT_DIR="+config.OutDir())
+	cmd.Env = config.Environment().Environ()
+	cmd.Args = append(cmd.Args, extra_targets...)
+	// TODO: error out when Stderr contains any content
+	cmd.Stderr = ctx.Stderr()
+	ctx.Verboseln(cmd.Path, cmd.Args)
+	output, err := cmd.Output()
+	if err != nil {
+		return nil, err
+	}
+
+	ret := make(map[string]string, len(vars))
+	for _, line := range strings.Split(string(output), "\n") {
+		if len(line) == 0 {
+			continue
+		}
+
+		if key, value, ok := decodeKeyValue(line); ok {
+			if value, ok = singleUnquote(value); ok {
+				ret[key] = value
+				ctx.Verboseln(key, value)
+			} else {
+				return nil, fmt.Errorf("Failed to parse make line: %q", line)
+			}
+		} else {
+			return nil, fmt.Errorf("Failed to parse make line: %q", line)
+		}
+	}
+
+	return ret, nil
+}
+
+func runMakeProductConfig(ctx Context, config Config) {
+	// Variables to export into the environment of Kati/Ninja
+	exportEnvVars := []string{
+		// So that we can use the correct TARGET_PRODUCT if it's been
+		// modified by PRODUCT-* arguments
+		"TARGET_PRODUCT",
+
+		// compiler wrappers set up by make
+		"CC_WRAPPER",
+		"CXX_WRAPPER",
+		"JAVAC_WRAPPER",
+
+		// ccache settings
+		"CCACHE_COMPILERCHECK",
+		"CCACHE_SLOPPINESS",
+		"CCACHE_BASEDIR",
+		"CCACHE_CPP2",
+	}
+
+	// Variables to print out in the top banner
+	bannerVars := []string{
+		"PLATFORM_VERSION_CODENAME",
+		"PLATFORM_VERSION",
+		"TARGET_PRODUCT",
+		"TARGET_BUILD_VARIANT",
+		"TARGET_BUILD_TYPE",
+		"TARGET_BUILD_APPS",
+		"TARGET_ARCH",
+		"TARGET_ARCH_VARIANT",
+		"TARGET_CPU_VARIANT",
+		"TARGET_2ND_ARCH",
+		"TARGET_2ND_ARCH_VARIANT",
+		"TARGET_2ND_CPU_VARIANT",
+		"HOST_ARCH",
+		"HOST_2ND_ARCH",
+		"HOST_OS",
+		"HOST_OS_EXTRA",
+		"HOST_CROSS_OS",
+		"HOST_CROSS_ARCH",
+		"HOST_CROSS_2ND_ARCH",
+		"HOST_BUILD_TYPE",
+		"BUILD_ID",
+		"OUT_DIR",
+		"AUX_OS_VARIANT_LIST",
+		"TARGET_BUILD_PDK",
+		"PDK_FUSION_PLATFORM_ZIP",
+	}
+
+	allVars := append(append([]string{
+		// Used to execute Kati and Ninja
+		"NINJA_GOALS",
+		"KATI_GOALS",
+	}, exportEnvVars...), bannerVars...)
+
+	make_vars, err := DumpMakeVars(ctx, config, config.Arguments(), []string{
+		filepath.Join(config.SoongOutDir(), "soong.variables"),
+	}, allVars)
+	if err != nil {
+		ctx.Fatalln("Error dumping make vars:", err)
+	}
+
+	// Print the banner like make does
+	fmt.Fprintln(ctx.Stdout(), "============================================")
+	for _, name := range bannerVars {
+		if make_vars[name] != "" {
+			fmt.Fprintf(ctx.Stdout(), "%s=%s\n", name, make_vars[name])
+		}
+	}
+	fmt.Fprintln(ctx.Stdout(), "============================================")
+
+	// Populate the environment
+	env := config.Environment()
+	for _, name := range exportEnvVars {
+		if make_vars[name] == "" {
+			env.Unset(name)
+		} else {
+			env.Set(name, make_vars[name])
+		}
+	}
+
+	config.SetKatiArgs(strings.Fields(make_vars["KATI_GOALS"]))
+	config.SetNinjaArgs(strings.Fields(make_vars["NINJA_GOALS"]))
+}
diff --git a/ui/build/ninja.go b/ui/build/ninja.go
new file mode 100644
index 0000000..33f9a07
--- /dev/null
+++ b/ui/build/ninja.go
@@ -0,0 +1,85 @@
+// Copyright 2017 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package build
+
+import (
+	"os/exec"
+	"path/filepath"
+	"strconv"
+	"strings"
+	"time"
+)
+
+func runNinja(ctx Context, config Config) {
+	ctx.BeginTrace("ninja")
+	defer ctx.EndTrace()
+
+	executable := "prebuilts/build-tools/" + config.HostPrebuiltTag() + "/bin/ninja"
+	args := []string{
+		"-d", "keepdepfile",
+	}
+
+	args = append(args, config.NinjaArgs()...)
+
+	var parallel int
+	if config.UseGoma() {
+		parallel = config.RemoteParallel()
+	} else {
+		parallel = config.Parallel()
+	}
+	args = append(args, "-j", strconv.Itoa(parallel))
+	if config.keepGoing != 1 {
+		args = append(args, "-k", strconv.Itoa(config.keepGoing))
+	}
+
+	args = append(args, "-f", config.CombinedNinjaFile())
+
+	if config.IsVerbose() {
+		args = append(args, "-v")
+	}
+	args = append(args, "-w", "dupbuild=err")
+
+	env := config.Environment().Copy()
+	env.AppendFromKati(config.KatiEnvFile())
+
+	// Allow both NINJA_ARGS and NINJA_EXTRA_ARGS, since both have been
+	// used in the past to specify extra ninja arguments.
+	if extra, ok := env.Get("NINJA_ARGS"); ok {
+		args = append(args, strings.Fields(extra)...)
+	}
+	if extra, ok := env.Get("NINJA_EXTRA_ARGS"); ok {
+		args = append(args, strings.Fields(extra)...)
+	}
+
+	if _, ok := env.Get("NINJA_STATUS"); !ok {
+		env.Set("NINJA_STATUS", "[%p %f/%t] ")
+	}
+
+	cmd := exec.CommandContext(ctx.Context, executable, args...)
+	cmd.Env = env.Environ()
+	cmd.Stdin = ctx.Stdin()
+	cmd.Stdout = ctx.Stdout()
+	cmd.Stderr = ctx.Stderr()
+	ctx.Verboseln(cmd.Path, cmd.Args)
+	startTime := time.Now()
+	defer ctx.ImportNinjaLog(filepath.Join(config.OutDir(), ".ninja_log"), startTime)
+	if err := cmd.Run(); err != nil {
+		if e, ok := err.(*exec.ExitError); ok {
+			ctx.Fatalln("ninja failed with:", e.ProcessState.String())
+		} else {
+			ctx.Fatalln("Failed to run ninja:", err)
+		}
+	}
+}
diff --git a/ui/build/signal.go b/ui/build/signal.go
new file mode 100644
index 0000000..6643e2f
--- /dev/null
+++ b/ui/build/signal.go
@@ -0,0 +1,94 @@
+// Copyright 2017 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package build
+
+import (
+	"os"
+	"os/signal"
+	"runtime/debug"
+	"syscall"
+
+	"android/soong/ui/logger"
+	"time"
+)
+
+// SetupSignals sets up signal handling to ensure all of our subprocesses are killed and that
+// our log/trace buffers are flushed to disk.
+//
+// All of our subprocesses are in the same process group, so they'll receive a SIGINT at the
+// same time we do. Most of the time this means we just need to ignore the signal and we'll
+// just see errors from all of our subprocesses. But in case that fails, when we get a signal:
+//
+//   1. Wait two seconds to exit normally.
+//   2. Call cancel() which is normally the cancellation of a Context. This will send a SIGKILL
+//      to any subprocesses attached to that context.
+//   3. Wait two seconds to exit normally.
+//   4. Call cleanup() to close the log/trace buffers, then panic.
+//   5. If another two seconds passes (if cleanup got stuck, etc), then panic.
+//
+func SetupSignals(log logger.Logger, cancel, cleanup func()) {
+	signals := make(chan os.Signal, 5)
+	signal.Notify(signals, os.Interrupt, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM)
+	go handleSignals(signals, log, cancel, cleanup)
+}
+
+func handleSignals(signals chan os.Signal, log logger.Logger, cancel, cleanup func()) {
+	var timeouts int
+	var timeout <-chan time.Time
+
+	handleTimeout := func() {
+		timeouts += 1
+		switch timeouts {
+		case 1:
+			// Things didn't exit cleanly, cancel our ctx (SIGKILL to subprocesses)
+			// Do this asynchronously to ensure it won't block and prevent us from
+			// taking more drastic measures.
+			log.Println("Still alive, killing subprocesses...")
+			go cancel()
+		case 2:
+			// Cancel didn't work. Try to run cleanup manually, then we'll panic
+			// at the next timer whether it finished or not.
+			log.Println("Still alive, cleaning up...")
+
+			// Get all stacktraces to see what was stuck
+			debug.SetTraceback("all")
+
+			go func() {
+				defer log.Panicln("Timed out exiting...")
+				cleanup()
+			}()
+		default:
+			// In case cleanup() deadlocks, the next tick will panic.
+			log.Panicln("Got signal, but timed out exiting...")
+		}
+	}
+
+	for {
+		select {
+		case s := <-signals:
+			log.Println("Got signal:", s)
+
+			// Another signal triggers our next timeout handler early
+			if timeout != nil {
+				handleTimeout()
+			}
+
+			// Wait 2 seconds for everything to exit cleanly.
+			timeout = time.Tick(time.Second * 2)
+		case <-timeout:
+			handleTimeout()
+		}
+	}
+}
diff --git a/ui/build/soong.go b/ui/build/soong.go
new file mode 100644
index 0000000..6554f1d
--- /dev/null
+++ b/ui/build/soong.go
@@ -0,0 +1,64 @@
+// Copyright 2017 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package build
+
+import (
+	"os/exec"
+	"path/filepath"
+)
+
+func runSoongBootstrap(ctx Context, config Config) {
+	ctx.BeginTrace("bootstrap soong")
+	defer ctx.EndTrace()
+
+	cmd := exec.CommandContext(ctx.Context, "./bootstrap.bash")
+	env := config.Environment().Copy()
+	env.Set("BUILDDIR", config.SoongOutDir())
+	cmd.Env = env.Environ()
+	cmd.Stdout = ctx.Stdout()
+	cmd.Stderr = ctx.Stderr()
+	ctx.Verboseln(cmd.Path, cmd.Args)
+	if err := cmd.Run(); err != nil {
+		if e, ok := err.(*exec.ExitError); ok {
+			ctx.Fatalln("soong bootstrap failed with:", e.ProcessState.String())
+		} else {
+			ctx.Fatalln("Failed to run soong bootstrap:", err)
+		}
+	}
+}
+
+func runSoong(ctx Context, config Config) {
+	ctx.BeginTrace("soong")
+	defer ctx.EndTrace()
+
+	cmd := exec.CommandContext(ctx.Context, filepath.Join(config.SoongOutDir(), "soong"), "-w", "dupbuild=err")
+	if config.IsVerbose() {
+		cmd.Args = append(cmd.Args, "-v")
+	}
+	env := config.Environment().Copy()
+	env.Set("SKIP_NINJA", "true")
+	cmd.Env = env.Environ()
+	cmd.Stdin = ctx.Stdin()
+	cmd.Stdout = ctx.Stdout()
+	cmd.Stderr = ctx.Stderr()
+	ctx.Verboseln(cmd.Path, cmd.Args)
+	if err := cmd.Run(); err != nil {
+		if e, ok := err.(*exec.ExitError); ok {
+			ctx.Fatalln("soong bootstrap failed with:", e.ProcessState.String())
+		} else {
+			ctx.Fatalln("Failed to run soong bootstrap:", err)
+		}
+	}
+}
diff --git a/ui/build/util.go b/ui/build/util.go
new file mode 100644
index 0000000..37ac6b9
--- /dev/null
+++ b/ui/build/util.go
@@ -0,0 +1,161 @@
+// Copyright 2017 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package build
+
+import (
+	"bytes"
+	"io"
+	"os"
+	"path/filepath"
+	"strings"
+	"syscall"
+	"unsafe"
+)
+
+// indexList finds the index of a string in a []string
+func indexList(s string, list []string) int {
+	for i, l := range list {
+		if l == s {
+			return i
+		}
+	}
+
+	return -1
+}
+
+// inList determines whether a string is in a []string
+func inList(s string, list []string) bool {
+	return indexList(s, list) != -1
+}
+
+// ensureDirectoriesExist is a shortcut to os.MkdirAll, sending errors to the ctx logger.
+func ensureDirectoriesExist(ctx Context, dirs ...string) {
+	for _, dir := range dirs {
+		err := os.MkdirAll(dir, 0777)
+		if err != nil {
+			ctx.Fatalf("Error creating %s: %q\n", dir, err)
+		}
+	}
+}
+
+// ensureEmptyFileExists ensures that the containing directory exists, and the
+// specified file exists. If it doesn't exist, it will write an empty file.
+func ensureEmptyFileExists(ctx Context, file string) {
+	ensureDirectoriesExist(ctx, filepath.Dir(file))
+	if _, err := os.Stat(file); os.IsNotExist(err) {
+		f, err := os.Create(file)
+		if err != nil {
+			ctx.Fatalf("Error creating %s: %q\n", file, err)
+		}
+		f.Close()
+	} else if err != nil {
+		ctx.Fatalf("Error checking %s: %q\n", file, err)
+	}
+}
+
+// singleUnquote is similar to strconv.Unquote, but can handle multi-character strings inside single quotes.
+func singleUnquote(str string) (string, bool) {
+	if len(str) < 2 || str[0] != '\'' || str[len(str)-1] != '\'' {
+		return "", false
+	}
+	return str[1 : len(str)-1], true
+}
+
+// decodeKeyValue decodes a key=value string
+func decodeKeyValue(str string) (string, string, bool) {
+	idx := strings.IndexRune(str, '=')
+	if idx == -1 {
+		return "", "", false
+	}
+	return str[:idx], str[idx+1:], true
+}
+
+func isTerminal(w io.Writer) bool {
+	if f, ok := w.(*os.File); ok {
+		var termios syscall.Termios
+		_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, f.Fd(),
+			ioctlGetTermios, uintptr(unsafe.Pointer(&termios)),
+			0, 0, 0)
+		return err == 0
+	}
+	return false
+}
+
+func termWidth(w io.Writer) (int, bool) {
+	if f, ok := w.(*os.File); ok {
+		var winsize struct {
+			ws_row, ws_column    uint16
+			ws_xpixel, ws_ypixel uint16
+		}
+		_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, f.Fd(),
+			syscall.TIOCGWINSZ, uintptr(unsafe.Pointer(&winsize)),
+			0, 0, 0)
+		return int(winsize.ws_column), err == 0
+	}
+	return 0, false
+}
+
+// stripAnsiEscapes strips ANSI control codes from a byte array in place.
+func stripAnsiEscapes(input []byte) []byte {
+	// read represents the remaining part of input that needs to be processed.
+	read := input
+	// write represents where we should be writing in input.
+	// It will share the same backing store as input so that we make our modifications
+	// in place.
+	write := input
+
+	// advance will copy count bytes from read to write and advance those slices
+	advance := func(write, read []byte, count int) ([]byte, []byte) {
+		copy(write, read[:count])
+		return write[count:], read[count:]
+	}
+
+	for {
+		// Find the next escape sequence
+		i := bytes.IndexByte(read, 0x1b)
+		// If it isn't found, or if there isn't room for <ESC>[, finish
+		if i == -1 || i+1 >= len(read) {
+			copy(write, read)
+			break
+		}
+
+		// Not a CSI code, continue searching
+		if read[i+1] != '[' {
+			write, read = advance(write, read, i+1)
+			continue
+		}
+
+		// Found a CSI code, advance up to the <ESC>
+		write, read = advance(write, read, i)
+
+		// Find the end of the CSI code
+		i = bytes.IndexFunc(read, func(r rune) bool {
+			return (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z')
+		})
+		if i == -1 {
+			// We didn't find the end of the code, just remove the rest
+			i = len(read) - 1
+		}
+
+		// Strip off the end marker too
+		i = i + 1
+
+		// Skip the reader forward and reduce final length by that amount
+		read = read[i:]
+		input = input[:len(input)-i]
+	}
+
+	return input
+}
diff --git a/ui/build/util_darwin.go b/ui/build/util_darwin.go
new file mode 100644
index 0000000..254a9b8
--- /dev/null
+++ b/ui/build/util_darwin.go
@@ -0,0 +1,21 @@
+// Copyright 2017 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package build
+
+import (
+	"syscall"
+)
+
+const ioctlGetTermios = syscall.TIOCGETA
diff --git a/ui/build/util_linux.go b/ui/build/util_linux.go
new file mode 100644
index 0000000..0a4e1d2
--- /dev/null
+++ b/ui/build/util_linux.go
@@ -0,0 +1,21 @@
+// Copyright 2017 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package build
+
+import (
+	"syscall"
+)
+
+const ioctlGetTermios = syscall.TCGETS
diff --git a/ui/build/util_test.go b/ui/build/util_test.go
new file mode 100644
index 0000000..e85eada
--- /dev/null
+++ b/ui/build/util_test.go
@@ -0,0 +1,62 @@
+// Copyright 2017 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package build
+
+import "testing"
+
+func TestStripAnsiEscapes(t *testing.T) {
+	testcases := []struct {
+		input  string
+		output string
+	}{
+		{
+			"",
+			"",
+		},
+		{
+			"This is a test",
+			"This is a test",
+		},
+		{
+			"interrupted: \x1b[12",
+			"interrupted: ",
+		},
+		{
+			"other \x1bescape \x1b",
+			"other \x1bescape \x1b",
+		},
+		{ // from pretty-error macro
+			"\x1b[1mart/Android.mk: \x1b[31merror:\x1b[0m\x1b[1m art: test error \x1b[0m",
+			"art/Android.mk: error: art: test error ",
+		},
+		{ // from envsetup.sh make wrapper
+			"\x1b[0;31m#### make failed to build some targets (2 seconds) ####\x1b[00m",
+			"#### make failed to build some targets (2 seconds) ####",
+		},
+		{ // from clang (via ninja testcase)
+			"\x1b[1maffixmgr.cxx:286:15: \x1b[0m\x1b[0;1;35mwarning: \x1b[0m\x1b[1musing the result... [-Wparentheses]\x1b[0m",
+			"affixmgr.cxx:286:15: warning: using the result... [-Wparentheses]",
+		},
+	}
+	for _, tc := range testcases {
+		got := string(stripAnsiEscapes([]byte(tc.input)))
+		if got != tc.output {
+			t.Errorf("output strings didn't match\n"+
+				"input: %#v\n"+
+				" want: %#v\n"+
+				"  got: %#v", tc.input, tc.output, got)
+		}
+	}
+}
diff --git a/ui/logger/Android.bp b/ui/logger/Android.bp
new file mode 100644
index 0000000..8091ef9
--- /dev/null
+++ b/ui/logger/Android.bp
@@ -0,0 +1,24 @@
+// Copyright 2017 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+bootstrap_go_package {
+    name: "soong-ui-logger",
+    pkgPath: "android/soong/ui/logger",
+    srcs: [
+        "logger.go",
+    ],
+    testSrcs: [
+        "logger_test.go",
+    ],
+}
diff --git a/ui/logger/logger.go b/ui/logger/logger.go
new file mode 100644
index 0000000..db7e82a
--- /dev/null
+++ b/ui/logger/logger.go
@@ -0,0 +1,302 @@
+// Copyright 2017 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package logger implements a logging package designed for command line
+// utilities.  It uses the standard 'log' package and function, but splits
+// output between stderr and a rotating log file.
+//
+// In addition to the standard logger functions, Verbose[f|ln] calls only go to
+// the log file by default, unless SetVerbose(true) has been called.
+//
+// The log file also includes extended date/time/source information, which are
+// omitted from the stderr output for better readability.
+//
+// In order to better handle resource cleanup after a Fatal error, the Fatal
+// functions panic instead of calling os.Exit(). To actually do the cleanup,
+// and prevent the printing of the panic, call defer logger.Cleanup() at the
+// beginning of your main function.
+package logger
+
+import (
+	"errors"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"log"
+	"os"
+	"path/filepath"
+	"strconv"
+	"sync"
+)
+
+type Logger interface {
+	// Print* prints to both stderr and the file log.
+	// Arguments to Print are handled in the manner of fmt.Print.
+	Print(v ...interface{})
+	// Arguments to Printf are handled in the manner of fmt.Printf
+	Printf(format string, v ...interface{})
+	// Arguments to Println are handled in the manner of fmt.Println
+	Println(v ...interface{})
+
+	// Verbose* is equivalent to Print*, but skips stderr unless the
+	// logger has been configured in verbose mode.
+	Verbose(v ...interface{})
+	Verbosef(format string, v ...interface{})
+	Verboseln(v ...interface{})
+
+	// Fatal* is equivalent to Print* followed by a call to panic that
+	// can be converted to an error using Recover, or will be converted
+	// to a call to os.Exit(1) with a deferred call to Cleanup()
+	Fatal(v ...interface{})
+	Fatalf(format string, v ...interface{})
+	Fatalln(v ...interface{})
+
+	// Panic is equivalent to Print* followed by a call to panic.
+	Panic(v ...interface{})
+	Panicf(format string, v ...interface{})
+	Panicln(v ...interface{})
+
+	// Output writes the string to both stderr and the file log.
+	Output(calldepth int, str string) error
+}
+
+// fatalLog is the type used when Fatal[f|ln]
+type fatalLog error
+
+func fileRotation(from, baseName, ext string, cur, max int) error {
+	newName := baseName + "." + strconv.Itoa(cur) + ext
+
+	if _, err := os.Lstat(newName); err == nil {
+		if cur+1 <= max {
+			fileRotation(newName, baseName, ext, cur+1, max)
+		}
+	}
+
+	if err := os.Rename(from, newName); err != nil {
+		return fmt.Errorf("Failed to rotate", from, "to", newName, ".", err)
+	}
+	return nil
+}
+
+// CreateFileWithRotation returns a new os.File using os.Create, renaming any
+// existing files to <filename>.#.<ext>, keeping up to maxCount files.
+// <filename>.1.<ext> is the most recent backup, <filename>.2.<ext> is the
+// second most recent backup, etc.
+//
+// TODO: This function is not guaranteed to be atomic, if there are multiple
+// users attempting to do the same operation, the result is undefined.
+func CreateFileWithRotation(filename string, maxCount int) (*os.File, error) {
+	if _, err := os.Lstat(filename); err == nil {
+		ext := filepath.Ext(filename)
+		basename := filename[:len(filename)-len(ext)]
+		if err = fileRotation(filename, basename, ext, 1, maxCount); err != nil {
+			return nil, err
+		}
+	}
+
+	return os.Create(filename)
+}
+
+// Recover can be used with defer in a GoRoutine to convert a Fatal panics to
+// an error that can be handled.
+func Recover(fn func(err error)) {
+	p := recover()
+
+	if p == nil {
+		return
+	} else if log, ok := p.(fatalLog); ok {
+		fn(error(log))
+	} else {
+		panic(p)
+	}
+}
+
+type stdLogger struct {
+	stderr  *log.Logger
+	verbose bool
+
+	fileLogger *log.Logger
+	mutex      sync.Mutex
+	file       *os.File
+}
+
+var _ Logger = &stdLogger{}
+
+// New creates a new Logger. The out variable sets the destination, commonly
+// os.Stderr, but it may be a buffer for tests, or a separate log file if
+// the user doesn't need to see the output.
+func New(out io.Writer) *stdLogger {
+	return &stdLogger{
+		stderr:     log.New(out, "", log.Ltime),
+		fileLogger: log.New(ioutil.Discard, "", log.Ldate|log.Lmicroseconds|log.Llongfile),
+	}
+}
+
+// SetVerbose controls whether Verbose[f|ln] logs to stderr as well as the
+// file-backed log.
+func (s *stdLogger) SetVerbose(v bool) {
+	s.verbose = v
+}
+
+// SetOutput controls where the file-backed log will be saved. It will keep
+// some number of backups of old log files.
+func (s *stdLogger) SetOutput(path string) {
+	if f, err := CreateFileWithRotation(path, 5); err == nil {
+		s.mutex.Lock()
+		defer s.mutex.Unlock()
+
+		if s.file != nil {
+			s.file.Close()
+		}
+		s.file = f
+		s.fileLogger.SetOutput(f)
+	} else {
+		s.Fatal(err.Error())
+	}
+}
+
+// Close disables logging to the file and closes the file handle.
+func (s *stdLogger) Close() {
+	s.mutex.Lock()
+	defer s.mutex.Unlock()
+	if s.file != nil {
+		s.fileLogger.SetOutput(ioutil.Discard)
+		s.file.Close()
+		s.file = nil
+	}
+}
+
+// Cleanup should be used with defer in your main function. It will close the
+// log file and convert any Fatal panics back to os.Exit(1)
+func (s *stdLogger) Cleanup() {
+	fatal := false
+	p := recover()
+
+	if _, ok := p.(fatalLog); ok {
+		fatal = true
+		p = nil
+	} else if p != nil {
+		s.Println(p)
+	}
+
+	s.Close()
+
+	if p != nil {
+		panic(p)
+	} else if fatal {
+		os.Exit(1)
+	}
+}
+
+// Output writes string to both stderr and the file log.
+func (s *stdLogger) Output(calldepth int, str string) error {
+	s.stderr.Output(calldepth+1, str)
+	return s.fileLogger.Output(calldepth+1, str)
+}
+
+// VerboseOutput is equivalent to Output, but only goes to the file log
+// unless SetVerbose(true) has been called.
+func (s *stdLogger) VerboseOutput(calldepth int, str string) error {
+	if s.verbose {
+		s.stderr.Output(calldepth+1, str)
+	}
+	return s.fileLogger.Output(calldepth+1, str)
+}
+
+// Print prints to both stderr and the file log.
+// Arguments are handled in the manner of fmt.Print.
+func (s *stdLogger) Print(v ...interface{}) {
+	output := fmt.Sprint(v...)
+	s.Output(2, output)
+}
+
+// Printf prints to both stderr and the file log.
+// Arguments are handled in the manner of fmt.Printf.
+func (s *stdLogger) Printf(format string, v ...interface{}) {
+	output := fmt.Sprintf(format, v...)
+	s.Output(2, output)
+}
+
+// Println prints to both stderr and the file log.
+// Arguments are handled in the manner of fmt.Println.
+func (s *stdLogger) Println(v ...interface{}) {
+	output := fmt.Sprintln(v...)
+	s.Output(2, output)
+}
+
+// Verbose is equivalent to Print, but only goes to the file log unless
+// SetVerbose(true) has been called.
+func (s *stdLogger) Verbose(v ...interface{}) {
+	output := fmt.Sprint(v...)
+	s.VerboseOutput(2, output)
+}
+
+// Verbosef is equivalent to Printf, but only goes to the file log unless
+// SetVerbose(true) has been called.
+func (s *stdLogger) Verbosef(format string, v ...interface{}) {
+	output := fmt.Sprintf(format, v...)
+	s.VerboseOutput(2, output)
+}
+
+// Verboseln is equivalent to Println, but only goes to the file log unless
+// SetVerbose(true) has been called.
+func (s *stdLogger) Verboseln(v ...interface{}) {
+	output := fmt.Sprintln(v...)
+	s.VerboseOutput(2, output)
+}
+
+// Fatal is equivalent to Print() followed by a call to panic() that
+// Cleanup will convert to a os.Exit(1).
+func (s *stdLogger) Fatal(v ...interface{}) {
+	output := fmt.Sprint(v...)
+	s.Output(2, output)
+	panic(fatalLog(errors.New(output)))
+}
+
+// Fatalf is equivalent to Printf() followed by a call to panic() that
+// Cleanup will convert to a os.Exit(1).
+func (s *stdLogger) Fatalf(format string, v ...interface{}) {
+	output := fmt.Sprintf(format, v...)
+	s.Output(2, output)
+	panic(fatalLog(errors.New(output)))
+}
+
+// Fatalln is equivalent to Println() followed by a call to panic() that
+// Cleanup will convert to a os.Exit(1).
+func (s *stdLogger) Fatalln(v ...interface{}) {
+	output := fmt.Sprintln(v...)
+	s.Output(2, output)
+	panic(fatalLog(errors.New(output)))
+}
+
+// Panic is equivalent to Print() followed by a call to panic().
+func (s *stdLogger) Panic(v ...interface{}) {
+	output := fmt.Sprint(v...)
+	s.Output(2, output)
+	panic(output)
+}
+
+// Panicf is equivalent to Printf() followed by a call to panic().
+func (s *stdLogger) Panicf(format string, v ...interface{}) {
+	output := fmt.Sprintf(format, v...)
+	s.Output(2, output)
+	panic(output)
+}
+
+// Panicln is equivalent to Println() followed by a call to panic().
+func (s *stdLogger) Panicln(v ...interface{}) {
+	output := fmt.Sprintln(v...)
+	s.Output(2, output)
+	panic(output)
+}
diff --git a/ui/logger/logger_test.go b/ui/logger/logger_test.go
new file mode 100644
index 0000000..0f88ab3
--- /dev/null
+++ b/ui/logger/logger_test.go
@@ -0,0 +1,198 @@
+// Copyright 2017 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package logger
+
+import (
+	"bytes"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"reflect"
+	"sort"
+	"syscall"
+	"testing"
+)
+
+func TestCreateFileWithRotation(t *testing.T) {
+	dir, err := ioutil.TempDir("", "test-rotation")
+	if err != nil {
+		t.Fatalf("Failed to get TempDir: %v", err)
+	}
+	defer os.RemoveAll(dir)
+
+	file := filepath.Join(dir, "build.log")
+
+	writeFile := func(name string, data string) {
+		f, err := CreateFileWithRotation(name, 3)
+		if err != nil {
+			t.Fatalf("Failed to create file: %v", err)
+		}
+		if n, err := io.WriteString(f, data); err == nil && n < len(data) {
+			t.Fatalf("Short write")
+		} else if err != nil {
+			t.Fatalf("Failed to write: %v", err)
+		}
+		if err := f.Close(); err != nil {
+			t.Fatalf("Failed to close: %v", err)
+		}
+	}
+
+	writeFile(file, "a")
+	writeFile(file, "b")
+	writeFile(file, "c")
+	writeFile(file, "d")
+	writeFile(file, "e")
+
+	d, err := os.Open(dir)
+	if err != nil {
+		t.Fatalf("Failed to open dir: %v", err)
+	}
+	names, err := d.Readdirnames(0)
+	if err != nil {
+		t.Fatalf("Failed to read dir: %v", err)
+	}
+	sort.Strings(names)
+	expected := []string{"build.1.log", "build.2.log", "build.3.log", "build.log"}
+	if !reflect.DeepEqual(names, expected) {
+		t.Errorf("File list does not match.")
+		t.Errorf("     got: %v", names)
+		t.Errorf("expected: %v", expected)
+		t.FailNow()
+	}
+
+	expectFileContents := func(name, expected string) {
+		data, err := ioutil.ReadFile(filepath.Join(dir, name))
+		if err != nil {
+			t.Errorf("Error reading file: %v", err)
+			return
+		}
+		str := string(data)
+		if str != expected {
+			t.Errorf("Contents of %v does not match.", name)
+			t.Errorf("     got: %v", data)
+			t.Errorf("expected: %v", expected)
+		}
+	}
+
+	expectFileContents("build.log", "e")
+	expectFileContents("build.1.log", "d")
+	expectFileContents("build.2.log", "c")
+	expectFileContents("build.3.log", "b")
+}
+
+func TestPanic(t *testing.T) {
+	if os.Getenv("ACTUALLY_PANIC") == "1" {
+		panicValue := "foo"
+		log := New(&bytes.Buffer{})
+
+		defer func() {
+			p := recover()
+
+			if p == panicValue {
+				os.Exit(42)
+			} else {
+				fmt.Fprintln(os.Stderr, "Expected %q, got %v", panicValue, p)
+				os.Exit(3)
+			}
+		}()
+		defer log.Cleanup()
+
+		log.Panic(panicValue)
+		os.Exit(2)
+		return
+	}
+
+	// Run this in an external process so that we don't pollute stderr
+	cmd := exec.Command(os.Args[0], "-test.run=TestPanic")
+	cmd.Env = append(os.Environ(), "ACTUALLY_PANIC=1")
+	err := cmd.Run()
+	if e, ok := err.(*exec.ExitError); ok && e.Sys().(syscall.WaitStatus).ExitStatus() == 42 {
+		return
+	}
+	t.Errorf("Expected process to exit with status 42, got %v", err)
+}
+
+func TestFatal(t *testing.T) {
+	if os.Getenv("ACTUALLY_FATAL") == "1" {
+		log := New(&bytes.Buffer{})
+		defer func() {
+			// Shouldn't get here
+			os.Exit(3)
+		}()
+		defer log.Cleanup()
+		log.Fatal("Test")
+		os.Exit(0)
+		return
+	}
+
+	cmd := exec.Command(os.Args[0], "-test.run=TestFatal")
+	cmd.Env = append(os.Environ(), "ACTUALLY_FATAL=1")
+	err := cmd.Run()
+	if e, ok := err.(*exec.ExitError); ok && e.Sys().(syscall.WaitStatus).ExitStatus() == 1 {
+		return
+	}
+	t.Errorf("Expected process to exit with status 1, got %v", err)
+}
+
+func TestNonFatal(t *testing.T) {
+	if os.Getenv("ACTUAL_TEST") == "1" {
+		log := New(&bytes.Buffer{})
+		defer log.Cleanup()
+		log.Println("Test")
+		return
+	}
+
+	cmd := exec.Command(os.Args[0], "-test.run=TestNonFatal")
+	cmd.Env = append(os.Environ(), "ACTUAL_TEST=1")
+	err := cmd.Run()
+	if e, ok := err.(*exec.ExitError); ok || (ok && !e.Success()) {
+		t.Errorf("Expected process to exit cleanly, got %v", err)
+	}
+}
+
+func TestRecoverFatal(t *testing.T) {
+	log := New(&bytes.Buffer{})
+	defer func() {
+		if p := recover(); p != nil {
+			t.Errorf("Unexpected panic: %#v", p)
+		}
+	}()
+	defer Recover(func(err error) {
+		if err.Error() != "Test" {
+			t.Errorf("Expected %q, but got %q", "Test", err.Error())
+		}
+	})
+	log.Fatal("Test")
+	t.Errorf("Should not get here")
+}
+
+func TestRecoverNonFatal(t *testing.T) {
+	log := New(&bytes.Buffer{})
+	defer func() {
+		if p := recover(); p == nil {
+			t.Errorf("Panic not thrown")
+		} else if p != "Test" {
+			t.Errorf("Expected %q, but got %#v", "Test", p)
+		}
+	}()
+	defer Recover(func(err error) {
+		t.Errorf("Recover function should not be called")
+	})
+	log.Panic("Test")
+	t.Errorf("Should not get here")
+}
diff --git a/ui/tracer/Android.bp b/ui/tracer/Android.bp
new file mode 100644
index 0000000..89812a1
--- /dev/null
+++ b/ui/tracer/Android.bp
@@ -0,0 +1,23 @@
+// 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.
+
+bootstrap_go_package {
+    name: "soong-ui-tracer",
+    pkgPath: "android/soong/ui/tracer",
+    deps: ["soong-ui-logger"],
+    srcs: [
+        "ninja.go",
+        "tracer.go",
+    ],
+}
diff --git a/ui/tracer/ninja.go b/ui/tracer/ninja.go
new file mode 100644
index 0000000..da558f1
--- /dev/null
+++ b/ui/tracer/ninja.go
@@ -0,0 +1,130 @@
+// 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 tracer
+
+import (
+	"bufio"
+	"os"
+	"sort"
+	"strconv"
+	"strings"
+	"time"
+)
+
+type ninjaLogEntry struct {
+	Name  string
+	Begin int
+	End   int
+}
+type ninjaLogEntries []*ninjaLogEntry
+
+func (n ninjaLogEntries) Len() int           { return len(n) }
+func (n ninjaLogEntries) Less(i, j int) bool { return n[i].Begin < n[j].Begin }
+func (n ninjaLogEntries) Swap(i, j int)      { n[i], n[j] = n[j], n[i] }
+
+// ImportNinjaLog reads a .ninja_log file from ninja and writes the events out
+// to the trace.
+//
+// startOffset is when the ninja process started, and is used to position the
+// relative times from the ninja log into the trace. It's also used to skip
+// reading the ninja log if nothing was run.
+func (t *tracerImpl) ImportNinjaLog(thread Thread, filename string, startOffset time.Time) {
+	t.Begin("ninja log import", thread)
+	defer t.End(thread)
+
+	if stat, err := os.Stat(filename); err != nil {
+		t.log.Println("Missing ninja log:", err)
+		return
+	} else if stat.ModTime().Before(startOffset) {
+		t.log.Verboseln("Ninja log not modified, not importing any entries.")
+		return
+	}
+
+	f, err := os.Open(filename)
+	if err != nil {
+		t.log.Println("Error opening ninja log:", err)
+		return
+	}
+	defer f.Close()
+
+	s := bufio.NewScanner(f)
+	header := true
+	entries := ninjaLogEntries{}
+	prevEnd := 0
+	for s.Scan() {
+		if header {
+			hdr := s.Text()
+			if hdr != "# ninja log v5" {
+				t.log.Printf("Unknown ninja log header: %q", hdr)
+				return
+			}
+			header = false
+			continue
+		}
+
+		fields := strings.Split(s.Text(), "\t")
+		begin, err := strconv.Atoi(fields[0])
+		if err != nil {
+			t.log.Printf("Unable to parse ninja entry %q: %v", s.Text(), err)
+			return
+		}
+		end, err := strconv.Atoi(fields[1])
+		if err != nil {
+			t.log.Printf("Unable to parse ninja entry %q: %v", s.Text(), err)
+			return
+		}
+		if end < prevEnd {
+			entries = nil
+		}
+		prevEnd = end
+		entries = append(entries, &ninjaLogEntry{
+			Name:  fields[3],
+			Begin: begin,
+			End:   end,
+		})
+	}
+	if err := s.Err(); err != nil {
+		t.log.Println("Unable to parse ninja log:", err)
+		return
+	}
+
+	sort.Sort(entries)
+
+	cpus := []int{}
+	offset := uint64(startOffset.UnixNano()) / 1000
+	for _, entry := range entries {
+		tid := -1
+		for cpu, endTime := range cpus {
+			if endTime <= entry.Begin {
+				tid = cpu
+				cpus[cpu] = entry.End
+				break
+			}
+		}
+		if tid == -1 {
+			tid = len(cpus)
+			cpus = append(cpus, entry.End)
+		}
+
+		t.writeEvent(&viewerEvent{
+			Name:  entry.Name,
+			Phase: "X",
+			Time:  offset + uint64(entry.Begin)*1000,
+			Dur:   uint64(entry.End-entry.Begin) * 1000,
+			Pid:   1,
+			Tid:   uint64(tid),
+		})
+	}
+}
diff --git a/ui/tracer/tracer.go b/ui/tracer/tracer.go
new file mode 100644
index 0000000..b372885
--- /dev/null
+++ b/ui/tracer/tracer.go
@@ -0,0 +1,244 @@
+// Copyright 2017 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// This package implements a trace file writer, whose files can be opened in
+// chrome://tracing.
+//
+// It implements the JSON Array Format defined here:
+// https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/edit
+package tracer
+
+import (
+	"bytes"
+	"compress/gzip"
+	"encoding/json"
+	"fmt"
+	"io"
+	"os"
+	"strings"
+	"sync"
+	"time"
+
+	"android/soong/ui/logger"
+)
+
+type Thread uint64
+
+const (
+	MainThread     = Thread(iota)
+	MaxInitThreads = Thread(iota)
+)
+
+type Tracer interface {
+	Begin(name string, thread Thread)
+	End(thread Thread)
+	Complete(name string, thread Thread, begin, end uint64)
+
+	ImportNinjaLog(thread Thread, filename string, startOffset time.Time)
+}
+
+type tracerImpl struct {
+	lock sync.Mutex
+	log  logger.Logger
+
+	buf  bytes.Buffer
+	file *os.File
+	w    io.WriteCloser
+
+	firstEvent bool
+	nextTid    uint64
+}
+
+var _ Tracer = &tracerImpl{}
+
+type viewerEvent struct {
+	Name  string      `json:"name,omitempty"`
+	Phase string      `json:"ph"`
+	Scope string      `json:"s,omitempty"`
+	Time  uint64      `json:"ts"`
+	Dur   uint64      `json:"dur,omitempty"`
+	Pid   uint64      `json:"pid"`
+	Tid   uint64      `json:"tid"`
+	ID    uint64      `json:"id,omitempty"`
+	Arg   interface{} `json:"args,omitempty"`
+}
+
+type nameArg struct {
+	Name string `json:"name"`
+}
+
+type nopCloser struct{ io.Writer }
+
+func (nopCloser) Close() error { return nil }
+
+// New creates a new Tracer, storing log in order to log errors later.
+// Events are buffered in memory until SetOutput is called.
+func New(log logger.Logger) *tracerImpl {
+	ret := &tracerImpl{
+		log: log,
+
+		firstEvent: true,
+		nextTid:    uint64(MaxInitThreads),
+	}
+	ret.startBuffer()
+
+	return ret
+}
+
+func (t *tracerImpl) startBuffer() {
+	t.w = nopCloser{&t.buf}
+	fmt.Fprintln(t.w, "[")
+
+	t.defineThread(MainThread, "main")
+}
+
+func (t *tracerImpl) close() {
+	if t.file != nil {
+		fmt.Fprintln(t.w, "]")
+
+		if err := t.w.Close(); err != nil {
+			t.log.Println("Error closing trace writer:", err)
+		}
+
+		if err := t.file.Close(); err != nil {
+			t.log.Println("Error closing trace file:", err)
+		}
+		t.file = nil
+		t.startBuffer()
+	}
+}
+
+// SetOutput creates the output file (rotating old files).
+func (t *tracerImpl) SetOutput(filename string) {
+	t.lock.Lock()
+	defer t.lock.Unlock()
+
+	t.close()
+
+	// chrome://tracing requires that compressed trace files end in .gz
+	if !strings.HasSuffix(filename, ".gz") {
+		filename += ".gz"
+	}
+
+	f, err := logger.CreateFileWithRotation(filename, 5)
+	if err != nil {
+		t.log.Println("Failed to create trace file:", err)
+		return
+	}
+	// Save the file, since closing the gzip Writer doesn't close the
+	// underlying file.
+	t.file = f
+	t.w = gzip.NewWriter(f)
+
+	// Write out everything that happened since the start
+	if _, err := io.Copy(t.w, &t.buf); err != nil {
+		t.log.Println("Failed to write trace buffer to file:", err)
+	}
+	t.buf = bytes.Buffer{}
+}
+
+// Close closes the output file. Any future events will be buffered until the
+// next call to SetOutput.
+func (t *tracerImpl) Close() {
+	t.lock.Lock()
+	defer t.lock.Unlock()
+
+	t.close()
+}
+
+func (t *tracerImpl) writeEvent(event *viewerEvent) {
+	t.lock.Lock()
+	defer t.lock.Unlock()
+
+	t.writeEventLocked(event)
+}
+
+func (t *tracerImpl) writeEventLocked(event *viewerEvent) {
+	bytes, err := json.Marshal(event)
+	if err != nil {
+		t.log.Println("Failed to marshal event:", err)
+		t.log.Verbosef("Event: %#v", event)
+		return
+	}
+
+	if !t.firstEvent {
+		fmt.Fprintln(t.w, ",")
+	} else {
+		t.firstEvent = false
+	}
+
+	if _, err = t.w.Write(bytes); err != nil {
+		t.log.Println("Trace write error:", err)
+	}
+}
+
+func (t *tracerImpl) defineThread(thread Thread, name string) {
+	t.writeEventLocked(&viewerEvent{
+		Name:  "thread_name",
+		Phase: "M",
+		Pid:   0,
+		Tid:   uint64(thread),
+		Arg: &nameArg{
+			Name: name,
+		},
+	})
+}
+
+// NewThread returns a new Thread with an unused tid, writing the name out to
+// the trace file.
+func (t *tracerImpl) NewThread(name string) Thread {
+	t.lock.Lock()
+	defer t.lock.Unlock()
+
+	ret := Thread(t.nextTid)
+	t.nextTid += 1
+
+	t.defineThread(ret, name)
+	return ret
+}
+
+// Begin starts a new Duration Event. More than one Duration Event may be active
+// at a time on each Thread, but they're nested.
+func (t *tracerImpl) Begin(name string, thread Thread) {
+	t.writeEvent(&viewerEvent{
+		Name:  name,
+		Phase: "B",
+		Time:  uint64(time.Now().UnixNano()) / 1000,
+		Pid:   0,
+		Tid:   uint64(thread),
+	})
+}
+
+// End finishes the most recent active Duration Event on the thread.
+func (t *tracerImpl) End(thread Thread) {
+	t.writeEvent(&viewerEvent{
+		Phase: "E",
+		Time:  uint64(time.Now().UnixNano()) / 1000,
+		Pid:   0,
+		Tid:   uint64(thread),
+	})
+}
+
+// Complete writes a Complete Event, which are like Duration Events, but include
+// a begin and end timestamp in the same event.
+func (t *tracerImpl) Complete(name string, thread Thread, begin, end uint64) {
+	t.writeEvent(&viewerEvent{
+		Name:  name,
+		Phase: "X",
+		Time:  begin / 1000,
+		Dur:   (end - begin) / 1000,
+		Pid:   0,
+		Tid:   uint64(thread),
+	})
+}