Merge "Revert "Default to building with OpenJDK 9 -target 1.8.""
diff --git a/Android.bp b/Android.bp
index a296da1..643e8df 100644
--- a/Android.bp
+++ b/Android.bp
@@ -50,6 +50,7 @@
         "android/makevars.go",
         "android/module.go",
         "android/mutator.go",
+        "android/namespace.go",
         "android/onceper.go",
         "android/package_ctx.go",
         "android/paths.go",
@@ -67,6 +68,7 @@
     testSrcs: [
         "android/config_test.go",
         "android/expand_test.go",
+        "android/namespace_test.go",
         "android/paths_test.go",
         "android/prebuilt_test.go",
         "android/util_test.go",
@@ -216,6 +218,7 @@
         "java/app.go",
         "java/builder.go",
         "java/gen.go",
+        "java/genrule.go",
         "java/jacoco.go",
         "java/java.go",
         "java/proto.go",
diff --git a/README.md b/README.md
index 3d24e75..4013a2a 100644
--- a/README.md
+++ b/README.md
@@ -95,6 +95,38 @@
 }
 ```
 
+### Name resolution
+
+Soong provides the ability for modules in different directories to specify
+the same name, as long as each module is declared within a separate namespace.
+A namespace can be declared like this:
+
+```
+soong_namespace {
+    imports: ["path/to/otherNamespace1", "path/to/otherNamespace2"],
+}
+```
+
+Each Soong module is assigned a namespace based on its location in the tree.
+Each Soong module is considered to be in the namespace defined by the
+soong_namespace found in an Android.bp in the current directory or closest
+ancestor directory, unless no such soong_namespace module is found, in which
+case the module is considered to be in the implicit root namespace.
+
+When Soong attempts to resolve dependency D declared my module M in namespace
+N which imports namespaces I1, I2, I3..., then if D is a fully-qualified name
+of the form "//namespace:module", only the specified namespace will be searched
+for the specified module name. Otherwise, Soong will first look for a module
+named D declared in namespace N. If that module does not exist, Soong will look
+for a module named D in namespaces I1, I2, I3... Lastly, Soong will look in the
+root namespace.
+
+Until we have fully converted from Make to Soong, it will be necessary for the
+Make product config to specify a value of PRODUCT_SOONG_NAMESPACES. Its value
+should be a space-separated list of namespaces that Soong export to Make to be
+built by the `m` command. After we have fully converted from Make to Soong, the
+details of enabling namespaces could potentially change.
+
 ### Formatter
 
 Soong includes a canonical formatter for blueprint files, similar to
diff --git a/android/androidmk.go b/android/androidmk.go
index d88ba8f..12aa5fa 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -157,6 +157,12 @@
 		return nil
 	}
 
+	if !amod.commonProperties.NamespaceExportedToMake {
+		// TODO(jeffrygaston) do we want to validate that there are no modules being
+		// exported to Kati that depend on this module?
+		return nil
+	}
+
 	data := provider.AndroidMk()
 
 	if data.Include == "" {
diff --git a/android/arch.go b/android/arch.go
index 7f9abc6..af3919c 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -1072,19 +1072,30 @@
 	return ret
 }
 
+func preferTargets(targets []Target, filters ...string) []Target {
+	for _, filter := range filters {
+		buildTargets := filterMultilibTargets(targets, filter)
+		if len(buildTargets) > 0 {
+			return buildTargets
+		}
+	}
+	return nil
+}
+
 // Use the module multilib setting to select one or more targets from a target list
 func decodeMultilib(multilib string, targets []Target, prefer32 bool) ([]Target, error) {
 	buildTargets := []Target{}
-	if multilib == "first" {
-		if prefer32 {
-			multilib = "prefer32"
-		} else {
-			multilib = "prefer64"
-		}
-	}
+
 	switch multilib {
 	case "common":
-		buildTargets = append(buildTargets, getCommonTargets(targets)...)
+		buildTargets = getCommonTargets(targets)
+	case "common_first":
+		buildTargets = getCommonTargets(targets)
+		if prefer32 {
+			buildTargets = append(buildTargets, preferTargets(targets, "lib32", "lib64")...)
+		} else {
+			buildTargets = append(buildTargets, preferTargets(targets, "lib64", "lib32")...)
+		}
 	case "both":
 		if prefer32 {
 			buildTargets = append(buildTargets, filterMultilibTargets(targets, "lib32")...)
@@ -1097,16 +1108,14 @@
 		buildTargets = filterMultilibTargets(targets, "lib32")
 	case "64":
 		buildTargets = filterMultilibTargets(targets, "lib64")
+	case "first":
+		if prefer32 {
+			buildTargets = preferTargets(targets, "lib32", "lib64")
+		} else {
+			buildTargets = preferTargets(targets, "lib64", "lib32")
+		}
 	case "prefer32":
-		buildTargets = filterMultilibTargets(targets, "lib32")
-		if len(buildTargets) == 0 {
-			buildTargets = filterMultilibTargets(targets, "lib64")
-		}
-	case "prefer64":
-		buildTargets = filterMultilibTargets(targets, "lib64")
-		if len(buildTargets) == 0 {
-			buildTargets = filterMultilibTargets(targets, "lib32")
-		}
+		buildTargets = preferTargets(targets, "lib32", "lib64")
 	default:
 		return nil, fmt.Errorf(`compile_multilib must be "both", "first", "32", "64", or "prefer32" found %q`,
 			multilib)
diff --git a/android/module.go b/android/module.go
index c728487..865764f 100644
--- a/android/module.go
+++ b/android/module.go
@@ -151,6 +151,7 @@
 	VisitAllModuleVariants(visit func(Module))
 
 	GetMissingDependencies() []string
+	Namespace() blueprint.Namespace
 }
 
 type Module interface {
@@ -235,6 +236,8 @@
 	ArchSpecific          bool                  `blueprint:"mutated"`
 
 	SkipInstall bool `blueprint:"mutated"`
+
+	NamespaceExportedToMake bool `blueprint:"mutated"`
 }
 
 type hostAndDeviceProperties struct {
@@ -245,10 +248,11 @@
 type Multilib string
 
 const (
-	MultilibBoth    Multilib = "both"
-	MultilibFirst   Multilib = "first"
-	MultilibCommon  Multilib = "common"
-	MultilibDefault Multilib = ""
+	MultilibBoth        Multilib = "both"
+	MultilibFirst       Multilib = "first"
+	MultilibCommon      Multilib = "common"
+	MultilibCommonFirst Multilib = "common_first"
+	MultilibDefault     Multilib = ""
 )
 
 type HostOrDeviceSupported int
@@ -500,8 +504,13 @@
 
 	var deps Paths
 
+	namespacePrefix := ctx.Namespace().(*Namespace).id
+	if namespacePrefix != "" {
+		namespacePrefix = namespacePrefix + "-"
+	}
+
 	if len(allInstalledFiles) > 0 {
-		name := PathForPhony(ctx, ctx.ModuleName()+"-install")
+		name := PathForPhony(ctx, namespacePrefix+ctx.ModuleName()+"-install")
 		ctx.Build(pctx, BuildParams{
 			Rule:      blueprint.Phony,
 			Output:    name,
@@ -513,7 +522,7 @@
 	}
 
 	if len(allCheckbuildFiles) > 0 {
-		name := PathForPhony(ctx, ctx.ModuleName()+"-checkbuild")
+		name := PathForPhony(ctx, namespacePrefix+ctx.ModuleName()+"-checkbuild")
 		ctx.Build(pctx, BuildParams{
 			Rule:      blueprint.Phony,
 			Output:    name,
@@ -529,9 +538,10 @@
 			suffix = "-soong"
 		}
 
+		name := PathForPhony(ctx, namespacePrefix+ctx.ModuleName()+suffix)
 		ctx.Build(pctx, BuildParams{
 			Rule:      blueprint.Phony,
-			Output:    PathForPhony(ctx, ctx.ModuleName()+suffix),
+			Outputs:   []WritablePath{name},
 			Implicits: deps,
 		})
 
diff --git a/android/mutator.go b/android/mutator.go
index db3eaa3..876d161 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -86,6 +86,7 @@
 }
 
 var preDeps = []RegisterMutatorFunc{
+	RegisterNamespaceMutator,
 	registerArchMutator,
 }
 
diff --git a/android/namespace.go b/android/namespace.go
new file mode 100644
index 0000000..1f8ef5a
--- /dev/null
+++ b/android/namespace.go
@@ -0,0 +1,423 @@
+// 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 (
+	"errors"
+	"fmt"
+	"path/filepath"
+	"sort"
+	"strconv"
+	"strings"
+	"sync"
+
+	"github.com/google/blueprint"
+)
+
+// This file implements namespaces
+const (
+	namespacePrefix = "//"
+	modulePrefix    = ":"
+)
+
+func init() {
+	RegisterModuleType("soong_namespace", NamespaceFactory)
+}
+
+// threadsafe sorted list
+type sortedNamespaces struct {
+	lock   sync.Mutex
+	items  []*Namespace
+	sorted bool
+}
+
+func (s *sortedNamespaces) add(namespace *Namespace) {
+	s.lock.Lock()
+	defer s.lock.Unlock()
+	if s.sorted {
+		panic("It is not supported to call sortedNamespaces.add() after sortedNamespaces.sortedItems()")
+	}
+	s.items = append(s.items, namespace)
+}
+
+func (s *sortedNamespaces) sortedItems() []*Namespace {
+	s.lock.Lock()
+	defer s.lock.Unlock()
+	if !s.sorted {
+		less := func(i int, j int) bool {
+			return s.items[i].Path < s.items[j].Path
+		}
+		sort.Slice(s.items, less)
+		s.sorted = true
+	}
+	return s.items
+}
+
+func (s *sortedNamespaces) index(namespace *Namespace) int {
+	for i, candidate := range s.sortedItems() {
+		if namespace == candidate {
+			return i
+		}
+	}
+	return -1
+}
+
+// A NameResolver implements blueprint.NameInterface, and implements the logic to
+// find a module from namespaces based on a query string.
+// A query string can be a module name or can be be "//namespace_path:module_path"
+type NameResolver struct {
+	rootNamespace *Namespace
+
+	// id counter for atomic.AddInt32
+	nextNamespaceId int32
+
+	// All namespaces, without duplicates.
+	sortedNamespaces sortedNamespaces
+
+	// Map from dir to namespace. Will have duplicates if two dirs are part of the same namespace.
+	namespacesByDir sync.Map // if generics were supported, this would be sync.Map[string]*Namespace
+
+	// func telling whether to export a namespace to Kati
+	namespaceExportFilter func(*Namespace) bool
+}
+
+func NewNameResolver(namespaceExportFilter func(*Namespace) bool) *NameResolver {
+	namespacesByDir := sync.Map{}
+
+	r := &NameResolver{
+		namespacesByDir:       namespacesByDir,
+		namespaceExportFilter: namespaceExportFilter,
+	}
+	r.rootNamespace = r.newNamespace(".")
+	r.rootNamespace.visibleNamespaces = []*Namespace{r.rootNamespace}
+	r.addNamespace(r.rootNamespace)
+
+	return r
+}
+
+func (r *NameResolver) newNamespace(path string) *Namespace {
+	namespace := NewNamespace(path)
+
+	namespace.exportToKati = r.namespaceExportFilter(namespace)
+
+	return namespace
+}
+
+func (r *NameResolver) addNewNamespaceForModule(module *NamespaceModule, path string) error {
+	fileName := filepath.Base(path)
+	if fileName != "Android.bp" {
+		return errors.New("A namespace may only be declared in a file named Android.bp")
+	}
+	dir := filepath.Dir(path)
+
+	namespace := r.newNamespace(dir)
+	module.namespace = namespace
+	module.resolver = r
+	namespace.importedNamespaceNames = module.properties.Imports
+	return r.addNamespace(namespace)
+}
+
+func (r *NameResolver) addNamespace(namespace *Namespace) (err error) {
+	existingNamespace, exists := r.namespaceAt(namespace.Path)
+	if exists {
+		if existingNamespace.Path == namespace.Path {
+			return fmt.Errorf("namespace %v already exists", namespace.Path)
+		} else {
+			// It would probably confuse readers if namespaces were declared anywhere but
+			// the top of the file, so we forbid declaring namespaces after anything else.
+			return fmt.Errorf("a namespace must be the first module in the file")
+		}
+	}
+	r.sortedNamespaces.add(namespace)
+
+	r.namespacesByDir.Store(namespace.Path, namespace)
+	return nil
+}
+
+// non-recursive check for namespace
+func (r *NameResolver) namespaceAt(path string) (namespace *Namespace, found bool) {
+	mapVal, found := r.namespacesByDir.Load(path)
+	if !found {
+		return nil, false
+	}
+	return mapVal.(*Namespace), true
+}
+
+// recursive search upward for a namespace
+func (r *NameResolver) findNamespace(path string) (namespace *Namespace) {
+	namespace, found := r.namespaceAt(path)
+	if found {
+		return namespace
+	}
+	parentDir := filepath.Dir(path)
+	if parentDir == path {
+		return nil
+	}
+	namespace = r.findNamespace(parentDir)
+	r.namespacesByDir.Store(path, namespace)
+	return namespace
+}
+
+func (r *NameResolver) NewModule(ctx blueprint.NamespaceContext, moduleGroup blueprint.ModuleGroup, module blueprint.Module) (namespace blueprint.Namespace, errs []error) {
+	// if this module is a namespace, then save it to our list of namespaces
+	newNamespace, ok := module.(*NamespaceModule)
+	if ok {
+		err := r.addNewNamespaceForModule(newNamespace, ctx.ModulePath())
+		if err != nil {
+			return nil, []error{err}
+		}
+		return nil, nil
+	}
+
+	// if this module is not a namespace, then save it into the appropriate namespace
+	ns := r.findNamespaceFromCtx(ctx)
+
+	_, errs = ns.moduleContainer.NewModule(ctx, moduleGroup, module)
+	if len(errs) > 0 {
+		return nil, errs
+	}
+
+	amod, ok := module.(Module)
+	if ok {
+		// inform the module whether its namespace is one that we want to export to Make
+		amod.base().commonProperties.NamespaceExportedToMake = ns.exportToKati
+	}
+
+	return ns, nil
+}
+
+func (r *NameResolver) AllModules() []blueprint.ModuleGroup {
+	childLists := [][]blueprint.ModuleGroup{}
+	totalCount := 0
+	for _, namespace := range r.sortedNamespaces.sortedItems() {
+		newModules := namespace.moduleContainer.AllModules()
+		totalCount += len(newModules)
+		childLists = append(childLists, newModules)
+	}
+
+	allModules := make([]blueprint.ModuleGroup, 0, totalCount)
+	for _, childList := range childLists {
+		allModules = append(allModules, childList...)
+	}
+	return allModules
+}
+
+// parses a fully-qualified path (like "//namespace_path:module_name") into a namespace name and a
+// module name
+func (r *NameResolver) parseFullyQualifiedName(name string) (namespaceName string, moduleName string, ok bool) {
+	if !strings.HasPrefix(name, namespacePrefix) {
+		return "", "", false
+	}
+	name = strings.TrimPrefix(name, namespacePrefix)
+	components := strings.Split(name, modulePrefix)
+	if len(components) != 2 {
+		return "", "", false
+	}
+	return components[0], components[1], true
+
+}
+
+func (r *NameResolver) getNamespacesToSearchForModule(sourceNamespace *Namespace) (searchOrder []*Namespace) {
+	return sourceNamespace.visibleNamespaces
+}
+
+func (r *NameResolver) ModuleFromName(name string, namespace blueprint.Namespace) (group blueprint.ModuleGroup, found bool) {
+	// handle fully qualified references like "//namespace_path:module_name"
+	nsName, moduleName, isAbs := r.parseFullyQualifiedName(name)
+	if isAbs {
+		namespace, found := r.namespaceAt(nsName)
+		if !found {
+			return blueprint.ModuleGroup{}, false
+		}
+		container := namespace.moduleContainer
+		return container.ModuleFromName(moduleName, nil)
+	}
+	for _, candidate := range r.getNamespacesToSearchForModule(namespace.(*Namespace)) {
+		group, found = candidate.moduleContainer.ModuleFromName(name, nil)
+		if found {
+			return group, true
+		}
+	}
+	return blueprint.ModuleGroup{}, false
+
+}
+
+func (r *NameResolver) Rename(oldName string, newName string, namespace blueprint.Namespace) []error {
+	oldNs := r.findNamespace(oldName)
+	newNs := r.findNamespace(newName)
+	if oldNs != newNs {
+		return []error{fmt.Errorf("cannot rename %v to %v because the destination is outside namespace %v", oldName, newName, oldNs.Path)}
+	}
+
+	oldName, err := filepath.Rel(oldNs.Path, oldName)
+	if err != nil {
+		panic(err)
+	}
+	newName, err = filepath.Rel(newNs.Path, newName)
+	if err != nil {
+		panic(err)
+	}
+
+	return oldNs.moduleContainer.Rename(oldName, newName, nil)
+}
+
+// resolve each element of namespace.importedNamespaceNames and put the result in namespace.visibleNamespaces
+func (r *NameResolver) FindNamespaceImports(namespace *Namespace) (err error) {
+	namespace.visibleNamespaces = make([]*Namespace, 0, 2+len(namespace.importedNamespaceNames))
+	// search itself first
+	namespace.visibleNamespaces = append(namespace.visibleNamespaces, namespace)
+	// search its imports next
+	for _, name := range namespace.importedNamespaceNames {
+		imp, ok := r.namespaceAt(name)
+		if !ok {
+			return fmt.Errorf("namespace %v does not exist", name)
+		}
+		namespace.visibleNamespaces = append(namespace.visibleNamespaces, imp)
+	}
+	// search the root namespace last
+	namespace.visibleNamespaces = append(namespace.visibleNamespaces, r.rootNamespace)
+	return nil
+}
+
+func (r *NameResolver) chooseId(namespace *Namespace) {
+	id := r.sortedNamespaces.index(namespace)
+	if id < 0 {
+		panic(fmt.Sprintf("Namespace not found: %v\n", namespace.id))
+	}
+	namespace.id = strconv.Itoa(id)
+}
+
+func (r *NameResolver) MissingDependencyError(depender string, dependerNamespace blueprint.Namespace, depName string) (err error) {
+	text := fmt.Sprintf("%q depends on undefined module %q", depender, depName)
+
+	_, _, isAbs := r.parseFullyQualifiedName(depName)
+	if isAbs {
+		// if the user gave a fully-qualified name, we don't need to look for other
+		// modules that they might have been referring to
+		return fmt.Errorf(text)
+	}
+
+	// determine which namespaces the module can be found in
+	foundInNamespaces := []string{}
+	for _, namespace := range r.sortedNamespaces.sortedItems() {
+		_, found := namespace.moduleContainer.ModuleFromName(depName, nil)
+		if found {
+			foundInNamespaces = append(foundInNamespaces, namespace.Path)
+		}
+	}
+	if len(foundInNamespaces) > 0 {
+		// determine which namespaces are visible to dependerNamespace
+		dependerNs := dependerNamespace.(*Namespace)
+		searched := r.getNamespacesToSearchForModule(dependerNs)
+		importedNames := []string{}
+		for _, ns := range searched {
+			importedNames = append(importedNames, ns.Path)
+		}
+		text += fmt.Sprintf("\nModule %q is defined in namespace %q which can read these %v namespaces: %q", depender, dependerNs.Path, len(importedNames), importedNames)
+		text += fmt.Sprintf("\nModule %q can be found in these namespaces: %q", depName, foundInNamespaces)
+	}
+
+	return fmt.Errorf(text)
+}
+
+func (r *NameResolver) GetNamespace(ctx blueprint.NamespaceContext) blueprint.Namespace {
+	return r.findNamespaceFromCtx(ctx)
+}
+
+func (r *NameResolver) findNamespaceFromCtx(ctx blueprint.NamespaceContext) *Namespace {
+	return r.findNamespace(filepath.Dir(ctx.ModulePath()))
+}
+
+func (r *NameResolver) UniqueName(ctx blueprint.NamespaceContext, name string) (unique string) {
+	prefix := r.findNamespaceFromCtx(ctx).id
+	if prefix != "" {
+		prefix = prefix + "-"
+	}
+	return prefix + name
+}
+
+var _ blueprint.NameInterface = (*NameResolver)(nil)
+
+type Namespace struct {
+	blueprint.NamespaceMarker
+	Path string
+
+	// names of namespaces listed as imports by this namespace
+	importedNamespaceNames []string
+	// all namespaces that should be searched when a module in this namespace declares a dependency
+	visibleNamespaces []*Namespace
+
+	id string
+
+	exportToKati bool
+
+	moduleContainer blueprint.NameInterface
+}
+
+func NewNamespace(path string) *Namespace {
+	return &Namespace{Path: path, moduleContainer: blueprint.NewSimpleNameInterface()}
+}
+
+var _ blueprint.Namespace = (*Namespace)(nil)
+
+type NamespaceModule struct {
+	ModuleBase
+
+	namespace *Namespace
+	resolver  *NameResolver
+
+	properties struct {
+		Imports []string
+	}
+}
+
+func (n *NamespaceModule) DepsMutator(context BottomUpMutatorContext) {
+}
+
+func (n *NamespaceModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+}
+
+func (n *NamespaceModule) GenerateBuildActions(ctx blueprint.ModuleContext) {
+}
+
+func (n *NamespaceModule) Name() (name string) {
+	return *n.nameProperties.Name
+}
+
+func NamespaceFactory() Module {
+	module := &NamespaceModule{}
+
+	name := "soong_namespace"
+	module.nameProperties.Name = &name
+
+	module.AddProperties(&module.properties)
+	return module
+}
+
+func RegisterNamespaceMutator(ctx RegisterMutatorsContext) {
+	ctx.BottomUp("namespace_deps", namespaceMutator).Parallel()
+}
+
+func namespaceMutator(ctx BottomUpMutatorContext) {
+	module, ok := ctx.Module().(*NamespaceModule)
+	if ok {
+		err := module.resolver.FindNamespaceImports(module.namespace)
+		if err != nil {
+			ctx.ModuleErrorf(err.Error())
+		}
+
+		module.resolver.chooseId(module.namespace)
+	}
+}
diff --git a/android/namespace_test.go b/android/namespace_test.go
new file mode 100644
index 0000000..9ab186b
--- /dev/null
+++ b/android/namespace_test.go
@@ -0,0 +1,703 @@
+// 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 (
+	"errors"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"reflect"
+	"testing"
+
+	"github.com/google/blueprint"
+)
+
+func TestDependingOnModuleInSameNamespace(t *testing.T) {
+	ctx := setupTest(t,
+		map[string]string{
+			"dir1": `
+			soong_namespace {
+			}
+			test_module {
+				name: "a",
+			}
+			test_module {
+				name: "b",
+				deps: ["a"],
+			}
+			`,
+		},
+	)
+
+	a := getModule(ctx, "a")
+	b := getModule(ctx, "b")
+	if !dependsOn(ctx, b, a) {
+		t.Errorf("module b does not depend on module a in the same namespace")
+	}
+}
+
+func TestDependingOnModuleInRootNamespace(t *testing.T) {
+	ctx := setupTest(t,
+		map[string]string{
+			".": `
+			test_module {
+				name: "b",
+				deps: ["a"],
+			}
+			test_module {
+				name: "a",
+			}
+			`,
+		},
+	)
+
+	a := getModule(ctx, "a")
+	b := getModule(ctx, "b")
+	if !dependsOn(ctx, b, a) {
+		t.Errorf("module b in root namespace does not depend on module a in the root namespace")
+	}
+}
+
+func TestImplicitlyImportRootNamespace(t *testing.T) {
+	_ = setupTest(t,
+		map[string]string{
+			".": `
+			test_module {
+				name: "a",
+			}
+			`,
+			"dir1": `
+			soong_namespace {
+			}
+			test_module {
+				name: "b",
+				deps: ["a"],
+			}
+			`,
+		},
+	)
+
+	// setupTest will report any errors
+}
+
+func TestDependingOnModuleInImportedNamespace(t *testing.T) {
+	ctx := setupTest(t,
+		map[string]string{
+			"dir1": `
+			soong_namespace {
+			}
+			test_module {
+				name: "a",
+			}
+			`,
+			"dir2": `
+			soong_namespace {
+				imports: ["dir1"],
+			}
+			test_module {
+				name: "b",
+				deps: ["a"],
+			}
+			`,
+		},
+	)
+
+	a := getModule(ctx, "a")
+	b := getModule(ctx, "b")
+	if !dependsOn(ctx, b, a) {
+		t.Errorf("module b does not depend on module a in the same namespace")
+	}
+}
+
+func TestDependingOnModuleInNonImportedNamespace(t *testing.T) {
+	_, errs := setupTestExpectErrs(
+		map[string]string{
+			"dir1": `
+			soong_namespace {
+			}
+			test_module {
+				name: "a",
+			}
+			`,
+			"dir2": `
+			soong_namespace {
+			}
+			test_module {
+				name: "a",
+			}
+			`,
+			"dir3": `
+			soong_namespace {
+			}
+			test_module {
+				name: "b",
+				deps: ["a"],
+			}
+			`,
+		},
+	)
+
+	expectedErrors := []error{
+		errors.New(
+			`dir3/Android.bp:4:4: "b" depends on undefined module "a"
+Module "b" is defined in namespace "dir3" which can read these 2 namespaces: ["dir3" "."]
+Module "a" can be found in these namespaces: ["dir1" "dir2"]`),
+	}
+
+	if len(errs) != 1 || errs[0].Error() != expectedErrors[0].Error() {
+		t.Errorf("Incorrect errors. Expected:\n%v\n, got:\n%v\n", expectedErrors, errs)
+	}
+}
+
+func TestDependingOnModuleByFullyQualifiedReference(t *testing.T) {
+	ctx := setupTest(t,
+		map[string]string{
+			"dir1": `
+			soong_namespace {
+			}
+			test_module {
+				name: "a",
+			}
+			`,
+			"dir2": `
+			soong_namespace {
+			}
+			test_module {
+				name: "b",
+				deps: ["//dir1:a"],
+			}
+			`,
+		},
+	)
+	a := getModule(ctx, "a")
+	b := getModule(ctx, "b")
+	if !dependsOn(ctx, b, a) {
+		t.Errorf("module b does not depend on module a")
+	}
+}
+
+func TestSameNameInTwoNamespaces(t *testing.T) {
+	ctx := setupTest(t,
+		map[string]string{
+			"dir1": `
+			soong_namespace {
+			}
+			test_module {
+				name: "a",
+				id: "1",
+			}
+			test_module {
+				name: "b",
+				deps: ["a"],
+				id: "2",
+			}
+			`,
+			"dir2": `
+			soong_namespace {
+			}
+			test_module {
+				name: "a",
+				id:"3",
+			}
+			test_module {
+				name: "b",
+				deps: ["a"],
+				id:"4",
+			}
+			`,
+		},
+	)
+
+	one := findModuleById(ctx, "1")
+	two := findModuleById(ctx, "2")
+	three := findModuleById(ctx, "3")
+	four := findModuleById(ctx, "4")
+	if !dependsOn(ctx, two, one) {
+		t.Fatalf("Module 2 does not depend on module 1 in its namespace")
+	}
+	if dependsOn(ctx, two, three) {
+		t.Fatalf("Module 2 depends on module 3 in another namespace")
+	}
+	if !dependsOn(ctx, four, three) {
+		t.Fatalf("Module 4 does not depend on module 3 in its namespace")
+	}
+	if dependsOn(ctx, four, one) {
+		t.Fatalf("Module 4 depends on module 1 in another namespace")
+	}
+}
+
+func TestSearchOrder(t *testing.T) {
+	ctx := setupTest(t,
+		map[string]string{
+			"dir1": `
+			soong_namespace {
+			}
+			test_module {
+				name: "a",
+				id: "1",
+			}
+			`,
+			"dir2": `
+			soong_namespace {
+			}
+			test_module {
+				name: "a",
+				id:"2",
+			}
+			test_module {
+				name: "b",
+				id:"3",
+			}
+			`,
+			"dir3": `
+			soong_namespace {
+			}
+			test_module {
+				name: "a",
+				id:"4",
+			}
+			test_module {
+				name: "b",
+				id:"5",
+			}
+			test_module {
+				name: "c",
+				id:"6",
+			}
+			`,
+			".": `
+			test_module {
+				name: "a",
+				id: "7",
+			}
+			test_module {
+				name: "b",
+				id: "8",
+			}
+			test_module {
+				name: "c",
+				id: "9",
+			}
+			test_module {
+				name: "d",
+				id: "10",
+			}
+			`,
+			"dir4": `
+			soong_namespace {
+				imports: ["dir1", "dir2", "dir3"]
+			}
+			test_module {
+				name: "test_me",
+				id:"0",
+				deps: ["a", "b", "c", "d"],
+			}
+			`,
+		},
+	)
+
+	testMe := findModuleById(ctx, "0")
+	if !dependsOn(ctx, testMe, findModuleById(ctx, "1")) {
+		t.Errorf("test_me doesn't depend on id 1")
+	}
+	if !dependsOn(ctx, testMe, findModuleById(ctx, "3")) {
+		t.Errorf("test_me doesn't depend on id 3")
+	}
+	if !dependsOn(ctx, testMe, findModuleById(ctx, "6")) {
+		t.Errorf("test_me doesn't depend on id 6")
+	}
+	if !dependsOn(ctx, testMe, findModuleById(ctx, "10")) {
+		t.Errorf("test_me doesn't depend on id 10")
+	}
+	if numDeps(ctx, testMe) != 4 {
+		t.Errorf("num dependencies of test_me = %v, not 4\n", numDeps(ctx, testMe))
+	}
+}
+
+func TestTwoNamespacesCanImportEachOther(t *testing.T) {
+	_ = setupTest(t,
+		map[string]string{
+			"dir1": `
+			soong_namespace {
+				imports: ["dir2"]
+			}
+			test_module {
+				name: "a",
+			}
+			test_module {
+				name: "c",
+				deps: ["b"],
+			}
+			`,
+			"dir2": `
+			soong_namespace {
+				imports: ["dir1"],
+			}
+			test_module {
+				name: "b",
+				deps: ["a"],
+			}
+			`,
+		},
+	)
+
+	// setupTest will report any errors
+}
+
+func TestImportingNonexistentNamespace(t *testing.T) {
+	_, errs := setupTestExpectErrs(
+		map[string]string{
+			"dir1": `
+			soong_namespace {
+				imports: ["a_nonexistent_namespace"]
+			}
+			test_module {
+				name: "a",
+				deps: ["a_nonexistent_module"]
+			}
+			`,
+		},
+	)
+
+	// should complain about the missing namespace and not complain about the unresolvable dependency
+	expectedErrors := []error{
+		errors.New(`dir1/Android.bp:2:4: module "soong_namespace": namespace a_nonexistent_namespace does not exist`),
+	}
+	if len(errs) != 1 || errs[0].Error() != expectedErrors[0].Error() {
+		t.Errorf("Incorrect errors. Expected:\n%v\n, got:\n%v\n", expectedErrors, errs)
+	}
+}
+
+func TestNamespacesDontInheritParentNamespaces(t *testing.T) {
+	_, errs := setupTestExpectErrs(
+		map[string]string{
+			"dir1": `
+			soong_namespace {
+			}
+			test_module {
+				name: "a",
+			}
+			`,
+			"dir1/subdir1": `
+			soong_namespace {
+			}
+			test_module {
+				name: "b",
+				deps: ["a"],
+			}
+			`,
+		},
+	)
+
+	expectedErrors := []error{
+		errors.New(`dir1/subdir1/Android.bp:4:4: "b" depends on undefined module "a"
+Module "b" is defined in namespace "dir1/subdir1" which can read these 2 namespaces: ["dir1/subdir1" "."]
+Module "a" can be found in these namespaces: ["dir1"]`),
+	}
+	if len(errs) != 1 || errs[0].Error() != expectedErrors[0].Error() {
+		t.Errorf("Incorrect errors. Expected:\n%v\n, got:\n%v\n", expectedErrors, errs)
+	}
+}
+
+func TestModulesDoReceiveParentNamespace(t *testing.T) {
+	_ = setupTest(t,
+		map[string]string{
+			"dir1": `
+			soong_namespace {
+			}
+			test_module {
+				name: "a",
+			}
+			`,
+			"dir1/subdir": `
+			test_module {
+				name: "b",
+				deps: ["a"],
+			}
+			`,
+		},
+	)
+
+	// setupTest will report any errors
+}
+
+func TestNamespaceImportsNotTransitive(t *testing.T) {
+	_, errs := setupTestExpectErrs(
+		map[string]string{
+			"dir1": `
+			soong_namespace {
+			}
+			test_module {
+				name: "a",
+			}
+			`,
+			"dir2": `
+			soong_namespace {
+				imports: ["dir1"],
+			}
+			test_module {
+				name: "b",
+				deps: ["a"],
+			}
+			`,
+			"dir3": `
+			soong_namespace {
+				imports: ["dir2"],
+			}
+			test_module {
+				name: "c",
+				deps: ["a"],
+			}
+			`,
+		},
+	)
+
+	expectedErrors := []error{
+		errors.New(`dir3/Android.bp:5:4: "c" depends on undefined module "a"
+Module "c" is defined in namespace "dir3" which can read these 3 namespaces: ["dir3" "dir2" "."]
+Module "a" can be found in these namespaces: ["dir1"]`),
+	}
+	if len(errs) != 1 || errs[0].Error() != expectedErrors[0].Error() {
+		t.Errorf("Incorrect errors. Expected:\n%v\n, got:\n%v\n", expectedErrors, errs)
+	}
+}
+
+func TestTwoNamepacesInSameDir(t *testing.T) {
+	_, errs := setupTestExpectErrs(
+		map[string]string{
+			"dir1": `
+			soong_namespace {
+			}
+			soong_namespace {
+			}
+			`,
+		},
+	)
+
+	expectedErrors := []error{
+		errors.New(`dir1/Android.bp:4:4: namespace dir1 already exists`),
+	}
+	if len(errs) != 1 || errs[0].Error() != expectedErrors[0].Error() {
+		t.Errorf("Incorrect errors. Expected:\n%v\n, got:\n%v\n", expectedErrors, errs)
+	}
+}
+
+func TestNamespaceNotAtTopOfFile(t *testing.T) {
+	_, errs := setupTestExpectErrs(
+		map[string]string{
+			"dir1": `
+			test_module {
+				name: "a"
+			}
+			soong_namespace {
+			}
+			`,
+		},
+	)
+
+	expectedErrors := []error{
+		errors.New(`dir1/Android.bp:5:4: a namespace must be the first module in the file`),
+	}
+	if len(errs) != 1 || errs[0].Error() != expectedErrors[0].Error() {
+		t.Errorf("Incorrect errors. Expected:\n%v\n, got:\n%v\n", expectedErrors, errs)
+	}
+}
+
+func TestTwoModulesWithSameNameInSameNamespace(t *testing.T) {
+	_, errs := setupTestExpectErrs(
+		map[string]string{
+			"dir1": `
+			soong_namespace {
+			}
+			test_module {
+				name: "a"
+			}
+			test_module {
+				name: "a"
+			}
+			`,
+		},
+	)
+
+	expectedErrors := []error{
+		errors.New(`dir1/Android.bp:7:4: module "a" already defined
+       dir1/Android.bp:4:4 <-- previous definition here`),
+	}
+	if len(errs) != 1 || errs[0].Error() != expectedErrors[0].Error() {
+		t.Errorf("Incorrect errors. Expected:\n%v\n, got:\n%v\n", expectedErrors, errs)
+	}
+}
+
+func TestDeclaringNamespaceInNonAndroidBpFile(t *testing.T) {
+	_, errs := setupTestFromFiles(
+		map[string][]byte{
+			"Android.bp": []byte(`
+				build = ["include.bp"]
+			`),
+			"include.bp": []byte(`
+				soong_namespace {
+				}
+			`),
+		},
+	)
+
+	expectedErrors := []error{
+		errors.New(`include.bp:2:5: A namespace may only be declared in a file named Android.bp`),
+	}
+
+	if len(errs) != 1 || errs[0].Error() != expectedErrors[0].Error() {
+		t.Errorf("Incorrect errors. Expected:\n%v\n, got:\n%v\n", expectedErrors, errs)
+	}
+}
+
+// so that the generated .ninja file will have consistent names
+func TestConsistentNamespaceNames(t *testing.T) {
+	ctx := setupTest(t,
+		map[string]string{
+			"dir1": "soong_namespace{}",
+			"dir2": "soong_namespace{}",
+			"dir3": "soong_namespace{}",
+		})
+
+	ns1, _ := ctx.NameResolver.namespaceAt("dir1")
+	ns2, _ := ctx.NameResolver.namespaceAt("dir2")
+	ns3, _ := ctx.NameResolver.namespaceAt("dir3")
+	actualIds := []string{ns1.id, ns2.id, ns3.id}
+	expectedIds := []string{"1", "2", "3"}
+	if !reflect.DeepEqual(actualIds, expectedIds) {
+		t.Errorf("Incorrect namespace ids.\nactual: %s\nexpected: %s\n", actualIds, expectedIds)
+	}
+}
+
+// some utils to support the tests
+
+func mockFiles(bps map[string]string) (files map[string][]byte) {
+	files = make(map[string][]byte, len(bps))
+	files["Android.bp"] = []byte("")
+	for dir, text := range bps {
+		files[filepath.Join(dir, "Android.bp")] = []byte(text)
+	}
+	return files
+}
+
+func setupTestFromFiles(bps map[string][]byte) (ctx *TestContext, errs []error) {
+	buildDir, err := ioutil.TempDir("", "soong_namespace_test")
+	if err != nil {
+		return nil, []error{err}
+	}
+	defer os.RemoveAll(buildDir)
+
+	config := TestConfig(buildDir, nil)
+
+	ctx = NewTestContext()
+	ctx.MockFileSystem(bps)
+	ctx.RegisterModuleType("test_module", ModuleFactoryAdaptor(newTestModule))
+	ctx.RegisterModuleType("soong_namespace", ModuleFactoryAdaptor(NamespaceFactory))
+	ctx.PreDepsMutators(RegisterNamespaceMutator)
+	ctx.Register()
+
+	_, errs = ctx.ParseBlueprintsFiles("Android.bp")
+	if len(errs) > 0 {
+		return ctx, errs
+	}
+	_, errs = ctx.PrepareBuildActions(config)
+	return ctx, errs
+}
+
+func setupTestExpectErrs(bps map[string]string) (ctx *TestContext, errs []error) {
+	files := make(map[string][]byte, len(bps))
+	files["Android.bp"] = []byte("")
+	for dir, text := range bps {
+		files[filepath.Join(dir, "Android.bp")] = []byte(text)
+	}
+	return setupTestFromFiles(files)
+}
+
+func setupTest(t *testing.T, bps map[string]string) (ctx *TestContext) {
+	ctx, errs := setupTestExpectErrs(bps)
+	failIfErrored(t, errs)
+	return ctx
+}
+
+func dependsOn(ctx *TestContext, module TestingModule, possibleDependency TestingModule) bool {
+	depends := false
+	visit := func(dependency blueprint.Module) {
+		if dependency == possibleDependency.module {
+			depends = true
+		}
+	}
+	ctx.VisitDirectDeps(module.module, visit)
+	return depends
+}
+
+func numDeps(ctx *TestContext, module TestingModule) int {
+	count := 0
+	visit := func(dependency blueprint.Module) {
+		count++
+	}
+	ctx.VisitDirectDeps(module.module, visit)
+	return count
+}
+
+func getModule(ctx *TestContext, moduleName string) TestingModule {
+	return ctx.ModuleForTests(moduleName, "")
+}
+
+func findModuleById(ctx *TestContext, id string) (module TestingModule) {
+	visit := func(candidate blueprint.Module) {
+		testModule, ok := candidate.(*testModule)
+		if ok {
+			if testModule.properties.Id == id {
+				module = TestingModule{testModule}
+			}
+		}
+	}
+	ctx.VisitAllModules(visit)
+	return module
+}
+
+type testModule struct {
+	ModuleBase
+	properties struct {
+		Deps []string
+		Id   string
+	}
+}
+
+func (m *testModule) DepsMutator(ctx BottomUpMutatorContext) {
+	for _, d := range m.properties.Deps {
+		ctx.AddDependency(ctx.Module(), nil, d)
+	}
+}
+
+func (m *testModule) GenerateAndroidBuildActions(ModuleContext) {
+}
+
+func newTestModule() Module {
+	m := &testModule{}
+	m.AddProperties(&m.properties)
+	InitAndroidModule(m)
+	return m
+}
+
+func failIfErrored(t *testing.T, errs []error) {
+	if len(errs) > 0 {
+		for _, err := range errs {
+			t.Error(err)
+		}
+		t.FailNow()
+	}
+}
diff --git a/android/paths.go b/android/paths.go
index e0cbd21..80863c9 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -92,7 +92,7 @@
 
 	// 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.
+	// directory, and OutputPath.Join("foo").Rel() would return "foo".
 	Rel() string
 }
 
@@ -456,6 +456,12 @@
 	return p.path
 }
 
+func (p basePath) withRel(rel string) basePath {
+	p.path = filepath.Join(p.path, rel)
+	p.rel = rel
+	return p
+}
+
 // SourcePath is a Path representing a file path rooted from SrcDir
 type SourcePath struct {
 	basePath
@@ -463,6 +469,11 @@
 
 var _ Path = SourcePath{}
 
+func (p SourcePath) withRel(rel string) SourcePath {
+	p.basePath = p.basePath.withRel(rel)
+	return p
+}
+
 // safePathForSource is for paths that we expect are safe -- only for use by go
 // code that is embedding ninja variables in paths
 func safePathForSource(ctx PathContext, path string) SourcePath {
@@ -589,7 +600,7 @@
 // provided paths... may not use '..' to escape from the current path.
 func (p SourcePath) Join(ctx PathContext, paths ...string) SourcePath {
 	path := validatePath(ctx, paths...)
-	return PathForSource(ctx, p.path, path)
+	return p.withRel(path)
 }
 
 // OverlayPath returns the overlay for `path' if it exists. This assumes that the
@@ -631,8 +642,7 @@
 }
 
 func (p OutputPath) withRel(rel string) OutputPath {
-	p.basePath.path = filepath.Join(p.basePath.path, rel)
-	p.basePath.rel = rel
+	p.basePath = p.basePath.withRel(rel)
 	return p
 }
 
@@ -660,7 +670,7 @@
 // provided paths... may not use '..' to escape from the current path.
 func (p OutputPath) Join(ctx PathContext, paths ...string) OutputPath {
 	path := validatePath(ctx, paths...)
-	return PathForOutput(ctx, p.path, path)
+	return p.withRel(path)
 }
 
 // PathForIntermediates returns an OutputPath representing the top-level
diff --git a/android/testing.go b/android/testing.go
index fc58cec..ae012b0 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -23,9 +23,19 @@
 )
 
 func NewTestContext() *TestContext {
-	return &TestContext{
-		Context: blueprint.NewContext(),
+	namespaceExportFilter := func(namespace *Namespace) bool {
+		return true
 	}
+
+	nameResolver := NewNameResolver(namespaceExportFilter)
+	ctx := &TestContext{
+		Context:      blueprint.NewContext(),
+		NameResolver: nameResolver,
+	}
+
+	ctx.SetNameInterface(nameResolver)
+
+	return ctx
 }
 
 func NewTestArchContext() *TestContext {
@@ -37,6 +47,7 @@
 type TestContext struct {
 	*blueprint.Context
 	preArch, preDeps, postDeps []RegisterMutatorFunc
+	NameResolver               *NameResolver
 }
 
 func (ctx *TestContext) PreArchMutators(f RegisterMutatorFunc) {
@@ -125,6 +136,7 @@
 }
 
 func (m TestingModule) Output(file string) BuildParams {
+	var searchedOutputs []string
 	for _, p := range m.module.BuildParamsForTests() {
 		outputs := append(WritablePaths(nil), p.Outputs...)
 		if p.Output != nil {
@@ -134,7 +146,9 @@
 			if f.String() == file || f.Rel() == file {
 				return p
 			}
+			searchedOutputs = append(searchedOutputs, f.Rel())
 		}
 	}
-	panic(fmt.Errorf("couldn't find output %q", file))
+	panic(fmt.Errorf("couldn't find output %q.\nall outputs: %v",
+		file, searchedOutputs))
 }
diff --git a/android/variable.go b/android/variable.go
index a3920a1..ab8103a 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -194,6 +194,8 @@
 	DistDir             *string  `json:",omitempty"`
 
 	ExtraVndkVersions []string `json:",omitempty"`
+
+	NamespacesToExport []string `json:",omitempty"`
 }
 
 func boolPtr(v bool) *bool {
diff --git a/cc/config/global.go b/cc/config/global.go
index fb71e6a..a24b60c 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -131,7 +131,6 @@
 		"frameworks/native/libs/vr/libbufferhubqueue/",
 		"frameworks/native/libs/vr/libdvr/tests/",
 		"frameworks/native/services/surfaceflinger/tests/",
-		"frameworks/native/services/vr/",
 		"vendor/",
 	}
 
@@ -139,20 +138,15 @@
 	WarningAllowedOldProjects = []string{
 		"cts/hostsidetests/security/securityPatch/",
 		"cts/tests/tests/permission/jni/",
-		"development/tutorials/ReverseDebug/",
 		"frameworks/av/drm/mediacas/plugins/",
 		"frameworks/av/services/mediaextractor/",
-		"frameworks/base/core/tests/webkit/apk_with_native_libs/jni/",
 		"frameworks/base/tests/backup/",
 		"frameworks/native/cmds/cmd/",
 		"frameworks/webview/chromium/",
 		"hardware/libhardware/modules/",
-		"hardware/libhardware/tests/",
 		"hardware/qcom/",
 		"sdk/emulator/mksdcard/",
 		"system/vold/tests/",
-		"test/vts-testcase/kernel/api/qtaguid/",
-		"test/vts-testcase/security/poc/target/",
 		"tools/adt/idea/android/ultimate/get_modification_time/jni/",
 	}
 )
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index e15a6bd..ddde1c5 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -25,6 +25,22 @@
 	"android/soong/android"
 )
 
+func newNameResolver(config android.Config) *android.NameResolver {
+	namespacePathsToExport := make(map[string]bool)
+
+	for _, namespaceName := range config.ProductVariables.NamespacesToExport {
+		namespacePathsToExport[namespaceName] = true
+	}
+
+	namespacePathsToExport["."] = true // always export the root namespace
+
+	exportFilter := func(namespace *android.Namespace) bool {
+		return namespacePathsToExport[namespace.Path]
+	}
+
+	return android.NewNameResolver(exportFilter)
+}
+
 func main() {
 	flag.Parse()
 
@@ -40,8 +56,7 @@
 		os.Exit(1)
 	}
 
-	// Temporary hack
-	//ctx.SetIgnoreUnknownModuleTypes(true)
+	ctx.SetNameInterface(newNameResolver(configuration))
 
 	ctx.SetAllowMissingDependencies(configuration.AllowMissingDependencies())
 
diff --git a/java/androidmk.go b/java/androidmk.go
index 2e67639..f52d5e9 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -95,28 +95,35 @@
 }
 
 func (binary *Binary) AndroidMk() android.AndroidMkData {
-	return android.AndroidMkData{
-		Class:      "JAVA_LIBRARIES",
-		OutputFile: android.OptionalPathForPath(binary.implementationJarFile),
-		Include:    "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
-		Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
-			android.WriteAndroidMkData(w, data)
 
-			fmt.Fprintln(w, "jar_installed_module := $(LOCAL_INSTALLED_MODULE)")
-			fmt.Fprintln(w, "include $(CLEAR_VARS)")
-			fmt.Fprintln(w, "LOCAL_MODULE := "+name)
-			fmt.Fprintln(w, "LOCAL_MODULE_CLASS := EXECUTABLES")
-			if strings.Contains(prefix, "HOST_") {
-				fmt.Fprintln(w, "LOCAL_IS_HOST_MODULE := true")
-			}
-			fmt.Fprintln(w, "LOCAL_STRIP_MODULE := false")
-			fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", binary.wrapperFile.String())
-			fmt.Fprintln(w, "include $(BUILD_PREBUILT)")
+	if !binary.isWrapperVariant {
+		return android.AndroidMkData{
+			Class:      "JAVA_LIBRARIES",
+			OutputFile: android.OptionalPathForPath(binary.implementationJarFile),
+			Include:    "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
+			Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
+				android.WriteAndroidMkData(w, data)
 
-			// Ensure that the wrapper script timestamp is always updated when the jar is updated
-			fmt.Fprintln(w, "$(LOCAL_INSTALLED_MODULE): $(jar_installed_module)")
-			fmt.Fprintln(w, "jar_installed_module :=")
-		},
+				fmt.Fprintln(w, "jar_installed_module := $(LOCAL_INSTALLED_MODULE)")
+			},
+		}
+	} else {
+		return android.AndroidMkData{
+			Class:      "EXECUTABLES",
+			OutputFile: android.OptionalPathForPath(binary.wrapperFile),
+			Extra: []android.AndroidMkExtraFunc{
+				func(w io.Writer, outputFile android.Path) {
+					fmt.Fprintln(w, "LOCAL_STRIP_MODULE := false")
+				},
+			},
+			Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
+				android.WriteAndroidMkData(w, data)
+
+				// Ensure that the wrapper script timestamp is always updated when the jar is updated
+				fmt.Fprintln(w, "$(LOCAL_INSTALLED_MODULE): $(jar_installed_module)")
+				fmt.Fprintln(w, "jar_installed_module :=")
+			},
+		}
 	}
 }
 
diff --git a/java/genrule.go b/java/genrule.go
new file mode 100644
index 0000000..80b7030
--- /dev/null
+++ b/java/genrule.go
@@ -0,0 +1,35 @@
+// 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 java
+
+import (
+	"android/soong/android"
+	"android/soong/genrule"
+)
+
+func init() {
+	android.RegisterModuleType("java_genrule", genRuleFactory)
+}
+
+// java_genrule is a genrule that can depend on other java_* objects.
+// The cmd may be run multiple times, once for each of the different host/device
+// variations.
+func genRuleFactory() android.Module {
+	module := genrule.NewGenRule()
+
+	android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibCommon)
+
+	return module
+}
diff --git a/java/java.go b/java/java.go
index 4355200..0e54e3c 100644
--- a/java/java.go
+++ b/java/java.go
@@ -211,6 +211,12 @@
 	compiledSrcJars  android.Paths
 }
 
+func (j *Module) Srcs() android.Paths {
+	return android.Paths{j.implementationJarFile}
+}
+
+var _ android.SourceFileProducer = (*Module)(nil)
+
 type Dependency interface {
 	HeaderJars() android.Paths
 	ImplementationJars() android.Paths
@@ -446,6 +452,15 @@
 	kotlinStdlib       android.Paths
 }
 
+func checkProducesJars(ctx android.ModuleContext, dep android.SourceFileProducer) {
+	for _, f := range dep.Srcs() {
+		if f.Ext() != ".jar" {
+			ctx.ModuleErrorf("genrule %q must generate files ending with .jar to be used as a libs or static_libs dependency",
+				ctx.OtherModuleName(dep.(blueprint.Module)))
+		}
+	}
+}
+
 func (j *Module) collectDeps(ctx android.ModuleContext) deps {
 	var deps deps
 
@@ -462,8 +477,46 @@
 		otherName := ctx.OtherModuleName(module)
 		tag := ctx.OtherModuleDependencyTag(module)
 
-		dep, _ := module.(Dependency)
-		if dep == nil {
+		switch dep := module.(type) {
+		case Dependency:
+			switch tag {
+			case bootClasspathTag:
+				deps.bootClasspath = append(deps.bootClasspath, dep.HeaderJars()...)
+			case libTag:
+				deps.classpath = append(deps.classpath, dep.HeaderJars()...)
+			case staticLibTag:
+				deps.classpath = append(deps.classpath, dep.HeaderJars()...)
+				deps.staticJars = append(deps.staticJars, dep.ImplementationJars()...)
+				deps.staticHeaderJars = append(deps.staticHeaderJars, dep.HeaderJars()...)
+			case frameworkResTag:
+				if ctx.ModuleName() == "framework" {
+					// framework.jar has a one-off dependency on the R.java and Manifest.java files
+					// generated by framework-res.apk
+					deps.srcJars = append(deps.srcJars, dep.(*AndroidApp).aaptSrcJar)
+				}
+			case kotlinStdlibTag:
+				deps.kotlinStdlib = dep.HeaderJars()
+			default:
+				panic(fmt.Errorf("unknown dependency %q for %q", otherName, ctx.ModuleName()))
+			}
+
+			deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs()...)
+		case android.SourceFileProducer:
+			switch tag {
+			case libTag:
+				checkProducesJars(ctx, dep)
+				deps.classpath = append(deps.classpath, dep.Srcs()...)
+			case staticLibTag:
+				checkProducesJars(ctx, dep)
+				deps.classpath = append(deps.classpath, dep.Srcs()...)
+				deps.staticJars = append(deps.staticJars, dep.Srcs()...)
+				deps.staticHeaderJars = append(deps.staticHeaderJars, dep.Srcs()...)
+			case android.DefaultsDepTag, android.SourceDepTag:
+				// Nothing to do
+			default:
+				ctx.ModuleErrorf("dependency on genrule %q may only be in srcs, libs, or static_libs", otherName)
+			}
+		default:
 			switch tag {
 			case android.DefaultsDepTag, android.SourceDepTag:
 				// Nothing to do
@@ -479,31 +532,7 @@
 			default:
 				ctx.ModuleErrorf("depends on non-java module %q", otherName)
 			}
-			return
 		}
-
-		switch tag {
-		case bootClasspathTag:
-			deps.bootClasspath = append(deps.bootClasspath, dep.HeaderJars()...)
-		case libTag:
-			deps.classpath = append(deps.classpath, dep.HeaderJars()...)
-		case staticLibTag:
-			deps.classpath = append(deps.classpath, dep.HeaderJars()...)
-			deps.staticJars = append(deps.staticJars, dep.ImplementationJars()...)
-			deps.staticHeaderJars = append(deps.staticHeaderJars, dep.HeaderJars()...)
-		case frameworkResTag:
-			if ctx.ModuleName() == "framework" {
-				// framework.jar has a one-off dependency on the R.java and Manifest.java files
-				// generated by framework-res.apk
-				deps.srcJars = append(deps.srcJars, dep.(*AndroidApp).aaptSrcJar)
-			}
-		case kotlinStdlibTag:
-			deps.kotlinStdlib = dep.HeaderJars()
-		default:
-			panic(fmt.Errorf("unknown dependency %q for %q", otherName, ctx.ModuleName()))
-		}
-
-		deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs()...)
 	})
 
 	return deps
@@ -985,6 +1014,8 @@
 
 	binaryProperties binaryProperties
 
+	isWrapperVariant bool
+
 	wrapperFile android.SourcePath
 	binaryFile  android.OutputPath
 }
@@ -994,21 +1025,32 @@
 }
 
 func (j *Binary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	j.Library.GenerateAndroidBuildActions(ctx)
-
-	// Depend on the installed jar (j.installFile) so that the wrapper doesn't get executed by
-	// another build rule before the jar has been installed.
-	if String(j.binaryProperties.Wrapper) != "" {
-		j.wrapperFile = android.PathForModuleSrc(ctx, String(j.binaryProperties.Wrapper)).SourcePath
+	if ctx.Arch().ArchType == android.Common {
+		// Compile the jar
+		j.Library.GenerateAndroidBuildActions(ctx)
 	} else {
-		j.wrapperFile = android.PathForSource(ctx, "build/soong/scripts/jar-wrapper.sh")
+		// Handle the binary wrapper
+		j.isWrapperVariant = true
+
+		if String(j.binaryProperties.Wrapper) != "" {
+			j.wrapperFile = android.PathForModuleSrc(ctx, String(j.binaryProperties.Wrapper)).SourcePath
+		} else {
+			j.wrapperFile = android.PathForSource(ctx, "build/soong/scripts/jar-wrapper.sh")
+		}
+
+		// Depend on the installed jar so that the wrapper doesn't get executed by
+		// another build rule before the jar has been installed.
+		jarFile := ctx.PrimaryModule().(*Binary).installFile
+
+		j.binaryFile = ctx.InstallExecutable(android.PathForModuleInstall(ctx, "bin"),
+			ctx.ModuleName(), j.wrapperFile, jarFile)
 	}
-	j.binaryFile = ctx.InstallExecutable(android.PathForModuleInstall(ctx, "bin"),
-		ctx.ModuleName(), j.wrapperFile, j.installFile)
 }
 
 func (j *Binary) DepsMutator(ctx android.BottomUpMutatorContext) {
-	j.deps(ctx)
+	if ctx.Arch().ArchType == android.Common {
+		j.deps(ctx)
+	}
 }
 
 func BinaryFactory() android.Module {
@@ -1020,7 +1062,8 @@
 		&module.Module.protoProperties,
 		&module.binaryProperties)
 
-	InitJavaModule(module, android.HostAndDeviceSupported)
+	android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibCommonFirst)
+	android.InitDefaultableModule(module)
 	return module
 }
 
@@ -1033,7 +1076,8 @@
 		&module.Module.protoProperties,
 		&module.binaryProperties)
 
-	InitJavaModule(module, android.HostSupported)
+	android.InitAndroidArchModule(module, android.HostSupported, android.MultilibCommonFirst)
+	android.InitDefaultableModule(module)
 	return module
 }
 
diff --git a/java/java_test.go b/java/java_test.go
index dbecc70..78fbd41 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -62,11 +62,13 @@
 
 	ctx := android.NewTestArchContext()
 	ctx.RegisterModuleType("android_app", android.ModuleFactoryAdaptor(AndroidAppFactory))
+	ctx.RegisterModuleType("java_binary_host", android.ModuleFactoryAdaptor(BinaryHostFactory))
 	ctx.RegisterModuleType("java_library", android.ModuleFactoryAdaptor(LibraryFactory(true)))
 	ctx.RegisterModuleType("java_library_host", android.ModuleFactoryAdaptor(LibraryHostFactory))
 	ctx.RegisterModuleType("java_import", android.ModuleFactoryAdaptor(ImportFactory))
 	ctx.RegisterModuleType("java_defaults", android.ModuleFactoryAdaptor(defaultsFactory))
 	ctx.RegisterModuleType("java_system_modules", android.ModuleFactoryAdaptor(SystemModulesFactory))
+	ctx.RegisterModuleType("java_genrule", android.ModuleFactoryAdaptor(genRuleFactory))
 	ctx.RegisterModuleType("filegroup", android.ModuleFactoryAdaptor(genrule.FileGroupFactory))
 	ctx.RegisterModuleType("genrule", android.ModuleFactoryAdaptor(genrule.GenRuleFactory))
 	ctx.PreArchMutators(android.RegisterPrebuiltsPreArchMutators)
@@ -146,6 +148,8 @@
 		// For framework-res, which is an implicit dependency for framework
 		"AndroidManifest.xml":                   nil,
 		"build/target/product/security/testkey": nil,
+
+		"build/soong/scripts/jar-wrapper.sh": nil,
 	}
 
 	for k, v := range fs {
@@ -158,6 +162,7 @@
 }
 
 func run(t *testing.T, ctx *android.TestContext, config android.Config) {
+	t.Helper()
 	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
 	fail(t, errs)
 	_, errs = ctx.PrepareBuildActions(config)
@@ -165,6 +170,7 @@
 }
 
 func testJava(t *testing.T, bp string) *android.TestContext {
+	t.Helper()
 	config := testConfig(nil)
 	ctx := testContext(config, bp, nil)
 	run(t, ctx, config)
@@ -249,6 +255,35 @@
 	}
 }
 
+func TestBinary(t *testing.T) {
+	ctx := testJava(t, `
+		java_library_host {
+			name: "foo",
+			srcs: ["a.java"],
+		}
+
+		java_binary_host {
+			name: "bar",
+			srcs: ["b.java"],
+			static_libs: ["foo"],
+		}
+	`)
+
+	buildOS := android.BuildOs.String()
+
+	bar := ctx.ModuleForTests("bar", buildOS+"_common")
+	barJar := bar.Output("bar.jar").Output.String()
+	barWrapper := ctx.ModuleForTests("bar", buildOS+"_x86_64")
+	barWrapperDeps := barWrapper.Output("bar").Implicits.Strings()
+
+	// Test that the install binary wrapper depends on the installed jar file
+	if len(barWrapperDeps) != 1 || barWrapperDeps[0] != barJar {
+		t.Errorf("expected binary wrapper implicits [%q], got %v",
+			barJar, barWrapperDeps)
+	}
+
+}
+
 var classpathTestcases = []struct {
 	name          string
 	moduleType    string
@@ -775,7 +810,62 @@
 	}
 }
 
+func TestJarGenrules(t *testing.T) {
+	ctx := testJava(t, `
+		java_library {
+			name: "foo",
+			srcs: ["a.java"],
+		}
+
+		java_genrule {
+			name: "jargen",
+			tool_files: ["b.java"],
+			cmd: "$(location b.java) $(in) $(out)",
+			out: ["jargen.jar"],
+			srcs: [":foo"],
+		}
+
+		java_library {
+			name: "bar",
+			static_libs: ["jargen"],
+			srcs: ["c.java"],
+		}
+
+		java_library {
+			name: "baz",
+			libs: ["jargen"],
+			srcs: ["c.java"],
+		}
+	`)
+
+	foo := ctx.ModuleForTests("foo", "android_common").Output("javac/foo.jar")
+	jargen := ctx.ModuleForTests("jargen", "android_common").Output("jargen.jar")
+	bar := ctx.ModuleForTests("bar", "android_common").Output("javac/bar.jar")
+	baz := ctx.ModuleForTests("baz", "android_common").Output("javac/baz.jar")
+	barCombined := ctx.ModuleForTests("bar", "android_common").Output("combined/bar.jar")
+
+	if len(jargen.Inputs) != 1 || jargen.Inputs[0].String() != foo.Output.String() {
+		t.Errorf("expected jargen inputs [%q], got %q", foo.Output.String(), jargen.Inputs.Strings())
+	}
+
+	if !strings.Contains(bar.Args["classpath"], jargen.Output.String()) {
+		t.Errorf("bar classpath %v does not contain %q", bar.Args["classpath"], jargen.Output.String())
+	}
+
+	if !strings.Contains(baz.Args["classpath"], jargen.Output.String()) {
+		t.Errorf("baz classpath %v does not contain %q", baz.Args["classpath"], jargen.Output.String())
+	}
+
+	if len(barCombined.Inputs) != 2 ||
+		barCombined.Inputs[0].String() != bar.Output.String() ||
+		barCombined.Inputs[1].String() != jargen.Output.String() {
+		t.Errorf("bar combined jar inputs %v is not [%q, %q]",
+			barCombined.Inputs.Strings(), bar.Output.String(), jargen.Output.String())
+	}
+}
+
 func fail(t *testing.T, errs []error) {
+	t.Helper()
 	if len(errs) > 0 {
 		for _, err := range errs {
 			t.Error(err)
diff --git a/scripts/diff_build_graphs.sh b/scripts/diff_build_graphs.sh
index e7d8749..81010f3 100755
--- a/scripts/diff_build_graphs.sh
+++ b/scripts/diff_build_graphs.sh
@@ -120,7 +120,7 @@
   diff -r "$unzipped1" "$unzipped2" -x build_date.txt -x build_number.txt -x '\.*' -x '*.log' -x build_fingerprint.txt -x build.ninja.d -x '*.zip' > $diffFile || true
   if [[ -s "$diffFile" ]]; then
     # outputs are different, so remove the unzipped versions but keep the zipped versions
-    echo "Some differences for product $product:"
+    echo "First few differences (total diff linecount=$(wc -l $diffFile)) for product $product:"
     cat "$diffFile" | head -n 10
     echo "End of differences for product $product"
     rm -rf "$unzipped1" "$unzipped2"
diff --git a/ui/build/dumpvars.go b/ui/build/dumpvars.go
index fb20d63..96f2274 100644
--- a/ui/build/dumpvars.go
+++ b/ui/build/dumpvars.go
@@ -106,6 +106,7 @@
 	"AUX_OS_VARIANT_LIST",
 	"TARGET_BUILD_PDK",
 	"PDK_FUSION_PLATFORM_ZIP",
+	"PRODUCT_SOONG_NAMESPACES",
 }
 
 func Banner(make_vars map[string]string) string {