diff --git a/android/Android.bp b/android/Android.bp index 118087db7..94d2c04f6 100644 --- a/android/Android.bp +++ b/android/Android.bp @@ -77,6 +77,7 @@ bootstrap_go_package { "path_properties.go", "paths.go", "phony.go", + "plugin.go", "prebuilt.go", "prebuilt_build_tool.go", "proto.go", diff --git a/android/config.go b/android/config.go index 4f0a64dfe..d0f2ea44c 100644 --- a/android/config.go +++ b/android/config.go @@ -1880,6 +1880,10 @@ func (c *deviceConfig) ShippingApiLevel() ApiLevel { return uncheckedFinalApiLevel(apiLevel) } +func (c *deviceConfig) BuildBrokenPluginValidation() []string { + return c.config.productVariables.BuildBrokenPluginValidation +} + func (c *deviceConfig) BuildBrokenClangAsFlags() bool { return c.config.productVariables.BuildBrokenClangAsFlags } diff --git a/android/plugin.go b/android/plugin.go new file mode 100644 index 000000000..c9d1338f8 --- /dev/null +++ b/android/plugin.go @@ -0,0 +1,140 @@ +// Copyright 2022 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 ( + "encoding/json" + "fmt" + "io/ioutil" + "os" + "strings" + + "github.com/google/blueprint" +) + +func init() { + RegisterPluginSingletonBuildComponents(InitRegistrationContext) +} + +func RegisterPluginSingletonBuildComponents(ctx RegistrationContext) { + ctx.RegisterSingletonType("plugins", pluginSingletonFactory) +} + +// pluginSingleton is a singleton to handle allowlisting of the final Android-.mk file +// output. +func pluginSingletonFactory() Singleton { + return &pluginSingleton{} +} + +type pluginSingleton struct{} + +var allowedPluginsByName = map[string]bool{ + "aidl-soong-rules": true, + "arm_compute_library_nn_driver": true, + "cuttlefish-soong-rules": true, + "gki-soong-rules": true, + "hidl-soong-rules": true, + "kernel-config-soong-rules": true, + "soong-angle-codegen": true, + "soong-api": true, + "soong-art": true, + "soong-ca-certificates": true, + "soong-ca-certificates-apex": true, + "soong-clang": true, + "soong-clang-prebuilts": true, + "soong-csuite": true, + "soong-fluoride": true, + "soong-fs_config": true, + "soong-icu": true, + "soong-java-config-error_prone": true, + "soong-libchrome": true, + "soong-llvm": true, + "soong-robolectric": true, + "soong-rust-prebuilts": true, + "soong-selinux": true, + "soong-wayland-protocol-codegen": true, + "treble_report_app": true, + "treble_report_local": true, + "treble_report_module": true, + "vintf-compatibility-matrix-soong-rules": true, + "xsdc-soong-rules": true, +} + +const ( + internalPluginsPath = "vendor/google/build/soong/internal_plugins.json" +) + +type pluginProvider interface { + IsPluginFor(string) bool +} + +func maybeAddInternalPluginsToAllowlist(ctx SingletonContext) { + if path := ExistentPathForSource(ctx, internalPluginsPath); path.Valid() { + ctx.AddNinjaFileDeps(path.String()) + absPath := absolutePath(path.String()) + var moreAllowed map[string]bool + data, err := ioutil.ReadFile(absPath) + if err != nil { + ctx.Errorf("Failed to open internal plugins path %q %q", internalPluginsPath, err) + } + if err := json.Unmarshal(data, &moreAllowed); err != nil { + fmt.Fprintf(os.Stderr, "Internal plugins file %q did not parse correctly: %q", data, err) + } + for k, v := range moreAllowed { + allowedPluginsByName[k] = v + } + } +} + +func (p *pluginSingleton) GenerateBuildActions(ctx SingletonContext) { + for _, p := range ctx.DeviceConfig().BuildBrokenPluginValidation() { + allowedPluginsByName[p] = true + } + maybeAddInternalPluginsToAllowlist(ctx) + + disallowedPlugins := map[string]bool{} + ctx.VisitAllModulesBlueprint(func(module blueprint.Module) { + if ctx.ModuleType(module) != "bootstrap_go_package" { + return + } + + p, ok := module.(pluginProvider) + if !ok || !p.IsPluginFor("soong_build") { + return + } + + name := ctx.ModuleName(module) + if _, ok := allowedPluginsByName[name]; ok { + return + } + + dir := ctx.ModuleDir(module) + + // allow use of plugins within Soong to not allowlist everything + if strings.HasPrefix(dir, "build/soong") { + return + } + + // allow third party users outside of external to create new plugins, i.e. non-google paths + // under vendor or hardware + if !strings.HasPrefix(dir, "external/") && IsThirdPartyPath(dir) { + return + } + disallowedPlugins[name] = true + }) + if len(disallowedPlugins) > 0 { + ctx.Errorf("New plugins are not supported; however %q were found. Please reach out to the build team or use BUILD_BROKEN_PLUGIN_VALIDATION (see Changes.md for more info).", SortedStringKeys(disallowedPlugins)) + } +} diff --git a/android/variable.go b/android/variable.go index 97171e7b9..40eb00675 100644 --- a/android/variable.go +++ b/android/variable.go @@ -440,6 +440,7 @@ type productVariables struct { ShippingApiLevel *string `json:",omitempty"` + BuildBrokenPluginValidation []string `json:",omitempty"` BuildBrokenClangAsFlags bool `json:",omitempty"` BuildBrokenClangCFlags bool `json:",omitempty"` BuildBrokenClangProperty bool `json:",omitempty"` diff --git a/tests/bootstrap_test.sh b/tests/bootstrap_test.sh index 59352477c..5fc05f8b1 100755 --- a/tests/bootstrap_test.sh +++ b/tests/bootstrap_test.sh @@ -207,8 +207,8 @@ EOF function test_soong_build_rerun_iff_environment_changes() { setup - mkdir -p cherry - cat > cherry/Android.bp <<'EOF' + mkdir -p build/soong/cherry + cat > build/soong/cherry/Android.bp <<'EOF' bootstrap_go_package { name: "cherry", pkgPath: "android/soong/cherry", @@ -224,7 +224,7 @@ bootstrap_go_package { } EOF - cat > cherry/cherry.go <<'EOF' + cat > build/soong/cherry/cherry.go <<'EOF' package cherry import ( @@ -317,8 +317,8 @@ function test_add_file_to_soong_build() { run_soong local -r mtime1=$(stat -c "%y" out/soong/build.ninja) - mkdir -p a - cat > a/Android.bp <<'EOF' + mkdir -p vendor/foo/picard + cat > vendor/foo/picard/Android.bp <<'EOF' bootstrap_go_package { name: "picard-soong-rules", pkgPath: "android/soong/picard", @@ -334,7 +334,7 @@ bootstrap_go_package { } EOF - cat > a/picard.go <<'EOF' + cat > vendor/foo/picard/picard.go <<'EOF' package picard import ( @@ -390,11 +390,11 @@ EOF function test_glob_during_bootstrapping() { setup - mkdir -p a - cat > a/Android.bp <<'EOF' + mkdir -p build/soong/picard + cat > build/soong/picard/Android.bp <<'EOF' build=["foo*.bp"] EOF - cat > a/fooa.bp <<'EOF' + cat > build/soong/picard/fooa.bp <<'EOF' bootstrap_go_package { name: "picard-soong-rules", pkgPath: "android/soong/picard", @@ -410,7 +410,7 @@ bootstrap_go_package { } EOF - cat > a/picard.go <<'EOF' + cat > build/soong/picard/picard.go <<'EOF' package picard import ( @@ -459,7 +459,7 @@ EOF grep -q "Make it so" out/soong/build.ninja || fail "Original action not present" - cat > a/foob.bp <<'EOF' + cat > build/soong/picard/foob.bp <<'EOF' bootstrap_go_package { name: "worf-soong-rules", pkgPath: "android/soong/worf", @@ -476,7 +476,7 @@ bootstrap_go_package { } EOF - cat > a/worf.go <<'EOF' + cat > build/soong/picard/worf.go <<'EOF' package worf import "android/soong/picard"