Merge "Use 1.49.0 prebuilts"
diff --git a/android/Android.bp b/android/Android.bp
index f8c1d55..efa70a9 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -30,6 +30,9 @@
"filegroup.go",
"hooks.go",
"image.go",
+ "license.go",
+ "license_kind.go",
+ "licenses.go",
"makefile_goal.go",
"makevars.go",
"metrics.go",
@@ -56,6 +59,7 @@
"sandbox.go",
"sdk.go",
"singleton.go",
+ "singleton_module.go",
"soong_config_modules.go",
"test_suites.go",
"testing.go",
@@ -77,6 +81,9 @@
"depset_test.go",
"deptag_test.go",
"expand_test.go",
+ "license_kind_test.go",
+ "license_test.go",
+ "licenses_test.go",
"module_test.go",
"mutator_test.go",
"namespace_test.go",
@@ -89,6 +96,7 @@
"paths_test.go",
"prebuilt_test.go",
"rule_builder_test.go",
+ "singleton_module_test.go",
"soong_config_modules_test.go",
"util_test.go",
"variable_test.go",
diff --git a/android/androidmk.go b/android/androidmk.go
index 73f60d0..5856851 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -28,6 +28,7 @@
"io/ioutil"
"os"
"path/filepath"
+ "reflect"
"sort"
"strings"
@@ -434,6 +435,17 @@
return generateDistContributionsForMake(distContributions)
}
+// Write the license variables to Make for AndroidMkData.Custom(..) methods that do not call WriteAndroidMkData(..)
+// It's required to propagate the license metadata even for module types that have non-standard interfaces to Make.
+func (a *AndroidMkEntries) WriteLicenseVariables(w io.Writer) {
+ fmt.Fprintln(w, "LOCAL_LICENSE_KINDS :=", strings.Join(a.EntryMap["LOCAL_LICENSE_KINDS"], " "))
+ fmt.Fprintln(w, "LOCAL_LICENSE_CONDITIONS :=", strings.Join(a.EntryMap["LOCAL_LICENSE_CONDITIONS"], " "))
+ fmt.Fprintln(w, "LOCAL_NOTICE_FILE :=", strings.Join(a.EntryMap["LOCAL_NOTICE_FILE"], " "))
+ if pn, ok := a.EntryMap["LOCAL_LICENSE_PACKAGE_NAME"]; ok {
+ fmt.Fprintln(w, "LOCAL_LICENSE_PACKAGE_NAME :=", strings.Join(pn, " "))
+ }
+}
+
// fillInEntries goes through the common variable processing and calls the extra data funcs to
// generate and fill in AndroidMkEntries's in-struct data, ready to be flushed to a file.
func (a *AndroidMkEntries) fillInEntries(config Config, bpPath string, mod blueprint.Module) {
@@ -460,6 +472,13 @@
// Collect make variable assignment entries.
a.SetString("LOCAL_PATH", filepath.Dir(bpPath))
a.SetString("LOCAL_MODULE", name+a.SubName)
+ a.AddStrings("LOCAL_LICENSE_KINDS", amod.commonProperties.Effective_license_kinds...)
+ a.AddStrings("LOCAL_LICENSE_CONDITIONS", amod.commonProperties.Effective_license_conditions...)
+ a.AddStrings("LOCAL_NOTICE_FILE", amod.commonProperties.Effective_license_text...)
+ // TODO(b/151177513): Does this code need to set LOCAL_MODULE_IS_CONTAINER ?
+ if amod.commonProperties.Effective_package_name != nil {
+ a.SetString("LOCAL_LICENSE_PACKAGE_NAME", *amod.commonProperties.Effective_package_name)
+ }
a.SetString("LOCAL_MODULE_CLASS", a.Class)
a.SetString("LOCAL_PREBUILT_MODULE_FILE", a.OutputFile.String())
a.AddStrings("LOCAL_REQUIRED_MODULES", a.Required...)
@@ -682,6 +701,7 @@
}
}()
+ // Additional cases here require review for correct license propagation to make.
switch x := mod.(type) {
case AndroidMkDataProvider:
return translateAndroidModule(ctx, w, mod, x)
@@ -690,6 +710,7 @@
case AndroidMkEntriesProvider:
return translateAndroidMkEntriesModule(ctx, w, mod, x)
default:
+ // Not exported to make so no make variables to set.
return nil
}
}
@@ -703,6 +724,10 @@
fmt.Fprintln(w, ".PHONY:", name)
fmt.Fprintln(w, name+":", goBinary.InstallPath())
fmt.Fprintln(w, "")
+ // Assuming no rules in make include go binaries in distributables.
+ // If the assumption is wrong, make will fail to build without the necessary .meta_lic and .meta_module files.
+ // In that case, add the targets and rules here to build a .meta_lic file for `name` and a .meta_module for
+ // `goBinary.InstallPath()` pointing to the `name`.meta_lic file.
return nil
}
@@ -768,6 +793,25 @@
blueprintDir := filepath.Dir(ctx.BlueprintFile(mod))
if data.Custom != nil {
+ // List of module types allowed to use .Custom(...)
+ // Additions to the list require careful review for proper license handling.
+ switch reflect.TypeOf(mod).String() { // ctx.ModuleType(mod) doesn't work: aidl_interface creates phony without type
+ case "*aidl.aidlApi": // writes non-custom before adding .phony
+ case "*aidl.aidlMapping": // writes non-custom before adding .phony
+ case "*android.customModule": // appears in tests only
+ case "*apex.apexBundle": // license properties written
+ case "*bpf.bpf": // license properties written (both for module and objs)
+ case "*genrule.Module": // writes non-custom before adding .phony
+ case "*java.SystemModules": // doesn't go through base_rules
+ case "*java.systemModulesImport": // doesn't go through base_rules
+ case "*phony.phony": // license properties written
+ case "*selinux.selinuxContextsModule": // license properties written
+ case "*sysprop.syspropLibrary": // license properties written
+ default:
+ if ctx.Config().IsEnvTrue("ANDROID_REQUIRE_LICENSES") {
+ return fmt.Errorf("custom make rules not allowed for %q (%q) module %q", ctx.ModuleType(mod), reflect.TypeOf(mod), ctx.ModuleName(mod))
+ }
+ }
data.Custom(w, name, prefix, blueprintDir, data)
} else {
WriteAndroidMkData(w, data)
@@ -804,6 +848,7 @@
return nil
}
+ // Any new or special cases here need review to verify correct propagation of license information.
for _, entries := range provider.AndroidMkEntries() {
entries.fillInEntries(ctx.Config(), ctx.BlueprintFile(mod), mod)
entries.write(w)
diff --git a/android/config.go b/android/config.go
index ddb2de3..a7e0b67 100644
--- a/android/config.go
+++ b/android/config.go
@@ -1256,6 +1256,27 @@
return HasAnyPrefix(path, c.productVariables.CFIIncludePaths)
}
+func (c *config) MemtagHeapDisabledForPath(path string) bool {
+ if len(c.productVariables.MemtagHeapExcludePaths) == 0 {
+ return false
+ }
+ return HasAnyPrefix(path, c.productVariables.MemtagHeapExcludePaths)
+}
+
+func (c *config) MemtagHeapAsyncEnabledForPath(path string) bool {
+ if len(c.productVariables.MemtagHeapAsyncIncludePaths) == 0 {
+ return false
+ }
+ return HasAnyPrefix(path, c.productVariables.MemtagHeapAsyncIncludePaths)
+}
+
+func (c *config) MemtagHeapSyncEnabledForPath(path string) bool {
+ if len(c.productVariables.MemtagHeapSyncIncludePaths) == 0 {
+ return false
+ }
+ return HasAnyPrefix(path, c.productVariables.MemtagHeapSyncIncludePaths)
+}
+
func (c *config) VendorConfig(name string) VendorConfig {
return soongconfig.Config(c.productVariables.VendorVars[name])
}
diff --git a/android/defaults.go b/android/defaults.go
index 44753ce..aacfbac 100644
--- a/android/defaults.go
+++ b/android/defaults.go
@@ -204,6 +204,9 @@
// its checking phase and parsing phase so add it to the list as a normal property.
AddVisibilityProperty(module, "visibility", &commonProperties.Visibility)
+ // The applicable licenses property for defaults is 'licenses'.
+ setPrimaryLicensesProperty(module, "licenses", &commonProperties.Licenses)
+
base.module = module
}
diff --git a/android/license.go b/android/license.go
new file mode 100644
index 0000000..b140b55
--- /dev/null
+++ b/android/license.go
@@ -0,0 +1,82 @@
+// Copyright 2020 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 (
+ "github.com/google/blueprint"
+)
+
+type licenseKindDependencyTag struct {
+ blueprint.BaseDependencyTag
+}
+
+var (
+ licenseKindTag = licenseKindDependencyTag{}
+)
+
+func init() {
+ RegisterLicenseBuildComponents(InitRegistrationContext)
+}
+
+// Register the license module type.
+func RegisterLicenseBuildComponents(ctx RegistrationContext) {
+ ctx.RegisterModuleType("license", LicenseFactory)
+}
+
+type licenseProperties struct {
+ // Specifies the kinds of license that apply.
+ License_kinds []string
+ // Specifies a short copyright notice to use for the license.
+ Copyright_notice *string
+ // Specifies the path or label for the text of the license.
+ License_text []string `android:"path"`
+ // Specifies the package name to which the license applies.
+ Package_name *string
+ // Specifies where this license can be used
+ Visibility []string
+}
+
+type licenseModule struct {
+ ModuleBase
+ DefaultableModuleBase
+
+ properties licenseProperties
+}
+
+func (m *licenseModule) DepsMutator(ctx BottomUpMutatorContext) {
+ ctx.AddVariationDependencies(nil, licenseKindTag, m.properties.License_kinds...)
+}
+
+func (m *licenseModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+ // Nothing to do.
+}
+
+func LicenseFactory() Module {
+ module := &licenseModule{}
+
+ base := module.base()
+ module.AddProperties(&base.nameProperties, &module.properties)
+
+ base.generalProperties = module.GetProperties()
+ base.customizableProperties = module.GetProperties()
+
+ // The visibility property needs to be checked and parsed by the visibility module.
+ setPrimaryVisibilityProperty(module, "visibility", &module.properties.Visibility)
+
+ initAndroidModuleBase(module)
+ InitDefaultableModule(module)
+
+ return module
+}
diff --git a/android/license_kind.go b/android/license_kind.go
new file mode 100644
index 0000000..ddecd77
--- /dev/null
+++ b/android/license_kind.go
@@ -0,0 +1,66 @@
+// Copyright 2020 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
+
+func init() {
+ RegisterLicenseKindBuildComponents(InitRegistrationContext)
+}
+
+// Register the license_kind module type.
+func RegisterLicenseKindBuildComponents(ctx RegistrationContext) {
+ ctx.RegisterModuleType("license_kind", LicenseKindFactory)
+}
+
+type licenseKindProperties struct {
+ // Specifies the conditions for all licenses of the kind.
+ Conditions []string
+ // Specifies the url to the canonical license definition.
+ Url string
+ // Specifies where this license can be used
+ Visibility []string
+}
+
+type licenseKindModule struct {
+ ModuleBase
+ DefaultableModuleBase
+
+ properties licenseKindProperties
+}
+
+func (m *licenseKindModule) DepsMutator(ctx BottomUpMutatorContext) {
+ // Nothing to do.
+}
+
+func (m *licenseKindModule) GenerateAndroidBuildActions(ModuleContext) {
+ // Nothing to do.
+}
+
+func LicenseKindFactory() Module {
+ module := &licenseKindModule{}
+
+ base := module.base()
+ module.AddProperties(&base.nameProperties, &module.properties)
+
+ base.generalProperties = module.GetProperties()
+ base.customizableProperties = module.GetProperties()
+
+ // The visibility property needs to be checked and parsed by the visibility module.
+ setPrimaryVisibilityProperty(module, "visibility", &module.properties.Visibility)
+
+ initAndroidModuleBase(module)
+ InitDefaultableModule(module)
+
+ return module
+}
diff --git a/android/license_kind_test.go b/android/license_kind_test.go
new file mode 100644
index 0000000..767b64e
--- /dev/null
+++ b/android/license_kind_test.go
@@ -0,0 +1,174 @@
+package android
+
+import (
+ "testing"
+
+ "github.com/google/blueprint"
+)
+
+var licenseKindTests = []struct {
+ name string
+ fs map[string][]byte
+ expectedErrors []string
+}{
+ {
+ name: "license_kind must not accept licenses property",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ license_kind {
+ name: "top_license",
+ licenses: ["other_license"],
+ }`),
+ },
+ expectedErrors: []string{
+ `top/Blueprints:4:14: unrecognized property "licenses"`,
+ },
+ },
+ {
+ name: "bad license_kind",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ license_kind {
+ name: "top_notice",
+ conditions: ["notice"],
+ }`),
+ "other/Blueprints": []byte(`
+ mock_license {
+ name: "other_notice",
+ license_kinds: ["notice"],
+ }`),
+ },
+ expectedErrors: []string{
+ `other/Blueprints:2:5: "other_notice" depends on undefined module "notice"`,
+ },
+ },
+ {
+ name: "good license kind",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ license_kind {
+ name: "top_by_exception_only",
+ conditions: ["by_exception_only"],
+ }
+
+ mock_license {
+ name: "top_proprietary",
+ license_kinds: ["top_by_exception_only"],
+ }`),
+ "other/Blueprints": []byte(`
+ mock_license {
+ name: "other_proprietary",
+ license_kinds: ["top_proprietary"],
+ }`),
+ },
+ },
+ {
+ name: "multiple license kinds",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ license_kind {
+ name: "top_notice",
+ conditions: ["notice"],
+ }
+
+ license_kind {
+ name: "top_by_exception_only",
+ conditions: ["by_exception_only"],
+ }
+
+ mock_license {
+ name: "top_allowed_as_notice",
+ license_kinds: ["top_notice"],
+ }
+
+ mock_license {
+ name: "top_proprietary",
+ license_kinds: ["top_by_exception_only"],
+ }`),
+ "other/Blueprints": []byte(`
+ mock_license {
+ name: "other_rule",
+ license_kinds: ["top_by_exception_only"],
+ }`),
+ },
+ },
+}
+
+func TestLicenseKind(t *testing.T) {
+ for _, test := range licenseKindTests {
+ t.Run(test.name, func(t *testing.T) {
+ _, errs := testLicenseKind(test.fs)
+
+ expectedErrors := test.expectedErrors
+ if expectedErrors == nil {
+ FailIfErrored(t, errs)
+ } else {
+ for _, expectedError := range expectedErrors {
+ FailIfNoMatchingErrors(t, expectedError, errs)
+ }
+ if len(errs) > len(expectedErrors) {
+ t.Errorf("additional errors found, expected %d, found %d", len(expectedErrors), len(errs))
+ for i, expectedError := range expectedErrors {
+ t.Errorf("expectedErrors[%d] = %s", i, expectedError)
+ }
+ for i, err := range errs {
+ t.Errorf("errs[%d] = %s", i, err)
+ }
+ }
+ }
+ })
+ }
+}
+
+func testLicenseKind(fs map[string][]byte) (*TestContext, []error) {
+
+ // Create a new config per test as license_kind information is stored in the config.
+ config := TestArchConfig(buildDir, nil, "", fs)
+
+ ctx := NewTestArchContext(config)
+ RegisterLicenseKindBuildComponents(ctx)
+ ctx.RegisterModuleType("mock_license", newMockLicenseModule)
+ ctx.Register()
+
+ _, errs := ctx.ParseBlueprintsFiles(".")
+ if len(errs) > 0 {
+ return ctx, errs
+ }
+
+ _, errs = ctx.PrepareBuildActions(config)
+ return ctx, errs
+}
+
+type mockLicenseProperties struct {
+ License_kinds []string
+}
+
+type mockLicenseModule struct {
+ ModuleBase
+ DefaultableModuleBase
+
+ properties mockLicenseProperties
+}
+
+func newMockLicenseModule() Module {
+ m := &mockLicenseModule{}
+ m.AddProperties(&m.properties)
+ InitAndroidArchModule(m, HostAndDeviceSupported, MultilibCommon)
+ InitDefaultableModule(m)
+ return m
+}
+
+type licensekindTag struct {
+ blueprint.BaseDependencyTag
+}
+
+func (j *mockLicenseModule) DepsMutator(ctx BottomUpMutatorContext) {
+ m, ok := ctx.Module().(Module)
+ if !ok {
+ return
+ }
+ ctx.AddDependency(m, licensekindTag{}, j.properties.License_kinds...)
+}
+
+func (p *mockLicenseModule) GenerateAndroidBuildActions(ModuleContext) {
+}
diff --git a/android/license_test.go b/android/license_test.go
new file mode 100644
index 0000000..552bbae
--- /dev/null
+++ b/android/license_test.go
@@ -0,0 +1,233 @@
+package android
+
+import (
+ "testing"
+)
+
+var licenseTests = []struct {
+ name string
+ fs map[string][]byte
+ expectedErrors []string
+}{
+ {
+ name: "license must not accept licenses property",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ license {
+ name: "top_license",
+ visibility: ["//visibility:private"],
+ licenses: ["other_license"],
+ }`),
+ },
+ expectedErrors: []string{
+ `top/Blueprints:5:14: unrecognized property "licenses"`,
+ },
+ },
+ {
+ name: "private license",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ license_kind {
+ name: "top_notice",
+ conditions: ["notice"],
+ visibility: ["//visibility:private"],
+ }
+
+ license {
+ name: "top_allowed_as_notice",
+ license_kinds: ["top_notice"],
+ visibility: ["//visibility:private"],
+ }`),
+ "other/Blueprints": []byte(`
+ rule {
+ name: "arule",
+ licenses: ["top_allowed_as_notice"],
+ }`),
+ "yetmore/Blueprints": []byte(`
+ package {
+ default_applicable_licenses: ["top_allowed_as_notice"],
+ }`),
+ },
+ expectedErrors: []string{
+ `other/Blueprints:2:5: module "arule": depends on //top:top_allowed_as_notice `+
+ `which is not visible to this module`,
+ `yetmore/Blueprints:2:5: module "//yetmore": depends on //top:top_allowed_as_notice `+
+ `which is not visible to this module`,
+ },
+ },
+ {
+ name: "must reference license_kind module",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ rule {
+ name: "top_by_exception_only",
+ }
+
+ license {
+ name: "top_proprietary",
+ license_kinds: ["top_by_exception_only"],
+ visibility: ["//visibility:public"],
+ }`),
+ },
+ expectedErrors: []string{
+ `top/Blueprints:6:5: module "top_proprietary": license_kinds property `+
+ `"top_by_exception_only" is not a license_kind module`,
+ },
+ },
+ {
+ name: "license_kind module must exist",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ license {
+ name: "top_notice_allowed",
+ license_kinds: ["top_notice"],
+ visibility: ["//visibility:public"],
+ }`),
+ },
+ expectedErrors: []string{
+ `top/Blueprints:2:5: "top_notice_allowed" depends on undefined module "top_notice"`,
+ },
+ },
+ {
+ name: "public license",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ license_kind {
+ name: "top_by_exception_only",
+ conditions: ["by_exception_only"],
+ visibility: ["//visibility:private"],
+ }
+
+ license {
+ name: "top_proprietary",
+ license_kinds: ["top_by_exception_only"],
+ visibility: ["//visibility:public"],
+ }`),
+ "other/Blueprints": []byte(`
+ rule {
+ name: "arule",
+ licenses: ["top_proprietary"],
+ }`),
+ "yetmore/Blueprints": []byte(`
+ package {
+ default_applicable_licenses: ["top_proprietary"],
+ }`),
+ },
+ },
+ {
+ name: "multiple licenses",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ package {
+ default_applicable_licenses: ["top_proprietary"],
+ }
+
+ license_kind {
+ name: "top_notice",
+ conditions: ["notice"],
+ }
+
+ license_kind {
+ name: "top_by_exception_only",
+ conditions: ["by_exception_only"],
+ visibility: ["//visibility:public"],
+ }
+
+ license {
+ name: "top_allowed_as_notice",
+ license_kinds: ["top_notice"],
+ }
+
+ license {
+ name: "top_proprietary",
+ license_kinds: ["top_by_exception_only"],
+ visibility: ["//visibility:public"],
+ }
+ rule {
+ name: "myrule",
+ licenses: ["top_allowed_as_notice", "top_proprietary"]
+ }`),
+ "other/Blueprints": []byte(`
+ rule {
+ name: "arule",
+ licenses: ["top_proprietary"],
+ }`),
+ "yetmore/Blueprints": []byte(`
+ package {
+ default_applicable_licenses: ["top_proprietary"],
+ }`),
+ },
+ },
+}
+
+func TestLicense(t *testing.T) {
+ for _, test := range licenseTests {
+ t.Run(test.name, func(t *testing.T) {
+ _, errs := testLicense(test.fs)
+
+ expectedErrors := test.expectedErrors
+ if expectedErrors == nil {
+ FailIfErrored(t, errs)
+ } else {
+ for _, expectedError := range expectedErrors {
+ FailIfNoMatchingErrors(t, expectedError, errs)
+ }
+ if len(errs) > len(expectedErrors) {
+ t.Errorf("additional errors found, expected %d, found %d", len(expectedErrors), len(errs))
+ for i, expectedError := range expectedErrors {
+ t.Errorf("expectedErrors[%d] = %s", i, expectedError)
+ }
+ for i, err := range errs {
+ t.Errorf("errs[%d] = %s", i, err)
+ }
+ }
+ }
+ })
+ }
+}
+
+func testLicense(fs map[string][]byte) (*TestContext, []error) {
+
+ // Create a new config per test as visibility information is stored in the config.
+ env := make(map[string]string)
+ env["ANDROID_REQUIRE_LICENSES"] = "1"
+ config := TestArchConfig(buildDir, env, "", fs)
+
+ ctx := NewTestArchContext(config)
+ RegisterPackageBuildComponents(ctx)
+ registerTestPrebuiltBuildComponents(ctx)
+ RegisterLicenseKindBuildComponents(ctx)
+ RegisterLicenseBuildComponents(ctx)
+ ctx.RegisterModuleType("rule", newMockRuleModule)
+ ctx.PreArchMutators(RegisterVisibilityRuleChecker)
+ ctx.PreArchMutators(RegisterLicensesPackageMapper)
+ ctx.PreArchMutators(RegisterDefaultsPreArchMutators)
+ ctx.PreArchMutators(RegisterLicensesPropertyGatherer)
+ ctx.PreArchMutators(RegisterVisibilityRuleGatherer)
+ ctx.PostDepsMutators(RegisterVisibilityRuleEnforcer)
+ ctx.PostDepsMutators(RegisterLicensesDependencyChecker)
+ ctx.Register()
+
+ _, errs := ctx.ParseBlueprintsFiles(".")
+ if len(errs) > 0 {
+ return ctx, errs
+ }
+
+ _, errs = ctx.PrepareBuildActions(config)
+ return ctx, errs
+}
+
+type mockRuleModule struct {
+ ModuleBase
+ DefaultableModuleBase
+}
+
+func newMockRuleModule() Module {
+ m := &mockRuleModule{}
+ InitAndroidModule(m)
+ InitDefaultableModule(m)
+ return m
+}
+
+func (p *mockRuleModule) GenerateAndroidBuildActions(ModuleContext) {
+}
diff --git a/android/licenses.go b/android/licenses.go
new file mode 100644
index 0000000..1000429
--- /dev/null
+++ b/android/licenses.go
@@ -0,0 +1,295 @@
+// Copyright 2020 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 (
+ "reflect"
+ "sync"
+
+ "github.com/google/blueprint"
+)
+
+// Adds cross-cutting licenses dependency to propagate license metadata through the build system.
+//
+// Stage 1 - bottom-up records package-level default_applicable_licenses property mapped by package name.
+// Stage 2 - bottom-up converts licenses property or package default_applicable_licenses to dependencies.
+// Stage 3 - bottom-up type-checks every added applicable license dependency and license_kind dependency.
+// Stage 4 - GenerateBuildActions calculates properties for the union of license kinds, conditions and texts.
+
+type licensesDependencyTag struct {
+ blueprint.BaseDependencyTag
+}
+
+var (
+ licensesTag = licensesDependencyTag{}
+)
+
+// Describes the property provided by a module to reference applicable licenses.
+type applicableLicensesProperty interface {
+ // The name of the property. e.g. default_applicable_licenses or licenses
+ getName() string
+ // The values assigned to the property. (Must reference license modules.)
+ getStrings() []string
+}
+
+type applicableLicensesPropertyImpl struct {
+ name string
+ licensesProperty *[]string
+}
+
+func newApplicableLicensesProperty(name string, licensesProperty *[]string) applicableLicensesProperty {
+ return applicableLicensesPropertyImpl{
+ name: name,
+ licensesProperty: licensesProperty,
+ }
+}
+
+func (p applicableLicensesPropertyImpl) getName() string {
+ return p.name
+}
+
+func (p applicableLicensesPropertyImpl) getStrings() []string {
+ return *p.licensesProperty
+}
+
+// Set the primary applicable licenses property for a module.
+func setPrimaryLicensesProperty(module Module, name string, licensesProperty *[]string) {
+ module.base().primaryLicensesProperty = newApplicableLicensesProperty(name, licensesProperty)
+}
+
+// Storage blob for a package's default_applicable_licenses mapped by package directory.
+type licensesContainer struct {
+ licenses []string
+}
+
+func (r licensesContainer) getLicenses() []string {
+ return r.licenses
+}
+
+var packageDefaultLicensesMap = NewOnceKey("packageDefaultLicensesMap")
+
+// The map from package dir name to default applicable licenses as a licensesContainer.
+func moduleToPackageDefaultLicensesMap(config Config) *sync.Map {
+ return config.Once(packageDefaultLicensesMap, func() interface{} {
+ return &sync.Map{}
+ }).(*sync.Map)
+}
+
+// Registers the function that maps each package to its default_applicable_licenses.
+//
+// This goes before defaults expansion so the defaults can pick up the package default.
+func RegisterLicensesPackageMapper(ctx RegisterMutatorsContext) {
+ ctx.BottomUp("licensesPackageMapper", licensesPackageMapper).Parallel()
+}
+
+// Registers the function that gathers the license dependencies for each module.
+//
+// This goes after defaults expansion so that it can pick up default licenses and before visibility enforcement.
+func RegisterLicensesPropertyGatherer(ctx RegisterMutatorsContext) {
+ ctx.BottomUp("licensesPropertyGatherer", licensesPropertyGatherer).Parallel()
+}
+
+// Registers the function that verifies the licenses and license_kinds dependency types for each module.
+func RegisterLicensesDependencyChecker(ctx RegisterMutatorsContext) {
+ ctx.BottomUp("licensesPropertyChecker", licensesDependencyChecker).Parallel()
+}
+
+// Maps each package to its default applicable licenses.
+func licensesPackageMapper(ctx BottomUpMutatorContext) {
+ p, ok := ctx.Module().(*packageModule)
+ if !ok {
+ return
+ }
+
+ licenses := getLicenses(ctx, p)
+
+ dir := ctx.ModuleDir()
+ c := makeLicensesContainer(licenses)
+ moduleToPackageDefaultLicensesMap(ctx.Config()).Store(dir, c)
+}
+
+// Copies the default_applicable_licenses property values for mapping by package directory.
+func makeLicensesContainer(propVals []string) licensesContainer {
+ licenses := make([]string, 0, len(propVals))
+ licenses = append(licenses, propVals...)
+
+ return licensesContainer{licenses}
+}
+
+// Gathers the applicable licenses into dependency references after defaults expansion.
+func licensesPropertyGatherer(ctx BottomUpMutatorContext) {
+ m, ok := ctx.Module().(Module)
+ if !ok {
+ return
+ }
+
+ if exemptFromRequiredApplicableLicensesProperty(m) {
+ return
+ }
+
+ licenses := getLicenses(ctx, m)
+
+ ctx.AddVariationDependencies(nil, licensesTag, licenses...)
+}
+
+// Verifies the license and license_kind dependencies are each the correct kind of module.
+func licensesDependencyChecker(ctx BottomUpMutatorContext) {
+ m, ok := ctx.Module().(Module)
+ if !ok {
+ return
+ }
+
+ // license modules have no licenses, but license_kinds must refer to license_kind modules
+ if _, ok := m.(*licenseModule); ok {
+ for _, module := range ctx.GetDirectDepsWithTag(licenseKindTag) {
+ if _, ok := module.(*licenseKindModule); !ok {
+ ctx.ModuleErrorf("license_kinds property %q is not a license_kind module", ctx.OtherModuleName(module))
+ }
+ }
+ return
+ }
+
+ if exemptFromRequiredApplicableLicensesProperty(m) {
+ return
+ }
+
+ for _, module := range ctx.GetDirectDepsWithTag(licensesTag) {
+ if _, ok := module.(*licenseModule); !ok {
+ propertyName := "licenses"
+ primaryProperty := m.base().primaryLicensesProperty
+ if primaryProperty != nil {
+ propertyName = primaryProperty.getName()
+ }
+ ctx.ModuleErrorf("%s property %q is not a license module", propertyName, ctx.OtherModuleName(module))
+ }
+ }
+}
+
+// Flattens license and license_kind dependencies into calculated properties.
+//
+// Re-validates applicable licenses properties refer only to license modules and license_kinds properties refer
+// only to license_kind modules.
+func licensesPropertyFlattener(ctx ModuleContext) {
+ m, ok := ctx.Module().(Module)
+ if !ok {
+ return
+ }
+
+ // license modules have no licenses, but license_kinds must refer to license_kind modules
+ if l, ok := m.(*licenseModule); ok {
+ mergeProps(&m.base().commonProperties.Effective_licenses, ctx.ModuleName())
+ mergeProps(&m.base().commonProperties.Effective_license_text, PathsForModuleSrc(ctx, l.properties.License_text).Strings()...)
+ for _, module := range ctx.GetDirectDepsWithTag(licenseKindTag) {
+ if lk, ok := module.(*licenseKindModule); ok {
+ mergeProps(&m.base().commonProperties.Effective_license_conditions, lk.properties.Conditions...)
+ mergeProps(&m.base().commonProperties.Effective_license_kinds, ctx.OtherModuleName(module))
+ } else {
+ ctx.ModuleErrorf("license_kinds property %q is not a license_kind module", ctx.OtherModuleName(module))
+ }
+ }
+ return
+ }
+
+ if exemptFromRequiredApplicableLicensesProperty(m) {
+ return
+ }
+
+ for _, module := range ctx.GetDirectDepsWithTag(licensesTag) {
+ if l, ok := module.(*licenseModule); ok {
+ if m.base().commonProperties.Effective_package_name == nil && l.properties.Package_name != nil {
+ m.base().commonProperties.Effective_package_name = l.properties.Package_name
+ }
+ mergeProps(&m.base().commonProperties.Effective_licenses, module.base().commonProperties.Effective_licenses...)
+ mergeProps(&m.base().commonProperties.Effective_license_text, module.base().commonProperties.Effective_license_text...)
+ mergeProps(&m.base().commonProperties.Effective_license_kinds, module.base().commonProperties.Effective_license_kinds...)
+ mergeProps(&m.base().commonProperties.Effective_license_conditions, module.base().commonProperties.Effective_license_conditions...)
+ } else {
+ propertyName := "licenses"
+ primaryProperty := m.base().primaryLicensesProperty
+ if primaryProperty != nil {
+ propertyName = primaryProperty.getName()
+ }
+ ctx.ModuleErrorf("%s property %q is not a license module", propertyName, ctx.OtherModuleName(module))
+ }
+ }
+}
+
+// Update a property string array with a distinct union of its values and a list of new values.
+func mergeProps(prop *[]string, values ...string) {
+ s := make(map[string]bool)
+ for _, v := range *prop {
+ s[v] = true
+ }
+ for _, v := range values {
+ s[v] = true
+ }
+ *prop = []string{}
+ *prop = append(*prop, SortedStringKeys(s)...)
+}
+
+// Get the licenses property falling back to the package default.
+func getLicenses(ctx BaseModuleContext, module Module) []string {
+ if exemptFromRequiredApplicableLicensesProperty(module) {
+ return nil
+ }
+
+ primaryProperty := module.base().primaryLicensesProperty
+ if primaryProperty == nil {
+ if ctx.Config().IsEnvTrue("ANDROID_REQUIRE_LICENSES") {
+ ctx.ModuleErrorf("module type %q must have an applicable licenses property", ctx.OtherModuleType(module))
+ }
+ return nil
+ }
+
+ licenses := primaryProperty.getStrings()
+ if len(licenses) > 0 {
+ s := make(map[string]bool)
+ for _, l := range licenses {
+ if _, ok := s[l]; ok {
+ ctx.ModuleErrorf("duplicate %q %s", l, primaryProperty.getName())
+ }
+ s[l] = true
+ }
+ return licenses
+ }
+
+ dir := ctx.OtherModuleDir(module)
+
+ moduleToApplicableLicenses := moduleToPackageDefaultLicensesMap(ctx.Config())
+ value, ok := moduleToApplicableLicenses.Load(dir)
+ var c licensesContainer
+ if ok {
+ c = value.(licensesContainer)
+ } else {
+ c = licensesContainer{}
+ }
+ return c.getLicenses()
+}
+
+// Returns whether a module is an allowed list of modules that do not have or need applicable licenses.
+func exemptFromRequiredApplicableLicensesProperty(module Module) bool {
+ switch reflect.TypeOf(module).String() {
+ case "*android.licenseModule": // is a license, doesn't need one
+ case "*android.licenseKindModule": // is a license, doesn't need one
+ case "*android.NamespaceModule": // just partitions things, doesn't add anything
+ case "*android.soongConfigModuleTypeModule": // creates aliases for modules with licenses
+ case "*android.soongConfigModuleTypeImport": // creates aliases for modules with licenses
+ case "*android.soongConfigStringVariableDummyModule": // used for creating aliases
+ case "*android.SoongConfigBoolVariableDummyModule": // used for creating aliases
+ default:
+ return false
+ }
+ return true
+}
diff --git a/android/licenses_test.go b/android/licenses_test.go
new file mode 100644
index 0000000..b94add7
--- /dev/null
+++ b/android/licenses_test.go
@@ -0,0 +1,863 @@
+package android
+
+import (
+ "testing"
+
+ "github.com/google/blueprint"
+)
+
+var licensesTests = []struct {
+ name string
+ fs map[string][]byte
+ expectedErrors []string
+ effectiveLicenses map[string][]string
+ effectiveInheritedLicenses map[string][]string
+ effectivePackage map[string]string
+ effectiveNotices map[string][]string
+ effectiveKinds map[string][]string
+ effectiveConditions map[string][]string
+}{
+ {
+ name: "invalid module type without licenses property",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ mock_bad_module {
+ name: "libexample",
+ }`),
+ },
+ expectedErrors: []string{`module type "mock_bad_module" must have an applicable licenses property`},
+ },
+ {
+ name: "license must exist",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ mock_library {
+ name: "libexample",
+ licenses: ["notice"],
+ }`),
+ },
+ expectedErrors: []string{`"libexample" depends on undefined module "notice"`},
+ },
+ {
+ name: "all good",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ license_kind {
+ name: "notice",
+ conditions: ["shownotice"],
+ }
+
+ license {
+ name: "top_Apache2",
+ license_kinds: ["notice"],
+ package_name: "topDog",
+ license_text: ["LICENSE", "NOTICE"],
+ }
+
+ mock_library {
+ name: "libexample1",
+ licenses: ["top_Apache2"],
+ }`),
+ "top/nested/Blueprints": []byte(`
+ mock_library {
+ name: "libnested",
+ licenses: ["top_Apache2"],
+ }`),
+ "other/Blueprints": []byte(`
+ mock_library {
+ name: "libother",
+ licenses: ["top_Apache2"],
+ }`),
+ },
+ effectiveLicenses: map[string][]string{
+ "libexample1": []string{"top_Apache2"},
+ "libnested": []string{"top_Apache2"},
+ "libother": []string{"top_Apache2"},
+ },
+ effectiveKinds: map[string][]string{
+ "libexample1": []string{"notice"},
+ "libnested": []string{"notice"},
+ "libother": []string{"notice"},
+ },
+ effectivePackage: map[string]string{
+ "libexample1": "topDog",
+ "libnested": "topDog",
+ "libother": "topDog",
+ },
+ effectiveConditions: map[string][]string{
+ "libexample1": []string{"shownotice"},
+ "libnested": []string{"shownotice"},
+ "libother": []string{"shownotice"},
+ },
+ effectiveNotices: map[string][]string{
+ "libexample1": []string{"top/LICENSE", "top/NOTICE"},
+ "libnested": []string{"top/LICENSE", "top/NOTICE"},
+ "libother": []string{"top/LICENSE", "top/NOTICE"},
+ },
+ },
+
+ // Defaults propagation tests
+ {
+ // Check that licenses is the union of the defaults modules.
+ name: "defaults union, basic",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ license_kind {
+ name: "top_notice",
+ conditions: ["notice"],
+ }
+
+ license {
+ name: "top_other",
+ license_kinds: ["top_notice"],
+ }
+
+ mock_defaults {
+ name: "libexample_defaults",
+ licenses: ["top_other"],
+ }
+ mock_library {
+ name: "libexample",
+ licenses: ["nested_other"],
+ defaults: ["libexample_defaults"],
+ }
+ mock_library {
+ name: "libsamepackage",
+ deps: ["libexample"],
+ }`),
+ "top/nested/Blueprints": []byte(`
+ license_kind {
+ name: "nested_notice",
+ conditions: ["notice"],
+ }
+
+ license {
+ name: "nested_other",
+ license_kinds: ["nested_notice"],
+ }
+
+ mock_library {
+ name: "libnested",
+ deps: ["libexample"],
+ }`),
+ "other/Blueprints": []byte(`
+ mock_library {
+ name: "libother",
+ deps: ["libexample"],
+ }`),
+ },
+ effectiveLicenses: map[string][]string{
+ "libexample": []string{"nested_other", "top_other"},
+ "libsamepackage": []string{},
+ "libnested": []string{},
+ "libother": []string{},
+ },
+ effectiveInheritedLicenses: map[string][]string{
+ "libexample": []string{"nested_other", "top_other"},
+ "libsamepackage": []string{"nested_other", "top_other"},
+ "libnested": []string{"nested_other", "top_other"},
+ "libother": []string{"nested_other", "top_other"},
+ },
+ effectiveKinds: map[string][]string{
+ "libexample": []string{"nested_notice", "top_notice"},
+ "libsamepackage": []string{},
+ "libnested": []string{},
+ "libother": []string{},
+ },
+ effectiveConditions: map[string][]string{
+ "libexample": []string{"notice"},
+ "libsamepackage": []string{},
+ "libnested": []string{},
+ "libother": []string{},
+ },
+ },
+ {
+ name: "defaults union, multiple defaults",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ license {
+ name: "top",
+ }
+ mock_defaults {
+ name: "libexample_defaults_1",
+ licenses: ["other"],
+ }
+ mock_defaults {
+ name: "libexample_defaults_2",
+ licenses: ["top_nested"],
+ }
+ mock_library {
+ name: "libexample",
+ defaults: ["libexample_defaults_1", "libexample_defaults_2"],
+ }
+ mock_library {
+ name: "libsamepackage",
+ deps: ["libexample"],
+ }`),
+ "top/nested/Blueprints": []byte(`
+ license {
+ name: "top_nested",
+ license_text: ["LICENSE.txt"],
+ }
+ mock_library {
+ name: "libnested",
+ deps: ["libexample"],
+ }`),
+ "other/Blueprints": []byte(`
+ license {
+ name: "other",
+ }
+ mock_library {
+ name: "libother",
+ deps: ["libexample"],
+ }`),
+ "outsider/Blueprints": []byte(`
+ mock_library {
+ name: "liboutsider",
+ deps: ["libexample"],
+ }`),
+ },
+ effectiveLicenses: map[string][]string{
+ "libexample": []string{"other", "top_nested"},
+ "libsamepackage": []string{},
+ "libnested": []string{},
+ "libother": []string{},
+ "liboutsider": []string{},
+ },
+ effectiveInheritedLicenses: map[string][]string{
+ "libexample": []string{"other", "top_nested"},
+ "libsamepackage": []string{"other", "top_nested"},
+ "libnested": []string{"other", "top_nested"},
+ "libother": []string{"other", "top_nested"},
+ "liboutsider": []string{"other", "top_nested"},
+ },
+ effectiveKinds: map[string][]string{
+ "libexample": []string{},
+ "libsamepackage": []string{},
+ "libnested": []string{},
+ "libother": []string{},
+ "liboutsider": []string{},
+ },
+ effectiveNotices: map[string][]string{
+ "libexample": []string{"top/nested/LICENSE.txt"},
+ "libsamepackage": []string{},
+ "libnested": []string{},
+ "libother": []string{},
+ "liboutsider": []string{},
+ },
+ },
+
+ // Defaults module's defaults_licenses tests
+ {
+ name: "defaults_licenses invalid",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ mock_defaults {
+ name: "top_defaults",
+ licenses: ["notice"],
+ }`),
+ },
+ expectedErrors: []string{`"top_defaults" depends on undefined module "notice"`},
+ },
+ {
+ name: "defaults_licenses overrides package default",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ package {
+ default_applicable_licenses: ["by_exception_only"],
+ }
+ license {
+ name: "by_exception_only",
+ }
+ license {
+ name: "notice",
+ }
+ mock_defaults {
+ name: "top_defaults",
+ licenses: ["notice"],
+ }
+ mock_library {
+ name: "libexample",
+ }
+ mock_library {
+ name: "libdefaults",
+ defaults: ["top_defaults"],
+ }`),
+ },
+ effectiveLicenses: map[string][]string{
+ "libexample": []string{"by_exception_only"},
+ "libdefaults": []string{"notice"},
+ },
+ effectiveInheritedLicenses: map[string][]string{
+ "libexample": []string{"by_exception_only"},
+ "libdefaults": []string{"notice"},
+ },
+ },
+
+ // Package default_applicable_licenses tests
+ {
+ name: "package default_applicable_licenses must exist",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ package {
+ default_applicable_licenses: ["notice"],
+ }`),
+ },
+ expectedErrors: []string{`"//top" depends on undefined module "notice"`},
+ },
+ {
+ // This test relies on the default licenses being legacy_public.
+ name: "package default_applicable_licenses property used when no licenses specified",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ package {
+ default_applicable_licenses: ["top_notice"],
+ }
+
+ license {
+ name: "top_notice",
+ }
+ mock_library {
+ name: "libexample",
+ }`),
+ "outsider/Blueprints": []byte(`
+ mock_library {
+ name: "liboutsider",
+ deps: ["libexample"],
+ }`),
+ },
+ effectiveLicenses: map[string][]string{
+ "libexample": []string{"top_notice"},
+ "liboutsider": []string{},
+ },
+ effectiveInheritedLicenses: map[string][]string{
+ "libexample": []string{"top_notice"},
+ "liboutsider": []string{"top_notice"},
+ },
+ },
+ {
+ name: "package default_applicable_licenses not inherited to subpackages",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ package {
+ default_applicable_licenses: ["top_notice"],
+ }
+ license {
+ name: "top_notice",
+ }
+ mock_library {
+ name: "libexample",
+ }`),
+ "top/nested/Blueprints": []byte(`
+ package {
+ default_applicable_licenses: ["outsider"],
+ }
+
+ mock_library {
+ name: "libnested",
+ }`),
+ "top/other/Blueprints": []byte(`
+ mock_library {
+ name: "libother",
+ }`),
+ "outsider/Blueprints": []byte(`
+ license {
+ name: "outsider",
+ }
+ mock_library {
+ name: "liboutsider",
+ deps: ["libexample", "libother", "libnested"],
+ }`),
+ },
+ effectiveLicenses: map[string][]string{
+ "libexample": []string{"top_notice"},
+ "libnested": []string{"outsider"},
+ "libother": []string{},
+ "liboutsider": []string{},
+ },
+ effectiveInheritedLicenses: map[string][]string{
+ "libexample": []string{"top_notice"},
+ "libnested": []string{"outsider"},
+ "libother": []string{},
+ "liboutsider": []string{"top_notice", "outsider"},
+ },
+ },
+ {
+ name: "verify that prebuilt dependencies are included",
+ fs: map[string][]byte{
+ "prebuilts/Blueprints": []byte(`
+ license {
+ name: "prebuilt"
+ }
+ prebuilt {
+ name: "module",
+ licenses: ["prebuilt"],
+ }`),
+ "top/sources/source_file": nil,
+ "top/sources/Blueprints": []byte(`
+ license {
+ name: "top_sources"
+ }
+ source {
+ name: "module",
+ licenses: ["top_sources"],
+ }`),
+ "top/other/source_file": nil,
+ "top/other/Blueprints": []byte(`
+ source {
+ name: "other",
+ deps: [":module"],
+ }`),
+ },
+ effectiveLicenses: map[string][]string{
+ "other": []string{},
+ },
+ effectiveInheritedLicenses: map[string][]string{
+ "other": []string{"prebuilt", "top_sources"},
+ },
+ },
+ {
+ name: "verify that prebuilt dependencies are ignored for licenses reasons (preferred)",
+ fs: map[string][]byte{
+ "prebuilts/Blueprints": []byte(`
+ license {
+ name: "prebuilt"
+ }
+ prebuilt {
+ name: "module",
+ licenses: ["prebuilt"],
+ prefer: true,
+ }`),
+ "top/sources/source_file": nil,
+ "top/sources/Blueprints": []byte(`
+ license {
+ name: "top_sources"
+ }
+ source {
+ name: "module",
+ licenses: ["top_sources"],
+ }`),
+ "top/other/source_file": nil,
+ "top/other/Blueprints": []byte(`
+ source {
+ name: "other",
+ deps: [":module"],
+ }`),
+ },
+ effectiveLicenses: map[string][]string{
+ "other": []string{},
+ },
+ effectiveInheritedLicenses: map[string][]string{
+ "module": []string{"prebuilt", "top_sources"},
+ "other": []string{"prebuilt", "top_sources"},
+ },
+ },
+}
+
+func TestLicenses(t *testing.T) {
+ for _, test := range licensesTests {
+ t.Run(test.name, func(t *testing.T) {
+ ctx, errs := testLicenses(buildDir, test.fs)
+
+ CheckErrorsAgainstExpectations(t, errs, test.expectedErrors)
+
+ if test.effectiveLicenses != nil {
+ checkEffectiveLicenses(t, ctx, test.effectiveLicenses)
+ }
+
+ if test.effectivePackage != nil {
+ checkEffectivePackage(t, ctx, test.effectivePackage)
+ }
+
+ if test.effectiveNotices != nil {
+ checkEffectiveNotices(t, ctx, test.effectiveNotices)
+ }
+
+ if test.effectiveKinds != nil {
+ checkEffectiveKinds(t, ctx, test.effectiveKinds)
+ }
+
+ if test.effectiveConditions != nil {
+ checkEffectiveConditions(t, ctx, test.effectiveConditions)
+ }
+
+ if test.effectiveInheritedLicenses != nil {
+ checkEffectiveInheritedLicenses(t, ctx, test.effectiveInheritedLicenses)
+ }
+ })
+ }
+}
+
+func checkEffectiveLicenses(t *testing.T, ctx *TestContext, effectiveLicenses map[string][]string) {
+ actualLicenses := make(map[string][]string)
+ ctx.Context.Context.VisitAllModules(func(m blueprint.Module) {
+ if _, ok := m.(*licenseModule); ok {
+ return
+ }
+ if _, ok := m.(*licenseKindModule); ok {
+ return
+ }
+ if _, ok := m.(*packageModule); ok {
+ return
+ }
+ module, ok := m.(Module)
+ if !ok {
+ t.Errorf("%q not a module", m.Name())
+ return
+ }
+ base := module.base()
+ if base == nil {
+ return
+ }
+ actualLicenses[m.Name()] = base.commonProperties.Effective_licenses
+ })
+
+ for moduleName, expectedLicenses := range effectiveLicenses {
+ licenses, ok := actualLicenses[moduleName]
+ if !ok {
+ licenses = []string{}
+ }
+ if !compareUnorderedStringArrays(expectedLicenses, licenses) {
+ t.Errorf("effective licenses mismatch for module %q: expected %q, found %q", moduleName, expectedLicenses, licenses)
+ }
+ }
+}
+
+func checkEffectiveInheritedLicenses(t *testing.T, ctx *TestContext, effectiveInheritedLicenses map[string][]string) {
+ actualLicenses := make(map[string][]string)
+ ctx.Context.Context.VisitAllModules(func(m blueprint.Module) {
+ if _, ok := m.(*licenseModule); ok {
+ return
+ }
+ if _, ok := m.(*licenseKindModule); ok {
+ return
+ }
+ if _, ok := m.(*packageModule); ok {
+ return
+ }
+ module, ok := m.(Module)
+ if !ok {
+ t.Errorf("%q not a module", m.Name())
+ return
+ }
+ base := module.base()
+ if base == nil {
+ return
+ }
+ inherited := make(map[string]bool)
+ for _, l := range base.commonProperties.Effective_licenses {
+ inherited[l] = true
+ }
+ ctx.Context.Context.VisitDepsDepthFirst(m, func(c blueprint.Module) {
+ if _, ok := c.(*licenseModule); ok {
+ return
+ }
+ if _, ok := c.(*licenseKindModule); ok {
+ return
+ }
+ if _, ok := c.(*packageModule); ok {
+ return
+ }
+ cmodule, ok := c.(Module)
+ if !ok {
+ t.Errorf("%q not a module", c.Name())
+ return
+ }
+ cbase := cmodule.base()
+ if cbase == nil {
+ return
+ }
+ for _, l := range cbase.commonProperties.Effective_licenses {
+ inherited[l] = true
+ }
+ })
+ actualLicenses[m.Name()] = []string{}
+ for l := range inherited {
+ actualLicenses[m.Name()] = append(actualLicenses[m.Name()], l)
+ }
+ })
+
+ for moduleName, expectedInheritedLicenses := range effectiveInheritedLicenses {
+ licenses, ok := actualLicenses[moduleName]
+ if !ok {
+ licenses = []string{}
+ }
+ if !compareUnorderedStringArrays(expectedInheritedLicenses, licenses) {
+ t.Errorf("effective inherited licenses mismatch for module %q: expected %q, found %q", moduleName, expectedInheritedLicenses, licenses)
+ }
+ }
+}
+
+func checkEffectivePackage(t *testing.T, ctx *TestContext, effectivePackage map[string]string) {
+ actualPackage := make(map[string]string)
+ ctx.Context.Context.VisitAllModules(func(m blueprint.Module) {
+ if _, ok := m.(*licenseModule); ok {
+ return
+ }
+ if _, ok := m.(*licenseKindModule); ok {
+ return
+ }
+ if _, ok := m.(*packageModule); ok {
+ return
+ }
+ module, ok := m.(Module)
+ if !ok {
+ t.Errorf("%q not a module", m.Name())
+ return
+ }
+ base := module.base()
+ if base == nil {
+ return
+ }
+
+ if base.commonProperties.Effective_package_name == nil {
+ actualPackage[m.Name()] = ""
+ } else {
+ actualPackage[m.Name()] = *base.commonProperties.Effective_package_name
+ }
+ })
+
+ for moduleName, expectedPackage := range effectivePackage {
+ packageName, ok := actualPackage[moduleName]
+ if !ok {
+ packageName = ""
+ }
+ if expectedPackage != packageName {
+ t.Errorf("effective package mismatch for module %q: expected %q, found %q", moduleName, expectedPackage, packageName)
+ }
+ }
+}
+
+func checkEffectiveNotices(t *testing.T, ctx *TestContext, effectiveNotices map[string][]string) {
+ actualNotices := make(map[string][]string)
+ ctx.Context.Context.VisitAllModules(func(m blueprint.Module) {
+ if _, ok := m.(*licenseModule); ok {
+ return
+ }
+ if _, ok := m.(*licenseKindModule); ok {
+ return
+ }
+ if _, ok := m.(*packageModule); ok {
+ return
+ }
+ module, ok := m.(Module)
+ if !ok {
+ t.Errorf("%q not a module", m.Name())
+ return
+ }
+ base := module.base()
+ if base == nil {
+ return
+ }
+ actualNotices[m.Name()] = base.commonProperties.Effective_license_text
+ })
+
+ for moduleName, expectedNotices := range effectiveNotices {
+ notices, ok := actualNotices[moduleName]
+ if !ok {
+ notices = []string{}
+ }
+ if !compareUnorderedStringArrays(expectedNotices, notices) {
+ t.Errorf("effective notice files mismatch for module %q: expected %q, found %q", moduleName, expectedNotices, notices)
+ }
+ }
+}
+
+func checkEffectiveKinds(t *testing.T, ctx *TestContext, effectiveKinds map[string][]string) {
+ actualKinds := make(map[string][]string)
+ ctx.Context.Context.VisitAllModules(func(m blueprint.Module) {
+ if _, ok := m.(*licenseModule); ok {
+ return
+ }
+ if _, ok := m.(*licenseKindModule); ok {
+ return
+ }
+ if _, ok := m.(*packageModule); ok {
+ return
+ }
+ module, ok := m.(Module)
+ if !ok {
+ t.Errorf("%q not a module", m.Name())
+ return
+ }
+ base := module.base()
+ if base == nil {
+ return
+ }
+ actualKinds[m.Name()] = base.commonProperties.Effective_license_kinds
+ })
+
+ for moduleName, expectedKinds := range effectiveKinds {
+ kinds, ok := actualKinds[moduleName]
+ if !ok {
+ kinds = []string{}
+ }
+ if !compareUnorderedStringArrays(expectedKinds, kinds) {
+ t.Errorf("effective license kinds mismatch for module %q: expected %q, found %q", moduleName, expectedKinds, kinds)
+ }
+ }
+}
+
+func checkEffectiveConditions(t *testing.T, ctx *TestContext, effectiveConditions map[string][]string) {
+ actualConditions := make(map[string][]string)
+ ctx.Context.Context.VisitAllModules(func(m blueprint.Module) {
+ if _, ok := m.(*licenseModule); ok {
+ return
+ }
+ if _, ok := m.(*licenseKindModule); ok {
+ return
+ }
+ if _, ok := m.(*packageModule); ok {
+ return
+ }
+ module, ok := m.(Module)
+ if !ok {
+ t.Errorf("%q not a module", m.Name())
+ return
+ }
+ base := module.base()
+ if base == nil {
+ return
+ }
+ actualConditions[m.Name()] = base.commonProperties.Effective_license_conditions
+ })
+
+ for moduleName, expectedConditions := range effectiveConditions {
+ conditions, ok := actualConditions[moduleName]
+ if !ok {
+ conditions = []string{}
+ }
+ if !compareUnorderedStringArrays(expectedConditions, conditions) {
+ t.Errorf("effective license conditions mismatch for module %q: expected %q, found %q", moduleName, expectedConditions, conditions)
+ }
+ }
+}
+
+func compareUnorderedStringArrays(expected, actual []string) bool {
+ if len(expected) != len(actual) {
+ return false
+ }
+ s := make(map[string]int)
+ for _, v := range expected {
+ s[v] += 1
+ }
+ for _, v := range actual {
+ c, ok := s[v]
+ if !ok {
+ return false
+ }
+ if c < 1 {
+ return false
+ }
+ s[v] -= 1
+ }
+ return true
+}
+
+func testLicenses(buildDir string, fs map[string][]byte) (*TestContext, []error) {
+
+ // Create a new config per test as licenses information is stored in the config.
+ env := make(map[string]string)
+ env["ANDROID_REQUIRE_LICENSES"] = "1"
+ config := TestArchConfig(buildDir, env, "", fs)
+
+ ctx := NewTestArchContext(config)
+ ctx.RegisterModuleType("mock_bad_module", newMockLicensesBadModule)
+ ctx.RegisterModuleType("mock_library", newMockLicensesLibraryModule)
+ ctx.RegisterModuleType("mock_defaults", defaultsLicensesFactory)
+
+ // Order of the following method calls is significant.
+ RegisterPackageBuildComponents(ctx)
+ registerTestPrebuiltBuildComponents(ctx)
+ RegisterLicenseKindBuildComponents(ctx)
+ RegisterLicenseBuildComponents(ctx)
+ ctx.PreArchMutators(RegisterVisibilityRuleChecker)
+ ctx.PreArchMutators(RegisterLicensesPackageMapper)
+ ctx.PreArchMutators(RegisterDefaultsPreArchMutators)
+ ctx.PreArchMutators(RegisterLicensesPropertyGatherer)
+ ctx.PreArchMutators(RegisterVisibilityRuleGatherer)
+ ctx.PostDepsMutators(RegisterVisibilityRuleEnforcer)
+ ctx.PostDepsMutators(RegisterLicensesDependencyChecker)
+ ctx.Register()
+
+ _, errs := ctx.ParseBlueprintsFiles(".")
+ if len(errs) > 0 {
+ return ctx, errs
+ }
+
+ _, errs = ctx.PrepareBuildActions(config)
+ return ctx, errs
+}
+
+type mockLicensesBadProperties struct {
+ Visibility []string
+}
+
+type mockLicensesBadModule struct {
+ ModuleBase
+ DefaultableModuleBase
+ properties mockLicensesBadProperties
+}
+
+func newMockLicensesBadModule() Module {
+ m := &mockLicensesBadModule{}
+
+ base := m.base()
+ m.AddProperties(&base.nameProperties, &m.properties)
+
+ base.generalProperties = m.GetProperties()
+ base.customizableProperties = m.GetProperties()
+
+ // The default_visibility property needs to be checked and parsed by the visibility module during
+ // its checking and parsing phases so make it the primary visibility property.
+ setPrimaryVisibilityProperty(m, "visibility", &m.properties.Visibility)
+
+ initAndroidModuleBase(m)
+ InitDefaultableModule(m)
+
+ return m
+}
+
+func (m *mockLicensesBadModule) GenerateAndroidBuildActions(ModuleContext) {
+}
+
+type mockLicensesLibraryProperties struct {
+ Deps []string
+}
+
+type mockLicensesLibraryModule struct {
+ ModuleBase
+ DefaultableModuleBase
+ properties mockLicensesLibraryProperties
+}
+
+func newMockLicensesLibraryModule() Module {
+ m := &mockLicensesLibraryModule{}
+ m.AddProperties(&m.properties)
+ InitAndroidArchModule(m, HostAndDeviceSupported, MultilibCommon)
+ InitDefaultableModule(m)
+ return m
+}
+
+type dependencyLicensesTag struct {
+ blueprint.BaseDependencyTag
+ name string
+}
+
+func (j *mockLicensesLibraryModule) DepsMutator(ctx BottomUpMutatorContext) {
+ ctx.AddVariationDependencies(nil, dependencyLicensesTag{name: "mockdeps"}, j.properties.Deps...)
+}
+
+func (p *mockLicensesLibraryModule) GenerateAndroidBuildActions(ModuleContext) {
+}
+
+type mockLicensesDefaults struct {
+ ModuleBase
+ DefaultsModuleBase
+}
+
+func defaultsLicensesFactory() Module {
+ m := &mockLicensesDefaults{}
+ InitDefaultsModule(m)
+ return m
+}
diff --git a/android/makevars.go b/android/makevars.go
index 546abcf..40c0ccd 100644
--- a/android/makevars.go
+++ b/android/makevars.go
@@ -134,8 +134,6 @@
// SingletonMakeVarsProvider is a Singleton with an extra method to provide extra values to be exported to Make.
type SingletonMakeVarsProvider interface {
- Singleton
-
// MakeVars uses a MakeVarsContext to provide extra values to be exported to Make.
MakeVars(ctx MakeVarsContext)
}
diff --git a/android/module.go b/android/module.go
index cfb32c1..17035bb 100644
--- a/android/module.go
+++ b/android/module.go
@@ -454,6 +454,7 @@
InstallForceOS() (*OsType, *ArchType)
HideFromMake()
IsHideFromMake() bool
+ IsSkipInstall() bool
MakeUninstallable()
ReplacedByPrebuilt()
IsReplacedByPrebuilt() bool
@@ -619,6 +620,20 @@
// more details.
Visibility []string
+ // Describes the licenses applicable to this module. Must reference license modules.
+ Licenses []string
+
+ // Flattened from direct license dependencies. Equal to Licenses unless particular module adds more.
+ Effective_licenses []string `blueprint:"mutated"`
+ // Override of module name when reporting licenses
+ Effective_package_name *string `blueprint:"mutated"`
+ // Notice files
+ Effective_license_text []string `blueprint:"mutated"`
+ // License names
+ Effective_license_kinds []string `blueprint:"mutated"`
+ // License conditions
+ Effective_license_conditions []string `blueprint:"mutated"`
+
// control whether this module compiles for 32-bit, 64-bit, or both. Possible values
// are "32" (compile for 32-bit only), "64" (compile for 64-bit only), "both" (compile for both
// architectures), or "first" (compile for 64-bit on a 64-bit platform, and 32-bit on a 32-bit
@@ -940,6 +955,10 @@
// The default_visibility property needs to be checked and parsed by the visibility module during
// its checking and parsing phases so make it the primary visibility property.
setPrimaryVisibilityProperty(m, "visibility", &base.commonProperties.Visibility)
+
+ // The default_applicable_licenses property needs to be checked and parsed by the licenses module during
+ // its checking and parsing phases so make it the primary licenses property.
+ setPrimaryLicensesProperty(m, "licenses", &base.commonProperties.Licenses)
}
// InitAndroidArchModule initializes the Module as an Android module that is architecture-specific.
@@ -1057,6 +1076,9 @@
// The primary visibility property, may be nil, that controls access to the module.
primaryVisibilityProperty visibilityProperty
+ // The primary licenses property, may be nil, records license metadata for the module.
+ primaryLicensesProperty applicableLicensesProperty
+
noAddressSanitizer bool
installFiles InstallPaths
installFilesDepSet *installPathsDepSet
@@ -1377,6 +1399,12 @@
m.commonProperties.SkipInstall = true
}
+// IsSkipInstall returns true if this variant is marked to not create install
+// rules when ctx.Install* are called.
+func (m *ModuleBase) IsSkipInstall() bool {
+ return m.commonProperties.SkipInstall
+}
+
// Similar to HideFromMake, but if the AndroidMk entry would set
// LOCAL_UNINSTALLABLE_MODULE then this variant may still output that entry
// rather than leaving it out altogether. That happens in cases where it would
@@ -1732,6 +1760,11 @@
}
}
+ licensesPropertyFlattener(ctx)
+ if ctx.Failed() {
+ return
+ }
+
m.module.GenerateAndroidBuildActions(ctx)
if ctx.Failed() {
return
diff --git a/android/mutator.go b/android/mutator.go
index 31edea3..72c68b2 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -115,6 +115,11 @@
//
RegisterVisibilityRuleChecker,
+ // Record the default_applicable_licenses for each package.
+ //
+ // This must run before the defaults so that defaults modules can pick up the package default.
+ RegisterLicensesPackageMapper,
+
// Apply properties from defaults modules to the referencing modules.
//
// Any mutators that are added before this will not see any modules created by
@@ -141,6 +146,12 @@
// prebuilt.
RegisterPrebuiltsPreArchMutators,
+ // Gather the licenses properties for all modules for use during expansion and enforcement.
+ //
+ // This must come after the defaults mutators to ensure that any licenses supplied
+ // in a defaults module has been successfully applied before the rules are gathered.
+ RegisterLicensesPropertyGatherer,
+
// Gather the visibility rules for all modules for us during visibility enforcement.
//
// This must come after the defaults mutators to ensure that any visibility supplied
@@ -162,6 +173,7 @@
registerPathDepsMutator,
RegisterPrebuiltsPostDepsMutators,
RegisterVisibilityRuleEnforcer,
+ RegisterLicensesDependencyChecker,
RegisterNeverallowMutator,
RegisterOverridePostDepsMutators,
}
diff --git a/android/package.go b/android/package.go
index 182b3ed..7012fc7 100644
--- a/android/package.go
+++ b/android/package.go
@@ -31,6 +31,8 @@
type packageProperties struct {
// Specifies the default visibility for all modules defined in this package.
Default_visibility []string
+ // Specifies the default license terms for all modules defined in this package.
+ Default_applicable_licenses []string
}
type packageModule struct {
@@ -68,5 +70,9 @@
// its checking and parsing phases so make it the primary visibility property.
setPrimaryVisibilityProperty(module, "default_visibility", &module.properties.Default_visibility)
+ // The default_applicable_licenses property needs to be checked and parsed by the licenses module during
+ // its checking and parsing phases so make it the primary licenses property.
+ setPrimaryLicensesProperty(module, "default_applicable_licenses", &module.properties.Default_applicable_licenses)
+
return module
}
diff --git a/android/package_test.go b/android/package_test.go
index ade95d4..99be13f 100644
--- a/android/package_test.go
+++ b/android/package_test.go
@@ -17,9 +17,11 @@
package {
name: "package",
visibility: ["//visibility:private"],
+ licenses: ["license"],
}`),
},
expectedErrors: []string{
+ `top/Blueprints:5:14: unrecognized property "licenses"`,
`top/Blueprints:3:10: unrecognized property "name"`,
`top/Blueprints:4:16: unrecognized property "visibility"`,
},
@@ -44,9 +46,10 @@
"top/Blueprints": []byte(`
package {
default_visibility: ["//visibility:private"],
+ default_applicable_licenses: ["license"],
}
- package {
+ package {
}`),
},
expectedErrors: []string{
diff --git a/android/paths.go b/android/paths.go
index 10d8d0d..592b9e1 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -1742,6 +1742,19 @@
return ioutil.WriteFile(absolutePath(path.String()), data, perm)
}
+func RemoveAllOutputDir(path WritablePath) error {
+ return os.RemoveAll(absolutePath(path.String()))
+}
+
+func CreateOutputDirIfNonexistent(path WritablePath, perm os.FileMode) error {
+ dir := absolutePath(path.String())
+ if _, err := os.Stat(dir); os.IsNotExist(err) {
+ return os.MkdirAll(dir, os.ModePerm)
+ } else {
+ return err
+ }
+}
+
func absolutePath(path string) string {
if filepath.IsAbs(path) {
return path
diff --git a/android/queryview.go b/android/queryview.go
index 1b7e77d..9e3e45a 100644
--- a/android/queryview.go
+++ b/android/queryview.go
@@ -26,7 +26,6 @@
// for calling the soong_build primary builder in the main build.ninja file.
func init() {
RegisterSingletonType("bazel_queryview", BazelQueryViewSingleton)
- RegisterSingletonType("bazel_converter", BazelConverterSingleton)
}
// BazelQueryViewSingleton is the singleton responsible for registering the
@@ -52,13 +51,7 @@
func generateBuildActionsForBazelConversion(ctx SingletonContext, converterMode bool) {
name := "queryview"
- additionalEnvVars := ""
descriptionTemplate := "[EXPERIMENTAL, PRE-PRODUCTION] Creating the Bazel QueryView workspace with %s at $outDir"
- if converterMode {
- name = "bp2build"
- additionalEnvVars = "CONVERT_TO_BAZEL=true"
- descriptionTemplate = "[EXPERIMENTAL, PRE-PRODUCTION] Converting all Android.bp to Bazel BUILD files with %s at $outDir"
- }
// Create a build and rule statement, using the Bazel QueryView's WORKSPACE
// file as the output file marker.
@@ -74,9 +67,8 @@
blueprint.RuleParams{
Command: fmt.Sprintf(
"rm -rf ${outDir}/* && "+
- "%s %s --bazel_queryview_dir ${outDir} %s && "+
+ "%s --bazel_queryview_dir ${outDir} %s && "+
"echo WORKSPACE: `cat %s` > ${outDir}/.queryview-depfile.d",
- additionalEnvVars,
primaryBuilder.String(),
strings.Join(os.Args[1:], " "),
moduleListFilePath.String(), // Use the contents of Android.bp.list as the depfile.
diff --git a/android/register.go b/android/register.go
index b26f9b9..ca658f5 100644
--- a/android/register.go
+++ b/android/register.go
@@ -16,6 +16,7 @@
import (
"fmt"
+ "reflect"
"github.com/google/blueprint"
)
@@ -26,6 +27,7 @@
}
var moduleTypes []moduleType
+var moduleTypesForDocs = map[string]reflect.Value{}
type singleton struct {
name string
@@ -35,6 +37,9 @@
var singletons []singleton
var preSingletons []singleton
+var bazelConverterSingletons []singleton
+var bazelConverterPreSingletons []singleton
+
type mutator struct {
name string
bottomUpMutator blueprint.BottomUpMutator
@@ -69,6 +74,16 @@
func RegisterModuleType(name string, factory ModuleFactory) {
moduleTypes = append(moduleTypes, moduleType{name, factory})
+ RegisterModuleTypeForDocs(name, reflect.ValueOf(factory))
+}
+
+// RegisterModuleTypeForDocs associates a module type name with a reflect.Value of the factory
+// function that has documentation for the module type. It is normally called automatically
+// by RegisterModuleType, but can be called manually after RegisterModuleType in order to
+// override the factory method used for documentation, for example if the method passed to
+// RegisterModuleType was a lambda.
+func RegisterModuleTypeForDocs(name string, factory reflect.Value) {
+ moduleTypesForDocs[name] = factory
}
func RegisterSingletonType(name string, factory SingletonFactory) {
@@ -79,6 +94,14 @@
preSingletons = append(preSingletons, singleton{name, factory})
}
+func RegisterBazelConverterSingletonType(name string, factory SingletonFactory) {
+ bazelConverterSingletons = append(bazelConverterSingletons, singleton{name, factory})
+}
+
+func RegisterBazelConverterPreSingletonType(name string, factory SingletonFactory) {
+ bazelConverterPreSingletons = append(bazelConverterPreSingletons, singleton{name, factory})
+}
+
type Context struct {
*blueprint.Context
config Config
@@ -94,13 +117,17 @@
// singletons, module types and mutators to register for converting Blueprint
// files to semantically equivalent BUILD files.
func (ctx *Context) RegisterForBazelConversion() {
+ for _, t := range bazelConverterPreSingletons {
+ ctx.RegisterPreSingletonType(t.name, SingletonFactoryAdaptor(ctx, t.factory))
+ }
+
for _, t := range moduleTypes {
ctx.RegisterModuleType(t.name, ModuleFactoryAdaptor(t.factory))
}
- bazelConverterSingleton := singleton{"bp2build", BazelConverterSingleton}
- ctx.RegisterSingletonType(bazelConverterSingleton.name,
- SingletonFactoryAdaptor(ctx, bazelConverterSingleton.factory))
+ for _, t := range bazelConverterSingletons {
+ ctx.RegisterSingletonType(t.name, SingletonFactoryAdaptor(ctx, t.factory))
+ }
registerMutatorsForBazelConversion(ctx.Context)
}
@@ -142,12 +169,17 @@
return ret
}
+func ModuleTypeFactoriesForDocs() map[string]reflect.Value {
+ return moduleTypesForDocs
+}
+
// Interface for registering build components.
//
// Provided to allow registration of build components to be shared between the runtime
// and test environments.
type RegistrationContext interface {
RegisterModuleType(name string, factory ModuleFactory)
+ RegisterSingletonModuleType(name string, factory SingletonModuleFactory)
RegisterSingletonType(name string, factory SingletonFactory)
PreArchMutators(f RegisterMutatorFunc)
@@ -186,8 +218,9 @@
var _ RegistrationContext = (*TestContext)(nil)
type initRegistrationContext struct {
- moduleTypes map[string]ModuleFactory
- singletonTypes map[string]SingletonFactory
+ moduleTypes map[string]ModuleFactory
+ singletonTypes map[string]SingletonFactory
+ moduleTypesForDocs map[string]reflect.Value
}
func (ctx *initRegistrationContext) RegisterModuleType(name string, factory ModuleFactory) {
@@ -196,6 +229,17 @@
}
ctx.moduleTypes[name] = factory
RegisterModuleType(name, factory)
+ RegisterModuleTypeForDocs(name, reflect.ValueOf(factory))
+}
+
+func (ctx *initRegistrationContext) RegisterSingletonModuleType(name string, factory SingletonModuleFactory) {
+ s, m := SingletonModuleFactoryAdaptor(name, factory)
+ ctx.RegisterSingletonType(name, s)
+ ctx.RegisterModuleType(name, m)
+ // Overwrite moduleTypesForDocs with the original factory instead of the lambda returned by
+ // SingletonModuleFactoryAdaptor so that docs can find the module type documentation on the
+ // factory method.
+ RegisterModuleTypeForDocs(name, reflect.ValueOf(factory))
}
func (ctx *initRegistrationContext) RegisterSingletonType(name string, factory SingletonFactory) {
diff --git a/android/singleton_module.go b/android/singleton_module.go
new file mode 100644
index 0000000..2351738
--- /dev/null
+++ b/android/singleton_module.go
@@ -0,0 +1,146 @@
+// Copyright 2020 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"
+
+ "github.com/google/blueprint"
+)
+
+// A SingletonModule is halfway between a Singleton and a Module. It has access to visiting
+// other modules via its GenerateSingletonBuildActions method, but must be defined in an Android.bp
+// file and can also be depended on like a module. It must be used zero or one times in an
+// Android.bp file, and it can only have a single variant.
+//
+// The SingletonModule's GenerateAndroidBuildActions method will be called before any normal or
+// singleton module that depends on it, but its GenerateSingletonBuildActions method will be called
+// after all modules, in registration order with other singletons and singleton modules.
+// GenerateAndroidBuildActions and GenerateSingletonBuildActions will not be called if the
+// SingletonModule was not instantiated in an Android.bp file.
+//
+// Since the SingletonModule rules likely depend on the modules visited during
+// GenerateSingletonBuildActions, the GenerateAndroidBuildActions is unlikely to produce any
+// rules directly. Instead, it will probably set some providers to paths that will later have rules
+// generated to produce them in GenerateSingletonBuildActions.
+//
+// The expected use case for a SingletonModule is a module that produces files that depend on all
+// modules in the tree and will be used by other modules. For example it could produce a text
+// file that lists all modules that meet a certain criteria, and that text file could be an input
+// to another module. Care must be taken that the ninja rules produced by the SingletonModule
+// don't produce a cycle by referencing output files of rules of modules that depend on the
+// SingletonModule.
+//
+// A SingletonModule must embed a SingletonModuleBase struct, and its factory method must be
+// registered with RegisterSingletonModuleType from an init() function.
+//
+// A SingletonModule can also implement SingletonMakeVarsProvider to export values to Make.
+type SingletonModule interface {
+ Module
+ GenerateSingletonBuildActions(SingletonContext)
+ singletonModuleBase() *SingletonModuleBase
+}
+
+// SingletonModuleBase must be embedded into implementers of the SingletonModule interface.
+type SingletonModuleBase struct {
+ ModuleBase
+
+ lock sync.Mutex
+ bp string
+ variant string
+}
+
+// GenerateBuildActions wraps the ModuleBase GenerateBuildActions method, verifying it was only
+// called once to prevent multiple variants of a SingletonModule.
+func (smb *SingletonModuleBase) GenerateBuildActions(ctx blueprint.ModuleContext) {
+ smb.lock.Lock()
+ if smb.variant != "" {
+ ctx.ModuleErrorf("GenerateAndroidBuildActions already called for variant %q, SingletonModules can only have one variant", smb.variant)
+ }
+ smb.variant = ctx.ModuleSubDir()
+ smb.lock.Unlock()
+
+ smb.ModuleBase.GenerateBuildActions(ctx)
+}
+
+// InitAndroidSingletonModule must be called from the SingletonModule's factory function to
+// initialize SingletonModuleBase.
+func InitAndroidSingletonModule(sm SingletonModule) {
+ InitAndroidModule(sm)
+}
+
+// singletonModuleBase retrieves the embedded SingletonModuleBase from a SingletonModule.
+func (smb *SingletonModuleBase) singletonModuleBase() *SingletonModuleBase { return smb }
+
+// SingletonModuleFactory is a factory method that returns a SingletonModule.
+type SingletonModuleFactory func() SingletonModule
+
+// SingletonModuleFactoryAdaptor converts a SingletonModuleFactory into a SingletonFactory and a
+// ModuleFactory.
+func SingletonModuleFactoryAdaptor(name string, factory SingletonModuleFactory) (SingletonFactory, ModuleFactory) {
+ // The sm variable acts as a static holder of the only SingletonModule instance. Calls to the
+ // returned SingletonFactory and ModuleFactory lambdas will always return the same sm value.
+ // The SingletonFactory is only expected to be called once, but the ModuleFactory may be
+ // called multiple times if the module is replaced with a clone of itself at the end of
+ // blueprint.ResolveDependencies.
+ var sm SingletonModule
+ s := func() Singleton {
+ sm = factory()
+ return &singletonModuleSingletonAdaptor{sm}
+ }
+ m := func() Module {
+ if sm == nil {
+ panic(fmt.Errorf("Singleton %q for SingletonModule was not instantiated", name))
+ }
+
+ // Check for multiple uses of a SingletonModule in a LoadHook. Checking directly in the
+ // factory would incorrectly flag when the factory was called again when the module is
+ // replaced with a clone of itself at the end of blueprint.ResolveDependencies.
+ AddLoadHook(sm, func(ctx LoadHookContext) {
+ smb := sm.singletonModuleBase()
+ smb.lock.Lock()
+ defer smb.lock.Unlock()
+ if smb.bp != "" {
+ ctx.ModuleErrorf("Duplicate SingletonModule %q, previously used in %s", name, smb.bp)
+ }
+ smb.bp = ctx.BlueprintsFile()
+ })
+ return sm
+ }
+ return s, m
+}
+
+// singletonModuleSingletonAdaptor makes a SingletonModule into a Singleton by translating the
+// GenerateSingletonBuildActions method to Singleton.GenerateBuildActions.
+type singletonModuleSingletonAdaptor struct {
+ sm SingletonModule
+}
+
+// GenerateBuildActions calls the SingletonModule's GenerateSingletonBuildActions method, but only
+// if the module was defined in an Android.bp file.
+func (smsa *singletonModuleSingletonAdaptor) GenerateBuildActions(ctx SingletonContext) {
+ if smsa.sm.singletonModuleBase().bp != "" {
+ smsa.sm.GenerateSingletonBuildActions(ctx)
+ }
+}
+
+func (smsa *singletonModuleSingletonAdaptor) MakeVars(ctx MakeVarsContext) {
+ if smsa.sm.singletonModuleBase().bp != "" {
+ if makeVars, ok := smsa.sm.(SingletonMakeVarsProvider); ok {
+ makeVars.MakeVars(ctx)
+ }
+ }
+}
diff --git a/android/singleton_module_test.go b/android/singleton_module_test.go
new file mode 100644
index 0000000..9232eb4
--- /dev/null
+++ b/android/singleton_module_test.go
@@ -0,0 +1,149 @@
+// Copyright 2021 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 (
+ "reflect"
+ "strings"
+ "testing"
+)
+
+type testSingletonModule struct {
+ SingletonModuleBase
+ ops []string
+}
+
+func (tsm *testSingletonModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+ tsm.ops = append(tsm.ops, "GenerateAndroidBuildActions")
+}
+
+func (tsm *testSingletonModule) GenerateSingletonBuildActions(ctx SingletonContext) {
+ tsm.ops = append(tsm.ops, "GenerateSingletonBuildActions")
+}
+
+func (tsm *testSingletonModule) MakeVars(ctx MakeVarsContext) {
+ tsm.ops = append(tsm.ops, "MakeVars")
+}
+
+func testSingletonModuleFactory() SingletonModule {
+ tsm := &testSingletonModule{}
+ InitAndroidSingletonModule(tsm)
+ return tsm
+}
+
+func runSingletonModuleTest(bp string) (*TestContext, []error) {
+ config := TestConfig(buildDir, nil, bp, nil)
+ // Enable Kati output to test SingletonModules with MakeVars.
+ config.katiEnabled = true
+ ctx := NewTestContext(config)
+ ctx.RegisterSingletonModuleType("test_singleton_module", testSingletonModuleFactory)
+ ctx.RegisterSingletonType("makevars", makeVarsSingletonFunc)
+ ctx.Register()
+
+ _, errs := ctx.ParseBlueprintsFiles("Android.bp")
+ if len(errs) > 0 {
+ return ctx, errs
+ }
+
+ _, errs = ctx.PrepareBuildActions(config)
+ return ctx, errs
+}
+
+func TestSingletonModule(t *testing.T) {
+ bp := `
+ test_singleton_module {
+ name: "test_singleton_module",
+ }
+ `
+ ctx, errs := runSingletonModuleTest(bp)
+ if len(errs) > 0 {
+ t.Fatal(errs)
+ }
+
+ ops := ctx.ModuleForTests("test_singleton_module", "").Module().(*testSingletonModule).ops
+ wantOps := []string{"GenerateAndroidBuildActions", "GenerateSingletonBuildActions", "MakeVars"}
+ if !reflect.DeepEqual(ops, wantOps) {
+ t.Errorf("Expected operations %q, got %q", wantOps, ops)
+ }
+}
+
+func TestDuplicateSingletonModule(t *testing.T) {
+ bp := `
+ test_singleton_module {
+ name: "test_singleton_module",
+ }
+
+ test_singleton_module {
+ name: "test_singleton_module2",
+ }
+ `
+ _, errs := runSingletonModuleTest(bp)
+ if len(errs) == 0 {
+ t.Fatal("expected duplicate SingletonModule error")
+ }
+ if len(errs) != 1 || !strings.Contains(errs[0].Error(), `Duplicate SingletonModule "test_singleton_module", previously used in`) {
+ t.Fatalf("expected duplicate SingletonModule error, got %q", errs)
+ }
+}
+
+func TestUnusedSingletonModule(t *testing.T) {
+ bp := ``
+ ctx, errs := runSingletonModuleTest(bp)
+ if len(errs) > 0 {
+ t.Fatal(errs)
+ }
+
+ singleton := ctx.SingletonForTests("test_singleton_module").Singleton()
+ sm := singleton.(*singletonModuleSingletonAdaptor).sm
+ ops := sm.(*testSingletonModule).ops
+ if ops != nil {
+ t.Errorf("Expected no operations, got %q", ops)
+ }
+}
+
+func testVariantSingletonModuleMutator(ctx BottomUpMutatorContext) {
+ if _, ok := ctx.Module().(*testSingletonModule); ok {
+ ctx.CreateVariations("a", "b")
+ }
+}
+
+func TestVariantSingletonModule(t *testing.T) {
+ bp := `
+ test_singleton_module {
+ name: "test_singleton_module",
+ }
+ `
+
+ config := TestConfig(buildDir, nil, bp, nil)
+ ctx := NewTestContext(config)
+ ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
+ ctx.BottomUp("test_singleton_module_mutator", testVariantSingletonModuleMutator)
+ })
+ ctx.RegisterSingletonModuleType("test_singleton_module", testSingletonModuleFactory)
+ ctx.Register()
+
+ _, errs := ctx.ParseBlueprintsFiles("Android.bp")
+
+ if len(errs) == 0 {
+ _, errs = ctx.PrepareBuildActions(config)
+ }
+
+ if len(errs) == 0 {
+ t.Fatal("expected duplicate SingletonModule error")
+ }
+ if len(errs) != 1 || !strings.Contains(errs[0].Error(), `GenerateAndroidBuildActions already called for variant`) {
+ t.Fatalf("expected duplicate SingletonModule error, got %q", errs)
+ }
+}
diff --git a/android/testing.go b/android/testing.go
index 6539063..a66b1e1 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -103,6 +103,12 @@
ctx.Context.RegisterModuleType(name, ModuleFactoryAdaptor(factory))
}
+func (ctx *TestContext) RegisterSingletonModuleType(name string, factory SingletonModuleFactory) {
+ s, m := SingletonModuleFactoryAdaptor(name, factory)
+ ctx.RegisterSingletonType(name, s)
+ ctx.RegisterModuleType(name, m)
+}
+
func (ctx *TestContext) RegisterSingletonType(name string, factory SingletonFactory) {
ctx.Context.RegisterSingletonType(name, SingletonFactoryAdaptor(ctx.Context, factory))
}
diff --git a/android/variable.go b/android/variable.go
index 7532797..41cd337 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -139,10 +139,6 @@
Enabled *bool
}
- Experimental_mte struct {
- Cflags []string `android:"arch_variant"`
- } `android:"arch_variant"`
-
Native_coverage struct {
Src *string `android:"arch_variant"`
Srcs []string `android:"arch_variant"`
@@ -264,7 +260,9 @@
DisableScudo *bool `json:",omitempty"`
- Experimental_mte *bool `json:",omitempty"`
+ MemtagHeapExcludePaths []string `json:",omitempty"`
+ MemtagHeapAsyncIncludePaths []string `json:",omitempty"`
+ MemtagHeapSyncIncludePaths []string `json:",omitempty"`
VendorPath *string `json:",omitempty"`
OdmPath *string `json:",omitempty"`
diff --git a/apex/Android.bp b/apex/Android.bp
index 9e8c30d..e3a547c 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -7,6 +7,7 @@
"soong-android",
"soong-bpf",
"soong-cc",
+ "soong-filesystem",
"soong-java",
"soong-python",
"soong-rust",
diff --git a/apex/allowed_deps.txt b/apex/allowed_deps.txt
index 69bf64f..786496f 100644
--- a/apex/allowed_deps.txt
+++ b/apex/allowed_deps.txt
@@ -76,6 +76,7 @@
androidx.customview_customview(minSdkVersion:14)
androidx.documentfile_documentfile(minSdkVersion:14)
androidx.drawerlayout_drawerlayout(minSdkVersion:14)
+androidx.dynamicanimation_dynamicanimation(minSdkVersion:14)
androidx.fragment_fragment(minSdkVersion:14)
androidx.fragment_fragment-ktx(minSdkVersion:14)
androidx.interpolator_interpolator(minSdkVersion:14)
@@ -129,6 +130,7 @@
bionic_libc_platform_headers(minSdkVersion:29)
boringssl_self_test(minSdkVersion:29)
bouncycastle_ike_digests(minSdkVersion:current)
+bpf_syscall_wrappers(minSdkVersion:30)
brotli-java(minSdkVersion:current)
captiveportal-lib(minSdkVersion:29)
car-ui-lib(minSdkVersion:28)
@@ -384,6 +386,7 @@
libring(minSdkVersion:29)
libring-core(minSdkVersion:29)
librustc_demangle.rust_sysroot(minSdkVersion:29)
+libsdk_proto(minSdkVersion:30)
libsfplugin_ccodec_utils(minSdkVersion:29)
libsonivoxwithoutjet(minSdkVersion:29)
libspeexresampler(minSdkVersion:29)
@@ -466,7 +469,10 @@
ndk_libunwind(minSdkVersion:16)
net-utils-device-common(minSdkVersion:29)
net-utils-framework-common(minSdkVersion:current)
+netd-client(minSdkVersion:29)
+netd_aidl_interface-java(minSdkVersion:29)
netd_aidl_interface-unstable-java(minSdkVersion:29)
+netd_event_listener_interface-java(minSdkVersion:29)
netd_event_listener_interface-ndk_platform(minSdkVersion:29)
netd_event_listener_interface-unstable-ndk_platform(minSdkVersion:29)
netlink-client(minSdkVersion:29)
@@ -482,6 +488,8 @@
neuralnetworks_utils_hal_1_3(minSdkVersion:30)
neuralnetworks_utils_hal_common(minSdkVersion:30)
neuralnetworks_utils_hal_service(minSdkVersion:30)
+note_memtag_heap_async(minSdkVersion:16)
+note_memtag_heap_sync(minSdkVersion:16)
PermissionController(minSdkVersion:28)
permissioncontroller-statsd(minSdkVersion:current)
philox_random(minSdkVersion:(no version))
@@ -507,6 +515,7 @@
prebuilt_androidx.customview_customview-nodeps(minSdkVersion:(no version))
prebuilt_androidx.documentfile_documentfile-nodeps(minSdkVersion:(no version))
prebuilt_androidx.drawerlayout_drawerlayout-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.dynamicanimation_dynamicanimation-nodeps(minSdkVersion:(no version))
prebuilt_androidx.fragment_fragment-ktx-nodeps(minSdkVersion:(no version))
prebuilt_androidx.fragment_fragment-nodeps(minSdkVersion:(no version))
prebuilt_androidx.interpolator_interpolator-nodeps(minSdkVersion:(no version))
diff --git a/apex/androidmk.go b/apex/androidmk.go
index e7f8b7f..0b114f8 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -63,6 +63,16 @@
}
}
+// Return the full module name for a dependency module, which appends the apex module name unless re-using a system lib.
+func (a *apexBundle) fullModuleName(apexBundleName string, fi *apexFile) string {
+ linkToSystemLib := a.linkToSystemLib && fi.transitiveDep && fi.availableToPlatform()
+
+ if linkToSystemLib {
+ return fi.androidMkModuleName
+ }
+ return fi.androidMkModuleName + "." + apexBundleName + a.suffix
+}
+
func (a *apexBundle) androidMkForFiles(w io.Writer, apexBundleName, apexName, moduleDir string,
apexAndroidMkData android.AndroidMkData) []string {
@@ -114,12 +124,7 @@
linkToSystemLib := a.linkToSystemLib && fi.transitiveDep && fi.availableToPlatform()
- var moduleName string
- if linkToSystemLib {
- moduleName = fi.androidMkModuleName
- } else {
- moduleName = fi.androidMkModuleName + "." + apexBundleName + a.suffix
- }
+ moduleName := a.fullModuleName(apexBundleName, &fi)
if !android.InList(moduleName, moduleNames) {
moduleNames = append(moduleNames, moduleName)
@@ -311,14 +316,16 @@
return moduleNames
}
-func (a *apexBundle) writeRequiredModules(w io.Writer) {
+func (a *apexBundle) writeRequiredModules(w io.Writer, apexBundleName string) {
var required []string
var targetRequired []string
var hostRequired []string
+ installMapSet := make(map[string]bool) // set of dependency module:location mappings
for _, fi := range a.filesInfo {
required = append(required, fi.requiredModuleNames...)
targetRequired = append(targetRequired, fi.targetRequiredModuleNames...)
hostRequired = append(hostRequired, fi.hostRequiredModuleNames...)
+ installMapSet[a.fullModuleName(apexBundleName, &fi)+":"+fi.installDir+"/"+fi.builtFile.Base()] = true
}
if len(required) > 0 {
@@ -330,6 +337,11 @@
if len(hostRequired) > 0 {
fmt.Fprintln(w, "LOCAL_HOST_REQUIRED_MODULES +=", strings.Join(hostRequired, " "))
}
+ if len(installMapSet) > 0 {
+ var installs []string
+ installs = append(installs, android.SortedStringKeys(installMapSet)...)
+ fmt.Fprintln(w, "LOCAL_LICENSE_INSTALL_MAP +=", strings.Join(installs, " "))
+ }
}
func (a *apexBundle) androidMkForType() android.AndroidMkData {
@@ -347,16 +359,18 @@
fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
fmt.Fprintln(w, "LOCAL_MODULE :=", name+a.suffix)
+ data.Entries.WriteLicenseVariables(w)
if len(moduleNames) > 0 {
fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES :=", strings.Join(moduleNames, " "))
}
- a.writeRequiredModules(w)
+ a.writeRequiredModules(w, name)
fmt.Fprintln(w, "include $(BUILD_PHONY_PACKAGE)")
} else {
fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
fmt.Fprintln(w, "LOCAL_MODULE :=", name+a.suffix)
+ data.Entries.WriteLicenseVariables(w)
fmt.Fprintln(w, "LOCAL_MODULE_CLASS := ETC") // do we need a new class?
fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", a.outputFile.String())
fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", a.installDir.ToMakePath().String())
@@ -389,7 +403,7 @@
if len(a.requiredDeps) > 0 {
fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES +=", strings.Join(a.requiredDeps, " "))
}
- a.writeRequiredModules(w)
+ a.writeRequiredModules(w, name)
var postInstallCommands []string
if a.prebuiltFileToDelete != "" {
postInstallCommands = append(postInstallCommands, "rm -rf "+
diff --git a/apex/apex.go b/apex/apex.go
index 2182069..5cd18ed 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -30,6 +30,7 @@
"android/soong/bpf"
"android/soong/cc"
prebuilt_etc "android/soong/etc"
+ "android/soong/filesystem"
"android/soong/java"
"android/soong/python"
"android/soong/rust"
@@ -96,6 +97,9 @@
// List of BPF programs inside this APEX bundle.
Bpfs []string
+ // List of filesystem images that are embedded inside this APEX bundle.
+ Filesystems []string
+
// Name of the apex_key module that provides the private key to sign this APEX bundle.
Key *string
@@ -164,6 +168,10 @@
// used in tests.
Test_only_unsigned_payload *bool
+ // Whenever apex should be compressed, regardless of product flag used. Should be only
+ // used in tests.
+ Test_only_force_compression *bool
+
IsCoverageVariant bool `blueprint:"mutated"`
// List of sanitizer names that this APEX is enabled for
@@ -530,6 +538,7 @@
bpfTag = dependencyTag{name: "bpf", payload: true}
certificateTag = dependencyTag{name: "certificate"}
executableTag = dependencyTag{name: "executable", payload: true}
+ fsTag = dependencyTag{name: "filesystem", payload: true}
javaLibTag = dependencyTag{name: "javaLib", payload: true}
jniLibTag = dependencyTag{name: "jniLib", payload: true}
keyTag = dependencyTag{name: "key"}
@@ -709,6 +718,7 @@
commonVariation := ctx.Config().AndroidCommonTarget.Variations()
ctx.AddFarVariationDependencies(commonVariation, javaLibTag, a.properties.Java_libs...)
ctx.AddFarVariationDependencies(commonVariation, bpfTag, a.properties.Bpfs...)
+ ctx.AddFarVariationDependencies(commonVariation, fsTag, a.properties.Filesystems...)
// With EMMA_INSTRUMENT_FRAMEWORK=true the ART boot image includes jacoco library.
if a.artApex && ctx.Config().IsEnvTrue("EMMA_INSTRUMENT_FRAMEWORK") {
@@ -1235,6 +1245,11 @@
return proptools.Bool(a.properties.Test_only_unsigned_payload)
}
+// See the test_only_force_compression property
+func (a *apexBundle) testOnlyShouldForceCompression() bool {
+ return proptools.Bool(a.properties.Test_only_force_compression)
+}
+
// These functions are interfacing with cc/sanitizer.go. The entire APEX (along with all of its
// members) can be sanitized, either forcibly, or by the global configuration. For some of the
// sanitizers, extra dependencies can be forcibly added as well.
@@ -1481,6 +1496,11 @@
return newApexFile(ctx, builtFile, builtFile.Base(), dirInApex, etc, bpfProgram)
}
+func apexFileForFilesystem(ctx android.BaseModuleContext, buildFile android.Path, fs filesystem.Filesystem) apexFile {
+ dirInApex := filepath.Join("etc", "fs")
+ return newApexFile(ctx, buildFile, buildFile.Base(), dirInApex, etc, fs)
+}
+
// WalyPayloadDeps visits dependencies that contributes to the payload of this APEX. For each of the
// visited module, the `do` callback is executed. Returning true in the callback continues the visit
// to the child modules. Returning false makes the visit to continue in the sibling or the parent
@@ -1655,6 +1675,12 @@
} else {
ctx.PropertyErrorf("bpfs", "%q is not a bpf module", depName)
}
+ case fsTag:
+ if fs, ok := child.(filesystem.Filesystem); ok {
+ filesInfo = append(filesInfo, apexFileForFilesystem(ctx, fs.OutputPath(), fs))
+ } else {
+ ctx.PropertyErrorf("filesystems", "%q is not a filesystem module", depName)
+ }
case prebuiltTag:
if prebuilt, ok := child.(prebuilt_etc.PrebuiltEtcModule); ok {
filesInfo = append(filesInfo, apexFileForPrebuiltEtc(ctx, prebuilt, depName))
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 9d43348..fc74672 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -250,7 +250,7 @@
ctx.RegisterModuleType("cc_test", cc.TestFactory)
ctx.RegisterModuleType("vndk_prebuilt_shared", cc.VndkPrebuiltSharedFactory)
- ctx.RegisterModuleType("vndk_libraries_txt", cc.VndkLibrariesTxtFactory)
+ cc.RegisterVndkLibraryTxtTypes(ctx)
prebuilt_etc.RegisterPrebuiltEtcBuildComponents(ctx)
ctx.RegisterModuleType("platform_compat_config", java.PlatformCompatConfigFactory)
ctx.RegisterModuleType("sh_binary", sh.ShBinaryFactory)
@@ -3219,7 +3219,7 @@
if v == "current" {
for _, txt := range []string{"llndk", "vndkcore", "vndksp", "vndkprivate", "vndkproduct"} {
result += `
- vndk_libraries_txt {
+ ` + txt + `_libraries_txt {
name: "` + txt + `.libraries.txt",
}
`
diff --git a/apex/builder.go b/apex/builder.go
index 106302b..bc1b566 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -763,9 +763,13 @@
})
a.outputFile = signedOutputFile
- // Process APEX compression if enabled
+ // Process APEX compression if enabled or forced
+ if ctx.ModuleDir() != "system/apex/apexd/apexd_testdata" && a.testOnlyShouldForceCompression() {
+ ctx.PropertyErrorf("test_only_force_compression", "not available")
+ return
+ }
compressionEnabled := ctx.Config().CompressedApex() && proptools.BoolDefault(a.properties.Compressible, true)
- if compressionEnabled && apexType == imageApex {
+ if apexType == imageApex && (compressionEnabled || a.testOnlyShouldForceCompression()) {
a.isCompressed = true
unsignedCompressedOutputFile := android.PathForModuleOut(ctx, a.Name()+".capex.unsigned")
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index 7931e9e..50e892e 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -107,7 +107,7 @@
compatSymlinks []string
}
-type PrebuiltProperties struct {
+type ApexFileProperties struct {
// the path to the prebuilt .apex file to import.
Source string `blueprint:"mutated"`
@@ -126,6 +126,38 @@
Src *string
}
}
+}
+
+func (p *ApexFileProperties) selectSource(ctx android.BottomUpMutatorContext) error {
+ // This is called before prebuilt_select and prebuilt_postdeps mutators
+ // The mutators requires that src to be set correctly for each arch so that
+ // arch variants are disabled when src is not provided for the arch.
+ if len(ctx.MultiTargets()) != 1 {
+ return fmt.Errorf("compile_multilib shouldn't be \"both\" for prebuilt_apex")
+ }
+ var src string
+ switch ctx.MultiTargets()[0].Arch.ArchType {
+ case android.Arm:
+ src = String(p.Arch.Arm.Src)
+ case android.Arm64:
+ src = String(p.Arch.Arm64.Src)
+ case android.X86:
+ src = String(p.Arch.X86.Src)
+ case android.X86_64:
+ src = String(p.Arch.X86_64.Src)
+ default:
+ return fmt.Errorf("prebuilt_apex does not support %q", ctx.MultiTargets()[0].Arch.String())
+ }
+ if src == "" {
+ src = String(p.Src)
+ }
+ p.Source = src
+
+ return nil
+}
+
+type PrebuiltProperties struct {
+ ApexFileProperties
Installable *bool
// Optional name for the installed apex. If unspecified, name of the
@@ -175,31 +207,10 @@
}
func (p *Prebuilt) DepsMutator(ctx android.BottomUpMutatorContext) {
- // This is called before prebuilt_select and prebuilt_postdeps mutators
- // The mutators requires that src to be set correctly for each arch so that
- // arch variants are disabled when src is not provided for the arch.
- if len(ctx.MultiTargets()) != 1 {
- ctx.ModuleErrorf("compile_multilib shouldn't be \"both\" for prebuilt_apex")
+ if err := p.properties.selectSource(ctx); err != nil {
+ ctx.ModuleErrorf("%s", err)
return
}
- var src string
- switch ctx.MultiTargets()[0].Arch.ArchType {
- case android.Arm:
- src = String(p.properties.Arch.Arm.Src)
- case android.Arm64:
- src = String(p.properties.Arch.Arm64.Src)
- case android.X86:
- src = String(p.properties.Arch.X86.Src)
- case android.X86_64:
- src = String(p.properties.Arch.X86_64.Src)
- default:
- ctx.ModuleErrorf("prebuilt_apex does not support %q", ctx.MultiTargets()[0].Arch.String())
- return
- }
- if src == "" {
- src = String(p.properties.Src)
- }
- p.properties.Source = src
}
func (p *Prebuilt) GenerateAndroidBuildActions(ctx android.ModuleContext) {
diff --git a/bazel/aquery.go b/bazel/aquery.go
index 69d4fde..404be8c 100644
--- a/bazel/aquery.go
+++ b/bazel/aquery.go
@@ -16,6 +16,8 @@
import (
"encoding/json"
+ "fmt"
+ "path/filepath"
"strings"
"github.com/google/blueprint/proptools"
@@ -24,8 +26,14 @@
// artifact contains relevant portions of Bazel's aquery proto, Artifact.
// Represents a single artifact, whether it's a source file or a derived output file.
type artifact struct {
- Id string
- ExecPath string
+ Id int
+ PathFragmentId int
+}
+
+type pathFragment struct {
+ Id int
+ Label string
+ ParentId int
}
// KeyValuePair represents Bazel's aquery proto, KeyValuePair.
@@ -38,9 +46,9 @@
// Represents a data structure containing one or more files. Depsets in Bazel are an efficient
// data structure for storing large numbers of file paths.
type depSetOfFiles struct {
- Id string
+ Id int
// TODO(cparsons): Handle non-flat depsets.
- DirectArtifactIds []string
+ DirectArtifactIds []int
}
// action contains relevant portions of Bazel's aquery proto, Action.
@@ -48,9 +56,9 @@
type action struct {
Arguments []string
EnvironmentVariables []KeyValuePair
- InputDepSetIds []string
+ InputDepSetIds []int
Mnemonic string
- OutputIds []string
+ OutputIds []int
}
// actionGraphContainer contains relevant portions of Bazel's aquery proto, ActionGraphContainer.
@@ -59,6 +67,7 @@
Artifacts []artifact
Actions []action
DepSetOfFiles []depSetOfFiles
+ PathFragments []pathFragment
}
// BuildStatement contains information to register a build statement corresponding (one to one)
@@ -80,11 +89,20 @@
var aqueryResult actionGraphContainer
json.Unmarshal(aqueryJsonProto, &aqueryResult)
- artifactIdToPath := map[string]string{}
- for _, artifact := range aqueryResult.Artifacts {
- artifactIdToPath[artifact.Id] = artifact.ExecPath
+ pathFragments := map[int]pathFragment{}
+ for _, pathFragment := range aqueryResult.PathFragments {
+ pathFragments[pathFragment.Id] = pathFragment
}
- depsetIdToArtifactIds := map[string][]string{}
+ artifactIdToPath := map[int]string{}
+ for _, artifact := range aqueryResult.Artifacts {
+ artifactPath, err := expandPathFragment(artifact.PathFragmentId, pathFragments)
+ if err != nil {
+ // TODO(cparsons): Better error handling.
+ panic(err.Error())
+ }
+ artifactIdToPath[artifact.Id] = artifactPath
+ }
+ depsetIdToArtifactIds := map[int][]int{}
for _, depset := range aqueryResult.DepSetOfFiles {
depsetIdToArtifactIds[depset.Id] = depset.DirectArtifactIds
}
@@ -114,3 +132,18 @@
return buildStatements
}
+
+func expandPathFragment(id int, pathFragmentsMap map[int]pathFragment) (string, error) {
+ labels := []string{}
+ currId := id
+ // Only positive IDs are valid for path fragments. An ID of zero indicates a terminal node.
+ for currId > 0 {
+ currFragment, ok := pathFragmentsMap[currId]
+ if !ok {
+ return "", fmt.Errorf("undefined path fragment id '%s'", currId)
+ }
+ labels = append([]string{currFragment.Label}, labels...)
+ currId = currFragment.ParentId
+ }
+ return filepath.Join(labels...), nil
+}
diff --git a/bp2build/Android.bp b/bp2build/Android.bp
new file mode 100644
index 0000000..49587f4
--- /dev/null
+++ b/bp2build/Android.bp
@@ -0,0 +1,23 @@
+bootstrap_go_package {
+ name: "soong-bp2build",
+ pkgPath: "android/soong/bp2build",
+ srcs: [
+ "androidbp_to_build_templates.go",
+ "bp2build.go",
+ "build_conversion.go",
+ "bzl_conversion.go",
+ "conversion.go",
+ ],
+ deps: [
+ "soong-android",
+ ],
+ testSrcs: [
+ "build_conversion_test.go",
+ "bzl_conversion_test.go",
+ "conversion_test.go",
+ "testing.go",
+ ],
+ pluginFor: [
+ "soong_build",
+ ],
+}
diff --git a/cmd/soong_build/queryview_templates.go b/bp2build/androidbp_to_build_templates.go
similarity index 98%
rename from cmd/soong_build/queryview_templates.go
rename to bp2build/androidbp_to_build_templates.go
index 359c0d8..75c3ccb 100644
--- a/cmd/soong_build/queryview_templates.go
+++ b/bp2build/androidbp_to_build_templates.go
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package main
+package bp2build
const (
// The default `load` preamble for every generated BUILD file.
diff --git a/bp2build/bp2build.go b/bp2build/bp2build.go
new file mode 100644
index 0000000..30f298a
--- /dev/null
+++ b/bp2build/bp2build.go
@@ -0,0 +1,77 @@
+// Copyright 2020 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 bp2build
+
+import (
+ "android/soong/android"
+ "os"
+)
+
+// The Bazel bp2build singleton is responsible for writing .bzl files that are equivalent to
+// Android.bp files that are capable of being built with Bazel.
+func init() {
+ android.RegisterBazelConverterPreSingletonType("androidbp_to_build", AndroidBpToBuildSingleton)
+}
+
+func AndroidBpToBuildSingleton() android.Singleton {
+ return &androidBpToBuildSingleton{
+ name: "bp2build",
+ }
+}
+
+type androidBpToBuildSingleton struct {
+ name string
+ outputDir android.OutputPath
+}
+
+func (s *androidBpToBuildSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+ s.outputDir = android.PathForOutput(ctx, s.name)
+ android.RemoveAllOutputDir(s.outputDir)
+
+ if !ctx.Config().IsEnvTrue("CONVERT_TO_BAZEL") {
+ return
+ }
+
+ ruleShims := CreateRuleShims(android.ModuleTypeFactories())
+
+ buildToTargets := GenerateSoongModuleTargets(ctx)
+
+ filesToWrite := CreateBazelFiles(ruleShims, buildToTargets)
+ for _, f := range filesToWrite {
+ if err := s.writeFile(ctx, f); err != nil {
+ ctx.Errorf("Failed to write %q (dir %q) due to %q", f.Basename, f.Dir, err)
+ }
+ }
+}
+
+func (s *androidBpToBuildSingleton) getOutputPath(ctx android.PathContext, dir string) android.OutputPath {
+ return s.outputDir.Join(ctx, dir)
+}
+
+func (s *androidBpToBuildSingleton) writeFile(ctx android.PathContext, f BazelFile) error {
+ return writeReadOnlyFile(ctx, s.getOutputPath(ctx, f.Dir), f.Basename, f.Contents)
+}
+
+// The auto-conversion directory should be read-only, sufficient for bazel query. The files
+// are not intended to be edited by end users.
+func writeReadOnlyFile(ctx android.PathContext, dir android.OutputPath, baseName, content string) error {
+ android.CreateOutputDirIfNonexistent(dir, os.ModePerm)
+ pathToFile := dir.Join(ctx, baseName)
+
+ // 0444 is read-only
+ err := android.WriteFileToOutputDir(pathToFile, []byte(content), 0444)
+
+ return err
+}
diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go
new file mode 100644
index 0000000..0329685
--- /dev/null
+++ b/bp2build/build_conversion.go
@@ -0,0 +1,305 @@
+// Copyright 2020 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 bp2build
+
+import (
+ "android/soong/android"
+ "fmt"
+ "reflect"
+ "strings"
+
+ "github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
+)
+
+type BazelAttributes struct {
+ Attrs map[string]string
+}
+
+type BazelTarget struct {
+ name string
+ content string
+}
+
+type bpToBuildContext interface {
+ ModuleName(module blueprint.Module) string
+ ModuleDir(module blueprint.Module) string
+ ModuleSubDir(module blueprint.Module) string
+ ModuleType(module blueprint.Module) string
+
+ VisitAllModulesBlueprint(visit func(blueprint.Module))
+ VisitDirectDeps(module android.Module, visit func(android.Module))
+}
+
+// props is an unsorted map. This function ensures that
+// the generated attributes are sorted to ensure determinism.
+func propsToAttributes(props map[string]string) string {
+ var attributes string
+ for _, propName := range android.SortedStringKeys(props) {
+ if shouldGenerateAttribute(propName) {
+ attributes += fmt.Sprintf(" %s = %s,\n", propName, props[propName])
+ }
+ }
+ return attributes
+}
+
+func GenerateSoongModuleTargets(ctx bpToBuildContext) map[string][]BazelTarget {
+ buildFileToTargets := make(map[string][]BazelTarget)
+ ctx.VisitAllModulesBlueprint(func(m blueprint.Module) {
+ dir := ctx.ModuleDir(m)
+ t := generateSoongModuleTarget(ctx, m)
+ buildFileToTargets[ctx.ModuleDir(m)] = append(buildFileToTargets[dir], t)
+ })
+ return buildFileToTargets
+}
+
+// Convert a module and its deps and props into a Bazel macro/rule
+// representation in the BUILD file.
+func generateSoongModuleTarget(ctx bpToBuildContext, m blueprint.Module) BazelTarget {
+ props := getBuildProperties(ctx, m)
+
+ // TODO(b/163018919): DirectDeps can have duplicate (module, variant)
+ // items, if the modules are added using different DependencyTag. Figure
+ // out the implications of that.
+ depLabels := map[string]bool{}
+ if aModule, ok := m.(android.Module); ok {
+ ctx.VisitDirectDeps(aModule, func(depModule android.Module) {
+ depLabels[qualifiedTargetLabel(ctx, depModule)] = true
+ })
+ }
+ attributes := propsToAttributes(props.Attrs)
+
+ depLabelList := "[\n"
+ for depLabel, _ := range depLabels {
+ depLabelList += fmt.Sprintf(" %q,\n", depLabel)
+ }
+ depLabelList += " ]"
+
+ targetName := targetNameWithVariant(ctx, m)
+ return BazelTarget{
+ name: targetName,
+ content: fmt.Sprintf(
+ soongModuleTarget,
+ targetName,
+ ctx.ModuleName(m),
+ canonicalizeModuleType(ctx.ModuleType(m)),
+ ctx.ModuleSubDir(m),
+ depLabelList,
+ attributes),
+ }
+}
+
+func getBuildProperties(ctx bpToBuildContext, m blueprint.Module) BazelAttributes {
+ var allProps map[string]string
+ // TODO: this omits properties for blueprint modules (blueprint_go_binary,
+ // bootstrap_go_binary, bootstrap_go_package), which will have to be handled separately.
+ if aModule, ok := m.(android.Module); ok {
+ allProps = ExtractModuleProperties(aModule)
+ }
+
+ return BazelAttributes{
+ Attrs: allProps,
+ }
+}
+
+// Generically extract module properties and types into a map, keyed by the module property name.
+func ExtractModuleProperties(aModule android.Module) map[string]string {
+ ret := map[string]string{}
+
+ // Iterate over this android.Module's property structs.
+ for _, properties := range aModule.GetProperties() {
+ propertiesValue := reflect.ValueOf(properties)
+ // Check that propertiesValue is a pointer to the Properties struct, like
+ // *cc.BaseLinkerProperties or *java.CompilerProperties.
+ //
+ // propertiesValue can also be type-asserted to the structs to
+ // manipulate internal props, if needed.
+ if isStructPtr(propertiesValue.Type()) {
+ structValue := propertiesValue.Elem()
+ for k, v := range extractStructProperties(structValue, 0) {
+ ret[k] = v
+ }
+ } else {
+ panic(fmt.Errorf(
+ "properties must be a pointer to a struct, got %T",
+ propertiesValue.Interface()))
+ }
+ }
+
+ return ret
+}
+
+func isStructPtr(t reflect.Type) bool {
+ return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct
+}
+
+// prettyPrint a property value into the equivalent Starlark representation
+// recursively.
+func prettyPrint(propertyValue reflect.Value, indent int) (string, error) {
+ if isZero(propertyValue) {
+ // A property value being set or unset actually matters -- Soong does set default
+ // values for unset properties, like system_shared_libs = ["libc", "libm", "libdl"] at
+ // https://cs.android.com/android/platform/superproject/+/master:build/soong/cc/linker.go;l=281-287;drc=f70926eef0b9b57faf04c17a1062ce50d209e480
+ //
+ // In Bazel-parlance, we would use "attr.<type>(default = <default value>)" to set the default
+ // value of unset attributes.
+ return "", nil
+ }
+
+ var ret string
+ switch propertyValue.Kind() {
+ case reflect.String:
+ ret = fmt.Sprintf("\"%v\"", escapeString(propertyValue.String()))
+ case reflect.Bool:
+ ret = strings.Title(fmt.Sprintf("%v", propertyValue.Interface()))
+ case reflect.Int, reflect.Uint, reflect.Int64:
+ ret = fmt.Sprintf("%v", propertyValue.Interface())
+ case reflect.Ptr:
+ return prettyPrint(propertyValue.Elem(), indent)
+ case reflect.Slice:
+ ret = "[\n"
+ for i := 0; i < propertyValue.Len(); i++ {
+ indexedValue, err := prettyPrint(propertyValue.Index(i), indent+1)
+ if err != nil {
+ return "", err
+ }
+
+ if indexedValue != "" {
+ ret += makeIndent(indent + 1)
+ ret += indexedValue
+ ret += ",\n"
+ }
+ }
+ ret += makeIndent(indent)
+ ret += "]"
+ case reflect.Struct:
+ ret = "{\n"
+ // Sort and print the struct props by the key.
+ structProps := extractStructProperties(propertyValue, indent)
+ for _, k := range android.SortedStringKeys(structProps) {
+ ret += makeIndent(indent + 1)
+ ret += fmt.Sprintf("%q: %s,\n", k, structProps[k])
+ }
+ ret += makeIndent(indent)
+ ret += "}"
+ case reflect.Interface:
+ // TODO(b/164227191): implement pretty print for interfaces.
+ // Interfaces are used for for arch, multilib and target properties.
+ return "", nil
+ default:
+ return "", fmt.Errorf(
+ "unexpected kind for property struct field: %s", propertyValue.Kind())
+ }
+ return ret, nil
+}
+
+// Converts a reflected property struct value into a map of property names and property values,
+// which each property value correctly pretty-printed and indented at the right nest level,
+// since property structs can be nested. In Starlark, nested structs are represented as nested
+// dicts: https://docs.bazel.build/skylark/lib/dict.html
+func extractStructProperties(structValue reflect.Value, indent int) map[string]string {
+ if structValue.Kind() != reflect.Struct {
+ panic(fmt.Errorf("Expected a reflect.Struct type, but got %s", structValue.Kind()))
+ }
+
+ ret := map[string]string{}
+ structType := structValue.Type()
+ for i := 0; i < structValue.NumField(); i++ {
+ field := structType.Field(i)
+ if shouldSkipStructField(field) {
+ continue
+ }
+
+ fieldValue := structValue.Field(i)
+ if isZero(fieldValue) {
+ // Ignore zero-valued fields
+ continue
+ }
+
+ propertyName := proptools.PropertyNameForField(field.Name)
+ prettyPrintedValue, err := prettyPrint(fieldValue, indent+1)
+ if err != nil {
+ panic(
+ fmt.Errorf(
+ "Error while parsing property: %q. %s",
+ propertyName,
+ err))
+ }
+ if prettyPrintedValue != "" {
+ ret[propertyName] = prettyPrintedValue
+ }
+ }
+
+ return ret
+}
+
+func isZero(value reflect.Value) bool {
+ switch value.Kind() {
+ case reflect.Func, reflect.Map, reflect.Slice:
+ return value.IsNil()
+ case reflect.Array:
+ valueIsZero := true
+ for i := 0; i < value.Len(); i++ {
+ valueIsZero = valueIsZero && isZero(value.Index(i))
+ }
+ return valueIsZero
+ case reflect.Struct:
+ valueIsZero := true
+ for i := 0; i < value.NumField(); i++ {
+ if value.Field(i).CanSet() {
+ valueIsZero = valueIsZero && isZero(value.Field(i))
+ }
+ }
+ return valueIsZero
+ case reflect.Ptr:
+ if !value.IsNil() {
+ return isZero(reflect.Indirect(value))
+ } else {
+ return true
+ }
+ default:
+ zeroValue := reflect.Zero(value.Type())
+ result := value.Interface() == zeroValue.Interface()
+ return result
+ }
+}
+
+func escapeString(s string) string {
+ s = strings.ReplaceAll(s, "\\", "\\\\")
+ return strings.ReplaceAll(s, "\"", "\\\"")
+}
+
+func makeIndent(indent int) string {
+ if indent < 0 {
+ panic(fmt.Errorf("indent column cannot be less than 0, but got %d", indent))
+ }
+ return strings.Repeat(" ", indent)
+}
+
+func targetNameWithVariant(c bpToBuildContext, logicModule blueprint.Module) string {
+ name := ""
+ if c.ModuleSubDir(logicModule) != "" {
+ // TODO(b/162720883): Figure out a way to drop the "--" variant suffixes.
+ name = c.ModuleName(logicModule) + "--" + c.ModuleSubDir(logicModule)
+ } else {
+ name = c.ModuleName(logicModule)
+ }
+
+ return strings.Replace(name, "//", "", 1)
+}
+
+func qualifiedTargetLabel(c bpToBuildContext, logicModule blueprint.Module) string {
+ return fmt.Sprintf("//%s:%s", c.ModuleDir(logicModule), targetNameWithVariant(c, logicModule))
+}
diff --git a/bp2build/build_conversion_test.go b/bp2build/build_conversion_test.go
new file mode 100644
index 0000000..8230ad8
--- /dev/null
+++ b/bp2build/build_conversion_test.go
@@ -0,0 +1,221 @@
+// Copyright 2020 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 bp2build
+
+import (
+ "android/soong/android"
+ "testing"
+)
+
+func TestGenerateSoongModuleTargets(t *testing.T) {
+ testCases := []struct {
+ bp string
+ expectedBazelTarget string
+ }{
+ {
+ bp: `custom {
+ name: "foo",
+}
+ `,
+ expectedBazelTarget: `soong_module(
+ name = "foo",
+ module_name = "foo",
+ module_type = "custom",
+ module_variant = "",
+ module_deps = [
+ ],
+)`,
+ },
+ {
+ bp: `custom {
+ name: "foo",
+ ramdisk: true,
+}
+ `,
+ expectedBazelTarget: `soong_module(
+ name = "foo",
+ module_name = "foo",
+ module_type = "custom",
+ module_variant = "",
+ module_deps = [
+ ],
+ ramdisk = True,
+)`,
+ },
+ {
+ bp: `custom {
+ name: "foo",
+ owner: "a_string_with\"quotes\"_and_\\backslashes\\\\",
+}
+ `,
+ expectedBazelTarget: `soong_module(
+ name = "foo",
+ module_name = "foo",
+ module_type = "custom",
+ module_variant = "",
+ module_deps = [
+ ],
+ owner = "a_string_with\"quotes\"_and_\\backslashes\\\\",
+)`,
+ },
+ {
+ bp: `custom {
+ name: "foo",
+ required: ["bar"],
+}
+ `,
+ expectedBazelTarget: `soong_module(
+ name = "foo",
+ module_name = "foo",
+ module_type = "custom",
+ module_variant = "",
+ module_deps = [
+ ],
+ required = [
+ "bar",
+ ],
+)`,
+ },
+ {
+ bp: `custom {
+ name: "foo",
+ target_required: ["qux", "bazqux"],
+}
+ `,
+ expectedBazelTarget: `soong_module(
+ name = "foo",
+ module_name = "foo",
+ module_type = "custom",
+ module_variant = "",
+ module_deps = [
+ ],
+ target_required = [
+ "qux",
+ "bazqux",
+ ],
+)`,
+ },
+ {
+ bp: `custom {
+ name: "foo",
+ dist: {
+ targets: ["goal_foo"],
+ tag: ".foo",
+ },
+ dists: [
+ {
+ targets: ["goal_bar"],
+ tag: ".bar",
+ },
+ ],
+}
+ `,
+ expectedBazelTarget: `soong_module(
+ name = "foo",
+ module_name = "foo",
+ module_type = "custom",
+ module_variant = "",
+ module_deps = [
+ ],
+ dist = {
+ "tag": ".foo",
+ "targets": [
+ "goal_foo",
+ ],
+ },
+ dists = [
+ {
+ "tag": ".bar",
+ "targets": [
+ "goal_bar",
+ ],
+ },
+ ],
+)`,
+ },
+ {
+ bp: `custom {
+ name: "foo",
+ required: ["bar"],
+ target_required: ["qux", "bazqux"],
+ ramdisk: true,
+ owner: "custom_owner",
+ dists: [
+ {
+ tag: ".tag",
+ targets: ["my_goal"],
+ },
+ ],
+}
+ `,
+ expectedBazelTarget: `soong_module(
+ name = "foo",
+ module_name = "foo",
+ module_type = "custom",
+ module_variant = "",
+ module_deps = [
+ ],
+ dists = [
+ {
+ "tag": ".tag",
+ "targets": [
+ "my_goal",
+ ],
+ },
+ ],
+ owner = "custom_owner",
+ ramdisk = True,
+ required = [
+ "bar",
+ ],
+ target_required = [
+ "qux",
+ "bazqux",
+ ],
+)`,
+ },
+ }
+
+ dir := "."
+ for _, testCase := range testCases {
+ config := android.TestConfig(buildDir, nil, testCase.bp, nil)
+ ctx := android.NewTestContext(config)
+ ctx.RegisterModuleType("custom", customModuleFactory)
+ ctx.Register()
+
+ _, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
+ android.FailIfErrored(t, errs)
+ _, errs = ctx.PrepareBuildActions(config)
+ android.FailIfErrored(t, errs)
+
+ bp2BuildCtx := bp2buildBlueprintWrapContext{
+ bpCtx: ctx.Context.Context,
+ }
+
+ bazelTargets := GenerateSoongModuleTargets(&bp2BuildCtx)[dir]
+ if g, w := len(bazelTargets), 1; g != w {
+ t.Fatalf("Expected %d bazel target, got %d", w, g)
+ }
+
+ actualBazelTarget := bazelTargets[0]
+ if actualBazelTarget.content != testCase.expectedBazelTarget {
+ t.Errorf(
+ "Expected generated Bazel target to be '%s', got '%s'",
+ testCase.expectedBazelTarget,
+ actualBazelTarget,
+ )
+ }
+ }
+}
diff --git a/bp2build/bzl_conversion.go b/bp2build/bzl_conversion.go
new file mode 100644
index 0000000..04c4542
--- /dev/null
+++ b/bp2build/bzl_conversion.go
@@ -0,0 +1,230 @@
+// Copyright 2020 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 bp2build
+
+import (
+ "android/soong/android"
+ "fmt"
+ "reflect"
+ "runtime"
+ "sort"
+ "strings"
+
+ "github.com/google/blueprint/proptools"
+)
+
+var (
+ // An allowlist of prop types that are surfaced from module props to rule
+ // attributes. (nested) dictionaries are notably absent here, because while
+ // Soong supports multi value typed and nested dictionaries, Bazel's rule
+ // attr() API supports only single-level string_dicts.
+ allowedPropTypes = map[string]bool{
+ "int": true, // e.g. 42
+ "bool": true, // e.g. True
+ "string_list": true, // e.g. ["a", "b"]
+ "string": true, // e.g. "a"
+ }
+)
+
+type rule struct {
+ name string
+ attrs string
+}
+
+type RuleShim struct {
+ // The rule class shims contained in a bzl file. e.g. ["cc_object", "cc_library", ..]
+ rules []string
+
+ // The generated string content of the bzl file.
+ content string
+}
+
+// Create <module>.bzl containing Bazel rule shims for every module type available in Soong and
+// user-specified Go plugins.
+//
+// This function reuses documentation generation APIs to ensure parity between modules-as-docs
+// and modules-as-code, including the names and types of morule properties.
+func CreateRuleShims(moduleTypeFactories map[string]android.ModuleFactory) map[string]RuleShim {
+ ruleShims := map[string]RuleShim{}
+ for pkg, rules := range generateRules(moduleTypeFactories) {
+ shim := RuleShim{
+ rules: make([]string, 0, len(rules)),
+ }
+ shim.content = "load(\"//build/bazel/queryview_rules:providers.bzl\", \"SoongModuleInfo\")\n"
+
+ bzlFileName := strings.ReplaceAll(pkg, "android/soong/", "")
+ bzlFileName = strings.ReplaceAll(bzlFileName, ".", "_")
+ bzlFileName = strings.ReplaceAll(bzlFileName, "/", "_")
+
+ for _, r := range rules {
+ shim.content += fmt.Sprintf(moduleRuleShim, r.name, r.attrs)
+ shim.rules = append(shim.rules, r.name)
+ }
+ sort.Strings(shim.rules)
+ ruleShims[bzlFileName] = shim
+ }
+ return ruleShims
+}
+
+// Generate the content of soong_module.bzl with the rule shim load statements
+// and mapping of module_type to rule shim map for every module type in Soong.
+func generateSoongModuleBzl(bzlLoads map[string]RuleShim) string {
+ var loadStmts string
+ var moduleRuleMap string
+ for _, bzlFileName := range android.SortedStringKeys(bzlLoads) {
+ loadStmt := "load(\"//build/bazel/queryview_rules:"
+ loadStmt += bzlFileName
+ loadStmt += ".bzl\""
+ ruleShim := bzlLoads[bzlFileName]
+ for _, rule := range ruleShim.rules {
+ loadStmt += fmt.Sprintf(", %q", rule)
+ moduleRuleMap += " \"" + rule + "\": " + rule + ",\n"
+ }
+ loadStmt += ")\n"
+ loadStmts += loadStmt
+ }
+
+ return fmt.Sprintf(soongModuleBzl, loadStmts, moduleRuleMap)
+}
+
+func generateRules(moduleTypeFactories map[string]android.ModuleFactory) map[string][]rule {
+ // TODO: add shims for bootstrap/blueprint go modules types
+
+ rules := make(map[string][]rule)
+ // TODO: allow registration of a bzl rule when registring a factory
+ for _, moduleType := range android.SortedStringKeys(moduleTypeFactories) {
+ factory := moduleTypeFactories[moduleType]
+ factoryName := runtime.FuncForPC(reflect.ValueOf(factory).Pointer()).Name()
+ pkg := strings.Split(factoryName, ".")[0]
+ attrs := `{
+ "module_name": attr.string(mandatory = True),
+ "module_variant": attr.string(),
+ "module_deps": attr.label_list(providers = [SoongModuleInfo]),
+`
+ attrs += getAttributes(factory)
+ attrs += " },"
+
+ r := rule{
+ name: canonicalizeModuleType(moduleType),
+ attrs: attrs,
+ }
+
+ rules[pkg] = append(rules[pkg], r)
+ }
+ return rules
+}
+
+type property struct {
+ name string
+ starlarkAttrType string
+ properties []property
+}
+
+const (
+ attributeIndent = " "
+)
+
+func (p *property) attributeString() string {
+ if !shouldGenerateAttribute(p.name) {
+ return ""
+ }
+
+ if _, ok := allowedPropTypes[p.starlarkAttrType]; !ok {
+ // a struct -- let's just comment out sub-props
+ s := fmt.Sprintf(attributeIndent+"# %s start\n", p.name)
+ for _, nestedP := range p.properties {
+ s += "# " + nestedP.attributeString()
+ }
+ s += fmt.Sprintf(attributeIndent+"# %s end\n", p.name)
+ return s
+ }
+ return fmt.Sprintf(attributeIndent+"%q: attr.%s(),\n", p.name, p.starlarkAttrType)
+}
+
+func extractPropertyDescriptionsFromStruct(structType reflect.Type) []property {
+ properties := make([]property, 0)
+ for i := 0; i < structType.NumField(); i++ {
+ field := structType.Field(i)
+ if shouldSkipStructField(field) {
+ continue
+ }
+
+ properties = append(properties, extractPropertyDescriptions(field.Name, field.Type)...)
+ }
+ return properties
+}
+
+func extractPropertyDescriptions(name string, t reflect.Type) []property {
+ name = proptools.PropertyNameForField(name)
+
+ // TODO: handle android:paths tags, they should be changed to label types
+
+ starlarkAttrType := fmt.Sprintf("%s", t.Name())
+ props := make([]property, 0)
+
+ switch t.Kind() {
+ case reflect.Bool, reflect.String:
+ // do nothing
+ case reflect.Uint, reflect.Int, reflect.Int64:
+ starlarkAttrType = "int"
+ case reflect.Slice:
+ if t.Elem().Kind() != reflect.String {
+ // TODO: handle lists of non-strings (currently only list of Dist)
+ return []property{}
+ }
+ starlarkAttrType = "string_list"
+ case reflect.Struct:
+ props = extractPropertyDescriptionsFromStruct(t)
+ case reflect.Ptr:
+ return extractPropertyDescriptions(name, t.Elem())
+ case reflect.Interface:
+ // Interfaces are used for for arch, multilib and target properties, which are handled at runtime.
+ // These will need to be handled in a bazel-specific version of the arch mutator.
+ return []property{}
+ }
+
+ prop := property{
+ name: name,
+ starlarkAttrType: starlarkAttrType,
+ properties: props,
+ }
+
+ return []property{prop}
+}
+
+func getPropertyDescriptions(props []interface{}) []property {
+ // there may be duplicate properties, e.g. from defaults libraries
+ propertiesByName := make(map[string]property)
+ for _, p := range props {
+ for _, prop := range extractPropertyDescriptionsFromStruct(reflect.ValueOf(p).Elem().Type()) {
+ propertiesByName[prop.name] = prop
+ }
+ }
+
+ properties := make([]property, 0, len(propertiesByName))
+ for _, key := range android.SortedStringKeys(propertiesByName) {
+ properties = append(properties, propertiesByName[key])
+ }
+
+ return properties
+}
+
+func getAttributes(factory android.ModuleFactory) string {
+ attrs := ""
+ for _, p := range getPropertyDescriptions(factory().GetProperties()) {
+ attrs += p.attributeString()
+ }
+ return attrs
+}
diff --git a/bp2build/bzl_conversion_test.go b/bp2build/bzl_conversion_test.go
new file mode 100644
index 0000000..8bea3f6
--- /dev/null
+++ b/bp2build/bzl_conversion_test.go
@@ -0,0 +1,208 @@
+// Copyright 2020 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 bp2build
+
+import (
+ "android/soong/android"
+ "io/ioutil"
+ "os"
+ "strings"
+ "testing"
+)
+
+var buildDir string
+
+func setUp() {
+ var err error
+ buildDir, err = ioutil.TempDir("", "bazel_queryview_test")
+ if err != nil {
+ panic(err)
+ }
+}
+
+func tearDown() {
+ os.RemoveAll(buildDir)
+}
+
+func TestMain(m *testing.M) {
+ run := func() int {
+ setUp()
+ defer tearDown()
+
+ return m.Run()
+ }
+
+ os.Exit(run())
+}
+
+func TestGenerateModuleRuleShims(t *testing.T) {
+ moduleTypeFactories := map[string]android.ModuleFactory{
+ "custom": customModuleFactoryBase,
+ "custom_test": customTestModuleFactoryBase,
+ "custom_defaults": customDefaultsModuleFactoryBasic,
+ }
+ ruleShims := CreateRuleShims(moduleTypeFactories)
+
+ if len(ruleShims) != 1 {
+ t.Errorf("Expected to generate 1 rule shim, but got %d", len(ruleShims))
+ }
+
+ ruleShim := ruleShims["bp2build"]
+ expectedRules := []string{
+ "custom",
+ "custom_defaults",
+ "custom_test_",
+ }
+
+ if len(ruleShim.rules) != len(expectedRules) {
+ t.Errorf("Expected %d rules, but got %d", len(expectedRules), len(ruleShim.rules))
+ }
+
+ for i, rule := range ruleShim.rules {
+ if rule != expectedRules[i] {
+ t.Errorf("Expected rule shim to contain %s, but got %s", expectedRules[i], rule)
+ }
+ }
+ expectedBzl := `load("//build/bazel/queryview_rules:providers.bzl", "SoongModuleInfo")
+
+def _custom_impl(ctx):
+ return [SoongModuleInfo()]
+
+custom = rule(
+ implementation = _custom_impl,
+ attrs = {
+ "module_name": attr.string(mandatory = True),
+ "module_variant": attr.string(),
+ "module_deps": attr.label_list(providers = [SoongModuleInfo]),
+ "bool_prop": attr.bool(),
+ "bool_ptr_prop": attr.bool(),
+ "int64_ptr_prop": attr.int(),
+ # nested_props start
+# "nested_prop": attr.string(),
+ # nested_props end
+ # nested_props_ptr start
+# "nested_prop": attr.string(),
+ # nested_props_ptr end
+ "string_list_prop": attr.string_list(),
+ "string_prop": attr.string(),
+ "string_ptr_prop": attr.string(),
+ },
+)
+
+def _custom_defaults_impl(ctx):
+ return [SoongModuleInfo()]
+
+custom_defaults = rule(
+ implementation = _custom_defaults_impl,
+ attrs = {
+ "module_name": attr.string(mandatory = True),
+ "module_variant": attr.string(),
+ "module_deps": attr.label_list(providers = [SoongModuleInfo]),
+ "bool_prop": attr.bool(),
+ "bool_ptr_prop": attr.bool(),
+ "int64_ptr_prop": attr.int(),
+ # nested_props start
+# "nested_prop": attr.string(),
+ # nested_props end
+ # nested_props_ptr start
+# "nested_prop": attr.string(),
+ # nested_props_ptr end
+ "string_list_prop": attr.string_list(),
+ "string_prop": attr.string(),
+ "string_ptr_prop": attr.string(),
+ },
+)
+
+def _custom_test__impl(ctx):
+ return [SoongModuleInfo()]
+
+custom_test_ = rule(
+ implementation = _custom_test__impl,
+ attrs = {
+ "module_name": attr.string(mandatory = True),
+ "module_variant": attr.string(),
+ "module_deps": attr.label_list(providers = [SoongModuleInfo]),
+ "bool_prop": attr.bool(),
+ "bool_ptr_prop": attr.bool(),
+ "int64_ptr_prop": attr.int(),
+ # nested_props start
+# "nested_prop": attr.string(),
+ # nested_props end
+ # nested_props_ptr start
+# "nested_prop": attr.string(),
+ # nested_props_ptr end
+ "string_list_prop": attr.string_list(),
+ "string_prop": attr.string(),
+ "string_ptr_prop": attr.string(),
+ # test_prop start
+# "test_string_prop": attr.string(),
+ # test_prop end
+ },
+)
+`
+
+ if ruleShim.content != expectedBzl {
+ t.Errorf(
+ "Expected the generated rule shim bzl to be:\n%s\nbut got:\n%s",
+ expectedBzl,
+ ruleShim.content)
+ }
+}
+
+func TestGenerateSoongModuleBzl(t *testing.T) {
+ ruleShims := map[string]RuleShim{
+ "file1": RuleShim{
+ rules: []string{"a", "b"},
+ content: "irrelevant",
+ },
+ "file2": RuleShim{
+ rules: []string{"c", "d"},
+ content: "irrelevant",
+ },
+ }
+ files := CreateBazelFiles(ruleShims, make(map[string][]BazelTarget))
+
+ var actualSoongModuleBzl BazelFile
+ for _, f := range files {
+ if f.Basename == "soong_module.bzl" {
+ actualSoongModuleBzl = f
+ }
+ }
+
+ expectedLoad := `load("//build/bazel/queryview_rules:file1.bzl", "a", "b")
+load("//build/bazel/queryview_rules:file2.bzl", "c", "d")
+`
+ expectedRuleMap := `soong_module_rule_map = {
+ "a": a,
+ "b": b,
+ "c": c,
+ "d": d,
+}`
+ if !strings.Contains(actualSoongModuleBzl.Contents, expectedLoad) {
+ t.Errorf(
+ "Generated soong_module.bzl:\n\n%s\n\n"+
+ "Could not find the load statement in the generated soong_module.bzl:\n%s",
+ actualSoongModuleBzl.Contents,
+ expectedLoad)
+ }
+
+ if !strings.Contains(actualSoongModuleBzl.Contents, expectedRuleMap) {
+ t.Errorf(
+ "Generated soong_module.bzl:\n\n%s\n\n"+
+ "Could not find the module -> rule map in the generated soong_module.bzl:\n%s",
+ actualSoongModuleBzl.Contents,
+ expectedRuleMap)
+ }
+}
diff --git a/bp2build/conversion.go b/bp2build/conversion.go
new file mode 100644
index 0000000..cdfb38b
--- /dev/null
+++ b/bp2build/conversion.go
@@ -0,0 +1,118 @@
+package bp2build
+
+import (
+ "android/soong/android"
+ "reflect"
+ "sort"
+ "strings"
+
+ "github.com/google/blueprint/proptools"
+)
+
+type BazelFile struct {
+ Dir string
+ Basename string
+ Contents string
+}
+
+func CreateBazelFiles(
+ ruleShims map[string]RuleShim,
+ buildToTargets map[string][]BazelTarget) []BazelFile {
+ files := make([]BazelFile, 0, len(ruleShims)+len(buildToTargets)+numAdditionalFiles)
+
+ // Write top level files: WORKSPACE and BUILD. These files are empty.
+ files = append(files, newFile("", "WORKSPACE", ""))
+ // Used to denote that the top level directory is a package.
+ files = append(files, newFile("", "BUILD", ""))
+
+ files = append(files, newFile(bazelRulesSubDir, "BUILD", ""))
+ files = append(files, newFile(bazelRulesSubDir, "providers.bzl", providersBzl))
+
+ for bzlFileName, ruleShim := range ruleShims {
+ files = append(files, newFile(bazelRulesSubDir, bzlFileName+".bzl", ruleShim.content))
+ }
+ files = append(files, newFile(bazelRulesSubDir, "soong_module.bzl", generateSoongModuleBzl(ruleShims)))
+
+ files = append(files, createBuildFiles(buildToTargets)...)
+
+ return files
+}
+
+func createBuildFiles(buildToTargets map[string][]BazelTarget) []BazelFile {
+ files := make([]BazelFile, 0, len(buildToTargets))
+ for _, dir := range android.SortedStringKeys(buildToTargets) {
+ content := soongModuleLoad
+ targets := buildToTargets[dir]
+ sort.Slice(targets, func(i, j int) bool { return targets[i].name < targets[j].name })
+ for _, t := range targets {
+ content += "\n\n"
+ content += t.content
+ }
+ files = append(files, newFile(dir, "BUILD.bazel", content))
+ }
+ return files
+}
+
+func newFile(dir, basename, content string) BazelFile {
+ return BazelFile{
+ Dir: dir,
+ Basename: basename,
+ Contents: content,
+ }
+}
+
+const (
+ bazelRulesSubDir = "build/bazel/queryview_rules"
+
+ // additional files:
+ // * workspace file
+ // * base BUILD file
+ // * rules BUILD file
+ // * rules providers.bzl file
+ // * rules soong_module.bzl file
+ numAdditionalFiles = 5
+)
+
+var (
+ // Certain module property names are blocklisted/ignored here, for the reasons commented.
+ ignoredPropNames = map[string]bool{
+ "name": true, // redundant, since this is explicitly generated for every target
+ "from": true, // reserved keyword
+ "in": true, // reserved keyword
+ "arch": true, // interface prop type is not supported yet.
+ "multilib": true, // interface prop type is not supported yet.
+ "target": true, // interface prop type is not supported yet.
+ "visibility": true, // Bazel has native visibility semantics. Handle later.
+ "features": true, // There is already a built-in attribute 'features' which cannot be overridden.
+ }
+)
+
+func shouldGenerateAttribute(prop string) bool {
+ return !ignoredPropNames[prop]
+}
+
+func shouldSkipStructField(field reflect.StructField) bool {
+ if field.PkgPath != "" {
+ // Skip unexported fields. Some properties are
+ // internal to Soong only, and these fields do not have PkgPath.
+ return true
+ }
+ // fields with tag `blueprint:"mutated"` are exported to enable modification in mutators, etc
+ // but cannot be set in a .bp file
+ if proptools.HasTag(field, "blueprint", "mutated") {
+ return true
+ }
+ return false
+}
+
+// FIXME(b/168089390): In Bazel, rules ending with "_test" needs to be marked as
+// testonly = True, forcing other rules that depend on _test rules to also be
+// marked as testonly = True. This semantic constraint is not present in Soong.
+// To work around, rename "*_test" rules to "*_test_".
+func canonicalizeModuleType(moduleName string) string {
+ if strings.HasSuffix(moduleName, "_test") {
+ return moduleName + "_"
+ }
+
+ return moduleName
+}
diff --git a/bp2build/conversion_test.go b/bp2build/conversion_test.go
new file mode 100644
index 0000000..a38fa6a
--- /dev/null
+++ b/bp2build/conversion_test.go
@@ -0,0 +1,73 @@
+// Copyright 2020 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 bp2build
+
+import (
+ "sort"
+ "testing"
+)
+
+func TestCreateBazelFiles_AddsTopLevelFiles(t *testing.T) {
+ files := CreateBazelFiles(map[string]RuleShim{}, map[string][]BazelTarget{})
+ expectedFilePaths := []struct {
+ dir string
+ basename string
+ }{
+ {
+ dir: "",
+ basename: "BUILD",
+ },
+ {
+ dir: "",
+ basename: "WORKSPACE",
+ },
+ {
+ dir: bazelRulesSubDir,
+ basename: "BUILD",
+ },
+ {
+ dir: bazelRulesSubDir,
+ basename: "providers.bzl",
+ },
+ {
+ dir: bazelRulesSubDir,
+ basename: "soong_module.bzl",
+ },
+ }
+
+ if g, w := len(files), len(expectedFilePaths); g != w {
+ t.Errorf("Expected %d files, got %d", w, g)
+ }
+
+ sort.Slice(files, func(i, j int) bool {
+ if dir1, dir2 := files[i].Dir, files[j].Dir; dir1 == dir2 {
+ return files[i].Basename < files[j].Basename
+ } else {
+ return dir1 < dir2
+ }
+ })
+
+ for i := range files {
+ if g, w := files[i], expectedFilePaths[i]; g.Dir != w.dir || g.Basename != w.basename {
+ t.Errorf("Did not find expected file %s/%s", g.Dir, g.Basename)
+ } else if g.Basename == "BUILD" || g.Basename == "WORKSPACE" {
+ if g.Contents != "" {
+ t.Errorf("Expected %s to have no content.", g)
+ }
+ } else if g.Contents == "" {
+ t.Errorf("Contents of %s unexpected empty.", g)
+ }
+ }
+}
diff --git a/bp2build/testing.go b/bp2build/testing.go
new file mode 100644
index 0000000..160412d
--- /dev/null
+++ b/bp2build/testing.go
@@ -0,0 +1,136 @@
+package bp2build
+
+import (
+ "android/soong/android"
+
+ "github.com/google/blueprint"
+)
+
+type nestedProps struct {
+ Nested_prop string
+}
+
+type customProps struct {
+ Bool_prop bool
+ Bool_ptr_prop *bool
+ // Ensure that properties tagged `blueprint:mutated` are omitted
+ Int_prop int `blueprint:"mutated"`
+ Int64_ptr_prop *int64
+ String_prop string
+ String_ptr_prop *string
+ String_list_prop []string
+
+ Nested_props nestedProps
+ Nested_props_ptr *nestedProps
+}
+
+type customModule struct {
+ android.ModuleBase
+
+ props customProps
+}
+
+// OutputFiles is needed because some instances of this module use dist with a
+// tag property which requires the module implements OutputFileProducer.
+func (m *customModule) OutputFiles(tag string) (android.Paths, error) {
+ return android.PathsForTesting("path" + tag), nil
+}
+
+func (m *customModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ // nothing for now.
+}
+
+func customModuleFactoryBase() android.Module {
+ module := &customModule{}
+ module.AddProperties(&module.props)
+ return module
+}
+
+func customModuleFactory() android.Module {
+ m := customModuleFactoryBase()
+ android.InitAndroidModule(m)
+ return m
+}
+
+type testProps struct {
+ Test_prop struct {
+ Test_string_prop string
+ }
+}
+
+type customTestModule struct {
+ android.ModuleBase
+
+ props customProps
+ test_props testProps
+}
+
+func (m *customTestModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ // nothing for now.
+}
+
+func customTestModuleFactoryBase() android.Module {
+ m := &customTestModule{}
+ m.AddProperties(&m.props)
+ m.AddProperties(&m.test_props)
+ return m
+}
+
+func customTestModuleFactory() android.Module {
+ m := customTestModuleFactoryBase()
+ android.InitAndroidModule(m)
+ return m
+}
+
+type customDefaultsModule struct {
+ android.ModuleBase
+ android.DefaultsModuleBase
+}
+
+func customDefaultsModuleFactoryBase() android.DefaultsModule {
+ module := &customDefaultsModule{}
+ module.AddProperties(&customProps{})
+ return module
+}
+
+func customDefaultsModuleFactoryBasic() android.Module {
+ return customDefaultsModuleFactoryBase()
+}
+
+func customDefaultsModuleFactory() android.Module {
+ m := customDefaultsModuleFactoryBase()
+ android.InitDefaultsModule(m)
+ return m
+}
+
+type bp2buildBlueprintWrapContext struct {
+ bpCtx *blueprint.Context
+}
+
+func (ctx *bp2buildBlueprintWrapContext) ModuleName(module blueprint.Module) string {
+ return ctx.bpCtx.ModuleName(module)
+}
+
+func (ctx *bp2buildBlueprintWrapContext) ModuleDir(module blueprint.Module) string {
+ return ctx.bpCtx.ModuleDir(module)
+}
+
+func (ctx *bp2buildBlueprintWrapContext) ModuleSubDir(module blueprint.Module) string {
+ return ctx.bpCtx.ModuleSubDir(module)
+}
+
+func (ctx *bp2buildBlueprintWrapContext) ModuleType(module blueprint.Module) string {
+ return ctx.bpCtx.ModuleType(module)
+}
+
+func (ctx *bp2buildBlueprintWrapContext) VisitAllModulesBlueprint(visit func(blueprint.Module)) {
+ ctx.bpCtx.VisitAllModules(visit)
+}
+
+func (ctx *bp2buildBlueprintWrapContext) VisitDirectDeps(module android.Module, visit func(android.Module)) {
+ ctx.bpCtx.VisitDirectDeps(module, func(m blueprint.Module) {
+ if aModule, ok := m.(android.Module); ok {
+ visit(aModule)
+ }
+ })
+}
diff --git a/bpf/bpf.go b/bpf/bpf.go
index 297e13a..8142f10 100644
--- a/bpf/bpf.go
+++ b/bpf/bpf.go
@@ -120,6 +120,7 @@
names = append(names, objName)
fmt.Fprintln(w, "include $(CLEAR_VARS)")
fmt.Fprintln(w, "LOCAL_MODULE := ", objName)
+ data.Entries.WriteLicenseVariables(w)
fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", obj.String())
fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", obj.Base())
fmt.Fprintln(w, "LOCAL_MODULE_CLASS := ETC")
@@ -129,6 +130,7 @@
}
fmt.Fprintln(w, "include $(CLEAR_VARS)")
fmt.Fprintln(w, "LOCAL_MODULE := ", name)
+ data.Entries.WriteLicenseVariables(w)
fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES :=", strings.Join(names, " "))
fmt.Fprintln(w, "include $(BUILD_PHONY_PACKAGE)")
},
diff --git a/cc/cc.go b/cc/cc.go
index 0d0aec7..ca2bd4f 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -57,14 +57,14 @@
})
ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
- ctx.TopDown("asan_deps", sanitizerDepsMutator(asan))
- ctx.BottomUp("asan", sanitizerMutator(asan)).Parallel()
+ ctx.TopDown("asan_deps", sanitizerDepsMutator(Asan))
+ ctx.BottomUp("asan", sanitizerMutator(Asan)).Parallel()
ctx.TopDown("hwasan_deps", sanitizerDepsMutator(hwasan))
ctx.BottomUp("hwasan", sanitizerMutator(hwasan)).Parallel()
- ctx.TopDown("fuzzer_deps", sanitizerDepsMutator(fuzzer))
- ctx.BottomUp("fuzzer", sanitizerMutator(fuzzer)).Parallel()
+ ctx.TopDown("fuzzer_deps", sanitizerDepsMutator(Fuzzer))
+ ctx.BottomUp("fuzzer", sanitizerMutator(Fuzzer)).Parallel()
// cfi mutator shouldn't run before sanitizers that return true for
// incompatibleWithCfi()
@@ -369,10 +369,7 @@
// If set to false, this module becomes inaccessible from /vendor modules.
//
// The modules with vndk: {enabled: true} must define 'vendor_available'
- // to either 'true' or 'false'. In this case, 'vendor_available: false' has
- // a different meaning than that of non-VNDK modules.
- // 'vendor_available: false' for a VNDK module means 'VNDK-private' that
- // can only be depended on by VNDK libraries, not by non-VNDK vendor modules.
+ // to 'true'.
//
// Nothing happens if BOARD_VNDK_VERSION isn't set in the BoardConfig.mk
Vendor_available *bool
@@ -394,13 +391,6 @@
// vndk: {enabled: true} don't have to define 'product_available'. The VNDK
// library without 'product_available' may not be depended on by any other
// modules that has product variants including the product available VNDKs.
- // However, for the modules with vndk: {enabled: true},
- // 'product_available: false' creates the product variant that is available
- // only for the other product available VNDK modules but not by non-VNDK
- // product modules.
- // In the case of the modules with vndk: {enabled: true}, if
- // 'product_available' is defined, it must have the same value with the
- // 'vendor_available'.
//
// Nothing happens if BOARD_VNDK_VERSION isn't set in the BoardConfig.mk
// and PRODUCT_PRODUCT_VNDK_VERSION isn't set.
@@ -419,7 +409,7 @@
IsLLNDK bool `blueprint:"mutated"`
// IsLLNDKPrivate is set to true for the vendor variant of a cc_library module that has LLNDK
- // stubs and also sets llndk.vendor_available: false.
+ // stubs and also sets llndk.private: true.
IsLLNDKPrivate bool `blueprint:"mutated"`
}
@@ -430,6 +420,7 @@
type ModuleContextIntf interface {
static() bool
staticBinary() bool
+ testBinary() bool
header() bool
binary() bool
object() bool
@@ -785,6 +776,14 @@
hideApexVariantFromMake bool
}
+func (c *Module) SetPreventInstall() {
+ c.Properties.PreventInstall = true
+}
+
+func (c *Module) SetHideFromMake() {
+ c.Properties.HideFromMake = true
+}
+
func (c *Module) Toc() android.OptionalPath {
if c.linker != nil {
if library, ok := c.linker.(libraryInterface); ok {
@@ -1026,7 +1025,7 @@
// Returns true for dependency roots (binaries)
// TODO(ccross): also handle dlopenable libraries
-func (c *Module) isDependencyRoot() bool {
+func (c *Module) IsDependencyRoot() bool {
if root, ok := c.linker.(interface {
isDependencyRoot() bool
}); ok {
@@ -1079,11 +1078,11 @@
func (c *Module) isImplementationForLLNDKPublic() bool {
library, _ := c.library.(*libraryDecorator)
return library != nil && library.hasLLNDKStubs() &&
- (Bool(library.Properties.Llndk.Vendor_available) ||
+ (!Bool(library.Properties.Llndk.Private) ||
// TODO(b/170784825): until the LLNDK properties are moved into the cc_library,
// the non-Vendor variants of the cc_library don't know if the corresponding
- // llndk_library set vendor_available: false. Since libft2 is the only
- // private LLNDK library, hardcode it during the transition.
+ // llndk_library set private: true. Since libft2 is the only private LLNDK
+ // library, hardcode it during the transition.
c.BaseModuleName() != "libft2")
}
@@ -1091,20 +1090,12 @@
func (c *Module) IsVndkPrivate() bool {
// Check if VNDK-core-private or VNDK-SP-private
if c.IsVndk() {
- if Bool(c.vndkdep.Properties.Vndk.Private) {
- return true
- }
- // TODO(b/175768895) remove this when we clean up "vendor_available: false" use cases.
- if c.VendorProperties.Vendor_available != nil && !Bool(c.VendorProperties.Vendor_available) {
- return true
- }
- return false
+ return Bool(c.vndkdep.Properties.Vndk.Private)
}
// Check if LLNDK-private
if library, ok := c.library.(*libraryDecorator); ok && c.IsLlndk() {
- // TODO(b/175768895) replace this with 'private' property.
- return !Bool(library.Properties.Llndk.Vendor_available)
+ return Bool(library.Properties.Llndk.Private)
}
return false
@@ -1271,8 +1262,12 @@
return ctx.mod.staticBinary()
}
+func (ctx *moduleContextImpl) testBinary() bool {
+ return ctx.mod.testBinary()
+}
+
func (ctx *moduleContextImpl) header() bool {
- return ctx.mod.header()
+ return ctx.mod.Header()
}
func (ctx *moduleContextImpl) binary() bool {
@@ -1429,6 +1424,10 @@
return nil
}
+func (c *Module) IsPrebuilt() bool {
+ return c.Prebuilt() != nil
+}
+
func (c *Module) Name() string {
name := c.ModuleBase.Name()
if p, ok := c.linker.(interface {
@@ -2855,7 +2854,7 @@
return baseName + ".vendor"
}
- if c.inVendor() && vendorSuffixModules[baseName] {
+ if c.InVendor() && vendorSuffixModules[baseName] {
return baseName + ".vendor"
} else if c.InRecovery() && recoverySuffixModules[baseName] {
return baseName + ".recovery"
@@ -2967,7 +2966,17 @@
return false
}
-func (c *Module) header() bool {
+func (c *Module) testBinary() bool {
+ if test, ok := c.linker.(interface {
+ testBinary() bool
+ }); ok {
+ return test.testBinary()
+ }
+ return false
+}
+
+// Header returns true if the module is a header-only variant. (See cc/library.go header()).
+func (c *Module) Header() bool {
if h, ok := c.linker.(interface {
header() bool
}); ok {
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 2d02a6a..3399e93 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -234,9 +234,6 @@
t.Helper()
mod := ctx.ModuleForTests(name, variant).Module().(*Module)
- if !mod.HasVendorVariant() {
- t.Errorf("%q must have variant %q", name, variant)
- }
// Check library properties.
lib, ok := mod.compiler.(*libraryDecorator)
@@ -418,23 +415,24 @@
},
}
- vndk_libraries_txt {
+ llndk_libraries_txt {
name: "llndk.libraries.txt",
}
- vndk_libraries_txt {
+ vndkcore_libraries_txt {
name: "vndkcore.libraries.txt",
}
- vndk_libraries_txt {
+ vndksp_libraries_txt {
name: "vndksp.libraries.txt",
}
- vndk_libraries_txt {
+ vndkprivate_libraries_txt {
name: "vndkprivate.libraries.txt",
}
- vndk_libraries_txt {
+ vndkproduct_libraries_txt {
name: "vndkproduct.libraries.txt",
}
- vndk_libraries_txt {
+ vndkcorevariant_libraries_txt {
name: "vndkcorevariant.libraries.txt",
+ insert_vndk_version: false,
}
`
@@ -546,7 +544,7 @@
}
}
- vndk_libraries_txt {
+ vndkcore_libraries_txt {
name: "vndkcore.libraries.txt",
}
`)
@@ -556,8 +554,9 @@
func TestVndkLibrariesTxtAndroidMk(t *testing.T) {
bp := `
- vndk_libraries_txt {
+ llndk_libraries_txt {
name: "llndk.libraries.txt",
+ insert_vndk_version: true,
}`
config := TestConfig(buildDir, android.Android, nil, bp, nil)
config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
@@ -603,8 +602,9 @@
nocrt: true,
}
- vndk_libraries_txt {
+ vndkcorevariant_libraries_txt {
name: "vndkcorevariant.libraries.txt",
+ insert_vndk_version: false,
}
`
@@ -730,19 +730,37 @@
}
cc_library {
name: "libvndk-private",
- vendor_available: false,
- product_available: false,
+ vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
+ private: true,
},
nocrt: true,
}
+
+ cc_library {
+ name: "libllndk",
+ llndk_stubs: "libllndk.llndk",
+ }
+
+ llndk_library {
+ name: "libllndk.llndk",
+ symbol_file: "",
+ export_llndk_headers: ["libllndk_headers"],
+ }
+
+ llndk_headers {
+ name: "libllndk_headers",
+ export_include_dirs: ["include"],
+ }
`)
checkVndkOutput(t, ctx, "vndk/vndk.libraries.txt", []string{
"LLNDK: libc.so",
"LLNDK: libdl.so",
"LLNDK: libft2.so",
+ "LLNDK: libllndk.so",
"LLNDK: libm.so",
"VNDK-SP: libc++.so",
"VNDK-core: libvndk-private.so",
@@ -757,7 +775,7 @@
func TestVndkModuleError(t *testing.T) {
// Check the error message for vendor_available and product_available properties.
- testCcErrorProductVndk(t, "vndk: vendor_available must be set to either true or false when `vndk: {enabled: true}`", `
+ testCcErrorProductVndk(t, "vndk: vendor_available must be set to true when `vndk: {enabled: true}`", `
cc_library {
name: "libvndk",
vndk: {
@@ -767,7 +785,7 @@
}
`)
- testCcErrorProductVndk(t, "vndk: vendor_available must be set to either true or false when `vndk: {enabled: true}`", `
+ testCcErrorProductVndk(t, "vndk: vendor_available must be set to true when `vndk: {enabled: true}`", `
cc_library {
name: "libvndk",
product_available: true,
@@ -1245,6 +1263,15 @@
t.Errorf("%q expected but not found", jsonFile)
}
}
+
+ // fake snapshot should have all outputs in the normal snapshot.
+ fakeSnapshotSingleton := ctx.SingletonForTests("vendor-fake-snapshot")
+ for _, output := range snapshotSingleton.AllOutputs() {
+ fakeOutput := strings.Replace(output, "/vendor-snapshot/", "/fake/vendor-snapshot/", 1)
+ if fakeSnapshotSingleton.MaybeOutput(fakeOutput).Rule == nil {
+ t.Errorf("%q expected but not found", fakeOutput)
+ }
+ }
}
func TestVendorSnapshotUse(t *testing.T) {
@@ -1676,6 +1703,8 @@
`module "libvendor\{.+,image:vendor.+,arch:arm_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`,
`module "libvendor\{.+,image:vendor.+,arch:arm64_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`,
`module "libvendor\{.+,image:vendor.+,arch:arm_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`,
+ `module "libvendor\{.+,image:vendor.+,arch:arm64_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`,
+ `module "libvendor\{.+,image:vendor.+,arch:arm_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`,
})
}
@@ -1719,6 +1748,10 @@
`module "libinclude\{.+,image:,arch:arm_.+\}" may not use both "vendor_available: true" and "exclude_from_vendor_snapshot: true"`,
`module "libinclude\{.+,image:vendor.+,arch:arm64_.+\}" may not use both "vendor_available: true" and "exclude_from_vendor_snapshot: true"`,
`module "libinclude\{.+,image:vendor.+,arch:arm_.+\}" may not use both "vendor_available: true" and "exclude_from_vendor_snapshot: true"`,
+ `module "libinclude\{.+,image:,arch:arm64_.+\}" may not use both "vendor_available: true" and "exclude_from_vendor_snapshot: true"`,
+ `module "libinclude\{.+,image:,arch:arm_.+\}" may not use both "vendor_available: true" and "exclude_from_vendor_snapshot: true"`,
+ `module "libinclude\{.+,image:vendor.+,arch:arm64_.+\}" may not use both "vendor_available: true" and "exclude_from_vendor_snapshot: true"`,
+ `module "libinclude\{.+,image:vendor.+,arch:arm_.+\}" may not use both "vendor_available: true" and "exclude_from_vendor_snapshot: true"`,
})
}
@@ -3136,7 +3169,7 @@
}
llndk_library {
name: "libllndkprivate.llndk",
- vendor_available: false,
+ private: true,
symbol_file: "",
}`
@@ -4445,3 +4478,138 @@
t.Errorf("aidl command %q does not contain %q", aidlCommand, expectedAidlFlag)
}
}
+
+func checkHasImplicitDep(t *testing.T, m android.TestingModule, name string) {
+ implicits := m.Rule("ld").Implicits
+ for _, lib := range implicits {
+ if strings.Contains(lib.Rel(), name) {
+ return
+ }
+ }
+
+ t.Errorf("%q is not found in implicit deps of module %q", name, m.Module().(*Module).Name())
+}
+
+func checkDoesNotHaveImplicitDep(t *testing.T, m android.TestingModule, name string) {
+ implicits := m.Rule("ld").Implicits
+ for _, lib := range implicits {
+ if strings.Contains(lib.Rel(), name) {
+ t.Errorf("%q is found in implicit deps of module %q", name, m.Module().(*Module).Name())
+ }
+ }
+}
+
+func TestSanitizeMemtagHeap(t *testing.T) {
+ rootBp := `
+ cc_library_static {
+ name: "libstatic",
+ sanitize: { memtag_heap: true },
+ }
+
+ cc_library_shared {
+ name: "libshared",
+ sanitize: { memtag_heap: true },
+ }
+
+ cc_library {
+ name: "libboth",
+ sanitize: { memtag_heap: true },
+ }
+
+ cc_binary {
+ name: "binary",
+ shared_libs: [ "libshared" ],
+ static_libs: [ "libstatic" ],
+ }
+
+ cc_binary {
+ name: "binary_true",
+ sanitize: { memtag_heap: true },
+ }
+
+ cc_binary {
+ name: "binary_true_sync",
+ sanitize: { memtag_heap: true, diag: { memtag_heap: true }, },
+ }
+
+ cc_binary {
+ name: "binary_false",
+ sanitize: { memtag_heap: false },
+ }
+
+ cc_test {
+ name: "test",
+ gtest: false,
+ }
+
+ cc_test {
+ name: "test_true",
+ gtest: false,
+ sanitize: { memtag_heap: true },
+ }
+
+ cc_test {
+ name: "test_false",
+ gtest: false,
+ sanitize: { memtag_heap: false },
+ }
+
+ cc_test {
+ name: "test_true_async",
+ gtest: false,
+ sanitize: { memtag_heap: true, diag: { memtag_heap: false } },
+ }
+
+ `
+
+ subdirAsyncBp := `
+ cc_binary {
+ name: "binary_async",
+ }
+ `
+
+ subdirSyncBp := `
+ cc_binary {
+ name: "binary_sync",
+ }
+ `
+
+ mockFS := map[string][]byte{
+ "subdir_async/Android.bp": []byte(subdirAsyncBp),
+ "subdir_sync/Android.bp": []byte(subdirSyncBp),
+ }
+
+ config := TestConfig(buildDir, android.Android, nil, rootBp, mockFS)
+ config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
+ config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
+ config.TestProductVariables.MemtagHeapAsyncIncludePaths = []string{"subdir_async"}
+ config.TestProductVariables.MemtagHeapSyncIncludePaths = []string{"subdir_sync"}
+ ctx := CreateTestContext(config)
+ ctx.Register()
+
+ _, errs := ctx.ParseFileList(".", []string{"Android.bp", "subdir_sync/Android.bp", "subdir_async/Android.bp"})
+ android.FailIfErrored(t, errs)
+ _, errs = ctx.PrepareBuildActions(config)
+ android.FailIfErrored(t, errs)
+
+ variant := "android_arm64_armv8-a"
+ note_async := "note_memtag_heap_async"
+ note_sync := "note_memtag_heap_sync"
+ note_any := "note_memtag_"
+
+ checkDoesNotHaveImplicitDep(t, ctx.ModuleForTests("libshared", "android_arm64_armv8-a_shared"), note_any)
+ checkDoesNotHaveImplicitDep(t, ctx.ModuleForTests("libboth", "android_arm64_armv8-a_shared"), note_any)
+
+ checkDoesNotHaveImplicitDep(t, ctx.ModuleForTests("binary", variant), note_any)
+ checkHasImplicitDep(t, ctx.ModuleForTests("binary_true", variant), note_async)
+ checkHasImplicitDep(t, ctx.ModuleForTests("binary_true_sync", variant), note_sync)
+ checkDoesNotHaveImplicitDep(t, ctx.ModuleForTests("binary_false", variant), note_any)
+
+ checkHasImplicitDep(t, ctx.ModuleForTests("test", variant), note_sync)
+ checkHasImplicitDep(t, ctx.ModuleForTests("test_true", variant), note_async)
+ checkDoesNotHaveImplicitDep(t, ctx.ModuleForTests("test_false", variant), note_any)
+ checkHasImplicitDep(t, ctx.ModuleForTests("test_true_async", variant), note_async)
+
+ checkHasImplicitDep(t, ctx.ModuleForTests("binary_async", variant), note_async)
+ checkHasImplicitDep(t, ctx.ModuleForTests("binary_sync", variant), note_sync)
+}
diff --git a/cc/config/global.go b/cc/config/global.go
index fa8e0fb..d47e0ce 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -215,10 +215,6 @@
"frameworks/native/opengl/include",
"frameworks/av/include",
})
- // This is used by non-NDK modules to get jni.h. export_include_dirs doesn't help
- // with this, since there is no associated library.
- pctx.PrefixedExistentPathsForSourcesVariable("CommonNativehelperInclude", "-I",
- []string{"libnativehelper/include_jni"})
pctx.SourcePathVariable("ClangDefaultBase", ClangDefaultBase)
pctx.VariableFunc("ClangBase", func(ctx android.PackageVarContext) string {
diff --git a/cc/config/tidy.go b/cc/config/tidy.go
index 4ac9e58..e5bea4b 100644
--- a/cc/config/tidy.go
+++ b/cc/config/tidy.go
@@ -21,23 +21,28 @@
func init() {
// Most Android source files are not clang-tidy clean yet.
- // Global tidy checks include only google*, performance*,
- // and misc-macro-parentheses, but not google-readability*
- // or google-runtime-references.
+ // Default global tidy checks must exclude all checks that
+ // have found too many false positives.
pctx.VariableFunc("TidyDefaultGlobalChecks", func(ctx android.PackageVarContext) string {
if override := ctx.Config().Getenv("DEFAULT_GLOBAL_TIDY_CHECKS"); override != "" {
return override
}
return strings.Join([]string{
- "-*",
- "bugprone*",
- "clang-diagnostic-unused-command-line-argument",
- "google*",
- "misc-macro-parentheses",
- "performance*",
+ "*",
+ "-altera-*",
"-bugprone-narrowing-conversions",
- "-google-readability*",
+ "-cppcoreguidelines-*",
+ "-fuchsia-*",
+ "-google-readability-*",
"-google-runtime-references",
+ "-hicpp-*",
+ "-llvm-*",
+ "-llvmlibc-*",
+ "-misc-no-recursion",
+ "-misc-non-private-member-variables-in-classes",
+ "-misc-unused-parameters",
+ "-modernize-*",
+ "-readability-*",
}, ",")
})
diff --git a/cc/config/vndk.go b/cc/config/vndk.go
index 4bcad4b..ca30ce9 100644
--- a/cc/config/vndk.go
+++ b/cc/config/vndk.go
@@ -18,14 +18,20 @@
// For these libraries, the vendor variants must be installed even if the device
// has VndkUseCoreVariant set.
var VndkMustUseVendorVariantList = []string{
+ "android.hardware.authsecret-unstable-ndk_platform",
"android.hardware.automotive.occupant_awareness-ndk_platform",
"android.hardware.light-ndk_platform",
"android.hardware.identity-ndk_platform",
"android.hardware.nfc@1.2",
"android.hardware.memtrack-unstable-ndk_platform",
+ "android.hardware.oemlock-unstable-ndk_platform",
"android.hardware.power-ndk_platform",
"android.hardware.rebootescrow-ndk_platform",
"android.hardware.security.keymint-unstable-ndk_platform",
+ "android.hardware.security.secureclock-ndk_platform",
+ "android.hardware.security.secureclock-unstable-ndk_platform",
+ "android.hardware.security.sharedsecret-ndk_platform",
+ "android.hardware.security.sharedsecret-unstable-ndk_platform",
"android.hardware.vibrator-ndk_platform",
"android.system.keystore2-unstable-ndk_platform",
"libbinder",
diff --git a/cc/fuzz.go b/cc/fuzz.go
index 6b17c48..d7da5ab 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -41,6 +41,12 @@
// Specify who should be acknowledged for CVEs in the Android Security
// Bulletin.
Acknowledgement []string `json:"acknowledgement,omitempty"`
+ // Additional options to be passed to libfuzzer when run in Haiku.
+ Libfuzzer_options []string `json:"libfuzzer_options,omitempty"`
+ // Additional options to be passed to HWASAN when running on-device in Haiku.
+ Hwasan_options []string `json:"hwasan_options,omitempty"`
+ // Additional options to be passed to HWASAN when running on host in Haiku.
+ Asan_options []string `json:"asan_options,omitempty"`
}
func (f *FuzzConfig) String() string {
@@ -315,7 +321,7 @@
module, binary := NewBinary(hod)
binary.baseInstaller = NewFuzzInstaller()
- module.sanitize.SetSanitizer(fuzzer, true)
+ module.sanitize.SetSanitizer(Fuzzer, true)
fuzz := &fuzzBinary{
binaryDecorator: binary,
diff --git a/cc/image.go b/cc/image.go
index 11ba55c..f89194f 100644
--- a/cc/image.go
+++ b/cc/image.go
@@ -26,36 +26,18 @@
var _ android.ImageInterface = (*Module)(nil)
-type imageVariantType string
+type ImageVariantType string
const (
- coreImageVariant imageVariantType = "core"
- vendorImageVariant imageVariantType = "vendor"
- productImageVariant imageVariantType = "product"
- ramdiskImageVariant imageVariantType = "ramdisk"
- vendorRamdiskImageVariant imageVariantType = "vendor_ramdisk"
- recoveryImageVariant imageVariantType = "recovery"
- hostImageVariant imageVariantType = "host"
+ coreImageVariant ImageVariantType = "core"
+ vendorImageVariant ImageVariantType = "vendor"
+ productImageVariant ImageVariantType = "product"
+ ramdiskImageVariant ImageVariantType = "ramdisk"
+ vendorRamdiskImageVariant ImageVariantType = "vendor_ramdisk"
+ recoveryImageVariant ImageVariantType = "recovery"
+ hostImageVariant ImageVariantType = "host"
)
-func (c *Module) getImageVariantType() imageVariantType {
- if c.Host() {
- return hostImageVariant
- } else if c.inVendor() {
- return vendorImageVariant
- } else if c.InProduct() {
- return productImageVariant
- } else if c.InRamdisk() {
- return ramdiskImageVariant
- } else if c.InVendorRamdisk() {
- return vendorRamdiskImageVariant
- } else if c.InRecovery() {
- return recoveryImageVariant
- } else {
- return coreImageVariant
- }
-}
-
const (
// VendorVariationPrefix is the variant prefix used for /vendor code that compiles
// against the VNDK.
@@ -67,13 +49,15 @@
)
func (ctx *moduleContext) ProductSpecific() bool {
- return ctx.ModuleContext.ProductSpecific() ||
- (ctx.mod.HasProductVariant() && ctx.mod.InProduct())
+ // Additionally check if this module is inProduct() that means it is a "product" variant of a
+ // module. As well as product specific modules, product variants must be installed to /product.
+ return ctx.ModuleContext.ProductSpecific() || ctx.mod.InProduct()
}
func (ctx *moduleContext) SocSpecific() bool {
- return ctx.ModuleContext.SocSpecific() ||
- (ctx.mod.HasVendorVariant() && ctx.mod.inVendor())
+ // Additionally check if this module is inVendor() that means it is a "vendor" variant of a
+ // module. As well as SoC specific modules, vendor variants must be installed to /vendor.
+ return ctx.ModuleContext.SocSpecific() || ctx.mod.InVendor()
}
func (ctx *moduleContextImpl) inProduct() bool {
@@ -81,7 +65,7 @@
}
func (ctx *moduleContextImpl) inVendor() bool {
- return ctx.mod.inVendor()
+ return ctx.mod.InVendor()
}
func (ctx *moduleContextImpl) inRamdisk() bool {
@@ -98,18 +82,12 @@
// Returns true when this module is configured to have core and vendor variants.
func (c *Module) HasVendorVariant() bool {
- // In case of a VNDK, 'vendor_available: false' still creates a vendor variant.
- return c.IsVndk() || Bool(c.VendorProperties.Vendor_available)
+ return Bool(c.VendorProperties.Vendor_available)
}
// Returns true when this module is configured to have core and product variants.
func (c *Module) HasProductVariant() bool {
- if c.VendorProperties.Product_available == nil {
- // Without 'product_available', product variant will not be created even for VNDKs.
- return false
- }
- // However, 'product_available: false' in a VNDK still creates a product variant.
- return c.IsVndk() || Bool(c.VendorProperties.Product_available)
+ return Bool(c.VendorProperties.Product_available)
}
// Returns true when this module is configured to have core and either product or vendor variants.
@@ -123,7 +101,7 @@
}
// Returns true if the module is "vendor" variant. Usually these modules are installed in /vendor
-func (c *Module) inVendor() bool {
+func (c *Module) InVendor() bool {
return c.Properties.ImageVariationPrefix == VendorVariationPrefix
}
@@ -186,7 +164,7 @@
// This function is used only for the VNDK modules that is available to both vendor
// and product partitions.
func (c *Module) compareVendorAndProductProps() bool {
- if !c.IsVndk() && c.VendorProperties.Product_available != nil {
+ if !c.IsVndk() && !Bool(c.VendorProperties.Product_available) {
panic(fmt.Errorf("This is only for product available VNDK libs. %q is not a VNDK library or not product available", c.Name()))
}
for _, properties := range c.GetProperties() {
@@ -202,14 +180,14 @@
vendorSpecific := mctx.SocSpecific() || mctx.DeviceSpecific()
productSpecific := mctx.ProductSpecific()
- if m.VendorProperties.Vendor_available != nil {
+ if Bool(m.VendorProperties.Vendor_available) {
if vendorSpecific {
mctx.PropertyErrorf("vendor_available",
"doesn't make sense at the same time as `vendor: true`, `proprietary: true`, or `device_specific:true`")
}
}
- if m.VendorProperties.Product_available != nil {
+ if Bool(m.VendorProperties.Product_available) {
if productSpecific {
mctx.PropertyErrorf("product_available",
"doesn't make sense at the same time as `product_specific: true`")
@@ -226,10 +204,10 @@
if !vndkdep.isVndkExt() {
mctx.PropertyErrorf("vndk",
"must set `extends: \"...\"` to vndk extension")
- } else if m.VendorProperties.Vendor_available != nil {
+ } else if Bool(m.VendorProperties.Vendor_available) {
mctx.PropertyErrorf("vendor_available",
"must not set at the same time as `vndk: {extends: \"...\"}`")
- } else if m.VendorProperties.Product_available != nil {
+ } else if Bool(m.VendorProperties.Product_available) {
mctx.PropertyErrorf("product_available",
"must not set at the same time as `vndk: {extends: \"...\"}`")
}
@@ -239,11 +217,11 @@
"must set `vendor: true` or `product_specific: true` to set `extends: %q`",
m.getVndkExtendsModuleName())
}
- if m.VendorProperties.Vendor_available == nil {
+ if !Bool(m.VendorProperties.Vendor_available) {
mctx.PropertyErrorf("vndk",
- "vendor_available must be set to either true or false when `vndk: {enabled: true}`")
+ "vendor_available must be set to true when `vndk: {enabled: true}`")
}
- if m.VendorProperties.Product_available != nil {
+ if Bool(m.VendorProperties.Product_available) {
// If a VNDK module creates both product and vendor variants, they
// must have the same properties since they share a single VNDK
// library on runtime.
@@ -286,31 +264,34 @@
productVndkVersion = platformVndkVersion
}
- if boardVndkVersion == "" {
+ _, isLLNDKLibrary := m.linker.(*llndkStubDecorator)
+ _, isLLNDKHeaders := m.linker.(*llndkHeadersDecorator)
+ lib := moduleLibraryInterface(m)
+ hasLLNDKStubs := lib != nil && lib.hasLLNDKStubs()
+
+ if isLLNDKLibrary || isLLNDKHeaders || hasLLNDKStubs {
+ // This is an LLNDK library. The implementation of the library will be on /system,
+ // and vendor and product variants will be created with LLNDK stubs.
+ // The LLNDK libraries need vendor variants even if there is no VNDK.
+ // The obsolete llndk_library and llndk_headers modules also need the vendor variants
+ // so the cc_library LLNDK stubs can depend on them.
+ if hasLLNDKStubs {
+ coreVariantNeeded = true
+ }
+ if platformVndkVersion != "" {
+ vendorVariants = append(vendorVariants, platformVndkVersion)
+ productVariants = append(productVariants, platformVndkVersion)
+ }
+ if boardVndkVersion != "" {
+ vendorVariants = append(vendorVariants, boardVndkVersion)
+ }
+ if productVndkVersion != "" {
+ productVariants = append(productVariants, productVndkVersion)
+ }
+ } else if boardVndkVersion == "" {
// If the device isn't compiling against the VNDK, we always
// use the core mode.
coreVariantNeeded = true
- } else if _, ok := m.linker.(*llndkStubDecorator); ok {
- // LL-NDK stubs only exist in the vendor and product variants,
- // since the real libraries will be used in the core variant.
- vendorVariants = append(vendorVariants,
- platformVndkVersion,
- boardVndkVersion,
- )
- productVariants = append(productVariants,
- platformVndkVersion,
- productVndkVersion,
- )
- } else if _, ok := m.linker.(*llndkHeadersDecorator); ok {
- // ... and LL-NDK headers as well
- vendorVariants = append(vendorVariants,
- platformVndkVersion,
- boardVndkVersion,
- )
- productVariants = append(productVariants,
- platformVndkVersion,
- productVndkVersion,
- )
} else if m.isSnapshotPrebuilt() {
// Make vendor variants only for the versions in BOARD_VNDK_VERSION and
// PRODUCT_EXTRA_VNDK_VERSIONS.
@@ -368,18 +349,6 @@
} else {
vendorVariants = append(vendorVariants, platformVndkVersion)
}
- } else if lib := moduleLibraryInterface(m); lib != nil && lib.hasLLNDKStubs() {
- // This is an LLNDK library. The implementation of the library will be on /system,
- // and vendor and product variants will be created with LLNDK stubs.
- coreVariantNeeded = true
- vendorVariants = append(vendorVariants,
- platformVndkVersion,
- boardVndkVersion,
- )
- productVariants = append(productVariants,
- platformVndkVersion,
- productVndkVersion,
- )
} else {
// This is either in /system (or similar: /data), or is a
// modules built with the NDK. Modules built with the NDK
diff --git a/cc/linkable.go b/cc/linkable.go
index 489063f..ab5a552 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -6,6 +6,59 @@
"github.com/google/blueprint"
)
+// PlatformSanitizeable is an interface for sanitizing platform modules.
+type PlatformSanitizeable interface {
+ LinkableInterface
+
+ // SanitizePropDefined returns whether the Sanitizer properties struct for this module is defined.
+ SanitizePropDefined() bool
+
+ // IsDependencyRoot returns whether a module is of a type which cannot be a linkage dependency
+ // of another module. For example, cc_binary and rust_binary represent dependency roots as other
+ // modules cannot have linkage dependencies against these types.
+ IsDependencyRoot() bool
+
+ // IsSanitizerEnabled returns whether a sanitizer is enabled.
+ IsSanitizerEnabled(t SanitizerType) bool
+
+ // IsSanitizerExplicitlyDisabled returns whether a sanitizer has been explicitly disabled (set to false) rather
+ // than left undefined.
+ IsSanitizerExplicitlyDisabled(t SanitizerType) bool
+
+ // SanitizeDep returns the value of the SanitizeDep flag, which is set if a module is a dependency of a
+ // sanitized module.
+ SanitizeDep() bool
+
+ // SetSanitizer enables or disables the specified sanitizer type if it's supported, otherwise this should panic.
+ SetSanitizer(t SanitizerType, b bool)
+
+ // SetSanitizerDep returns true if the module is statically linked.
+ SetSanitizeDep(b bool)
+
+ // StaticallyLinked returns true if the module is statically linked.
+ StaticallyLinked() bool
+
+ // SetInSanitizerDir sets the module installation to the sanitizer directory.
+ SetInSanitizerDir()
+
+ // SanitizeNever returns true if this module should never be sanitized.
+ SanitizeNever() bool
+
+ // SanitizerSupported returns true if a sanitizer type is supported by this modules compiler.
+ SanitizerSupported(t SanitizerType) bool
+
+ // SanitizableDepTagChecker returns a SantizableDependencyTagChecker function type.
+ SanitizableDepTagChecker() SantizableDependencyTagChecker
+}
+
+// SantizableDependencyTagChecker functions check whether or not a dependency
+// tag can be sanitized. These functions should return true if the tag can be
+// sanitized, otherwise they should return false. These functions should also
+// handle all possible dependency tags in the dependency tree. For example,
+// Rust modules can depend on both Rust and CC libraries, so the Rust module
+// implementation should handle tags from both.
+type SantizableDependencyTagChecker func(tag blueprint.DependencyTag) bool
+
// LinkableInterface is an interface for a type of module that is linkable in a C++ library.
type LinkableInterface interface {
android.Module
@@ -27,6 +80,8 @@
SetShared()
Static() bool
Shared() bool
+ Header() bool
+ IsPrebuilt() bool
Toc() android.OptionalPath
Host() bool
@@ -40,6 +95,8 @@
InRecovery() bool
OnlyInRecovery() bool
+ InVendor() bool
+
UseSdk() bool
UseVndk() bool
MustUseVendorVariant() bool
@@ -56,6 +113,11 @@
IsSdkVariant() bool
SplitPerApiLevel() bool
+
+ // SetPreventInstall sets the PreventInstall property to 'true' for this module.
+ SetPreventInstall()
+ // SetHideFromMake sets the HideFromMake property to 'true' for this module.
+ SetHideFromMake()
}
var (
@@ -67,6 +129,26 @@
CoverageDepTag = dependencyTag{name: "coverage"}
)
+// GetImageVariantType returns the ImageVariantType string value for the given module
+// (these are defined in cc/image.go).
+func GetImageVariantType(c LinkableInterface) ImageVariantType {
+ if c.Host() {
+ return hostImageVariant
+ } else if c.InVendor() {
+ return vendorImageVariant
+ } else if c.InProduct() {
+ return productImageVariant
+ } else if c.InRamdisk() {
+ return ramdiskImageVariant
+ } else if c.InVendorRamdisk() {
+ return vendorRamdiskImageVariant
+ } else if c.InRecovery() {
+ return recoveryImageVariant
+ } else {
+ return coreImageVariant
+ }
+}
+
// SharedDepTag returns the dependency tag for any C++ shared libraries.
func SharedDepTag() blueprint.DependencyTag {
return libraryDependencyTag{Kind: sharedLibraryDependency}
diff --git a/cc/llndk_library.go b/cc/llndk_library.go
index 0c4bcfd..a46b31c 100644
--- a/cc/llndk_library.go
+++ b/cc/llndk_library.go
@@ -55,13 +55,6 @@
// Whether the system library uses symbol versions.
Unversioned *bool
- // whether this module can be directly depended upon by libs that are installed
- // to /vendor and /product.
- // When set to false, this module can only be depended on by VNDK libraries, not
- // vendor nor product libraries. This effectively hides this module from
- // non-system modules. Default value is true.
- Vendor_available *bool
-
// list of llndk headers to re-export include directories from.
Export_llndk_headers []string `android:"arch_variant"`
@@ -136,7 +129,6 @@
stub := &llndkStubDecorator{
libraryDecorator: library,
}
- stub.Properties.Vendor_available = BoolPtr(true)
module.compiler = stub
module.linker = stub
module.installer = nil
@@ -169,6 +161,13 @@
*libraryDecorator
}
+func (llndk *llndkHeadersDecorator) linkerDeps(ctx DepsContext, deps Deps) Deps {
+ deps.HeaderLibs = append(deps.HeaderLibs, llndk.Properties.Llndk.Export_llndk_headers...)
+ deps.ReexportHeaderLibHeaders = append(deps.ReexportHeaderLibHeaders,
+ llndk.Properties.Llndk.Export_llndk_headers...)
+ return deps
+}
+
// llndk_headers contains a set of c/c++ llndk headers files which are imported
// by other soongs cc modules.
func llndkHeadersFactory() android.Module {
@@ -186,11 +185,6 @@
module.installer = nil
module.library = decorator
- module.AddProperties(
- &module.Properties,
- &library.MutatedProperties,
- &library.flagExporter.Properties)
-
module.Init()
return module
diff --git a/cc/makevars.go b/cc/makevars.go
index 8301c6b..48d5636 100644
--- a/cc/makevars.go
+++ b/cc/makevars.go
@@ -154,16 +154,6 @@
ctx.Strict("SOONG_STRIP_PATH", "${stripPath}")
ctx.Strict("XZ", "${xzCmd}")
- nativeHelperIncludeFlags, err := ctx.Eval("${config.CommonNativehelperInclude}")
- if err != nil {
- panic(err)
- }
- nativeHelperIncludes, nativeHelperSystemIncludes := splitSystemIncludes(ctx, nativeHelperIncludeFlags)
- if len(nativeHelperSystemIncludes) > 0 {
- panic("native helper may not have any system includes")
- }
- ctx.Strict("JNI_H_INCLUDE", strings.Join(nativeHelperIncludes, " "))
-
includeFlags, err := ctx.Eval("${config.CommonGlobalIncludes}")
if err != nil {
panic(err)
diff --git a/cc/sanitize.go b/cc/sanitize.go
index bb92a88..af17490 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -71,7 +71,7 @@
"export_memory_stats=0", "max_malloc_fill_size=0"}
)
-type sanitizerType int
+type SanitizerType int
func boolPtr(v bool) *bool {
if v {
@@ -82,19 +82,20 @@
}
const (
- asan sanitizerType = iota + 1
+ Asan SanitizerType = iota + 1
hwasan
tsan
intOverflow
cfi
scs
- fuzzer
+ Fuzzer
+ memtag_heap
)
// Name of the sanitizer variation for this sanitizer type
-func (t sanitizerType) variationName() string {
+func (t SanitizerType) variationName() string {
switch t {
- case asan:
+ case Asan:
return "asan"
case hwasan:
return "hwasan"
@@ -106,20 +107,24 @@
return "cfi"
case scs:
return "scs"
- case fuzzer:
+ case memtag_heap:
+ return "memtag_heap"
+ case Fuzzer:
return "fuzzer"
default:
- panic(fmt.Errorf("unknown sanitizerType %d", t))
+ panic(fmt.Errorf("unknown SanitizerType %d", t))
}
}
// This is the sanitizer names in SANITIZE_[TARGET|HOST]
-func (t sanitizerType) name() string {
+func (t SanitizerType) name() string {
switch t {
- case asan:
+ case Asan:
return "address"
case hwasan:
return "hwaddress"
+ case memtag_heap:
+ return "memtag_heap"
case tsan:
return "thread"
case intOverflow:
@@ -128,15 +133,37 @@
return "cfi"
case scs:
return "shadow-call-stack"
- case fuzzer:
+ case Fuzzer:
return "fuzzer"
default:
- panic(fmt.Errorf("unknown sanitizerType %d", t))
+ panic(fmt.Errorf("unknown SanitizerType %d", t))
}
}
-func (t sanitizerType) incompatibleWithCfi() bool {
- return t == asan || t == fuzzer || t == hwasan
+func (*Module) SanitizerSupported(t SanitizerType) bool {
+ switch t {
+ case Asan:
+ return true
+ case hwasan:
+ return true
+ case tsan:
+ return true
+ case intOverflow:
+ return true
+ case cfi:
+ return true
+ case scs:
+ return true
+ case Fuzzer:
+ return true
+ default:
+ return false
+ }
+}
+
+// incompatibleWithCfi returns true if a sanitizer is incompatible with CFI.
+func (t SanitizerType) incompatibleWithCfi() bool {
+ return t == Asan || t == Fuzzer || t == hwasan
}
type SanitizeUserProps struct {
@@ -157,6 +184,7 @@
Integer_overflow *bool `android:"arch_variant"`
Scudo *bool `android:"arch_variant"`
Scs *bool `android:"arch_variant"`
+ Memtag_heap *bool `android:"arch_variant"`
// A modifier for ASAN and HWASAN for write only instrumentation
Writeonly *bool `android:"arch_variant"`
@@ -168,6 +196,7 @@
Undefined *bool `android:"arch_variant"`
Cfi *bool `android:"arch_variant"`
Integer_overflow *bool `android:"arch_variant"`
+ Memtag_heap *bool `android:"arch_variant"`
Misc_undefined []string `android:"arch_variant"`
No_recover []string `android:"arch_variant"`
} `android:"arch_variant"`
@@ -308,6 +337,11 @@
}
s.Writeonly = boolPtr(true)
}
+ if found, globalSanitizers = removeFromList("memtag_heap", globalSanitizers); found && s.Memtag_heap == nil {
+ if !ctx.Config().MemtagHeapDisabledForPath(ctx.ModuleDir()) {
+ s.Memtag_heap = boolPtr(true)
+ }
+ }
if len(globalSanitizers) > 0 {
ctx.ModuleErrorf("unknown global sanitizer option %s", globalSanitizers[0])
@@ -329,6 +363,25 @@
}
}
+ // cc_test targets default to SYNC MemTag.
+ if ctx.testBinary() && s.Memtag_heap == nil {
+ if !ctx.Config().MemtagHeapDisabledForPath(ctx.ModuleDir()) {
+ s.Memtag_heap = boolPtr(true)
+ s.Diag.Memtag_heap = boolPtr(true)
+ }
+ }
+
+ // Enable Memtag for all components in the include paths (for Aarch64 only)
+ if s.Memtag_heap == nil && ctx.Arch().ArchType == android.Arm64 {
+ if ctx.Config().MemtagHeapSyncEnabledForPath(ctx.ModuleDir()) {
+ s.Memtag_heap = boolPtr(true)
+ s.Diag.Memtag_heap = boolPtr(true)
+ } else if ctx.Config().MemtagHeapAsyncEnabledForPath(ctx.ModuleDir()) {
+ s.Memtag_heap = boolPtr(true)
+ s.Diag.Memtag_heap = boolPtr(false)
+ }
+ }
+
// Enable CFI for all components in the include paths (for Aarch64 only)
if s.Cfi == nil && ctx.Config().CFIEnabledForPath(ctx.ModuleDir()) && ctx.Arch().ArchType == android.Arm64 {
s.Cfi = boolPtr(true)
@@ -359,6 +412,11 @@
s.Scs = nil
}
+ // memtag_heap is only implemented on AArch64.
+ if ctx.Arch().ArchType != android.Arm64 {
+ s.Memtag_heap = nil
+ }
+
// Also disable CFI if ASAN is enabled.
if Bool(s.Address) || Bool(s.Hwaddress) {
s.Cfi = boolPtr(false)
@@ -413,7 +471,7 @@
if ctx.Os() != android.Windows && (Bool(s.All_undefined) || Bool(s.Undefined) || Bool(s.Address) || Bool(s.Thread) ||
Bool(s.Fuzzer) || Bool(s.Safestack) || Bool(s.Cfi) || Bool(s.Integer_overflow) || len(s.Misc_undefined) > 0 ||
- Bool(s.Scudo) || Bool(s.Hwaddress) || Bool(s.Scs)) {
+ Bool(s.Scudo) || Bool(s.Hwaddress) || Bool(s.Scs) || Bool(s.Memtag_heap)) {
sanitize.Properties.SanitizerEnabled = true
}
@@ -680,9 +738,10 @@
return sanitize.Properties.InSanitizerDir
}
-func (sanitize *sanitize) getSanitizerBoolPtr(t sanitizerType) *bool {
+// getSanitizerBoolPtr returns the SanitizerTypes associated bool pointer from SanitizeProperties.
+func (sanitize *sanitize) getSanitizerBoolPtr(t SanitizerType) *bool {
switch t {
- case asan:
+ case Asan:
return sanitize.Properties.Sanitize.Address
case hwasan:
return sanitize.Properties.Sanitize.Hwaddress
@@ -694,32 +753,37 @@
return sanitize.Properties.Sanitize.Cfi
case scs:
return sanitize.Properties.Sanitize.Scs
- case fuzzer:
+ case memtag_heap:
+ return sanitize.Properties.Sanitize.Memtag_heap
+ case Fuzzer:
return sanitize.Properties.Sanitize.Fuzzer
default:
- panic(fmt.Errorf("unknown sanitizerType %d", t))
+ panic(fmt.Errorf("unknown SanitizerType %d", t))
}
}
+// isUnsanitizedVariant returns true if no sanitizers are enabled.
func (sanitize *sanitize) isUnsanitizedVariant() bool {
- return !sanitize.isSanitizerEnabled(asan) &&
+ return !sanitize.isSanitizerEnabled(Asan) &&
!sanitize.isSanitizerEnabled(hwasan) &&
!sanitize.isSanitizerEnabled(tsan) &&
!sanitize.isSanitizerEnabled(cfi) &&
!sanitize.isSanitizerEnabled(scs) &&
- !sanitize.isSanitizerEnabled(fuzzer)
+ !sanitize.isSanitizerEnabled(memtag_heap) &&
+ !sanitize.isSanitizerEnabled(Fuzzer)
}
+// isVariantOnProductionDevice returns true if variant is for production devices (no non-production sanitizers enabled).
func (sanitize *sanitize) isVariantOnProductionDevice() bool {
- return !sanitize.isSanitizerEnabled(asan) &&
+ return !sanitize.isSanitizerEnabled(Asan) &&
!sanitize.isSanitizerEnabled(hwasan) &&
!sanitize.isSanitizerEnabled(tsan) &&
- !sanitize.isSanitizerEnabled(fuzzer)
+ !sanitize.isSanitizerEnabled(Fuzzer)
}
-func (sanitize *sanitize) SetSanitizer(t sanitizerType, b bool) {
+func (sanitize *sanitize) SetSanitizer(t SanitizerType, b bool) {
switch t {
- case asan:
+ case Asan:
sanitize.Properties.Sanitize.Address = boolPtr(b)
case hwasan:
sanitize.Properties.Sanitize.Hwaddress = boolPtr(b)
@@ -731,10 +795,12 @@
sanitize.Properties.Sanitize.Cfi = boolPtr(b)
case scs:
sanitize.Properties.Sanitize.Scs = boolPtr(b)
- case fuzzer:
+ case memtag_heap:
+ sanitize.Properties.Sanitize.Memtag_heap = boolPtr(b)
+ case Fuzzer:
sanitize.Properties.Sanitize.Fuzzer = boolPtr(b)
default:
- panic(fmt.Errorf("unknown sanitizerType %d", t))
+ panic(fmt.Errorf("unknown SanitizerType %d", t))
}
if b {
sanitize.Properties.SanitizerEnabled = true
@@ -743,7 +809,7 @@
// Check if the sanitizer is explicitly disabled (as opposed to nil by
// virtue of not being set).
-func (sanitize *sanitize) isSanitizerExplicitlyDisabled(t sanitizerType) bool {
+func (sanitize *sanitize) isSanitizerExplicitlyDisabled(t SanitizerType) bool {
if sanitize == nil {
return false
}
@@ -757,7 +823,7 @@
// indirectly (via a mutator) sets the bool ptr to true, and you can't
// distinguish between the cases. It isn't needed though - both cases can be
// treated identically.
-func (sanitize *sanitize) isSanitizerEnabled(t sanitizerType) bool {
+func (sanitize *sanitize) isSanitizerEnabled(t SanitizerType) bool {
if sanitize == nil {
return false
}
@@ -766,7 +832,8 @@
return sanitizerVal != nil && *sanitizerVal == true
}
-func isSanitizableDependencyTag(tag blueprint.DependencyTag) bool {
+// IsSanitizableDependencyTag returns true if the dependency tag is sanitizable.
+func IsSanitizableDependencyTag(tag blueprint.DependencyTag) bool {
switch t := tag.(type) {
case dependencyTag:
return t == reuseObjTag || t == objDepTag
@@ -777,6 +844,10 @@
}
}
+func (m *Module) SanitizableDepTagChecker() SantizableDependencyTagChecker {
+ return IsSanitizableDependencyTag
+}
+
// Determines if the current module is a static library going to be captured
// as vendor snapshot. Such modules must create both cfi and non-cfi variants,
// except for ones which explicitly disable cfi.
@@ -785,51 +856,58 @@
return false
}
- c := mctx.Module().(*Module)
+ c := mctx.Module().(PlatformSanitizeable)
- if !c.inVendor() {
+ if !c.InVendor() {
return false
}
- if !c.static() {
+ if !c.StaticallyLinked() {
return false
}
- if c.Prebuilt() != nil {
+ if c.IsPrebuilt() {
return false
}
- return c.sanitize != nil &&
- !Bool(c.sanitize.Properties.Sanitize.Never) &&
- !c.sanitize.isSanitizerExplicitlyDisabled(cfi)
+ if !c.SanitizerSupported(cfi) {
+ return false
+ }
+
+ return c.SanitizePropDefined() &&
+ !c.SanitizeNever() &&
+ !c.IsSanitizerExplicitlyDisabled(cfi)
}
// Propagate sanitizer requirements down from binaries
-func sanitizerDepsMutator(t sanitizerType) func(android.TopDownMutatorContext) {
+func sanitizerDepsMutator(t SanitizerType) func(android.TopDownMutatorContext) {
return func(mctx android.TopDownMutatorContext) {
- if c, ok := mctx.Module().(*Module); ok {
- enabled := c.sanitize.isSanitizerEnabled(t)
+ if c, ok := mctx.Module().(PlatformSanitizeable); ok {
+ enabled := c.IsSanitizerEnabled(t)
if t == cfi && needsCfiForVendorSnapshot(mctx) {
// We shouldn't change the result of isSanitizerEnabled(cfi) to correctly
// determine defaultVariation in sanitizerMutator below.
// Instead, just mark SanitizeDep to forcefully create cfi variant.
enabled = true
- c.sanitize.Properties.SanitizeDep = true
+ c.SetSanitizeDep(true)
}
if enabled {
+ isSanitizableDependencyTag := c.SanitizableDepTagChecker()
mctx.WalkDeps(func(child, parent android.Module) bool {
if !isSanitizableDependencyTag(mctx.OtherModuleDependencyTag(child)) {
return false
}
- if d, ok := child.(*Module); ok && d.sanitize != nil &&
- !Bool(d.sanitize.Properties.Sanitize.Never) &&
- !d.sanitize.isSanitizerExplicitlyDisabled(t) {
+ if d, ok := child.(PlatformSanitizeable); ok && d.SanitizePropDefined() &&
+ !d.SanitizeNever() &&
+ !d.IsSanitizerExplicitlyDisabled(t) {
if t == cfi || t == hwasan || t == scs {
- if d.static() {
- d.sanitize.Properties.SanitizeDep = true
+ if d.StaticallyLinked() && d.SanitizerSupported(t) {
+ // Rust does not support some of these sanitizers, so we need to check if it's
+ // supported before setting this true.
+ d.SetSanitizeDep(true)
}
} else {
- d.sanitize.Properties.SanitizeDep = true
+ d.SetSanitizeDep(true)
}
}
return true
@@ -847,9 +925,19 @@
}
}
+func (c *Module) SanitizeNever() bool {
+ return Bool(c.sanitize.Properties.Sanitize.Never)
+}
+
+func (c *Module) IsSanitizerExplicitlyDisabled(t SanitizerType) bool {
+ return c.sanitize.isSanitizerExplicitlyDisabled(t)
+}
+
// Propagate the ubsan minimal runtime dependency when there are integer overflow sanitized static dependencies.
func sanitizerRuntimeDepsMutator(mctx android.TopDownMutatorContext) {
+ // Change this to PlatformSanitizable when/if non-cc modules support ubsan sanitizers.
if c, ok := mctx.Module().(*Module); ok && c.sanitize != nil {
+ isSanitizableDependencyTag := c.SanitizableDepTagChecker()
mctx.WalkDeps(func(child, parent android.Module) bool {
if !isSanitizableDependencyTag(mctx.OtherModuleDependencyTag(child)) {
return false
@@ -985,6 +1073,20 @@
sanitizers = append(sanitizers, "shadow-call-stack")
}
+ if Bool(c.sanitize.Properties.Sanitize.Memtag_heap) && c.binary() {
+ noteDep := "note_memtag_heap_async"
+ if Bool(c.sanitize.Properties.Sanitize.Diag.Memtag_heap) {
+ noteDep = "note_memtag_heap_sync"
+ }
+ depTag := libraryDependencyTag{Kind: staticLibraryDependency, wholeStatic: true}
+ variations := append(mctx.Target().Variations(),
+ blueprint.Variation{Mutator: "link", Variation: "static"})
+ if c.Device() {
+ variations = append(variations, c.ImageVariation())
+ }
+ mctx.AddFarVariationDependencies(variations, depTag, noteDep)
+ }
+
if Bool(c.sanitize.Properties.Sanitize.Fuzzer) {
sanitizers = append(sanitizers, "fuzzer-no-link")
}
@@ -1057,7 +1159,7 @@
variations = append(variations, c.ImageVariation())
}
mctx.AddFarVariationDependencies(variations, depTag, deps...)
- } else if !c.static() && !c.header() {
+ } else if !c.static() && !c.Header() {
// If we're using snapshots and in vendor, redirect to snapshot whenever possible
if c.VndkVersion() == mctx.DeviceConfig().VndkVersion() {
snapshots := vendorSnapshotSharedLibs(mctx.Config())
@@ -1098,16 +1200,52 @@
AddSanitizerDependencies(ctx android.BottomUpMutatorContext, sanitizerName string)
}
+func (c *Module) SanitizePropDefined() bool {
+ return c.sanitize != nil
+}
+
+func (c *Module) IsSanitizerEnabled(t SanitizerType) bool {
+ return c.sanitize.isSanitizerEnabled(t)
+}
+
+func (c *Module) SanitizeDep() bool {
+ return c.sanitize.Properties.SanitizeDep
+}
+
+func (c *Module) StaticallyLinked() bool {
+ return c.static()
+}
+
+func (c *Module) SetInSanitizerDir() {
+ if c.sanitize != nil {
+ c.sanitize.Properties.InSanitizerDir = true
+ }
+}
+
+func (c *Module) SetSanitizer(t SanitizerType, b bool) {
+ if c.sanitize != nil {
+ c.sanitize.SetSanitizer(t, b)
+ }
+}
+
+func (c *Module) SetSanitizeDep(b bool) {
+ if c.sanitize != nil {
+ c.sanitize.Properties.SanitizeDep = b
+ }
+}
+
+var _ PlatformSanitizeable = (*Module)(nil)
+
// Create sanitized variants for modules that need them
-func sanitizerMutator(t sanitizerType) func(android.BottomUpMutatorContext) {
+func sanitizerMutator(t SanitizerType) func(android.BottomUpMutatorContext) {
return func(mctx android.BottomUpMutatorContext) {
- if c, ok := mctx.Module().(*Module); ok && c.sanitize != nil {
- if c.isDependencyRoot() && c.sanitize.isSanitizerEnabled(t) {
+ if c, ok := mctx.Module().(PlatformSanitizeable); ok && c.SanitizePropDefined() {
+ if c.IsDependencyRoot() && c.IsSanitizerEnabled(t) {
modules := mctx.CreateVariations(t.variationName())
- modules[0].(*Module).sanitize.SetSanitizer(t, true)
- } else if c.sanitize.isSanitizerEnabled(t) || c.sanitize.Properties.SanitizeDep {
- isSanitizerEnabled := c.sanitize.isSanitizerEnabled(t)
- if c.static() || c.header() || t == asan || t == fuzzer {
+ modules[0].(PlatformSanitizeable).SetSanitizer(t, true)
+ } else if c.IsSanitizerEnabled(t) || c.SanitizeDep() {
+ isSanitizerEnabled := c.IsSanitizerEnabled(t)
+ if c.StaticallyLinked() || c.Header() || t == Asan || t == Fuzzer {
// Static and header libs are split into non-sanitized and sanitized variants.
// Shared libs are not split. However, for asan and fuzzer, we split even for shared
// libs because a library sanitized for asan/fuzzer can't be linked from a library
@@ -1121,17 +1259,20 @@
// module. By setting it to the name of the sanitized variation, the dangling dependency
// is redirected to the sanitized variant of the dependent module.
defaultVariation := t.variationName()
+ // Not all PlatformSanitizeable modules support the CFI sanitizer
+ cfiSupported := mctx.Module().(PlatformSanitizeable).SanitizerSupported(cfi)
mctx.SetDefaultDependencyVariation(&defaultVariation)
- modules := mctx.CreateVariations("", t.variationName())
- modules[0].(*Module).sanitize.SetSanitizer(t, false)
- modules[1].(*Module).sanitize.SetSanitizer(t, true)
- modules[0].(*Module).sanitize.Properties.SanitizeDep = false
- modules[1].(*Module).sanitize.Properties.SanitizeDep = false
- if mctx.Device() && t.incompatibleWithCfi() {
+ modules := mctx.CreateVariations("", t.variationName())
+ modules[0].(PlatformSanitizeable).SetSanitizer(t, false)
+ modules[1].(PlatformSanitizeable).SetSanitizer(t, true)
+ modules[0].(PlatformSanitizeable).SetSanitizeDep(false)
+ modules[1].(PlatformSanitizeable).SetSanitizeDep(false)
+
+ if mctx.Device() && t.incompatibleWithCfi() && cfiSupported {
// TODO: Make sure that cfi mutator runs "after" any of the sanitizers that
// are incompatible with cfi
- modules[1].(*Module).sanitize.SetSanitizer(cfi, false)
+ modules[1].(PlatformSanitizeable).SetSanitizer(cfi, false)
}
// For cfi/scs/hwasan, we can export both sanitized and un-sanitized variants
@@ -1139,46 +1280,48 @@
// For other types of sanitizers, suppress the variation that is disabled.
if t != cfi && t != scs && t != hwasan {
if isSanitizerEnabled {
- modules[0].(*Module).Properties.PreventInstall = true
- modules[0].(*Module).Properties.HideFromMake = true
+ modules[0].(PlatformSanitizeable).SetPreventInstall()
+ modules[0].(PlatformSanitizeable).SetHideFromMake()
} else {
- modules[1].(*Module).Properties.PreventInstall = true
- modules[1].(*Module).Properties.HideFromMake = true
+ modules[1].(PlatformSanitizeable).SetPreventInstall()
+ modules[1].(PlatformSanitizeable).SetHideFromMake()
}
}
// Export the static lib name to make
- if c.static() && c.ExportedToMake() {
+ if c.StaticallyLinked() && c.ExportedToMake() {
if t == cfi {
- cfiStaticLibs(mctx.Config()).add(c, c.Name())
+ cfiStaticLibs(mctx.Config()).add(c, c.Module().Name())
} else if t == hwasan {
- hwasanStaticLibs(mctx.Config()).add(c, c.Name())
+ hwasanStaticLibs(mctx.Config()).add(c, c.Module().Name())
}
}
} else {
// Shared libs are not split. Only the sanitized variant is created.
modules := mctx.CreateVariations(t.variationName())
- modules[0].(*Module).sanitize.SetSanitizer(t, true)
- modules[0].(*Module).sanitize.Properties.SanitizeDep = false
+ modules[0].(PlatformSanitizeable).SetSanitizer(t, true)
+ modules[0].(PlatformSanitizeable).SetSanitizeDep(false)
// locate the asan libraries under /data/asan
- if mctx.Device() && t == asan && isSanitizerEnabled {
- modules[0].(*Module).sanitize.Properties.InSanitizerDir = true
+ if mctx.Device() && t == Asan && isSanitizerEnabled {
+ modules[0].(PlatformSanitizeable).SetInSanitizerDir()
}
if mctx.Device() && t.incompatibleWithCfi() {
// TODO: Make sure that cfi mutator runs "after" any of the sanitizers that
// are incompatible with cfi
- modules[0].(*Module).sanitize.SetSanitizer(cfi, false)
+ modules[0].(PlatformSanitizeable).SetSanitizer(cfi, false)
}
}
}
- c.sanitize.Properties.SanitizeDep = false
+ c.SetSanitizeDep(false)
} else if sanitizeable, ok := mctx.Module().(Sanitizeable); ok && sanitizeable.IsSanitizerEnabled(mctx, t.name()) {
// APEX modules fall here
sanitizeable.AddSanitizerDependencies(mctx, t.name())
mctx.CreateVariations(t.variationName())
} else if c, ok := mctx.Module().(*Module); ok {
+ //TODO: When Rust modules have vendor support, enable this path for PlatformSanitizeable
+
// Check if it's a snapshot module supporting sanitizer
if s, ok := c.linker.(snapshotSanitizer); ok && s.isSanitizerEnabled(t) {
// Set default variation as above.
@@ -1203,23 +1346,23 @@
type sanitizerStaticLibsMap struct {
// libsMap contains one list of modules per each image and each arch.
// e.g. libs[vendor]["arm"] contains arm modules installed to vendor
- libsMap map[imageVariantType]map[string][]string
+ libsMap map[ImageVariantType]map[string][]string
libsMapLock sync.Mutex
- sanitizerType sanitizerType
+ sanitizerType SanitizerType
}
-func newSanitizerStaticLibsMap(t sanitizerType) *sanitizerStaticLibsMap {
+func newSanitizerStaticLibsMap(t SanitizerType) *sanitizerStaticLibsMap {
return &sanitizerStaticLibsMap{
sanitizerType: t,
- libsMap: make(map[imageVariantType]map[string][]string),
+ libsMap: make(map[ImageVariantType]map[string][]string),
}
}
// Add the current module to sanitizer static libs maps
// Each module should pass its exported name as names of Make and Soong can differ.
-func (s *sanitizerStaticLibsMap) add(c *Module, name string) {
- image := c.getImageVariantType()
- arch := c.Arch().ArchType.String()
+func (s *sanitizerStaticLibsMap) add(c LinkableInterface, name string) {
+ image := GetImageVariantType(c)
+ arch := c.Module().Target().Arch.ArchType.String()
s.libsMapLock.Lock()
defer s.libsMapLock.Unlock()
@@ -1238,7 +1381,7 @@
// See build/make/core/binary.mk for more details.
func (s *sanitizerStaticLibsMap) exportToMake(ctx android.MakeVarsContext) {
for _, image := range android.SortedStringKeys(s.libsMap) {
- archMap := s.libsMap[imageVariantType(image)]
+ archMap := s.libsMap[ImageVariantType(image)]
for _, arch := range android.SortedStringKeys(archMap) {
libs := archMap[arch]
sort.Strings(libs)
diff --git a/cc/snapshot_prebuilt.go b/cc/snapshot_prebuilt.go
index 8c5d1a4..8979846 100644
--- a/cc/snapshot_prebuilt.go
+++ b/cc/snapshot_prebuilt.go
@@ -94,6 +94,8 @@
android.RegisterModuleType("vendor_snapshot_header", VendorSnapshotHeaderFactory)
android.RegisterModuleType("vendor_snapshot_binary", VendorSnapshotBinaryFactory)
android.RegisterModuleType("vendor_snapshot_object", VendorSnapshotObjectFactory)
+
+ android.RegisterSingletonType("vendor-fake-snapshot", VendorFakeSnapshotSingleton)
}
func (vendorSnapshotImage) shouldGenerateSnapshot(ctx android.SingletonContext) bool {
@@ -102,7 +104,7 @@
}
func (vendorSnapshotImage) inImage(m *Module) func() bool {
- return m.inVendor
+ return m.InVendor
}
func (vendorSnapshotImage) available(m *Module) *bool {
@@ -505,8 +507,8 @@
}
type snapshotSanitizer interface {
- isSanitizerEnabled(t sanitizerType) bool
- setSanitizerVariation(t sanitizerType, enabled bool)
+ isSanitizerEnabled(t SanitizerType) bool
+ setSanitizerVariation(t SanitizerType, enabled bool)
}
type snapshotLibraryDecorator struct {
@@ -544,7 +546,7 @@
func (p *snapshotLibraryDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps, objs Objects) android.Path {
m := ctx.Module().(*Module)
- if m.inVendor() && vendorSuffixModules(ctx.Config())[m.BaseModuleName()] {
+ if m.InVendor() && vendorSuffixModules(ctx.Config())[m.BaseModuleName()] {
p.androidMkSuffix = vendorSuffix
} else if m.InRecovery() && recoverySuffixModules(ctx.Config())[m.BaseModuleName()] {
p.androidMkSuffix = recoverySuffix
@@ -611,7 +613,7 @@
return false
}
-func (p *snapshotLibraryDecorator) isSanitizerEnabled(t sanitizerType) bool {
+func (p *snapshotLibraryDecorator) isSanitizerEnabled(t SanitizerType) bool {
switch t {
case cfi:
return p.sanitizerProperties.Cfi.Src != nil
@@ -620,7 +622,7 @@
}
}
-func (p *snapshotLibraryDecorator) setSanitizerVariation(t sanitizerType, enabled bool) {
+func (p *snapshotLibraryDecorator) setSanitizerVariation(t SanitizerType, enabled bool) {
if !enabled {
return
}
@@ -767,7 +769,7 @@
binName := in.Base()
m := ctx.Module().(*Module)
- if m.inVendor() && vendorSuffixModules(ctx.Config())[m.BaseModuleName()] {
+ if m.InVendor() && vendorSuffixModules(ctx.Config())[m.BaseModuleName()] {
p.androidMkSuffix = vendorSuffix
} else if m.InRecovery() && recoverySuffixModules(ctx.Config())[m.BaseModuleName()] {
p.androidMkSuffix = recoverySuffix
@@ -866,7 +868,7 @@
m := ctx.Module().(*Module)
- if m.inVendor() && vendorSuffixModules(ctx.Config())[m.BaseModuleName()] {
+ if m.InVendor() && vendorSuffixModules(ctx.Config())[m.BaseModuleName()] {
p.androidMkSuffix = vendorSuffix
} else if m.InRecovery() && recoverySuffixModules(ctx.Config())[m.BaseModuleName()] {
p.androidMkSuffix = recoverySuffix
diff --git a/cc/snapshot_utils.go b/cc/snapshot_utils.go
index 77d82f1..3e6444b 100644
--- a/cc/snapshot_utils.go
+++ b/cc/snapshot_utils.go
@@ -77,9 +77,12 @@
}
if _, _, ok := isVndkSnapshotAware(ctx.DeviceConfig(), m, apexInfo); ok {
return ctx.Config().VndkSnapshotBuildArtifacts()
- } else if isVendorSnapshotAware(m, isVendorProprietaryPath(ctx.ModuleDir()), apexInfo) ||
- isRecoverySnapshotAware(m, isRecoveryProprietaryPath(ctx.ModuleDir()), apexInfo) {
- return true
+ }
+
+ for _, image := range []snapshotImage{vendorSnapshotImageSingleton, recoverySnapshotImageSingleton} {
+ if isSnapshotAware(m, image.isProprietaryPath(ctx.ModuleDir()), apexInfo, image) {
+ return true
+ }
}
return false
}
diff --git a/cc/test.go b/cc/test.go
index 4ff5bf6..f715a8d 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -236,6 +236,10 @@
return BoolDefault(test.Properties.Gtest, true)
}
+func (test *testDecorator) testBinary() bool {
+ return true
+}
+
func (test *testDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags {
if !test.gtest() {
return flags
diff --git a/cc/testing.go b/cc/testing.go
index fc5b030..903f76c 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -299,7 +299,7 @@
llndk_library {
name: "libft2.llndk",
symbol_file: "",
- vendor_available: false,
+ private: true,
sdk_version: "current",
}
cc_library {
@@ -445,6 +445,14 @@
stl: "none",
system_shared_libs: [],
}
+
+ cc_library_static {
+ name: "note_memtag_heap_async",
+ }
+
+ cc_library_static {
+ name: "note_memtag_heap_sync",
+ }
`
supportLinuxBionic := false
@@ -568,15 +576,16 @@
ctx.RegisterModuleType("vendor_public_library", vendorPublicLibraryFactory)
ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
ctx.RegisterModuleType("vndk_prebuilt_shared", VndkPrebuiltSharedFactory)
- ctx.RegisterModuleType("vndk_libraries_txt", VndkLibrariesTxtFactory)
ctx.RegisterModuleType("vendor_snapshot_shared", VendorSnapshotSharedFactory)
ctx.RegisterModuleType("vendor_snapshot_static", VendorSnapshotStaticFactory)
ctx.RegisterModuleType("vendor_snapshot_binary", VendorSnapshotBinaryFactory)
+ RegisterVndkLibraryTxtTypes(ctx)
ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
android.RegisterPrebuiltMutators(ctx)
RegisterRequiredBuildComponentsForTest(ctx)
ctx.RegisterSingletonType("vndk-snapshot", VndkSnapshotSingleton)
ctx.RegisterSingletonType("vendor-snapshot", VendorSnapshotSingleton)
+ ctx.RegisterSingletonType("vendor-fake-snapshot", VendorFakeSnapshotSingleton)
ctx.RegisterSingletonType("recovery-snapshot", RecoverySnapshotSingleton)
return ctx
diff --git a/cc/vendor_snapshot.go b/cc/vendor_snapshot.go
index 417516b..0a89e47 100644
--- a/cc/vendor_snapshot.go
+++ b/cc/vendor_snapshot.go
@@ -34,6 +34,16 @@
android.OptionalPath{},
true,
vendorSnapshotImageSingleton,
+ false, /* fake */
+}
+
+var vendorFakeSnapshotSingleton = snapshotSingleton{
+ "vendor",
+ "SOONG_VENDOR_FAKE_SNAPSHOT_ZIP",
+ android.OptionalPath{},
+ true,
+ vendorSnapshotImageSingleton,
+ true, /* fake */
}
var recoverySnapshotSingleton = snapshotSingleton{
@@ -42,12 +52,17 @@
android.OptionalPath{},
false,
recoverySnapshotImageSingleton,
+ false, /* fake */
}
func VendorSnapshotSingleton() android.Singleton {
return &vendorSnapshotSingleton
}
+func VendorFakeSnapshotSingleton() android.Singleton {
+ return &vendorFakeSnapshotSingleton
+}
+
func RecoverySnapshotSingleton() android.Singleton {
return &recoverySnapshotSingleton
}
@@ -70,6 +85,11 @@
// associated with this snapshot (e.g., specific to the vendor image,
// recovery image, etc.).
image snapshotImage
+
+ // Whether this singleton is for fake snapshot or not.
+ // Fake snapshot is a snapshot whose prebuilt binaries and headers are empty.
+ // It is much faster to generate, and can be used to inspect dependencies.
+ fake bool
}
var (
@@ -177,25 +197,6 @@
return false
}
-// Determine if a module is going to be included in vendor snapshot or not.
-//
-// Targets of vendor snapshot are "vendor: true" or "vendor_available: true" modules in
-// AOSP. They are not guaranteed to be compatible with older vendor images. (e.g. might
-// depend on newer VNDK) So they are captured as vendor snapshot To build older vendor
-// image and newer system image altogether.
-func isVendorSnapshotAware(m *Module, inVendorProprietaryPath bool, apexInfo android.ApexInfo) bool {
- return isSnapshotAware(m, inVendorProprietaryPath, apexInfo, vendorSnapshotImageSingleton)
-}
-
-// Determine if a module is going to be included in recovery snapshot or not.
-//
-// Targets of recovery snapshot are "recovery: true" or "recovery_available: true"
-// modules in AOSP. They are not guaranteed to be compatible with older recovery images.
-// So they are captured as recovery snapshot To build older recovery image.
-func isRecoverySnapshotAware(m *Module, inRecoveryProprietaryPath bool, apexInfo android.ApexInfo) bool {
- return isSnapshotAware(m, inRecoveryProprietaryPath, apexInfo, recoverySnapshotImageSingleton)
-}
-
// Determines if the module is a candidate for snapshot.
func isSnapshotAware(m *Module, inProprietaryPath bool, apexInfo android.ApexInfo, image snapshotImage) bool {
if !m.Enabled() || m.Properties.HideFromMake {
@@ -246,7 +247,7 @@
if m.sanitize != nil {
// scs and hwasan export both sanitized and unsanitized variants for static and header
// Always use unsanitized variants of them.
- for _, t := range []sanitizerType{scs, hwasan} {
+ for _, t := range []SanitizerType{scs, hwasan} {
if !l.shared() && m.sanitize.isSanitizerEnabled(t) {
return false
}
@@ -351,6 +352,11 @@
*/
snapshotDir := c.name + "-snapshot"
+ if c.fake {
+ // If this is a fake snapshot singleton, place all files under fake/ subdirectory to avoid
+ // collision with real snapshot files
+ snapshotDir = filepath.Join("fake", snapshotDir)
+ }
snapshotArchDir := filepath.Join(snapshotDir, ctx.DeviceConfig().DeviceArch())
includeDir := filepath.Join(snapshotArchDir, "include")
@@ -362,6 +368,15 @@
var headers android.Paths
+ copyFile := copyFileRule
+ if c.fake {
+ // All prebuilt binaries and headers are installed by copyFile function. This makes a fake
+ // snapshot just touch prebuilts and headers, rather than installing real files.
+ copyFile = func(ctx android.SingletonContext, path android.Path, out string) android.OutputPath {
+ return writeStringToFileRule(ctx, "", out)
+ }
+ }
+
// installSnapshot function copies prebuilt file (.so, .a, or executable) and json flag file.
// For executables, init_rc and vintf_fragments files are also copied.
installSnapshot := func(m *Module) android.Paths {
@@ -400,7 +415,7 @@
out := filepath.Join(configsDir, path.Base())
if !installedConfigs[out] {
installedConfigs[out] = true
- ret = append(ret, copyFileRule(ctx, path, out))
+ ret = append(ret, copyFile(ctx, path, out))
}
}
@@ -451,7 +466,7 @@
prop.ModuleName += ".cfi"
}
snapshotLibOut := filepath.Join(snapshotArchDir, targetArch, libType, stem)
- ret = append(ret, copyFileRule(ctx, libPath, snapshotLibOut))
+ ret = append(ret, copyFile(ctx, libPath, snapshotLibOut))
} else {
stem = ctx.ModuleName(m)
}
@@ -465,7 +480,7 @@
// install bin
binPath := m.outputFile.Path()
snapshotBinOut := filepath.Join(snapshotArchDir, targetArch, "binary", binPath.Base())
- ret = append(ret, copyFileRule(ctx, binPath, snapshotBinOut))
+ ret = append(ret, copyFile(ctx, binPath, snapshotBinOut))
propOut = snapshotBinOut + ".json"
} else if m.object() {
// object files aren't installed to the device, so their names can conflict.
@@ -473,7 +488,7 @@
objPath := m.outputFile.Path()
snapshotObjOut := filepath.Join(snapshotArchDir, targetArch, "object",
ctx.ModuleName(m)+filepath.Ext(objPath.Base()))
- ret = append(ret, copyFileRule(ctx, objPath, snapshotObjOut))
+ ret = append(ret, copyFile(ctx, objPath, snapshotObjOut))
propOut = snapshotObjOut + ".json"
} else {
ctx.Errorf("unknown module %q in vendor snapshot", m.String())
@@ -538,16 +553,14 @@
// skip already copied notice file
if !installedNotices[noticeOut] {
installedNotices[noticeOut] = true
- snapshotOutputs = append(snapshotOutputs, combineNoticesRule(
- ctx, m.NoticeFiles(), noticeOut))
+ snapshotOutputs = append(snapshotOutputs, combineNoticesRule(ctx, m.NoticeFiles(), noticeOut))
}
}
})
// install all headers after removing duplicates
for _, header := range android.FirstUniquePaths(headers) {
- snapshotOutputs = append(snapshotOutputs, copyFileRule(
- ctx, header, filepath.Join(includeDir, header.String())))
+ snapshotOutputs = append(snapshotOutputs, copyFile(ctx, header, filepath.Join(includeDir, header.String())))
}
// All artifacts are ready. Sort them to normalize ninja and then zip.
diff --git a/cc/vndk.go b/cc/vndk.go
index 2071a03..daae1f7 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -302,7 +302,7 @@
llndkLibraries(mctx.Config())[name] = filename
m.VendorProperties.IsLLNDK = true
- if !Bool(lib.Properties.Vendor_available) {
+ if Bool(lib.Properties.Private) {
vndkPrivateLibraries(mctx.Config())[name] = filename
m.VendorProperties.IsLLNDKPrivate = true
}
@@ -349,7 +349,7 @@
if m.IsVndkPrivate() {
vndkPrivateLibraries(mctx.Config())[name] = filename
}
- if m.VendorProperties.Product_available != nil {
+ if Bool(m.VendorProperties.Product_available) {
vndkProductLibraries(mctx.Config())[name] = filename
}
}
@@ -394,7 +394,7 @@
useCoreVariant := m.VndkVersion() == mctx.DeviceConfig().PlatformVndkVersion() &&
mctx.DeviceConfig().VndkUseCoreVariant() && !m.MustUseVendorVariant()
- return lib.shared() && m.inVendor() && m.IsVndk() && !m.IsVndkExt() && !useCoreVariant
+ return lib.shared() && m.InVendor() && m.IsVndk() && !m.IsVndkExt() && !useCoreVariant
}
return false
}
@@ -449,15 +449,32 @@
}
func init() {
- android.RegisterModuleType("vndk_libraries_txt", VndkLibrariesTxtFactory)
+ RegisterVndkLibraryTxtTypes(android.InitRegistrationContext)
android.RegisterSingletonType("vndk-snapshot", VndkSnapshotSingleton)
}
+func RegisterVndkLibraryTxtTypes(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("llndk_libraries_txt", VndkLibrariesTxtFactory(libclangRTRemover(llndkLibraries)))
+ ctx.RegisterModuleType("vndksp_libraries_txt", VndkLibrariesTxtFactory(vndkSpLibraries))
+ ctx.RegisterModuleType("vndkcore_libraries_txt", VndkLibrariesTxtFactory(vndkCoreLibraries))
+ ctx.RegisterModuleType("vndkprivate_libraries_txt", VndkLibrariesTxtFactory(vndkPrivateLibraries))
+ ctx.RegisterModuleType("vndkproduct_libraries_txt", VndkLibrariesTxtFactory(vndkProductLibraries))
+ ctx.RegisterModuleType("vndkcorevariant_libraries_txt", VndkLibrariesTxtFactory(vndkUsingCoreVariantLibraries))
+}
+
type vndkLibrariesTxt struct {
android.ModuleBase
+
+ lister func(android.Config) map[string]string
+ properties VndkLibrariesTxtProperties
+
outputFile android.OutputPath
}
+type VndkLibrariesTxtProperties struct {
+ Insert_vndk_version *bool
+}
+
var _ etc.PrebuiltEtcModule = &vndkLibrariesTxt{}
var _ android.OutputFileProducer = &vndkLibrariesTxt{}
@@ -471,10 +488,15 @@
// A module behaves like a prebuilt_etc but its content is generated by soong.
// By being a soong module, these files can be referenced by other soong modules.
// For example, apex_vndk can depend on these files as prebuilt.
-func VndkLibrariesTxtFactory() android.Module {
- m := &vndkLibrariesTxt{}
- android.InitAndroidModule(m)
- return m
+func VndkLibrariesTxtFactory(lister func(android.Config) map[string]string) android.ModuleFactory {
+ return func() android.Module {
+ m := &vndkLibrariesTxt{
+ lister: lister,
+ }
+ m.AddProperties(&m.properties)
+ android.InitAndroidModule(m)
+ return m
+ }
}
func insertVndkVersion(filename string, vndkVersion string) string {
@@ -484,33 +506,25 @@
return filename
}
-func (txt *vndkLibrariesTxt) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- var list []string
- switch txt.Name() {
- case llndkLibrariesTxt:
- for _, filename := range android.SortedStringMapValues(llndkLibraries(ctx.Config())) {
- if strings.HasPrefix(filename, "libclang_rt.hwasan-") {
+func libclangRTRemover(lister func(android.Config) map[string]string) func(android.Config) map[string]string {
+ return func(config android.Config) map[string]string {
+ libs := lister(config)
+ filteredLibs := make(map[string]string, len(libs))
+ for lib, v := range libs {
+ if strings.HasPrefix(lib, "libclang_rt.hwasan-") {
continue
}
- list = append(list, filename)
+ filteredLibs[lib] = v
}
- case vndkCoreLibrariesTxt:
- list = android.SortedStringMapValues(vndkCoreLibraries(ctx.Config()))
- case vndkSpLibrariesTxt:
- list = android.SortedStringMapValues(vndkSpLibraries(ctx.Config()))
- case vndkPrivateLibrariesTxt:
- list = android.SortedStringMapValues(vndkPrivateLibraries(ctx.Config()))
- case vndkProductLibrariesTxt:
- list = android.SortedStringMapValues(vndkProductLibraries(ctx.Config()))
- case vndkUsingCoreVariantLibrariesTxt:
- list = android.SortedStringMapValues(vndkUsingCoreVariantLibraries(ctx.Config()))
- default:
- ctx.ModuleErrorf("name(%s) is unknown.", txt.Name())
- return
+ return filteredLibs
}
+}
+
+func (txt *vndkLibrariesTxt) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ list := android.SortedStringMapValues(txt.lister(ctx.Config()))
var filename string
- if txt.Name() != vndkUsingCoreVariantLibrariesTxt {
+ if BoolDefault(txt.properties.Insert_vndk_version, true) {
filename = insertVndkVersion(txt.Name(), ctx.DeviceConfig().PlatformVndkVersion())
} else {
filename = txt.Name()
@@ -572,7 +586,7 @@
// !inVendor: There's product/vendor variants for VNDK libs. We only care about vendor variants.
// !installable: Snapshot only cares about "installable" modules.
// isSnapshotPrebuilt: Snapshotting a snapshot doesn't make sense.
- if !m.inVendor() || !m.installable(apexInfo) || m.isSnapshotPrebuilt() {
+ if !m.InVendor() || !m.installable(apexInfo) || m.isSnapshotPrebuilt() {
return nil, "", false
}
l, ok := m.linker.(snapshotLibraryInterface)
diff --git a/cmd/soong_build/Android.bp b/cmd/soong_build/Android.bp
index 441ea0d..6714978 100644
--- a/cmd/soong_build/Android.bp
+++ b/cmd/soong_build/Android.bp
@@ -20,6 +20,7 @@
"golang-protobuf-proto",
"soong",
"soong-android",
+ "soong-bp2build",
"soong-env",
"soong-ui-metrics_proto",
],
@@ -27,10 +28,6 @@
"main.go",
"writedocs.go",
"queryview.go",
- "queryview_templates.go",
- ],
- testSrcs: [
- "queryview_test.go",
],
primaryBuilder: true,
}
diff --git a/cmd/soong_build/queryview.go b/cmd/soong_build/queryview.go
index f5aa685..3657ea8 100644
--- a/cmd/soong_build/queryview.go
+++ b/cmd/soong_build/queryview.go
@@ -16,512 +16,82 @@
import (
"android/soong/android"
- "fmt"
+ "android/soong/bp2build"
"io/ioutil"
"os"
"path/filepath"
- "reflect"
- "strings"
"github.com/google/blueprint"
- "github.com/google/blueprint/bootstrap/bpdoc"
- "github.com/google/blueprint/proptools"
)
-var (
- // An allowlist of prop types that are surfaced from module props to rule
- // attributes. (nested) dictionaries are notably absent here, because while
- // Soong supports multi value typed and nested dictionaries, Bazel's rule
- // attr() API supports only single-level string_dicts.
- allowedPropTypes = map[string]bool{
- "int": true, // e.g. 42
- "bool": true, // e.g. True
- "string_list": true, // e.g. ["a", "b"]
- "string": true, // e.g. "a"
- }
-
- // Certain module property names are blocklisted/ignored here, for the reasons commented.
- ignoredPropNames = map[string]bool{
- "name": true, // redundant, since this is explicitly generated for every target
- "from": true, // reserved keyword
- "in": true, // reserved keyword
- "arch": true, // interface prop type is not supported yet.
- "multilib": true, // interface prop type is not supported yet.
- "target": true, // interface prop type is not supported yet.
- "visibility": true, // Bazel has native visibility semantics. Handle later.
- "features": true, // There is already a built-in attribute 'features' which cannot be overridden.
- }
-)
-
-func targetNameWithVariant(c *blueprint.Context, logicModule blueprint.Module) string {
- name := ""
- if c.ModuleSubDir(logicModule) != "" {
- // TODO(b/162720883): Figure out a way to drop the "--" variant suffixes.
- name = c.ModuleName(logicModule) + "--" + c.ModuleSubDir(logicModule)
- } else {
- name = c.ModuleName(logicModule)
- }
-
- return strings.Replace(name, "//", "", 1)
+type queryviewContext struct {
+ bpCtx *blueprint.Context
}
-func qualifiedTargetLabel(c *blueprint.Context, logicModule blueprint.Module) string {
- return "//" +
- packagePath(c, logicModule) +
- ":" +
- targetNameWithVariant(c, logicModule)
+func (ctx *queryviewContext) ModuleName(module blueprint.Module) string {
+ return ctx.bpCtx.ModuleName(module)
}
-func packagePath(c *blueprint.Context, logicModule blueprint.Module) string {
- return filepath.Dir(c.BlueprintFile(logicModule))
+func (ctx *queryviewContext) ModuleDir(module blueprint.Module) string {
+ return ctx.bpCtx.ModuleDir(module)
}
-func escapeString(s string) string {
- s = strings.ReplaceAll(s, "\\", "\\\\")
- return strings.ReplaceAll(s, "\"", "\\\"")
+func (ctx *queryviewContext) ModuleSubDir(module blueprint.Module) string {
+ return ctx.bpCtx.ModuleSubDir(module)
}
-func makeIndent(indent int) string {
- if indent < 0 {
- panic(fmt.Errorf("indent column cannot be less than 0, but got %d", indent))
- }
- return strings.Repeat(" ", indent)
+func (ctx *queryviewContext) ModuleType(module blueprint.Module) string {
+ return ctx.bpCtx.ModuleType(module)
}
-// prettyPrint a property value into the equivalent Starlark representation
-// recursively.
-func prettyPrint(propertyValue reflect.Value, indent int) (string, error) {
- if isZero(propertyValue) {
- // A property value being set or unset actually matters -- Soong does set default
- // values for unset properties, like system_shared_libs = ["libc", "libm", "libdl"] at
- // https://cs.android.com/android/platform/superproject/+/master:build/soong/cc/linker.go;l=281-287;drc=f70926eef0b9b57faf04c17a1062ce50d209e480
- //
- // In Bazel-parlance, we would use "attr.<type>(default = <default value>)" to set the default
- // value of unset attributes.
- return "", nil
- }
-
- var ret string
- switch propertyValue.Kind() {
- case reflect.String:
- ret = fmt.Sprintf("\"%v\"", escapeString(propertyValue.String()))
- case reflect.Bool:
- ret = strings.Title(fmt.Sprintf("%v", propertyValue.Interface()))
- case reflect.Int, reflect.Uint, reflect.Int64:
- ret = fmt.Sprintf("%v", propertyValue.Interface())
- case reflect.Ptr:
- return prettyPrint(propertyValue.Elem(), indent)
- case reflect.Slice:
- ret = "[\n"
- for i := 0; i < propertyValue.Len(); i++ {
- indexedValue, err := prettyPrint(propertyValue.Index(i), indent+1)
- if err != nil {
- return "", err
- }
-
- if indexedValue != "" {
- ret += makeIndent(indent + 1)
- ret += indexedValue
- ret += ",\n"
- }
- }
- ret += makeIndent(indent)
- ret += "]"
- case reflect.Struct:
- ret = "{\n"
- // Sort and print the struct props by the key.
- structProps := extractStructProperties(propertyValue, indent)
- for _, k := range android.SortedStringKeys(structProps) {
- ret += makeIndent(indent + 1)
- ret += fmt.Sprintf("%q: %s,\n", k, structProps[k])
- }
- ret += makeIndent(indent)
- ret += "}"
- case reflect.Interface:
- // TODO(b/164227191): implement pretty print for interfaces.
- // Interfaces are used for for arch, multilib and target properties.
- return "", nil
- default:
- return "", fmt.Errorf(
- "unexpected kind for property struct field: %s", propertyValue.Kind())
- }
- return ret, nil
+func (ctx *queryviewContext) VisitAllModulesBlueprint(visit func(blueprint.Module)) {
+ ctx.bpCtx.VisitAllModules(visit)
}
-// Converts a reflected property struct value into a map of property names and property values,
-// which each property value correctly pretty-printed and indented at the right nest level,
-// since property structs can be nested. In Starlark, nested structs are represented as nested
-// dicts: https://docs.bazel.build/skylark/lib/dict.html
-func extractStructProperties(structValue reflect.Value, indent int) map[string]string {
- if structValue.Kind() != reflect.Struct {
- panic(fmt.Errorf("Expected a reflect.Struct type, but got %s", structValue.Kind()))
- }
-
- ret := map[string]string{}
- structType := structValue.Type()
- for i := 0; i < structValue.NumField(); i++ {
- field := structType.Field(i)
- if field.PkgPath != "" {
- // Skip unexported fields. Some properties are
- // internal to Soong only, and these fields do not have PkgPath.
- continue
+func (ctx *queryviewContext) VisitDirectDeps(module android.Module, visit func(android.Module)) {
+ ctx.bpCtx.VisitDirectDeps(module, func(m blueprint.Module) {
+ if aModule, ok := m.(android.Module); ok {
+ visit(aModule)
}
- if proptools.HasTag(field, "blueprint", "mutated") {
- continue
- }
-
- fieldValue := structValue.Field(i)
- if isZero(fieldValue) {
- // Ignore zero-valued fields
- continue
- }
-
- propertyName := proptools.PropertyNameForField(field.Name)
- prettyPrintedValue, err := prettyPrint(fieldValue, indent+1)
- if err != nil {
- panic(
- fmt.Errorf(
- "Error while parsing property: %q. %s",
- propertyName,
- err))
- }
- if prettyPrintedValue != "" {
- ret[propertyName] = prettyPrintedValue
- }
- }
-
- return ret
-}
-
-func isStructPtr(t reflect.Type) bool {
- return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct
-}
-
-// Generically extract module properties and types into a map, keyed by the module property name.
-func extractModuleProperties(aModule android.Module) map[string]string {
- ret := map[string]string{}
-
- // Iterate over this android.Module's property structs.
- for _, properties := range aModule.GetProperties() {
- propertiesValue := reflect.ValueOf(properties)
- // Check that propertiesValue is a pointer to the Properties struct, like
- // *cc.BaseLinkerProperties or *java.CompilerProperties.
- //
- // propertiesValue can also be type-asserted to the structs to
- // manipulate internal props, if needed.
- if isStructPtr(propertiesValue.Type()) {
- structValue := propertiesValue.Elem()
- for k, v := range extractStructProperties(structValue, 0) {
- ret[k] = v
- }
- } else {
- panic(fmt.Errorf(
- "properties must be a pointer to a struct, got %T",
- propertiesValue.Interface()))
- }
-
- }
-
- return ret
-}
-
-// FIXME(b/168089390): In Bazel, rules ending with "_test" needs to be marked as
-// testonly = True, forcing other rules that depend on _test rules to also be
-// marked as testonly = True. This semantic constraint is not present in Soong.
-// To work around, rename "*_test" rules to "*_test_".
-func canonicalizeModuleType(moduleName string) string {
- if strings.HasSuffix(moduleName, "_test") {
- return moduleName + "_"
- }
-
- return moduleName
-}
-
-type RuleShim struct {
- // The rule class shims contained in a bzl file. e.g. ["cc_object", "cc_library", ..]
- rules []string
-
- // The generated string content of the bzl file.
- content string
-}
-
-// Create <module>.bzl containing Bazel rule shims for every module type available in Soong and
-// user-specified Go plugins.
-//
-// This function reuses documentation generation APIs to ensure parity between modules-as-docs
-// and modules-as-code, including the names and types of module properties.
-func createRuleShims(packages []*bpdoc.Package) (map[string]RuleShim, error) {
- var propToAttr func(prop bpdoc.Property, propName string) string
- propToAttr = func(prop bpdoc.Property, propName string) string {
- // dots are not allowed in Starlark attribute names. Substitute them with double underscores.
- propName = strings.ReplaceAll(propName, ".", "__")
- if !shouldGenerateAttribute(propName) {
- return ""
- }
-
- // Canonicalize and normalize module property types to Bazel attribute types
- starlarkAttrType := prop.Type
- if starlarkAttrType == "list of string" {
- starlarkAttrType = "string_list"
- } else if starlarkAttrType == "int64" {
- starlarkAttrType = "int"
- } else if starlarkAttrType == "" {
- var attr string
- for _, nestedProp := range prop.Properties {
- nestedAttr := propToAttr(nestedProp, propName+"__"+nestedProp.Name)
- if nestedAttr != "" {
- // TODO(b/167662930): Fix nested props resulting in too many attributes.
- // Let's still generate these, but comment them out.
- attr += "# " + nestedAttr
- }
- }
- return attr
- }
-
- if !allowedPropTypes[starlarkAttrType] {
- return ""
- }
-
- return fmt.Sprintf(" %q: attr.%s(),\n", propName, starlarkAttrType)
- }
-
- ruleShims := map[string]RuleShim{}
- for _, pkg := range packages {
- content := "load(\"//build/bazel/queryview_rules:providers.bzl\", \"SoongModuleInfo\")\n"
-
- bzlFileName := strings.ReplaceAll(pkg.Path, "android/soong/", "")
- bzlFileName = strings.ReplaceAll(bzlFileName, ".", "_")
- bzlFileName = strings.ReplaceAll(bzlFileName, "/", "_")
-
- rules := []string{}
-
- for _, moduleTypeTemplate := range moduleTypeDocsToTemplates(pkg.ModuleTypes) {
- attrs := `{
- "module_name": attr.string(mandatory = True),
- "module_variant": attr.string(),
- "module_deps": attr.label_list(providers = [SoongModuleInfo]),
-`
- for _, prop := range moduleTypeTemplate.Properties {
- attrs += propToAttr(prop, prop.Name)
- }
-
- moduleTypeName := moduleTypeTemplate.Name
-
- // Certain SDK-related module types dynamically inject properties, instead of declaring
- // them as structs. These properties are registered in an SdkMemberTypesRegistry. If
- // the module type name matches, add these properties into the rule definition.
- var registeredTypes []android.SdkMemberType
- if moduleTypeName == "module_exports" || moduleTypeName == "module_exports_snapshot" {
- registeredTypes = android.ModuleExportsMemberTypes.RegisteredTypes()
- } else if moduleTypeName == "sdk" || moduleTypeName == "sdk_snapshot" {
- registeredTypes = android.SdkMemberTypes.RegisteredTypes()
- }
- for _, memberType := range registeredTypes {
- attrs += fmt.Sprintf(" %q: attr.string_list(),\n", memberType.SdkPropertyName())
- }
-
- attrs += " },"
-
- rule := canonicalizeModuleType(moduleTypeTemplate.Name)
- content += fmt.Sprintf(moduleRuleShim, rule, attrs)
- rules = append(rules, rule)
- }
-
- ruleShims[bzlFileName] = RuleShim{content: content, rules: rules}
- }
- return ruleShims, nil
+ })
}
func createBazelQueryView(ctx *android.Context, bazelQueryViewDir string) error {
- blueprintCtx := ctx.Context
- blueprintCtx.VisitAllModules(func(module blueprint.Module) {
- buildFile, err := buildFileForModule(blueprintCtx, module, bazelQueryViewDir)
- if err != nil {
- panic(err)
- }
-
- buildFile.Write([]byte(generateSoongModuleTarget(blueprintCtx, module) + "\n\n"))
- buildFile.Close()
- })
- var err error
-
- // Write top level files: WORKSPACE and BUILD. These files are empty.
- if err = writeReadOnlyFile(bazelQueryViewDir, "WORKSPACE", ""); err != nil {
- return err
+ qvCtx := queryviewContext{
+ bpCtx: ctx.Context,
}
+ ruleShims := bp2build.CreateRuleShims(android.ModuleTypeFactories())
+ buildToTargets := bp2build.GenerateSoongModuleTargets(&qvCtx)
- // Used to denote that the top level directory is a package.
- if err = writeReadOnlyFile(bazelQueryViewDir, "BUILD", ""); err != nil {
- return err
- }
-
- packages, err := getPackages(ctx)
- if err != nil {
- return err
- }
- ruleShims, err := createRuleShims(packages)
- if err != nil {
- return err
- }
-
- // Write .bzl Starlark files into the bazel_rules top level directory (provider and rule definitions)
- bazelRulesDir := bazelQueryViewDir + "/build/bazel/queryview_rules"
- if err = writeReadOnlyFile(bazelRulesDir, "BUILD", ""); err != nil {
- return err
- }
- if err = writeReadOnlyFile(bazelRulesDir, "providers.bzl", providersBzl); err != nil {
- return err
- }
-
- for bzlFileName, ruleShim := range ruleShims {
- if err = writeReadOnlyFile(bazelRulesDir, bzlFileName+".bzl", ruleShim.content); err != nil {
+ filesToWrite := bp2build.CreateBazelFiles(ruleShims, buildToTargets)
+ for _, f := range filesToWrite {
+ if err := writeReadOnlyFile(bazelQueryViewDir, f); err != nil {
return err
}
}
- return writeReadOnlyFile(bazelRulesDir, "soong_module.bzl", generateSoongModuleBzl(ruleShims))
+ return nil
}
-// Generate the content of soong_module.bzl with the rule shim load statements
-// and mapping of module_type to rule shim map for every module type in Soong.
-func generateSoongModuleBzl(bzlLoads map[string]RuleShim) string {
- var loadStmts string
- var moduleRuleMap string
- for bzlFileName, ruleShim := range bzlLoads {
- loadStmt := "load(\"//build/bazel/queryview_rules:"
- loadStmt += bzlFileName
- loadStmt += ".bzl\""
- for _, rule := range ruleShim.rules {
- loadStmt += fmt.Sprintf(", %q", rule)
- moduleRuleMap += " \"" + rule + "\": " + rule + ",\n"
- }
- loadStmt += ")\n"
- loadStmts += loadStmt
- }
-
- return fmt.Sprintf(soongModuleBzl, loadStmts, moduleRuleMap)
-}
-
-func shouldGenerateAttribute(prop string) bool {
- return !ignoredPropNames[prop]
-}
-
-// props is an unsorted map. This function ensures that
-// the generated attributes are sorted to ensure determinism.
-func propsToAttributes(props map[string]string) string {
- var attributes string
- for _, propName := range android.SortedStringKeys(props) {
- if shouldGenerateAttribute(propName) {
- attributes += fmt.Sprintf(" %s = %s,\n", propName, props[propName])
- }
- }
- return attributes
-}
-
-// Convert a module and its deps and props into a Bazel macro/rule
-// representation in the BUILD file.
-func generateSoongModuleTarget(
- blueprintCtx *blueprint.Context,
- module blueprint.Module) string {
-
- var props map[string]string
- if aModule, ok := module.(android.Module); ok {
- props = extractModuleProperties(aModule)
- }
- attributes := propsToAttributes(props)
-
- // TODO(b/163018919): DirectDeps can have duplicate (module, variant)
- // items, if the modules are added using different DependencyTag. Figure
- // out the implications of that.
- depLabels := map[string]bool{}
- blueprintCtx.VisitDirectDeps(module, func(depModule blueprint.Module) {
- depLabels[qualifiedTargetLabel(blueprintCtx, depModule)] = true
- })
-
- depLabelList := "[\n"
- for depLabel, _ := range depLabels {
- depLabelList += fmt.Sprintf(" %q,\n", depLabel)
- }
- depLabelList += " ]"
-
- return fmt.Sprintf(
- soongModuleTarget,
- targetNameWithVariant(blueprintCtx, module),
- blueprintCtx.ModuleName(module),
- canonicalizeModuleType(blueprintCtx.ModuleType(module)),
- blueprintCtx.ModuleSubDir(module),
- depLabelList,
- attributes)
-}
-
-func buildFileForModule(
- ctx *blueprint.Context, module blueprint.Module, bazelQueryViewDir string) (*os.File, error) {
- // Create nested directories for the BUILD file
- dirPath := filepath.Join(bazelQueryViewDir, packagePath(ctx, module))
- createDirectoryIfNonexistent(dirPath)
- // Open the file for appending, and create it if it doesn't exist
- f, err := os.OpenFile(
- filepath.Join(dirPath, "BUILD.bazel"),
- os.O_APPEND|os.O_CREATE|os.O_WRONLY,
- 0644)
- if err != nil {
- return nil, err
- }
-
- // If the file is empty, add the load statement for the `soong_module` rule
- fi, err := f.Stat()
- if err != nil {
- return nil, err
- }
- if fi.Size() == 0 {
- f.Write([]byte(soongModuleLoad + "\n"))
- }
-
- return f, nil
-}
-
-func createDirectoryIfNonexistent(dir string) {
- if _, err := os.Stat(dir); os.IsNotExist(err) {
- os.MkdirAll(dir, os.ModePerm)
- }
-}
-
-// The QueryView directory should be read-only, sufficient for bazel query. The files
+// The auto-conversion directory should be read-only, sufficient for bazel query. The files
// are not intended to be edited by end users.
-func writeReadOnlyFile(dir string, baseName string, content string) error {
- createDirectoryIfNonexistent(dir)
- pathToFile := filepath.Join(dir, baseName)
+func writeReadOnlyFile(dir string, f bp2build.BazelFile) error {
+ dir = filepath.Join(dir, f.Dir)
+ if err := createDirectoryIfNonexistent(dir); err != nil {
+ return err
+ }
+ pathToFile := filepath.Join(dir, f.Basename)
+
// 0444 is read-only
- return ioutil.WriteFile(pathToFile, []byte(content), 0444)
+ err := ioutil.WriteFile(pathToFile, []byte(f.Contents), 0444)
+
+ return err
}
-func isZero(value reflect.Value) bool {
- switch value.Kind() {
- case reflect.Func, reflect.Map, reflect.Slice:
- return value.IsNil()
- case reflect.Array:
- valueIsZero := true
- for i := 0; i < value.Len(); i++ {
- valueIsZero = valueIsZero && isZero(value.Index(i))
- }
- return valueIsZero
- case reflect.Struct:
- valueIsZero := true
- for i := 0; i < value.NumField(); i++ {
- if value.Field(i).CanSet() {
- valueIsZero = valueIsZero && isZero(value.Field(i))
- }
- }
- return valueIsZero
- case reflect.Ptr:
- if !value.IsNil() {
- return isZero(reflect.Indirect(value))
- } else {
- return true
- }
- default:
- zeroValue := reflect.Zero(value.Type())
- result := value.Interface() == zeroValue.Interface()
- return result
+func createDirectoryIfNonexistent(dir string) error {
+ if _, err := os.Stat(dir); os.IsNotExist(err) {
+ return os.MkdirAll(dir, os.ModePerm)
+ } else {
+ return err
}
}
diff --git a/cmd/soong_build/queryview_test.go b/cmd/soong_build/queryview_test.go
deleted file mode 100644
index 9471a91..0000000
--- a/cmd/soong_build/queryview_test.go
+++ /dev/null
@@ -1,470 +0,0 @@
-// Copyright 2020 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 main
-
-import (
- "android/soong/android"
- "io/ioutil"
- "os"
- "strings"
- "testing"
-
- "github.com/google/blueprint/bootstrap/bpdoc"
-)
-
-var buildDir string
-
-func setUp() {
- var err error
- buildDir, err = ioutil.TempDir("", "bazel_queryview_test")
- if err != nil {
- panic(err)
- }
-}
-
-func tearDown() {
- os.RemoveAll(buildDir)
-}
-
-func TestMain(m *testing.M) {
- run := func() int {
- setUp()
- defer tearDown()
-
- return m.Run()
- }
-
- os.Exit(run())
-}
-
-type customModule struct {
- android.ModuleBase
-}
-
-// OutputFiles is needed because some instances of this module use dist with a
-// tag property which requires the module implements OutputFileProducer.
-func (m *customModule) OutputFiles(tag string) (android.Paths, error) {
- return android.PathsForTesting("path" + tag), nil
-}
-
-func (m *customModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- // nothing for now.
-}
-
-func customModuleFactory() android.Module {
- module := &customModule{}
- android.InitAndroidModule(module)
- return module
-}
-
-func TestGenerateBazelQueryViewFromBlueprint(t *testing.T) {
- testCases := []struct {
- bp string
- expectedBazelTarget string
- }{
- {
- bp: `custom {
- name: "foo",
-}
- `,
- expectedBazelTarget: `soong_module(
- name = "foo",
- module_name = "foo",
- module_type = "custom",
- module_variant = "",
- module_deps = [
- ],
-)`,
- },
- {
- bp: `custom {
- name: "foo",
- ramdisk: true,
-}
- `,
- expectedBazelTarget: `soong_module(
- name = "foo",
- module_name = "foo",
- module_type = "custom",
- module_variant = "",
- module_deps = [
- ],
- ramdisk = True,
-)`,
- },
- {
- bp: `custom {
- name: "foo",
- owner: "a_string_with\"quotes\"_and_\\backslashes\\\\",
-}
- `,
- expectedBazelTarget: `soong_module(
- name = "foo",
- module_name = "foo",
- module_type = "custom",
- module_variant = "",
- module_deps = [
- ],
- owner = "a_string_with\"quotes\"_and_\\backslashes\\\\",
-)`,
- },
- {
- bp: `custom {
- name: "foo",
- required: ["bar"],
-}
- `,
- expectedBazelTarget: `soong_module(
- name = "foo",
- module_name = "foo",
- module_type = "custom",
- module_variant = "",
- module_deps = [
- ],
- required = [
- "bar",
- ],
-)`,
- },
- {
- bp: `custom {
- name: "foo",
- target_required: ["qux", "bazqux"],
-}
- `,
- expectedBazelTarget: `soong_module(
- name = "foo",
- module_name = "foo",
- module_type = "custom",
- module_variant = "",
- module_deps = [
- ],
- target_required = [
- "qux",
- "bazqux",
- ],
-)`,
- },
- {
- bp: `custom {
- name: "foo",
- dist: {
- targets: ["goal_foo"],
- tag: ".foo",
- },
- dists: [
- {
- targets: ["goal_bar"],
- tag: ".bar",
- },
- ],
-}
- `,
- expectedBazelTarget: `soong_module(
- name = "foo",
- module_name = "foo",
- module_type = "custom",
- module_variant = "",
- module_deps = [
- ],
- dist = {
- "tag": ".foo",
- "targets": [
- "goal_foo",
- ],
- },
- dists = [
- {
- "tag": ".bar",
- "targets": [
- "goal_bar",
- ],
- },
- ],
-)`,
- },
- {
- bp: `custom {
- name: "foo",
- required: ["bar"],
- target_required: ["qux", "bazqux"],
- ramdisk: true,
- owner: "custom_owner",
- dists: [
- {
- tag: ".tag",
- targets: ["my_goal"],
- },
- ],
-}
- `,
- expectedBazelTarget: `soong_module(
- name = "foo",
- module_name = "foo",
- module_type = "custom",
- module_variant = "",
- module_deps = [
- ],
- dists = [
- {
- "tag": ".tag",
- "targets": [
- "my_goal",
- ],
- },
- ],
- owner = "custom_owner",
- ramdisk = True,
- required = [
- "bar",
- ],
- target_required = [
- "qux",
- "bazqux",
- ],
-)`,
- },
- }
-
- for _, testCase := range testCases {
- config := android.TestConfig(buildDir, nil, testCase.bp, nil)
- ctx := android.NewTestContext(config)
- ctx.RegisterModuleType("custom", customModuleFactory)
- ctx.Register()
-
- _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
- android.FailIfErrored(t, errs)
- _, errs = ctx.PrepareBuildActions(config)
- android.FailIfErrored(t, errs)
-
- module := ctx.ModuleForTests("foo", "").Module().(*customModule)
- blueprintCtx := ctx.Context.Context
-
- actualBazelTarget := generateSoongModuleTarget(blueprintCtx, module)
- if actualBazelTarget != testCase.expectedBazelTarget {
- t.Errorf(
- "Expected generated Bazel target to be '%s', got '%s'",
- testCase.expectedBazelTarget,
- actualBazelTarget,
- )
- }
- }
-}
-
-func createPackageFixtures() []*bpdoc.Package {
- properties := []bpdoc.Property{
- bpdoc.Property{
- Name: "int64_prop",
- Type: "int64",
- },
- bpdoc.Property{
- Name: "int_prop",
- Type: "int",
- },
- bpdoc.Property{
- Name: "bool_prop",
- Type: "bool",
- },
- bpdoc.Property{
- Name: "string_prop",
- Type: "string",
- },
- bpdoc.Property{
- Name: "string_list_prop",
- Type: "list of string",
- },
- bpdoc.Property{
- Name: "nested_prop",
- Type: "",
- Properties: []bpdoc.Property{
- bpdoc.Property{
- Name: "int_prop",
- Type: "int",
- },
- bpdoc.Property{
- Name: "bool_prop",
- Type: "bool",
- },
- bpdoc.Property{
- Name: "string_prop",
- Type: "string",
- },
- },
- },
- bpdoc.Property{
- Name: "unknown_type",
- Type: "unknown",
- },
- }
-
- fooPropertyStruct := &bpdoc.PropertyStruct{
- Name: "FooProperties",
- Properties: properties,
- }
-
- moduleTypes := []*bpdoc.ModuleType{
- &bpdoc.ModuleType{
- Name: "foo_library",
- PropertyStructs: []*bpdoc.PropertyStruct{
- fooPropertyStruct,
- },
- },
-
- &bpdoc.ModuleType{
- Name: "foo_binary",
- PropertyStructs: []*bpdoc.PropertyStruct{
- fooPropertyStruct,
- },
- },
- &bpdoc.ModuleType{
- Name: "foo_test",
- PropertyStructs: []*bpdoc.PropertyStruct{
- fooPropertyStruct,
- },
- },
- }
-
- return [](*bpdoc.Package){
- &bpdoc.Package{
- Name: "foo_language",
- Path: "android/soong/foo",
- ModuleTypes: moduleTypes,
- },
- }
-}
-
-func TestGenerateModuleRuleShims(t *testing.T) {
- ruleShims, err := createRuleShims(createPackageFixtures())
- if err != nil {
- panic(err)
- }
-
- if len(ruleShims) != 1 {
- t.Errorf("Expected to generate 1 rule shim, but got %d", len(ruleShims))
- }
-
- fooRuleShim := ruleShims["foo"]
- expectedRules := []string{"foo_binary", "foo_library", "foo_test_"}
-
- if len(fooRuleShim.rules) != 3 {
- t.Errorf("Expected 3 rules, but got %d", len(fooRuleShim.rules))
- }
-
- for i, rule := range fooRuleShim.rules {
- if rule != expectedRules[i] {
- t.Errorf("Expected rule shim to contain %s, but got %s", expectedRules[i], rule)
- }
- }
-
- expectedBzl := `load("//build/bazel/queryview_rules:providers.bzl", "SoongModuleInfo")
-
-def _foo_binary_impl(ctx):
- return [SoongModuleInfo()]
-
-foo_binary = rule(
- implementation = _foo_binary_impl,
- attrs = {
- "module_name": attr.string(mandatory = True),
- "module_variant": attr.string(),
- "module_deps": attr.label_list(providers = [SoongModuleInfo]),
- "bool_prop": attr.bool(),
- "int64_prop": attr.int(),
- "int_prop": attr.int(),
-# "nested_prop__int_prop": attr.int(),
-# "nested_prop__bool_prop": attr.bool(),
-# "nested_prop__string_prop": attr.string(),
- "string_list_prop": attr.string_list(),
- "string_prop": attr.string(),
- },
-)
-
-def _foo_library_impl(ctx):
- return [SoongModuleInfo()]
-
-foo_library = rule(
- implementation = _foo_library_impl,
- attrs = {
- "module_name": attr.string(mandatory = True),
- "module_variant": attr.string(),
- "module_deps": attr.label_list(providers = [SoongModuleInfo]),
- "bool_prop": attr.bool(),
- "int64_prop": attr.int(),
- "int_prop": attr.int(),
-# "nested_prop__int_prop": attr.int(),
-# "nested_prop__bool_prop": attr.bool(),
-# "nested_prop__string_prop": attr.string(),
- "string_list_prop": attr.string_list(),
- "string_prop": attr.string(),
- },
-)
-
-def _foo_test__impl(ctx):
- return [SoongModuleInfo()]
-
-foo_test_ = rule(
- implementation = _foo_test__impl,
- attrs = {
- "module_name": attr.string(mandatory = True),
- "module_variant": attr.string(),
- "module_deps": attr.label_list(providers = [SoongModuleInfo]),
- "bool_prop": attr.bool(),
- "int64_prop": attr.int(),
- "int_prop": attr.int(),
-# "nested_prop__int_prop": attr.int(),
-# "nested_prop__bool_prop": attr.bool(),
-# "nested_prop__string_prop": attr.string(),
- "string_list_prop": attr.string_list(),
- "string_prop": attr.string(),
- },
-)
-`
-
- if fooRuleShim.content != expectedBzl {
- t.Errorf(
- "Expected the generated rule shim bzl to be:\n%s\nbut got:\n%s",
- expectedBzl,
- fooRuleShim.content)
- }
-}
-
-func TestGenerateSoongModuleBzl(t *testing.T) {
- ruleShims, err := createRuleShims(createPackageFixtures())
- if err != nil {
- panic(err)
- }
- actualSoongModuleBzl := generateSoongModuleBzl(ruleShims)
-
- expectedLoad := "load(\"//build/bazel/queryview_rules:foo.bzl\", \"foo_binary\", \"foo_library\", \"foo_test_\")"
- expectedRuleMap := `soong_module_rule_map = {
- "foo_binary": foo_binary,
- "foo_library": foo_library,
- "foo_test_": foo_test_,
-}`
- if !strings.Contains(actualSoongModuleBzl, expectedLoad) {
- t.Errorf(
- "Generated soong_module.bzl:\n\n%s\n\n"+
- "Could not find the load statement in the generated soong_module.bzl:\n%s",
- actualSoongModuleBzl,
- expectedLoad)
- }
-
- if !strings.Contains(actualSoongModuleBzl, expectedRuleMap) {
- t.Errorf(
- "Generated soong_module.bzl:\n\n%s\n\n"+
- "Could not find the module -> rule map in the generated soong_module.bzl:\n%s",
- actualSoongModuleBzl,
- expectedRuleMap)
- }
-}
diff --git a/cmd/soong_build/writedocs.go b/cmd/soong_build/writedocs.go
index 253979e..f2c2c9b 100644
--- a/cmd/soong_build/writedocs.go
+++ b/cmd/soong_build/writedocs.go
@@ -20,7 +20,6 @@
"html/template"
"io/ioutil"
"path/filepath"
- "reflect"
"sort"
"github.com/google/blueprint/bootstrap"
@@ -97,12 +96,8 @@
}
func getPackages(ctx *android.Context) ([]*bpdoc.Package, error) {
- moduleTypeFactories := android.ModuleTypeFactories()
- bpModuleTypeFactories := make(map[string]reflect.Value)
- for moduleType, factory := range moduleTypeFactories {
- bpModuleTypeFactories[moduleType] = reflect.ValueOf(factory)
- }
- return bootstrap.ModuleTypeDocs(ctx.Context, bpModuleTypeFactories)
+ moduleTypeFactories := android.ModuleTypeFactoriesForDocs()
+ return bootstrap.ModuleTypeDocs(ctx.Context, moduleTypeFactories)
}
func writeDocs(ctx *android.Context, filename string) error {
diff --git a/dexpreopt/class_loader_context.go b/dexpreopt/class_loader_context.go
index ab789aa..532d8fc 100644
--- a/dexpreopt/class_loader_context.go
+++ b/dexpreopt/class_loader_context.go
@@ -255,24 +255,13 @@
// Add class loader context for the given library to the map entry for the given SDK version.
func (clcMap ClassLoaderContextMap) addContext(ctx android.ModuleInstallPathContext, sdkVer int, lib string,
- hostPath, installPath android.Path, strict bool, nestedClcMap ClassLoaderContextMap) error {
-
- // If missing dependencies are allowed, the build shouldn't fail when a <uses-library> is
- // not found. However, this is likely to result is disabling dexpreopt, as it won't be
- // possible to construct class loader context without on-host and on-device library paths.
- strict = strict && !ctx.Config().AllowMissingDependencies()
-
- if hostPath == nil && strict {
- return fmt.Errorf("unknown build path to <uses-library> \"%s\"", lib)
- }
+ hostPath, installPath android.Path, nestedClcMap ClassLoaderContextMap) error {
devicePath := UnknownInstallLibraryPath
if installPath == nil {
if android.InList(lib, CompatUsesLibs) || android.InList(lib, OptionalCompatUsesLibs) {
// Assume that compatibility libraries are installed in /system/framework.
installPath = android.PathForModuleInstall(ctx, "framework", lib+".jar")
- } else if strict {
- return fmt.Errorf("unknown install path to <uses-library> \"%s\"", lib)
} else {
// For some stub libraries the only known thing is the name of their implementation
// library, but the library itself is unavailable (missing or part of a prebuilt). In
@@ -310,40 +299,17 @@
return nil
}
-// Wrapper around addContext that reports errors.
-func (clcMap ClassLoaderContextMap) addContextOrReportError(ctx android.ModuleInstallPathContext, sdkVer int, lib string,
- hostPath, installPath android.Path, strict bool, nestedClcMap ClassLoaderContextMap) {
-
- err := clcMap.addContext(ctx, sdkVer, lib, hostPath, installPath, strict, nestedClcMap)
- if err != nil {
- ctx.ModuleErrorf(err.Error())
- }
-}
-
-// Add class loader context. Fail on unknown build/install paths.
-func (clcMap ClassLoaderContextMap) AddContext(ctx android.ModuleInstallPathContext, lib string,
- hostPath, installPath android.Path) {
-
- clcMap.addContextOrReportError(ctx, AnySdkVersion, lib, hostPath, installPath, true, nil)
-}
-
-// Add class loader context if the library exists. Don't fail on unknown build/install paths.
-func (clcMap ClassLoaderContextMap) MaybeAddContext(ctx android.ModuleInstallPathContext, lib *string,
- hostPath, installPath android.Path) {
-
- if lib != nil {
- clcMap.addContextOrReportError(ctx, AnySdkVersion, *lib, hostPath, installPath, false, nil)
- }
-}
-
// Add class loader context for the given SDK version. Don't fail on unknown build/install paths, as
// libraries with unknown paths still need to be processed by manifest_fixer (which doesn't care
// about paths). For the subset of libraries that are used in dexpreopt, their build/install paths
// are validated later before CLC is used (in validateClassLoaderContext).
-func (clcMap ClassLoaderContextMap) AddContextForSdk(ctx android.ModuleInstallPathContext, sdkVer int,
+func (clcMap ClassLoaderContextMap) AddContext(ctx android.ModuleInstallPathContext, sdkVer int,
lib string, hostPath, installPath android.Path, nestedClcMap ClassLoaderContextMap) {
- clcMap.addContextOrReportError(ctx, sdkVer, lib, hostPath, installPath, false, nestedClcMap)
+ err := clcMap.addContext(ctx, sdkVer, lib, hostPath, installPath, nestedClcMap)
+ if err != nil {
+ ctx.ModuleErrorf(err.Error())
+ }
}
// Merge the other class loader context map into this one, do not override existing entries.
diff --git a/dexpreopt/class_loader_context_test.go b/dexpreopt/class_loader_context_test.go
index 6b6b162..86f7871 100644
--- a/dexpreopt/class_loader_context_test.go
+++ b/dexpreopt/class_loader_context_test.go
@@ -18,6 +18,7 @@
// For class loader context tests involving .bp files, see TestUsesLibraries in java package.
import (
+ "fmt"
"reflect"
"strings"
"testing"
@@ -50,36 +51,30 @@
m := make(ClassLoaderContextMap)
- m.AddContext(ctx, "a", buildPath(ctx, "a"), installPath(ctx, "a"))
- m.AddContext(ctx, "b", buildPath(ctx, "b"), installPath(ctx, "b"))
-
- // "Maybe" variant in the good case: add as usual.
- c := "c"
- m.MaybeAddContext(ctx, &c, buildPath(ctx, "c"), installPath(ctx, "c"))
-
- // "Maybe" variant in the bad case: don't add library with unknown name, keep going.
- m.MaybeAddContext(ctx, nil, nil, nil)
+ m.AddContext(ctx, AnySdkVersion, "a", buildPath(ctx, "a"), installPath(ctx, "a"), nil)
+ m.AddContext(ctx, AnySdkVersion, "b", buildPath(ctx, "b"), installPath(ctx, "b"), nil)
+ m.AddContext(ctx, AnySdkVersion, "c", buildPath(ctx, "c"), installPath(ctx, "c"), nil)
// Add some libraries with nested subcontexts.
m1 := make(ClassLoaderContextMap)
- m1.AddContext(ctx, "a1", buildPath(ctx, "a1"), installPath(ctx, "a1"))
- m1.AddContext(ctx, "b1", buildPath(ctx, "b1"), installPath(ctx, "b1"))
+ m1.AddContext(ctx, AnySdkVersion, "a1", buildPath(ctx, "a1"), installPath(ctx, "a1"), nil)
+ m1.AddContext(ctx, AnySdkVersion, "b1", buildPath(ctx, "b1"), installPath(ctx, "b1"), nil)
m2 := make(ClassLoaderContextMap)
- m2.AddContext(ctx, "a2", buildPath(ctx, "a2"), installPath(ctx, "a2"))
- m2.AddContext(ctx, "b2", buildPath(ctx, "b2"), installPath(ctx, "b2"))
- m2.AddContextForSdk(ctx, AnySdkVersion, "c2", buildPath(ctx, "c2"), installPath(ctx, "c2"), m1)
+ m2.AddContext(ctx, AnySdkVersion, "a2", buildPath(ctx, "a2"), installPath(ctx, "a2"), nil)
+ m2.AddContext(ctx, AnySdkVersion, "b2", buildPath(ctx, "b2"), installPath(ctx, "b2"), nil)
+ m2.AddContext(ctx, AnySdkVersion, "c2", buildPath(ctx, "c2"), installPath(ctx, "c2"), m1)
m3 := make(ClassLoaderContextMap)
- m3.AddContext(ctx, "a3", buildPath(ctx, "a3"), installPath(ctx, "a3"))
- m3.AddContext(ctx, "b3", buildPath(ctx, "b3"), installPath(ctx, "b3"))
+ m3.AddContext(ctx, AnySdkVersion, "a3", buildPath(ctx, "a3"), installPath(ctx, "a3"), nil)
+ m3.AddContext(ctx, AnySdkVersion, "b3", buildPath(ctx, "b3"), installPath(ctx, "b3"), nil)
- m.AddContextForSdk(ctx, AnySdkVersion, "d", buildPath(ctx, "d"), installPath(ctx, "d"), m2)
+ m.AddContext(ctx, AnySdkVersion, "d", buildPath(ctx, "d"), installPath(ctx, "d"), m2)
// When the same library is both in conditional and unconditional context, it should be removed
// from conditional context.
- m.AddContextForSdk(ctx, 42, "f", buildPath(ctx, "f"), installPath(ctx, "f"), nil)
- m.AddContextForSdk(ctx, AnySdkVersion, "f", buildPath(ctx, "f"), installPath(ctx, "f"), nil)
+ m.AddContext(ctx, 42, "f", buildPath(ctx, "f"), installPath(ctx, "f"), nil)
+ m.AddContext(ctx, AnySdkVersion, "f", buildPath(ctx, "f"), installPath(ctx, "f"), nil)
// Merge map with implicit root library that is among toplevel contexts => does nothing.
m.AddContextMap(m1, "c")
@@ -88,12 +83,12 @@
m.AddContextMap(m3, "m_g")
// Compatibility libraries with unknown install paths get default paths.
- m.AddContextForSdk(ctx, 29, AndroidHidlManager, buildPath(ctx, AndroidHidlManager), nil, nil)
- m.AddContextForSdk(ctx, 29, AndroidHidlBase, buildPath(ctx, AndroidHidlBase), nil, nil)
+ m.AddContext(ctx, 29, AndroidHidlManager, buildPath(ctx, AndroidHidlManager), nil, nil)
+ m.AddContext(ctx, 29, AndroidHidlBase, buildPath(ctx, AndroidHidlBase), nil, nil)
// Add "android.test.mock" to conditional CLC, observe that is gets removed because it is only
// needed as a compatibility library if "android.test.runner" is in CLC as well.
- m.AddContextForSdk(ctx, 30, AndroidTestMock, buildPath(ctx, AndroidTestMock), nil, nil)
+ m.AddContext(ctx, 30, AndroidTestMock, buildPath(ctx, AndroidTestMock), nil, nil)
valid, validationError := validateClassLoaderContext(m)
@@ -160,31 +155,19 @@
})
}
-// Test that an unexpected unknown build path causes immediate error.
-func TestCLCUnknownBuildPath(t *testing.T) {
- ctx := testContext()
- m := make(ClassLoaderContextMap)
- err := m.addContext(ctx, AnySdkVersion, "a", nil, nil, true, nil)
- checkError(t, err, "unknown build path to <uses-library> \"a\"")
-}
-
-// Test that an unexpected unknown install path causes immediate error.
-func TestCLCUnknownInstallPath(t *testing.T) {
- ctx := testContext()
- m := make(ClassLoaderContextMap)
- err := m.addContext(ctx, AnySdkVersion, "a", buildPath(ctx, "a"), nil, true, nil)
- checkError(t, err, "unknown install path to <uses-library> \"a\"")
-}
-
-func TestCLCMaybeAdd(t *testing.T) {
+// Test that unknown library paths cause a validation error.
+func testCLCUnknownPath(t *testing.T, whichPath string) {
ctx := testContext()
m := make(ClassLoaderContextMap)
- a := "a"
- m.MaybeAddContext(ctx, &a, nil, nil)
+ if whichPath == "build" {
+ m.AddContext(ctx, AnySdkVersion, "a", nil, nil, nil)
+ } else {
+ m.AddContext(ctx, AnySdkVersion, "a", buildPath(ctx, "a"), nil, nil)
+ }
// The library should be added to <uses-library> tags by the manifest_fixer.
- t.Run("maybe add", func(t *testing.T) {
+ t.Run("uses libs", func(t *testing.T) {
haveUsesLibs := m.UsesLibs()
wantUsesLibs := []string{"a"}
if !reflect.DeepEqual(wantUsesLibs, haveUsesLibs) {
@@ -192,20 +175,28 @@
}
})
- // But class loader context in such cases should raise an error on validation.
- t.Run("validate", func(t *testing.T) {
- _, err := validateClassLoaderContext(m)
- checkError(t, err, "invalid build path for <uses-library> \"a\"")
- })
+ // But CLC cannot be constructed: there is a validation error.
+ _, err := validateClassLoaderContext(m)
+ checkError(t, err, fmt.Sprintf("invalid %s path for <uses-library> \"a\"", whichPath))
+}
+
+// Test that unknown build path is an error.
+func TestCLCUnknownBuildPath(t *testing.T) {
+ testCLCUnknownPath(t, "build")
+}
+
+// Test that unknown install path is an error.
+func TestCLCUnknownInstallPath(t *testing.T) {
+ testCLCUnknownPath(t, "install")
}
// An attempt to add conditional nested subcontext should fail.
func TestCLCNestedConditional(t *testing.T) {
ctx := testContext()
m1 := make(ClassLoaderContextMap)
- m1.AddContextForSdk(ctx, 42, "a", buildPath(ctx, "a"), installPath(ctx, "a"), nil)
+ m1.AddContext(ctx, 42, "a", buildPath(ctx, "a"), installPath(ctx, "a"), nil)
m := make(ClassLoaderContextMap)
- err := m.addContext(ctx, AnySdkVersion, "b", buildPath(ctx, "b"), installPath(ctx, "b"), true, m1)
+ err := m.addContext(ctx, AnySdkVersion, "b", buildPath(ctx, "b"), installPath(ctx, "b"), m1)
checkError(t, err, "nested class loader context shouldn't have conditional part")
}
@@ -214,10 +205,10 @@
func TestCLCSdkVersionOrder(t *testing.T) {
ctx := testContext()
m := make(ClassLoaderContextMap)
- m.AddContextForSdk(ctx, 28, "a", buildPath(ctx, "a"), installPath(ctx, "a"), nil)
- m.AddContextForSdk(ctx, 29, "b", buildPath(ctx, "b"), installPath(ctx, "b"), nil)
- m.AddContextForSdk(ctx, 30, "c", buildPath(ctx, "c"), installPath(ctx, "c"), nil)
- m.AddContextForSdk(ctx, AnySdkVersion, "d", buildPath(ctx, "d"), installPath(ctx, "d"), nil)
+ m.AddContext(ctx, 28, "a", buildPath(ctx, "a"), installPath(ctx, "a"), nil)
+ m.AddContext(ctx, 29, "b", buildPath(ctx, "b"), installPath(ctx, "b"), nil)
+ m.AddContext(ctx, 30, "c", buildPath(ctx, "c"), installPath(ctx, "c"), nil)
+ m.AddContext(ctx, AnySdkVersion, "d", buildPath(ctx, "d"), installPath(ctx, "d"), nil)
valid, validationError := validateClassLoaderContext(m)
diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go
index c6181bc..2b3fbae 100644
--- a/filesystem/filesystem.go
+++ b/filesystem/filesystem.go
@@ -46,7 +46,10 @@
return module
}
-var dependencyTag = struct{ blueprint.BaseDependencyTag }{}
+var dependencyTag = struct {
+ blueprint.BaseDependencyTag
+ android.InstallAlwaysNeededDependencyTag
+}{}
func (f *filesystem) DepsMutator(ctx android.BottomUpMutatorContext) {
f.AddDeps(ctx, dependencyTag)
@@ -80,7 +83,7 @@
Text(">").Output(propFile).
Implicit(mkuserimg)
- f.output = android.PathForModuleOut(ctx, "filesystem.img").OutputPath
+ f.output = android.PathForModuleOut(ctx, f.installFileName()).OutputPath
builder.Command().BuiltTool("build_image").
Text(rootDir.String()). // input directory
Input(propFile).
@@ -109,3 +112,16 @@
},
}}
}
+
+// Filesystem is the public interface for the filesystem struct. Currently, it's only for the apex
+// package to have access to the output file.
+type Filesystem interface {
+ android.Module
+ OutputPath() android.Path
+}
+
+var _ Filesystem = (*filesystem)(nil)
+
+func (f *filesystem) OutputPath() android.Path {
+ return f.output
+}
diff --git a/go.mod b/go.mod
index 117fa65..7297dea 100644
--- a/go.mod
+++ b/go.mod
@@ -8,4 +8,4 @@
replace github.com/google/blueprint v0.0.0 => ../blueprint
-go 1.15.6
+go 1.15
diff --git a/java/app.go b/java/app.go
index 574472c..e6c9a2d 100755
--- a/java/app.go
+++ b/java/app.go
@@ -1226,7 +1226,7 @@
if tag, ok := ctx.OtherModuleDependencyTag(m).(usesLibraryDependencyTag); ok {
dep := ctx.OtherModuleName(m)
if lib, ok := m.(UsesLibraryDependency); ok {
- clcMap.AddContextForSdk(ctx, tag.sdkVersion, dep,
+ clcMap.AddContext(ctx, tag.sdkVersion, dep,
lib.DexJarBuildPath(), lib.DexJarInstallPath(), lib.ClassLoaderContexts())
} else if ctx.Config().AllowMissingDependencies() {
ctx.AddMissingDependencies([]string{dep})
diff --git a/java/hiddenapi_singleton.go b/java/hiddenapi_singleton.go
index 419dc34..d302524 100644
--- a/java/hiddenapi_singleton.go
+++ b/java/hiddenapi_singleton.go
@@ -251,6 +251,8 @@
FlagWithInput("--unsupported ",
android.PathForSource(ctx, "frameworks/base/config/hiddenapi-unsupported.txt")).
FlagWithInput("--unsupported ", combinedRemovedApis).Flag("--ignore-conflicts ").FlagWithArg("--tag ", "removed").
+ FlagWithInput("--max-target-r ",
+ android.PathForSource(ctx, "frameworks/base/config/hiddenapi-max-target-r-loprio.txt")).FlagWithArg("--tag ", "lo-prio").
FlagWithInput("--max-target-q ",
android.PathForSource(ctx, "frameworks/base/config/hiddenapi-max-target-q.txt")).
FlagWithInput("--max-target-p ",
@@ -259,8 +261,6 @@
ctx, "frameworks/base/config/hiddenapi-max-target-o.txt")).Flag("--ignore-conflicts ").FlagWithArg("--tag ", "lo-prio").
FlagWithInput("--blocked ",
android.PathForSource(ctx, "frameworks/base/config/hiddenapi-force-blocked.txt")).
- FlagWithInput("--blocked ",
- android.PathForSource(ctx, "frameworks/base/config/hiddenapi-temp-blocklist.txt")).FlagWithArg("--tag ", "lo-prio").
FlagWithInput("--unsupported ", android.PathForSource(
ctx, "frameworks/base/config/hiddenapi-unsupported-packages.txt")).Flag("--packages ").
FlagWithOutput("--output ", tempPath)
diff --git a/java/java.go b/java/java.go
index 82b53be..f684a00 100644
--- a/java/java.go
+++ b/java/java.go
@@ -3305,7 +3305,7 @@
}
if implicitSdkLib != nil {
- clcMap.AddContextForSdk(ctx, dexpreopt.AnySdkVersion, *implicitSdkLib,
+ clcMap.AddContext(ctx, dexpreopt.AnySdkVersion, *implicitSdkLib,
dep.DexJarBuildPath(), dep.DexJarInstallPath(), dep.ClassLoaderContexts())
} else {
depName := ctx.OtherModuleName(depModule)
diff --git a/java/system_modules.go b/java/system_modules.go
index 7394fd5..5cc546d 100644
--- a/java/system_modules.go
+++ b/java/system_modules.go
@@ -192,6 +192,7 @@
fmt.Fprintln(w, name+":", "$("+makevar+")")
fmt.Fprintln(w, ".PHONY:", name)
+ // TODO(b/151177513): Licenses: Doesn't go through base_rules. May have to generate meta_lic and meta_module here.
},
}
}
diff --git a/licenses/Android.bp b/licenses/Android.bp
new file mode 100644
index 0000000..217792f
--- /dev/null
+++ b/licenses/Android.bp
@@ -0,0 +1,1082 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// 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 {
+ default_visibility: ["//visibility:public"],
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+license {
+ name: "Android-Apache-2.0",
+ license_kinds: ["SPDX-license-identifier-Apache-2.0"],
+ copyright_notice: "Copyright (C) The Android Open Source Project",
+ license_text: ["LICENSE"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-AFL-1.1",
+ conditions: ["by_exception_only"],
+ url: "https://spdx.org/licenses/AFL-1.1.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-AFL-1.2",
+ conditions: ["by_exception_only"],
+ url: "https://spdx.org/licenses/AFL-1.2.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-AFL-2.0",
+ conditions: ["by_exception_only"],
+ url: "https://spdx.org/licenses/AFL-2.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-AFL-2.1",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/AFL-2.1.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-AFL-3.0",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/AFL-3.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-AGPL",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/AGPL.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-AGPL-1.0",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/AGPL-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-AGPL-1.0-only",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/AGPL-1.0-only.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-AGPL-1.0-or-later",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/AGPL-1.0-or-later.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-AGPL-3.0",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/AGPL-3.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-AGPL-3.0-only",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/AGPL-3.0-only.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-AGPL-3.0-or-later",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/AGPL-3.0-or-later.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-Apache",
+ conditions: ["notice"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-Apache-1.0",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/Apache-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-Apache-1.1",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/Apache-1.1.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-Apache-2.0",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/Apache-2.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-Artistic",
+ conditions: ["notice"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-Artistic-1.0",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/Artistic-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-Artistic-1.0-Perl",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/Artistic-1.0-Perl.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-Artistic-1.0-cl8",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/Artistic-1.0-cl8.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-Artistic-2.0",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/Artistic-2.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSD",
+ conditions: ["notice"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSD-1-Clause",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSD-1-Clause.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSD-2-Clause",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSD-2-Clause.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSD-2-Clause-FreeBSD",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSD-2-Clause-FreeBSD.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSD-2-Clause-NetBSD",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSD-2-Clause-NetBSD.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSD-2-Clause-Patent",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSD-2-Clause-Patent.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSD-3-Clause",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSD-3-Clause.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSD-3-Clause-Attribution",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSD-3-Clause-Attribution.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSD-3-Clause-Clear",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSD-3-Clause-Clear.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSD-3-Clause-LBNL",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSD-3-Clause-LBNL.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSD-3-Clause-No-Nuclear-License",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSD-3-Clause-No-Nuclear-License.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSD-3-Clause-No-Nuclear-License-2014",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSD-3-Clause-No-Nuclear-License-2014.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSD-3-Clause-No-Nuclear-Warranty",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSD-3-Clause-No-Nuclear-Warranty.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSD-3-Clause-Open-MPI",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSD-3-Clause-Open-MPI.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSD-4-Clause",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSD-4-Clause.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSD-4-Clause-UC",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSD-4-Clause-UC.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSD-Protection",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSD-Protection.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSD-Source-Code",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSD-Source-Code.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSL-1.0",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSL-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-Beerware",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/Beerware.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY",
+ conditions: ["notice"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-1.0",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/CC-BY-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-2.0",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/CC-BY-2.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-2.5",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/CC-BY-2.5.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-3.0",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/CC-BY-3.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-4.0",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/CC-BY-4.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-NC",
+ conditions: ["by_exception_only", "not_allowed"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-NC-1.0",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/CC-BY-NC-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-NC-2.0",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/CC-BY-NC-2.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-NC-2.5",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/CC-BY-NC-2.5.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-NC-3.0",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/CC-BY-NC-3.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-NC-4.0",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/CC-BY-NC-4.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-NC-ND-1.0",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/CC-BY-NC-ND-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-NC-ND-2.0",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/CC-BY-NC-ND-2.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-NC-ND-2.5",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/CC-BY-NC-ND-2.5.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-NC-ND-3.0",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/CC-BY-NC-ND-3.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-NC-ND-4.0",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/CC-BY-NC-ND-4.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-NC-SA-1.0",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/CC-BY-NC-SA-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-NC-SA-2.0",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/CC-BY-NC-SA-2.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-NC-SA-2.5",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/CC-BY-NC-SA-2.5.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-NC-SA-3.0",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/CC-BY-NC-SA-3.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-NC-SA-4.0",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/CC-BY-NC-SA-4.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-ND",
+ conditions: ["restricted"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-ND-1.0",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/CC-BY-ND-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-ND-2.0",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/CC-BY-ND-2.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-ND-2.5",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/CC-BY-ND-2.5.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-ND-3.0",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/CC-BY-ND-3.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-ND-4.0",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/CC-BY-ND-4.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-SA",
+ conditions: ["restricted"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-SA-1.0",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/CC-BY-SA-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-SA-2.0",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/CC-BY-SA-2.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-SA-2.5",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/CC-BY-SA-2.5.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-SA-3.0",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/CC-BY-SA-3.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-SA-4.0",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/CC-BY-SA-4.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-SA-ND",
+ conditions: ["restricted"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC0-1.0",
+ conditions: ["unencumbered"],
+ url: "https://spdx.org/licenses/CC0-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CPAL-1.0",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/CPAL-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CPL-1.0",
+ conditions: ["reciprocal"],
+ url: "https://spdx.org/licenses/CPL-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-EPL",
+ conditions: ["reciprocal"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-EPL-1.0",
+ conditions: ["reciprocal"],
+ url: "https://spdx.org/licenses/EPL-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-EPL-2.0",
+ conditions: ["reciprocal"],
+ url: "https://spdx.org/licenses/EPL-2.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-EUPL",
+ conditions: ["by_exception_only", "not_allowed"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-EUPL-1.0",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/EUPL-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-EUPL-1.1",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/EUPL-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-EUPL-1.2",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/EUPL-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-FTL",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/FTL.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL",
+ conditions: ["restricted"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-1.0",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-1.0+",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-1.0+.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-1.0-only",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-1.0-only.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-1.0-or-later",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-1.0-or-later.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-2.0",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-2.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-2.0+",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-2.0+.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-2.0-only",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-2.0-only.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-2.0-or-later",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-2.0-or-later.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-2.0-with-GCC-exception",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-2.0-with-GCC-exception.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-2.0-with-autoconf-exception",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-2.0-with-autoconf-exception.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-2.0-with-bison-exception",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-2.0-with-bison-exception.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-2.0-with-classpath-exception",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-2.0-with-classpath-exception.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-2.0-with-font-exception",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-2.0-with-font-exception.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-3.0",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-3.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-3.0+",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-3.0+.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-3.0-only",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-3.0-only.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-3.0-or-later",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-3.0-or-later.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-3.0-with-GCC-exception",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-3.0-with-GCC-exception.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-3.0-with-autoconf-exception",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-3.0-with-autoconf-exception.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-with-classpath-exception",
+ conditions: ["restricted"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-HPND",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/HPND.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-ICU",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/ICU.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-ISC",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/ISC.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-JSON",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/JSON.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-LGPL",
+ conditions: ["restricted"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-LGPL-2.0",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/LGPL-2.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-LGPL-2.0+",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/LGPL-2.0+.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-LGPL-2.0-only",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/LGPL-2.0-only.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-LGPL-2.0-or-later",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/LGPL-2.0-or-later.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-LGPL-2.1",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/LGPL-2.1.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-LGPL-2.1+",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/LGPL-2.1+.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-LGPL-2.1-only",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/LGPL-2.1-only.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-LGPL-2.1-or-later",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/LGPL-2.1-or-later.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-LGPL-3.0",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/LGPL-3.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-LGPL-3.0+",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/LGPL-3.0+.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-LGPL-3.0-only",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/LGPL-3.0-only.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-LGPL-3.0-or-later",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/LGPL-3.0-or-later.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-LGPLLR",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/LGPLLR.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-LPL-1.02",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/LPL-1.02.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-MIT",
+ conditions: ["notice"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-MIT-0",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/MIT-0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-MIT-CMU",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/MIT-CMU.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-MIT-advertising",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/MIT-advertising.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-MIT-enna",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/MIT-enna.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-MIT-feh",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/MIT-feh.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-MITNFA",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/MITNFA.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-MPL",
+ conditions: ["reciprocal"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-MPL-1.0",
+ conditions: ["reciprocal"],
+ url: "https://spdx.org/licenses/MPL-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-MPL-1.1",
+ conditions: ["reciprocal"],
+ url: "https://spdx.org/licenses/MPL-1.1.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-MPL-2.0",
+ conditions: ["reciprocal"],
+ url: "https://spdx.org/licenses/MPL-2.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-MPL-2.0-no-copyleft-exception",
+ conditions: ["reciprocal"],
+ url: "https://spdx.org/licenses/MPL-2.0-no-copyleft-exception.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-MS-PL",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/MS-PL.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-NCSA",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/NCSA.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-OFL",
+ conditions: ["by_exception_only"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-OFL-1.0",
+ conditions: ["by_exception_only"],
+ url: "https://spdx.org/licenses/OFL-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-OFL-1.0-RFN",
+ conditions: ["by_exception_only"],
+ url: "https://spdx.org/licenses/OFL-1.0-RFN.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-OFL-1.0-no-RFN",
+ conditions: ["by_exception_only"],
+ url: "https://spdx.org/licenses/OFL-1.0-no-RFN.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-OFL-1.1",
+ conditions: ["by_exception_only"],
+ url: "https://spdx.org/licenses/OFL-1.1.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-OFL-1.1-RFN",
+ conditions: ["by_exception_only"],
+ url: "https://spdx.org/licenses/OFL-1.1-RFN.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-OFL-1.1-no-RFN",
+ conditions: ["by_exception_only"],
+ url: "https://spdx.org/licenses/OFL-1.1-no-RFN.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-OpenSSL",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/OpenSSL.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-PSF-2.0",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/PSF-2.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-SISSL",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/SISSL.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-SISSL-1.2",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/SISSL-1.2.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-SPL-1.0",
+ conditions: ["by_exception_only", "reciprocal"],
+ url: "https://spdx.org/licenses/SPL-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-SSPL",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/SSPL.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-UPL-1.0",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/UPL-1.-.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-Unicode-DFS",
+ conditions: ["notice"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-Unicode-DFS-2015",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/Unicode-DFS-2015.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-Unicode-DFS-2016",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/Unicode-DFS-2016.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-Unlicense",
+ conditions: ["unencumbered"],
+ url: "https://spdx.org/licenses/Unlicense.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-W3C",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/W3C.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-W3C-19980720",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/W3C-19980720.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-W3C-20150513",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/W3C-20150513.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-WTFPL",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/WTFPL.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-Watcom-1.0",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/Watcom-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-Xnet",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/Xnet.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-ZPL",
+ conditions: ["notice"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-ZPL-1.1",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/ZPL-1.1.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-ZPL-2.0",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/ZPL-2.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-ZPL-2.1",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/ZPL-2.1.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-Zend-2.0",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/Zend-2.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-Zlib",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/Zlib.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-libtiff",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/libtiff.html",
+}
+
+
+// Legacy license kinds -- do not add new references -- use an spdx kind instead.
+license_kind {
+ name: "legacy_unknown",
+ conditions: ["by_exception_only"],
+}
+
+license_kind {
+ name: "legacy_unencumbered",
+ conditions: ["unencumbered"],
+}
+
+license_kind {
+ name: "legacy_notice",
+ conditions: ["notice"],
+}
+
+license_kind {
+ name: "legacy_reciprocal",
+ conditions: ["reciprocal"],
+}
+
+license_kind {
+ name: "legacy_restricted",
+ conditions: ["restricted"],
+}
+
+license_kind {
+ name: "legacy_by_exception_only",
+ conditions: ["by_exception_only"],
+}
+
+license_kind {
+ name: "legacy_not_allowed",
+ conditions: ["by_exception_only", "not_allowed"],
+}
+
+license_kind {
+ name: "legacy_proprietary",
+ conditions: ["by_exception_only", "not_allowed", "proprietary"],
+}
diff --git a/licenses/LICENSE b/licenses/LICENSE
new file mode 100644
index 0000000..dae0406
--- /dev/null
+++ b/licenses/LICENSE
@@ -0,0 +1,214 @@
+
+ Copyright (c) The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+
+ 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.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
diff --git a/phony/phony.go b/phony/phony.go
index cb60b9f..a31d402 100644
--- a/phony/phony.go
+++ b/phony/phony.go
@@ -52,6 +52,7 @@
fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
fmt.Fprintln(w, "LOCAL_MODULE :=", name)
+ data.Entries.WriteLicenseVariables(w)
if p.Host() {
fmt.Fprintln(w, "LOCAL_IS_HOST_MODULE := true")
}
diff --git a/rust/binary.go b/rust/binary.go
index c2d97f3..ca07d07 100644
--- a/rust/binary.go
+++ b/rust/binary.go
@@ -164,3 +164,7 @@
}
return binary.baseCompiler.stdLinkage(ctx)
}
+
+func (binary *binaryDecorator) isDependencyRoot() bool {
+ return true
+}
diff --git a/rust/compiler.go b/rust/compiler.go
index ee88a27..bcea6cc 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -236,6 +236,10 @@
panic(fmt.Errorf("baseCrater doesn't know how to crate things!"))
}
+func (compiler *baseCompiler) isDependencyRoot() bool {
+ return false
+}
+
func (compiler *baseCompiler) compilerDeps(ctx DepsContext, deps Deps) Deps {
deps.Rlibs = append(deps.Rlibs, compiler.Properties.Rlibs...)
deps.Dylibs = append(deps.Dylibs, compiler.Properties.Dylibs...)
diff --git a/rust/config/global.go b/rust/config/global.go
index 52a50a9..08ec877 100644
--- a/rust/config/global.go
+++ b/rust/config/global.go
@@ -44,6 +44,7 @@
GlobalRustFlags = []string{
"--remap-path-prefix $$(pwd)=",
"-C codegen-units=1",
+ "-C debuginfo=2",
"-C opt-level=3",
"-C relocation-model=pic",
}
diff --git a/rust/image.go b/rust/image.go
index af8c3b2..5e55e22 100644
--- a/rust/image.go
+++ b/rust/image.go
@@ -100,13 +100,13 @@
platformVndkVersion := mctx.DeviceConfig().PlatformVndkVersion()
// Rust does not support installing to the product image yet.
- if mod.VendorProperties.Product_available != nil {
+ if Bool(mod.VendorProperties.Product_available) {
mctx.PropertyErrorf("product_available",
"Rust modules do not yet support being available to the product image")
} else if mctx.ProductSpecific() {
mctx.PropertyErrorf("product_specific",
"Rust modules do not yet support installing to the product image.")
- } else if mod.VendorProperties.Double_loadable != nil {
+ } else if Bool(mod.VendorProperties.Double_loadable) {
mctx.PropertyErrorf("double_loadable",
"Rust modules do not yet support double loading")
}
@@ -114,7 +114,7 @@
coreVariantNeeded := true
var vendorVariants []string
- if mod.VendorProperties.Vendor_available != nil {
+ if Bool(mod.VendorProperties.Vendor_available) {
if vendorSpecific {
mctx.PropertyErrorf("vendor_available",
"doesn't make sense at the same time as `vendor: true`, `proprietary: true`, or `device_specific:true`")
diff --git a/rust/rust.go b/rust/rust.go
index 1053846..1fa97af 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -106,6 +106,42 @@
hideApexVariantFromMake bool
}
+func (mod *Module) Header() bool {
+ //TODO: If Rust libraries provide header variants, this needs to be updated.
+ return false
+}
+
+func (mod *Module) SetPreventInstall() {
+ mod.Properties.PreventInstall = true
+}
+
+// Returns true if the module is "vendor" variant. Usually these modules are installed in /vendor
+func (mod *Module) InVendor() bool {
+ return mod.Properties.ImageVariationPrefix == cc.VendorVariationPrefix
+}
+
+func (mod *Module) SetHideFromMake() {
+ mod.Properties.HideFromMake = true
+}
+
+func (mod *Module) SanitizePropDefined() bool {
+ return false
+}
+
+func (mod *Module) IsDependencyRoot() bool {
+ if mod.compiler != nil {
+ return mod.compiler.isDependencyRoot()
+ }
+ panic("IsDependencyRoot called on a non-compiler Rust module")
+}
+
+func (mod *Module) IsPrebuilt() bool {
+ if _, ok := mod.compiler.(*prebuiltLibraryDecorator); ok {
+ return true
+ }
+ return false
+}
+
func (mod *Module) OutputFiles(tag string) (android.Paths, error) {
switch tag {
case "":
@@ -281,6 +317,7 @@
SetDisabled()
stdLinkage(ctx *depsContext) RustLinkage
+ isDependencyRoot() bool
}
type exportedFlagsProducer interface {
diff --git a/scripts/build-mainline-modules.sh b/scripts/build-mainline-modules.sh
index 6db870f..ea62af4 100755
--- a/scripts/build-mainline-modules.sh
+++ b/scripts/build-mainline-modules.sh
@@ -26,6 +26,7 @@
platform-mainline-test-exports
runtime-module-host-exports
runtime-module-sdk
+ tzdata-module-test-exports
)
# List of libraries installed on the platform that are needed for ART chroot
diff --git a/sh/sh_binary.go b/sh/sh_binary.go
index 8db33a3..18749b5 100644
--- a/sh/sh_binary.go
+++ b/sh/sh_binary.go
@@ -345,15 +345,8 @@
depTag := ctx.OtherModuleDependencyTag(dep)
switch depTag {
case shTestDataBinsTag, shTestDataDeviceBinsTag:
- if cc, isCc := dep.(*cc.Module); isCc {
- s.addToDataModules(ctx, cc.OutputFile().Path().Base(), cc.OutputFile().Path())
- return
- }
- property := "data_bins"
- if depTag == shTestDataDeviceBinsTag {
- property = "data_device_bins"
- }
- ctx.PropertyErrorf(property, "%q of type %q is not supported", dep.Name(), ctx.OtherModuleType(dep))
+ path := android.OutputFileForModule(ctx, dep, "")
+ s.addToDataModules(ctx, path.Base(), path)
case shTestDataLibsTag, shTestDataDeviceLibsTag:
if cc, isCc := dep.(*cc.Module); isCc {
// Copy to an intermediate output directory to append "lib[64]" to the path,
diff --git a/sysprop/sysprop_library.go b/sysprop/sysprop_library.go
index 42caff2..38306ad 100644
--- a/sysprop/sysprop_library.go
+++ b/sysprop/sysprop_library.go
@@ -307,6 +307,7 @@
// Actual implementation libraries are created on LoadHookMutator
fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
fmt.Fprintf(w, "LOCAL_MODULE := %s\n", m.Name())
+ data.Entries.WriteLicenseVariables(w)
fmt.Fprintf(w, "LOCAL_MODULE_CLASS := FAKE\n")
fmt.Fprintf(w, "LOCAL_MODULE_TAGS := optional\n")
fmt.Fprintf(w, "include $(BUILD_SYSTEM)/base_rules.mk\n\n")