Make ninja file deterministic even with dup module names

Bug: 65683273
Test: ./build/soong/scripts/diff_build_graphs.sh \
      --products=aosp_arm '' ''

Change-Id: Ie01ff579d69e94b12363f53aec5d25810211c451
diff --git a/android/namespace.go b/android/namespace.go
index b3e718a..1f8ef5a 100644
--- a/android/namespace.go
+++ b/android/namespace.go
@@ -22,7 +22,6 @@
 	"strconv"
 	"strings"
 	"sync"
-	"sync/atomic"
 
 	"github.com/google/blueprint"
 )
@@ -66,6 +65,15 @@
 	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"
@@ -73,7 +81,7 @@
 	rootNamespace *Namespace
 
 	// id counter for atomic.AddInt32
-	numNamespaces int32
+	nextNamespaceId int32
 
 	// All namespaces, without duplicates.
 	sortedNamespaces sortedNamespaces
@@ -104,14 +112,6 @@
 
 	namespace.exportToKati = r.namespaceExportFilter(namespace)
 
-	nextId := atomic.AddInt32(&r.numNamespaces, 1)
-	id := nextId - 1
-	stringId := ""
-	if id > 0 {
-		stringId = strconv.Itoa(int(id))
-	}
-	namespace.id = stringId
-
 	return namespace
 }
 
@@ -291,6 +291,14 @@
 	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)
 
@@ -332,6 +340,14 @@
 	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 {
@@ -391,15 +407,17 @@
 }
 
 func RegisterNamespaceMutator(ctx RegisterMutatorsContext) {
-	ctx.BottomUp("namespace_deps", namespaceDeps)
+	ctx.BottomUp("namespace_deps", namespaceMutator).Parallel()
 }
 
-func namespaceDeps(ctx BottomUpMutatorContext) {
+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
index 13da88b..9ab186b 100644
--- a/android/namespace_test.go
+++ b/android/namespace_test.go
@@ -19,6 +19,7 @@
 	"io/ioutil"
 	"os"
 	"path/filepath"
+	"reflect"
 	"testing"
 
 	"github.com/google/blueprint"
@@ -562,6 +563,25 @@
 	}
 }
 
+// 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) {
diff --git a/android/testing.go b/android/testing.go
index 4f2a2da..1c0fac1 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -23,14 +23,17 @@
 )
 
 func NewTestContext() *TestContext {
-	ctx := &TestContext{
-		Context: blueprint.NewContext(),
-	}
-
 	namespaceExportFilter := func(namespace *Namespace) bool {
 		return true
 	}
-	ctx.SetNameInterface(NewNameResolver(namespaceExportFilter))
+
+	nameResolver := NewNameResolver(namespaceExportFilter)
+	ctx := &TestContext{
+		Context:      blueprint.NewContext(),
+		NameResolver: nameResolver,
+	}
+
+	ctx.SetNameInterface(nameResolver)
 
 	return ctx
 }
@@ -44,6 +47,7 @@
 type TestContext struct {
 	*blueprint.Context
 	preArch, preDeps, postDeps []RegisterMutatorFunc
+	NameResolver               *NameResolver
 }
 
 func (ctx *TestContext) PreArchMutators(f RegisterMutatorFunc) {