Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e2453c70 authored by Paul Duffin's avatar Paul Duffin
Browse files

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
parent bf46d96c
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -58,6 +58,7 @@ bootstrap_go_package {
        "android/notices.go",
        "android/onceper.go",
        "android/override_module.go",
        "android/package.go",
        "android/package_ctx.go",
        "android/path_properties.go",
        "android/paths.go",
@@ -88,6 +89,7 @@ bootstrap_go_package {
        "android/namespace_test.go",
        "android/neverallow_test.go",
        "android/onceper_test.go",
        "android/package_test.go",
        "android/path_properties_test.go",
        "android/paths_test.go",
        "android/prebuilt_test.go",
+33 −9
Original line number Diff line number Diff line
@@ -133,6 +133,25 @@ directory) there are two packages, `my/app`, and the subpackage `my/app/tests`.

This is based on the Bazel package concept.

The `package` module type allows information to be specified about a package. Only a single
`package` module can be specified per package and in the case where there are multiple `.bp` files
in the same package directory it is highly recommended that the `package` module (if required) is
specified in the `Android.bp` file.

Unlike most module type `package` does not have a `name` property. Instead the name is set to the
name of the package, e.g. if the package is in `top/intermediate/package` then the package name is
`//top/intermediate/package`.

E.g. The following will set the default visibility for all the modules defined in the package
(irrespective of whether they are in the same `.bp` file as the `package` module) to be visible to
all the subpackages by default.

```
package {
    default_visibility: [":__subpackages"]
}
```

### Name resolution

Soong provides the ability for modules in different directories to specify
@@ -191,7 +210,7 @@ this module. For example, `//project:rule`, `//project/library:lib` or
`//independent:evil`)
* `["//project"]`: This is shorthand for `["//project:__pkg__"]`
* `[":__subpackages__"]`: This is shorthand for `["//project:__subpackages__"]`
where `//project` is the module's package. e.g. using `[":__subpackages__"]` in
where `//project` is the module's package, e.g. using `[":__subpackages__"]` in
`packages/apps/Settings/Android.bp` is equivalent to
`//packages/apps/Settings:__subpackages__`.
* `["//visibility:legacy_public"]`: The default visibility, behaves as
@@ -207,13 +226,18 @@ in `vendor/`, e.g. a module in `libcore` cannot declare that it is visible to
say `vendor/google`, instead it must make itself visible to all packages within
`vendor/` using `//vendor:__subpackages__`.

If a module does not specify the `visibility` property the module is
`//visibility:legacy_public`. Once the build has been completely switched over to
soong it is possible that a global refactoring will be done to change this to
`//visibility:private` at which point all modules that do not currently specify
a `visibility` property will be updated to have
`visibility = [//visibility:legacy_public]` added. It will then be the owner's
responsibility to replace that with a more appropriate visibility.
If a module does not specify the `visibility` property then it uses the
`default_visibility` property of the `package` module in the module's package.

If the `default_visibility` property is not set for the module's package then
the module uses `//visibility:legacy_public`.

Once the build has been completely switched over to soong it is possible that a
global refactoring will be done to change this to `//visibility:private` at
which point all packages that do not currently specify a `default_visibility`
property will be updated to have
`default_visibility = [//visibility:legacy_public]` added. It will then be the
owner's responsibility to replace that with a more appropriate visibility.

### Formatter

+55 −0
Original line number Diff line number Diff line
@@ -202,6 +202,42 @@ type Module interface {
	BuildParamsForTests() []BuildParams
	RuleParamsForTests() map[blueprint.Rule]blueprint.RuleParams
	VariablesForTests() map[string]string

	// Get the qualified module id for this module.
	qualifiedModuleId(ctx BaseModuleContext) qualifiedModuleName

	// Get information about the properties that can contain visibility rules.
	visibilityProperties() []visibilityProperty
}

// Qualified id for a module
type qualifiedModuleName struct {
	// The package (i.e. directory) in which the module is defined, without trailing /
	pkg string

	// The name of the module, empty string if package.
	name string
}

func (q qualifiedModuleName) String() string {
	if q.name == "" {
		return "//" + q.pkg
	}
	return "//" + q.pkg + ":" + q.name
}

// Get the id for the package containing this module.
func (q qualifiedModuleName) getContainingPackageId() qualifiedModuleName {
	pkg := q.pkg
	if q.name == "" {
		panic(fmt.Errorf("Cannot get containing package id of package module %s", pkg))
	}
	return newPackageId(pkg)
}

func newPackageId(pkg string) qualifiedModuleName {
	// A qualified id for a package module has no name.
	return qualifiedModuleName{pkg: pkg, name: ""}
}

type nameProperties struct {
@@ -236,6 +272,13 @@ type commonProperties struct {
	//      //packages/apps/Settings:__subpackages__.
	//  ["//visibility:legacy_public"]: The default visibility, behaves as //visibility:public
	//      for now. It is an error if it is used in a module.
	//
	// If a module does not specify the `visibility` property then it uses the
	// `default_visibility` property of the `package` module in the module's package.
	//
	// If the `default_visibility` property is not set for the module's package then
	// the module uses `//visibility:legacy_public`.
	//
	// See https://android.googlesource.com/platform/build/soong/+/master/README.md#visibility for
	// more details.
	Visibility []string
@@ -571,6 +614,18 @@ func (m *ModuleBase) base() *ModuleBase {
	return m
}

func (m *ModuleBase) qualifiedModuleId(ctx BaseModuleContext) qualifiedModuleName {
	return qualifiedModuleName{pkg: ctx.ModuleDir(), name: ctx.ModuleName()}
}

func (m *ModuleBase) visibilityProperties() []visibilityProperty {
	return []visibilityProperty{
		newVisibilityProperty("visibility", func() []string {
			return m.base().commonProperties.Visibility
		}),
	}
}

func (m *ModuleBase) SetTarget(target Target, multiTargets []Target, primary bool) {
	m.commonProperties.CompileTarget = target
	m.commonProperties.CompileMultiTargets = multiTargets
+2 −0
Original line number Diff line number Diff line
@@ -75,6 +75,8 @@ type RegisterMutatorFunc func(RegisterMutatorsContext)
var preArch = []RegisterMutatorFunc{
	registerLoadHookMutator,
	RegisterNamespaceMutator,
	// Rename package module types.
	registerPackageRenamer,
	RegisterPrebuiltsPreArchMutators,
	registerVisibilityRuleChecker,
	RegisterDefaultsPreArchMutators,

android/package.go

0 → 100644
+194 −0
Original line number Diff line number Diff line
// 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)
	}
}
Loading