Allow default visibility to be set per package

Adds a package module type with a default_visibility property. The
package module type can only be specified once per package.

Bug: 133290645
Test: m droid
Change-Id: Ibb2fb499c9ea88ecaa662d3cd2cbde478e4b9a4b
diff --git a/android/package.go b/android/package.go
new file mode 100644
index 0000000..03f6a1e
--- /dev/null
+++ b/android/package.go
@@ -0,0 +1,194 @@
+// Copyright 2019 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 (
+	"fmt"
+	"sync/atomic"
+
+	"github.com/google/blueprint"
+)
+
+func init() {
+	RegisterModuleType("package", PackageFactory)
+}
+
+// The information maintained about each package.
+type packageInfo struct {
+	// The module from which this information was populated. If `duplicated` = true then this is the
+	// module that has been renamed and must be used to report errors.
+	module *packageModule
+
+	// If true this indicates that there are two package statements in the same package which is not
+	// allowed and will cause the build to fail. This flag is set by packageRenamer and checked in
+	// packageErrorReporter
+	duplicated bool
+}
+
+type packageProperties struct {
+	Name string `blueprint:"mutated"`
+
+	// Specifies the default visibility for all modules defined in this package.
+	Default_visibility []string
+}
+
+type packageModule struct {
+	ModuleBase
+
+	properties  packageProperties
+	packageInfo *packageInfo
+}
+
+func (p *packageModule) GenerateAndroidBuildActions(ModuleContext) {
+	// Nothing to do.
+}
+
+func (p *packageModule) GenerateBuildActions(ctx blueprint.ModuleContext) {
+	// Nothing to do.
+}
+
+func (p *packageModule) qualifiedModuleId(ctx BaseModuleContext) qualifiedModuleName {
+	// Override to create a package id.
+	return newPackageId(ctx.ModuleDir())
+}
+
+// Override to ensure that the default_visibility rules are checked by the visibility module during
+// its checking phase.
+func (p *packageModule) visibilityProperties() []visibilityProperty {
+	return []visibilityProperty{
+		newVisibilityProperty("default_visibility", func() []string {
+			return p.properties.Default_visibility
+		}),
+	}
+}
+
+func (p *packageModule) Name() string {
+	return p.properties.Name
+}
+
+func (p *packageModule) setName(name string) {
+	p.properties.Name = name
+}
+
+// Counter to ensure package modules are created with a unique name within whatever namespace they
+// belong.
+var packageCount uint32 = 0
+
+func PackageFactory() Module {
+	module := &packageModule{}
+
+	// Get a unique if for the package. Has to be done atomically as the creation of the modules are
+	// done in parallel.
+	id := atomic.AddUint32(&packageCount, 1)
+	name := fmt.Sprintf("soong_package_%d", id)
+
+	module.properties.Name = name
+
+	module.AddProperties(&module.properties)
+	return module
+}
+
+// Registers the function that renames the packages.
+func registerPackageRenamer(ctx RegisterMutatorsContext) {
+	ctx.BottomUp("packageRenamer", packageRenamer).Parallel()
+	ctx.BottomUp("packageErrorReporter", packageErrorReporter).Parallel()
+}
+
+// Renames the package to match the package directory.
+//
+// This also creates a PackageInfo object for each package and uses that to detect and remember
+// duplicates for later error reporting.
+func packageRenamer(ctx BottomUpMutatorContext) {
+	m, ok := ctx.Module().(*packageModule)
+	if !ok {
+		return
+	}
+
+	packageName := "//" + ctx.ModuleDir()
+
+	pi := newPackageInfo(ctx, packageName, m)
+	if pi.module != m {
+		// Remember that the package was duplicated but do not rename as that will cause an error to
+		// be logged with the generated name. Similarly, reporting the error here will use the generated
+		// name as renames are only processed after this phase.
+		pi.duplicated = true
+	} else {
+		// This is the first package module in this package so rename it to match the package name.
+		m.setName(packageName)
+		ctx.Rename(packageName)
+
+		// Store a package info reference in the module.
+		m.packageInfo = pi
+	}
+}
+
+// Logs any deferred errors.
+func packageErrorReporter(ctx BottomUpMutatorContext) {
+	m, ok := ctx.Module().(*packageModule)
+	if !ok {
+		return
+	}
+
+	packageDir := ctx.ModuleDir()
+	packageName := "//" + packageDir
+
+	// Get the PackageInfo for the package. Should have been populated in the packageRenamer phase.
+	pi := findPackageInfo(ctx, packageName)
+	if pi == nil {
+		ctx.ModuleErrorf("internal error, expected package info to be present for package '%s'",
+			packageName)
+		return
+	}
+
+	if pi.module != m {
+		// The package module has been duplicated but this is not the module that has been renamed so
+		// ignore it. An error will be logged for the renamed module which will ensure that the error
+		// message uses the correct name.
+		return
+	}
+
+	// Check to see whether there are duplicate package modules in the package.
+	if pi.duplicated {
+		ctx.ModuleErrorf("package {...} specified multiple times")
+		return
+	}
+}
+
+type defaultPackageInfoKey string
+
+func newPackageInfo(
+	ctx BaseModuleContext, packageName string, module *packageModule) *packageInfo {
+	key := NewCustomOnceKey(defaultPackageInfoKey(packageName))
+
+	return ctx.Config().Once(key, func() interface{} {
+		return &packageInfo{module: module}
+	}).(*packageInfo)
+}
+
+// Get the PackageInfo for the package name (starts with //, no trailing /), is nil if no package
+// module type was specified.
+func findPackageInfo(ctx BaseModuleContext, packageName string) *packageInfo {
+	key := NewCustomOnceKey(defaultPackageInfoKey(packageName))
+
+	pi := ctx.Config().Once(key, func() interface{} {
+		return nil
+	})
+
+	if pi == nil {
+		return nil
+	} else {
+		return pi.(*packageInfo)
+	}
+}