Relax module naming restrictions

Forcing module names to be valid ninja names is an unnecessary
restraint on the project build logic.  Allow any string as a
module name, and sanitize and uniquify the module name for use
in module-scoped variables.

Also move the module scope to be per-module instead of per-group
so that modules can use the same local variable name for each variant.

Change-Id: If44cca20712305e2c0b6d6b39daa5eace335c148
diff --git a/context.go b/context.go
index 0062e36..96946d4 100644
--- a/context.go
+++ b/context.go
@@ -26,6 +26,7 @@
 	"reflect"
 	"runtime"
 	"sort"
+	"strconv"
 	"strings"
 	"text/scanner"
 	"text/template"
@@ -67,6 +68,7 @@
 	moduleGroupsSorted []*moduleGroup
 	singletonInfo      map[string]*singletonInfo
 	mutatorInfo        []*mutatorInfo
+	moduleNinjaNames   map[string]*moduleGroup
 
 	dependenciesReady bool // set to true on a successful ResolveDependencies
 	buildActionsReady bool // set to true on a successful PrepareBuildActions
@@ -106,6 +108,7 @@
 type moduleGroup struct {
 	// set during Parse
 	typeName          string
+	ninjaName         string
 	relBlueprintsFile string
 	pos               scanner.Position
 	propertyPos       map[string]scanner.Position
@@ -182,10 +185,11 @@
 // useful.
 func NewContext() *Context {
 	return &Context{
-		moduleFactories: make(map[string]ModuleFactory),
-		moduleGroups:    make(map[string]*moduleGroup),
-		moduleInfo:      make(map[Module]*moduleInfo),
-		singletonInfo:   make(map[string]*singletonInfo),
+		moduleFactories:  make(map[string]ModuleFactory),
+		moduleGroups:     make(map[string]*moduleGroup),
+		moduleInfo:       make(map[Module]*moduleInfo),
+		singletonInfo:    make(map[string]*singletonInfo),
+		moduleNinjaNames: make(map[string]*moduleGroup),
 	}
 }
 
@@ -790,23 +794,23 @@
 		return nil, errs
 	}
 
+	ninjaName := toNinjaName(group.properties.Name)
+
+	// The sanitizing in toNinjaName can result in collisions, uniquify the name if it
+	// already exists
+	for i := 0; c.moduleNinjaNames[ninjaName] != nil; i++ {
+		ninjaName = toNinjaName(group.properties.Name) + strconv.Itoa(i)
+	}
+
+	c.moduleNinjaNames[ninjaName] = group
+	group.ninjaName = ninjaName
+
 	group.pos = moduleDef.Type.Pos
 	group.propertyPos = make(map[string]scanner.Position)
 	for name, propertyDef := range propertyMap {
 		group.propertyPos[name] = propertyDef.Pos
 	}
 
-	name := group.properties.Name
-	err := validateNinjaName(name)
-	if err != nil {
-		return nil, []error{
-			&Error{
-				Err: fmt.Errorf("invalid module name %q: %s", err),
-				Pos: group.propertyPos["name"],
-			},
-		}
-	}
-
 	module := &moduleInfo{
 		group:            group,
 		logicModule:      logicModule,
@@ -1317,12 +1321,13 @@
 	}()
 
 	c.parallelVisitAllBottomUp(func(group *moduleGroup) bool {
-		// The parent scope of the moduleContext's local scope gets overridden to be that of the
-		// calling Go package on a per-call basis.  Since the initial parent scope doesn't matter we
-		// just set it to nil.
-		scope := newLocalScope(nil, moduleNamespacePrefix(group.properties.Name))
-
 		for _, module := range group.modules {
+			// The parent scope of the moduleContext's local scope gets overridden to be that of the
+			// calling Go package on a per-call basis.  Since the initial parent scope doesn't matter we
+			// just set it to nil.
+			prefix := moduleNamespacePrefix(group.ninjaName + "_" + module.subName())
+			scope := newLocalScope(nil, prefix)
+
 			mctx := &moduleContext{
 				baseModuleContext: baseModuleContext{
 					context: c,