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)
+ }
+}