diff --git a/Android.bp b/Android.bp index 63de015893..b1db8e9379 100644 --- a/Android.bp +++ b/Android.bp @@ -130,3 +130,8 @@ buildinfo_prop { // Currently, only microdroid can refer to buildinfo.prop visibility: ["//packages/modules/Virtualization/microdroid"], } + +// container for apex_contributions selected using build flags +all_apex_contributions { + name: "all_apex_contributions", +} diff --git a/OWNERS b/OWNERS index 964e27a316..58edc8935b 100644 --- a/OWNERS +++ b/OWNERS @@ -1,29 +1,15 @@ +# Bug component: 99142 # This file is included by several other projects as the list of people # approving build related projects. # AMER -agespino@google.com -alexmarquez@google.com ccross@android.com colefaust@google.com -cparsons@google.com -dacek@google.com -delmerico@google.com dwillemsen@google.com -eakammer@google.com -jobredeaux@google.com +jihoonkang@google.com joeo@google.com -juu@google.com lamontjones@google.com +mrziwang@google.com spandandas@google.com -tradical@google.com -usta@google.com -vinhdaitran@google.com weiwli@google.com -yudiliu@google.com - -# APAC -jingwen@google.com - -# EMEA -lberki@google.com +yudiliu@google.com \ No newline at end of file diff --git a/README.md b/README.md index 70311cb449..5e9e04a182 100644 --- a/README.md +++ b/README.md @@ -26,8 +26,6 @@ review request is enough. For more substantial changes, file a bug in our [bug tracker](https://issuetracker.google.com/issues/new?component=381517) or or write us at android-building@googlegroups.com . -For Googlers, see our [internal documentation](http://go/soong). - ## Android.bp file format By design, Android.bp files are very simple. There are no conditionals or @@ -565,6 +563,12 @@ modules (`cc_defaults`, `java_defaults`, etc.), which can then be referenced by all of the vendor's other modules using the normal namespace and visibility rules. +`soongConfigTraceMutator` enables modules affected by soong config variables to +write outputs into a hashed directory path. It does this by recording accesses +to soong config variables on each module, and then accumulating records of each +module's all dependencies. `m soong_config_trace` builds information about +hashes to `$OUT_DIR/soong/soong_config_trace.json`. + ## Build logic The build logic is written in Go using the @@ -644,8 +648,8 @@ invocations are run in the debugger, e.g., running SOONG_DELVE=2345 SOONG_DELVE_STEPS='build,modulegraph' m ``` results in only `build` (main build step) and `modulegraph` being run in the debugger. -The allowed step names are `api_bp2build`, `bp2build_files`, `bp2build_workspace`, -`build`, `modulegraph`, `queryview`, `soong_docs`. +The allowed step names are `bp2build_files`, `bp2build_workspace`, `build`, +`modulegraph`, `queryview`, `soong_docs`. Note setting or unsetting `SOONG_DELVE` causes a recompilation of `soong_build`. This is because in order to debug the binary, it needs to be built with debug diff --git a/aconfig/Android.bp b/aconfig/Android.bp new file mode 100644 index 0000000000..3c79f19c15 --- /dev/null +++ b/aconfig/Android.bp @@ -0,0 +1,30 @@ +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +bootstrap_go_package { + name: "soong-aconfig", + pkgPath: "android/soong/aconfig", + deps: [ + "blueprint", + "blueprint-pathtools", + "sbox_proto", + "soong", + "soong-android", + ], + srcs: [ + "aconfig_declarations.go", + "aconfig_values.go", + "aconfig_value_set.go", + "all_aconfig_declarations.go", + "exported_java_aconfig_library.go", + "init.go", + "testing.go", + ], + testSrcs: [ + "aconfig_declarations_test.go", + "aconfig_values_test.go", + "aconfig_value_set_test.go", + ], + pluginFor: ["soong_build"], +} diff --git a/aconfig/aconfig_declarations.go b/aconfig/aconfig_declarations.go new file mode 100644 index 0000000000..697dc22aba --- /dev/null +++ b/aconfig/aconfig_declarations.go @@ -0,0 +1,232 @@ +// Copyright 2023 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 aconfig + +import ( + "fmt" + "strings" + + "android/soong/android" + + "github.com/google/blueprint" +) + +type DeclarationsModule struct { + android.ModuleBase + android.DefaultableModuleBase + + // Properties for "aconfig_declarations" + properties struct { + // aconfig files, relative to this Android.bp file + Srcs []string `android:"path"` + + // Release config flag package + Package string + + // Values from TARGET_RELEASE / RELEASE_ACONFIG_VALUE_SETS + Values []string `blueprint:"mutated"` + + // Container(system/vendor/apex) that this module belongs to + Container string + } + + intermediatePath android.WritablePath +} + +func DeclarationsFactory() android.Module { + module := &DeclarationsModule{} + + android.InitAndroidModule(module) + android.InitDefaultableModule(module) + module.AddProperties(&module.properties) + + return module +} + +type implicitValuesTagType struct { + blueprint.BaseDependencyTag +} + +var implicitValuesTag = implicitValuesTagType{} + +func (module *DeclarationsModule) DepsMutator(ctx android.BottomUpMutatorContext) { + // Validate Properties + if len(module.properties.Srcs) == 0 { + ctx.PropertyErrorf("srcs", "missing source files") + return + } + if len(module.properties.Package) == 0 { + ctx.PropertyErrorf("package", "missing package property") + } + // TODO(b/311155208): Add mandatory check for container after all pre-existing + // ones are changed. + + // Add a dependency on the aconfig_value_sets defined in + // RELEASE_ACONFIG_VALUE_SETS, and add any aconfig_values that + // match our package. + valuesFromConfig := ctx.Config().ReleaseAconfigValueSets() + if len(valuesFromConfig) > 0 { + ctx.AddDependency(ctx.Module(), implicitValuesTag, valuesFromConfig...) + } +} + +func (module *DeclarationsModule) OutputFiles(tag string) (android.Paths, error) { + switch tag { + case "": + // The default output of this module is the intermediates format, which is + // not installable and in a private format that no other rules can handle + // correctly. + return []android.Path{module.intermediatePath}, nil + default: + return nil, fmt.Errorf("unsupported aconfig_declarations module reference tag %q", tag) + } +} + +func joinAndPrefix(prefix string, values []string) string { + var sb strings.Builder + for _, v := range values { + sb.WriteString(prefix) + sb.WriteString(v) + } + return sb.String() +} + +func optionalVariable(prefix string, value string) string { + var sb strings.Builder + if value != "" { + sb.WriteString(prefix) + sb.WriteString(value) + } + return sb.String() +} + +// Provider published by aconfig_value_set +type DeclarationsProviderData struct { + Package string + Container string + IntermediateCacheOutputPath android.WritablePath + IntermediateDumpOutputPath android.WritablePath +} + +var DeclarationsProviderKey = blueprint.NewProvider(DeclarationsProviderData{}) + +// This is used to collect the aconfig declarations info on the transitive closure, +// the data is keyed on the container. +type TransitiveDeclarationsInfo struct { + AconfigFiles map[string]android.Paths +} + +var TransitiveDeclarationsInfoProvider = blueprint.NewProvider(TransitiveDeclarationsInfo{}) + +func (module *DeclarationsModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { + // Get the values that came from the global RELEASE_ACONFIG_VALUE_SETS flag + valuesFiles := make([]android.Path, 0) + ctx.VisitDirectDeps(func(dep android.Module) { + if !ctx.OtherModuleHasProvider(dep, valueSetProviderKey) { + // Other modules get injected as dependencies too, for example the license modules + return + } + depData := ctx.OtherModuleProvider(dep, valueSetProviderKey).(valueSetProviderData) + paths, ok := depData.AvailablePackages[module.properties.Package] + if ok { + valuesFiles = append(valuesFiles, paths...) + for _, path := range paths { + module.properties.Values = append(module.properties.Values, path.String()) + } + } + }) + + // Intermediate format + declarationFiles := android.PathsForModuleSrc(ctx, module.properties.Srcs) + intermediateCacheFilePath := android.PathForModuleOut(ctx, "intermediate.pb") + defaultPermission := ctx.Config().ReleaseAconfigFlagDefaultPermission() + inputFiles := make([]android.Path, len(declarationFiles)) + copy(inputFiles, declarationFiles) + inputFiles = append(inputFiles, valuesFiles...) + ctx.Build(pctx, android.BuildParams{ + Rule: aconfigRule, + Output: intermediateCacheFilePath, + Inputs: inputFiles, + Description: "aconfig_declarations", + Args: map[string]string{ + "release_version": ctx.Config().ReleaseVersion(), + "package": module.properties.Package, + "declarations": android.JoinPathsWithPrefix(declarationFiles, "--declarations "), + "values": joinAndPrefix(" --values ", module.properties.Values), + "default-permission": optionalVariable(" --default-permission ", defaultPermission), + }, + }) + + intermediateDumpFilePath := android.PathForModuleOut(ctx, "intermediate.txt") + ctx.Build(pctx, android.BuildParams{ + Rule: aconfigTextRule, + Output: intermediateDumpFilePath, + Inputs: android.Paths{intermediateCacheFilePath}, + Description: "aconfig_text", + }) + + ctx.SetProvider(DeclarationsProviderKey, DeclarationsProviderData{ + Package: module.properties.Package, + Container: module.properties.Container, + IntermediateCacheOutputPath: intermediateCacheFilePath, + IntermediateDumpOutputPath: intermediateDumpFilePath, + }) + +} +func CollectDependencyAconfigFiles(ctx android.ModuleContext, mergedAconfigFiles *map[string]android.Paths) { + if *mergedAconfigFiles == nil { + *mergedAconfigFiles = make(map[string]android.Paths) + } + ctx.VisitDirectDeps(func(module android.Module) { + if dep := ctx.OtherModuleProvider(module, DeclarationsProviderKey).(DeclarationsProviderData); dep.IntermediateCacheOutputPath != nil { + (*mergedAconfigFiles)[dep.Container] = append((*mergedAconfigFiles)[dep.Container], dep.IntermediateCacheOutputPath) + return + } + if dep := ctx.OtherModuleProvider(module, TransitiveDeclarationsInfoProvider).(TransitiveDeclarationsInfo); len(dep.AconfigFiles) > 0 { + for container, v := range dep.AconfigFiles { + (*mergedAconfigFiles)[container] = append((*mergedAconfigFiles)[container], v...) + } + } + }) + + for container, aconfigFiles := range *mergedAconfigFiles { + (*mergedAconfigFiles)[container] = mergeAconfigFiles(ctx, aconfigFiles) + } + + ctx.SetProvider(TransitiveDeclarationsInfoProvider, TransitiveDeclarationsInfo{ + AconfigFiles: *mergedAconfigFiles, + }) +} + +func mergeAconfigFiles(ctx android.ModuleContext, inputs android.Paths) android.Paths { + inputs = android.LastUniquePaths(inputs) + if len(inputs) == 1 { + return android.Paths{inputs[0]} + } + + output := android.PathForModuleOut(ctx, "aconfig_merged.pb") + + ctx.Build(pctx, android.BuildParams{ + Rule: mergeAconfigFilesRule, + Description: "merge aconfig files", + Inputs: inputs, + Output: output, + Args: map[string]string{ + "flags": android.JoinWithPrefix(inputs.Strings(), "--cache "), + }, + }) + + return android.Paths{output} +} diff --git a/aconfig/aconfig_declarations_test.go b/aconfig/aconfig_declarations_test.go new file mode 100644 index 0000000000..9035c710d8 --- /dev/null +++ b/aconfig/aconfig_declarations_test.go @@ -0,0 +1,50 @@ +// Copyright 2018 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 aconfig + +import ( + "strings" + "testing" + + "android/soong/android" +) + +func TestAconfigDeclarations(t *testing.T) { + bp := ` + aconfig_declarations { + name: "module_name", + package: "com.example.package", + container: "com.android.foo", + srcs: [ + "foo.aconfig", + "bar.aconfig", + ], + } + ` + result := runTest(t, android.FixtureExpectsNoErrors, bp) + + module := result.ModuleForTests("module_name", "").Module().(*DeclarationsModule) + + // Check that the provider has the right contents + depData := result.ModuleProvider(module, DeclarationsProviderKey).(DeclarationsProviderData) + android.AssertStringEquals(t, "package", depData.Package, "com.example.package") + android.AssertStringEquals(t, "container", depData.Container, "com.android.foo") + if !strings.HasSuffix(depData.IntermediateCacheOutputPath.String(), "/intermediate.pb") { + t.Errorf("Missing intermediates proto path in provider: %s", depData.IntermediateCacheOutputPath.String()) + } + if !strings.HasSuffix(depData.IntermediateDumpOutputPath.String(), "/intermediate.txt") { + t.Errorf("Missing intermediates text path in provider: %s", depData.IntermediateDumpOutputPath.String()) + } +} diff --git a/aconfig/aconfig_value_set.go b/aconfig/aconfig_value_set.go new file mode 100644 index 0000000000..94c322a42d --- /dev/null +++ b/aconfig/aconfig_value_set.go @@ -0,0 +1,90 @@ +// Copyright 2023 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 aconfig + +import ( + "android/soong/android" + "github.com/google/blueprint" +) + +// Properties for "aconfig_value_set" +type ValueSetModule struct { + android.ModuleBase + android.DefaultableModuleBase + + properties struct { + // aconfig_values modules + Values []string + } +} + +func ValueSetFactory() android.Module { + module := &ValueSetModule{} + + android.InitAndroidModule(module) + android.InitDefaultableModule(module) + module.AddProperties(&module.properties) + + return module +} + +// Dependency tag for values property +type valueSetType struct { + blueprint.BaseDependencyTag +} + +var valueSetTag = valueSetType{} + +// Provider published by aconfig_value_set +type valueSetProviderData struct { + // The package of each of the + // (map of package --> aconfig_module) + AvailablePackages map[string]android.Paths +} + +var valueSetProviderKey = blueprint.NewProvider(valueSetProviderData{}) + +func (module *ValueSetModule) DepsMutator(ctx android.BottomUpMutatorContext) { + deps := ctx.AddDependency(ctx.Module(), valueSetTag, module.properties.Values...) + for _, dep := range deps { + _, ok := dep.(*ValuesModule) + if !ok { + ctx.PropertyErrorf("values", "values must be a aconfig_values module") + return + } + } +} + +func (module *ValueSetModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { + // Accumulate the packages of the values modules listed, and set that as an + // valueSetProviderKey provider that aconfig modules can read and use + // to append values to their aconfig actions. + packages := make(map[string]android.Paths) + ctx.VisitDirectDeps(func(dep android.Module) { + if !ctx.OtherModuleHasProvider(dep, valuesProviderKey) { + // Other modules get injected as dependencies too, for example the license modules + return + } + depData := ctx.OtherModuleProvider(dep, valuesProviderKey).(valuesProviderData) + + srcs := make([]android.Path, len(depData.Values)) + copy(srcs, depData.Values) + packages[depData.Package] = srcs + + }) + ctx.SetProvider(valueSetProviderKey, valueSetProviderData{ + AvailablePackages: packages, + }) +} diff --git a/aconfig/aconfig_value_set_test.go b/aconfig/aconfig_value_set_test.go new file mode 100644 index 0000000000..91278729ef --- /dev/null +++ b/aconfig/aconfig_value_set_test.go @@ -0,0 +1,43 @@ +// Copyright 2018 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 aconfig + +import ( + "testing" + + "android/soong/android" +) + +func TestAconfigValueSet(t *testing.T) { + bp := ` + aconfig_values { + name: "one", + srcs: [ "blah.aconfig_values" ], + package: "foo.package" + } + + aconfig_value_set { + name: "module_name", + values: [ "one" ], + } + ` + result := runTest(t, android.FixtureExpectsNoErrors, bp) + + module := result.ModuleForTests("module_name", "").Module().(*ValueSetModule) + + // Check that the provider has the right contents + depData := result.ModuleProvider(module, valueSetProviderKey).(valueSetProviderData) + android.AssertStringEquals(t, "AvailablePackages", "blah.aconfig_values", depData.AvailablePackages["foo.package"][0].String()) +} diff --git a/aconfig/aconfig_values.go b/aconfig/aconfig_values.go new file mode 100644 index 0000000000..621aae8cf4 --- /dev/null +++ b/aconfig/aconfig_values.go @@ -0,0 +1,68 @@ +// Copyright 2023 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 aconfig + +import ( + "android/soong/android" + "github.com/google/blueprint" +) + +// Properties for "aconfig_value" +type ValuesModule struct { + android.ModuleBase + android.DefaultableModuleBase + + properties struct { + // aconfig files, relative to this Android.bp file + Srcs []string `android:"path"` + + // Release config flag package + Package string + } +} + +func ValuesFactory() android.Module { + module := &ValuesModule{} + + android.InitAndroidModule(module) + android.InitDefaultableModule(module) + module.AddProperties(&module.properties) + + return module +} + +// Provider published by aconfig_value_set +type valuesProviderData struct { + // The package that this values module values + Package string + + // The values aconfig files, relative to the root of the tree + Values android.Paths +} + +var valuesProviderKey = blueprint.NewProvider(valuesProviderData{}) + +func (module *ValuesModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { + if len(module.properties.Package) == 0 { + ctx.PropertyErrorf("package", "missing package property") + } + + // Provide the our source files list to the aconfig_value_set as a list of files + providerData := valuesProviderData{ + Package: module.properties.Package, + Values: android.PathsForModuleSrc(ctx, module.properties.Srcs), + } + ctx.SetProvider(valuesProviderKey, providerData) +} diff --git a/aconfig/aconfig_values_test.go b/aconfig/aconfig_values_test.go new file mode 100644 index 0000000000..ab457f06ac --- /dev/null +++ b/aconfig/aconfig_values_test.go @@ -0,0 +1,39 @@ +// Copyright 2018 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 aconfig + +import ( + "testing" + + "android/soong/android" +) + +func TestAconfigValues(t *testing.T) { + bp := ` + aconfig_values { + name: "module_name", + srcs: [ "blah.aconfig_values" ], + package: "foo.package" + } + ` + result := runTest(t, android.FixtureExpectsNoErrors, bp) + + module := result.ModuleForTests("module_name", "").Module().(*ValuesModule) + + // Check that the provider has the right contents + depData := result.ModuleProvider(module, valuesProviderKey).(valuesProviderData) + android.AssertStringEquals(t, "package", "foo.package", depData.Package) + android.AssertPathsEndWith(t, "srcs", []string{"blah.aconfig_values"}, depData.Values) +} diff --git a/aconfig/all_aconfig_declarations.go b/aconfig/all_aconfig_declarations.go new file mode 100644 index 0000000000..2686e760e9 --- /dev/null +++ b/aconfig/all_aconfig_declarations.go @@ -0,0 +1,63 @@ +// Copyright 2023 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 aconfig + +import ( + "android/soong/android" +) + +// A singleton module that collects all of the aconfig flags declared in the +// tree into a single combined file for export to the external flag setting +// server (inside Google it's Gantry). +// +// Note that this is ALL aconfig_declarations modules present in the tree, not just +// ones that are relevant to the product currently being built, so that that infra +// doesn't need to pull from multiple builds and merge them. +func AllAconfigDeclarationsFactory() android.Singleton { + return &allAconfigDeclarationsSingleton{} +} + +type allAconfigDeclarationsSingleton struct { + intermediatePath android.OutputPath +} + +func (this *allAconfigDeclarationsSingleton) GenerateBuildActions(ctx android.SingletonContext) { + // Find all of the aconfig_declarations modules + var cacheFiles android.Paths + ctx.VisitAllModules(func(module android.Module) { + if !ctx.ModuleHasProvider(module, DeclarationsProviderKey) { + return + } + decl := ctx.ModuleProvider(module, DeclarationsProviderKey).(DeclarationsProviderData) + cacheFiles = append(cacheFiles, decl.IntermediateCacheOutputPath) + }) + + // Generate build action for aconfig + this.intermediatePath = android.PathForIntermediates(ctx, "all_aconfig_declarations.pb") + ctx.Build(pctx, android.BuildParams{ + Rule: AllDeclarationsRule, + Inputs: cacheFiles, + Output: this.intermediatePath, + Description: "all_aconfig_declarations", + Args: map[string]string{ + "cache_files": android.JoinPathsWithPrefix(cacheFiles, "--cache "), + }, + }) + ctx.Phony("all_aconfig_declarations", this.intermediatePath) +} + +func (this *allAconfigDeclarationsSingleton) MakeVars(ctx android.MakeVarsContext) { + ctx.DistForGoal("droid", this.intermediatePath) +} diff --git a/aconfig/codegen/Android.bp b/aconfig/codegen/Android.bp new file mode 100644 index 0000000000..494f7e6937 --- /dev/null +++ b/aconfig/codegen/Android.bp @@ -0,0 +1,32 @@ +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +bootstrap_go_package { + name: "soong-aconfig-codegen", + pkgPath: "android/soong/aconfig/codegen", + deps: [ + "blueprint", + "blueprint-pathtools", + "sbox_proto", + "soong", + "soong-aconfig", + "soong-android", + "soong-bazel", + "soong-java", + "soong-rust", + ], + srcs: [ + "cc_aconfig_library.go", + "init.go", + "java_aconfig_library.go", + "rust_aconfig_library.go", + "testing.go", + ], + testSrcs: [ + "java_aconfig_library_test.go", + "cc_aconfig_library_test.go", + "rust_aconfig_library_test.go", + ], + pluginFor: ["soong_build"], +} diff --git a/aconfig/codegen/cc_aconfig_library.go b/aconfig/codegen/cc_aconfig_library.go new file mode 100644 index 0000000000..30f6863a0b --- /dev/null +++ b/aconfig/codegen/cc_aconfig_library.go @@ -0,0 +1,145 @@ +// Copyright 2023 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 codegen + +import ( + "android/soong/aconfig" + "android/soong/android" + "android/soong/cc" + + "github.com/google/blueprint" + "github.com/google/blueprint/proptools" + + "fmt" + "strings" +) + +type ccDeclarationsTagType struct { + blueprint.BaseDependencyTag +} + +var ccDeclarationsTag = ccDeclarationsTagType{} + +const baseLibDep = "server_configurable_flags" + +type CcAconfigLibraryProperties struct { + // name of the aconfig_declarations module to generate a library for + Aconfig_declarations string + + // default mode is "production", the other accepted modes are: + // "test": to generate test mode version of the library + // "exported": to generate exported mode version of the library + // an error will be thrown if the mode is not supported + Mode *string +} + +type CcAconfigLibraryCallbacks struct { + properties *CcAconfigLibraryProperties + + generatedDir android.WritablePath + headerDir android.WritablePath + generatedCpp android.WritablePath + generatedH android.WritablePath +} + +func CcAconfigLibraryFactory() android.Module { + callbacks := &CcAconfigLibraryCallbacks{ + properties: &CcAconfigLibraryProperties{}, + } + return cc.GeneratedCcLibraryModuleFactory("cc_aconfig_library", callbacks) +} + +func (this *CcAconfigLibraryCallbacks) GeneratorInit(ctx cc.BaseModuleContext) { +} + +func (this *CcAconfigLibraryCallbacks) GeneratorProps() []interface{} { + return []interface{}{this.properties} +} + +func (this *CcAconfigLibraryCallbacks) GeneratorDeps(ctx cc.DepsContext, deps cc.Deps) cc.Deps { + // Add a dependency for the declarations module + declarations := this.properties.Aconfig_declarations + if len(declarations) == 0 { + ctx.PropertyErrorf("aconfig_declarations", "aconfig_declarations property required") + } else { + ctx.AddDependency(ctx.Module(), ccDeclarationsTag, declarations) + } + + // Add a dependency for the aconfig flags base library + deps.SharedLibs = append(deps.SharedLibs, baseLibDep) + // TODO: It'd be really nice if we could reexport this library and not make everyone do it. + + return deps +} + +func (this *CcAconfigLibraryCallbacks) GeneratorSources(ctx cc.ModuleContext) cc.GeneratedSource { + result := cc.GeneratedSource{} + + // Get the values that came from the global RELEASE_ACONFIG_VALUE_SETS flag + declarationsModules := ctx.GetDirectDepsWithTag(ccDeclarationsTag) + if len(declarationsModules) != 1 { + panic(fmt.Errorf("Exactly one aconfig_declarations property required")) + } + declarations := ctx.OtherModuleProvider(declarationsModules[0], aconfig.DeclarationsProviderKey).(aconfig.DeclarationsProviderData) + + // Figure out the generated file paths. This has to match aconfig's codegen_cpp.rs. + this.generatedDir = android.PathForModuleGen(ctx) + + this.headerDir = android.PathForModuleGen(ctx, "include") + result.IncludeDirs = []android.Path{this.headerDir} + result.ReexportedDirs = []android.Path{this.headerDir} + + basename := strings.ReplaceAll(declarations.Package, ".", "_") + + this.generatedCpp = android.PathForModuleGen(ctx, basename+".cc") + result.Sources = []android.Path{this.generatedCpp} + + this.generatedH = android.PathForModuleGen(ctx, "include", basename+".h") + result.Headers = []android.Path{this.generatedH} + + return result +} + +func (this *CcAconfigLibraryCallbacks) GeneratorFlags(ctx cc.ModuleContext, flags cc.Flags, deps cc.PathDeps) cc.Flags { + return flags +} + +func (this *CcAconfigLibraryCallbacks) GeneratorBuildActions(ctx cc.ModuleContext, flags cc.Flags, deps cc.PathDeps) { + // Get the values that came from the global RELEASE_ACONFIG_VALUE_SETS flag + declarationsModules := ctx.GetDirectDepsWithTag(ccDeclarationsTag) + if len(declarationsModules) != 1 { + panic(fmt.Errorf("Exactly one aconfig_declarations property required")) + } + declarations := ctx.OtherModuleProvider(declarationsModules[0], aconfig.DeclarationsProviderKey).(aconfig.DeclarationsProviderData) + + mode := proptools.StringDefault(this.properties.Mode, "production") + if !isModeSupported(mode) { + ctx.PropertyErrorf("mode", "%q is not a supported mode", mode) + } + + ctx.Build(pctx, android.BuildParams{ + Rule: cppRule, + Input: declarations.IntermediateCacheOutputPath, + Outputs: []android.WritablePath{ + this.generatedCpp, + this.generatedH, + }, + Description: "cc_aconfig_library", + Args: map[string]string{ + "gendir": this.generatedDir.String(), + "mode": mode, + }, + }) +} diff --git a/aconfig/codegen/cc_aconfig_library_test.go b/aconfig/codegen/cc_aconfig_library_test.go new file mode 100644 index 0000000000..0c8a96936a --- /dev/null +++ b/aconfig/codegen/cc_aconfig_library_test.go @@ -0,0 +1,106 @@ +// Copyright 2023 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 codegen + +import ( + "fmt" + "testing" + + "android/soong/android" + "android/soong/cc" +) + +var ccCodegenModeTestData = []struct { + setting, expected string +}{ + {"", "production"}, + {"mode: `production`,", "production"}, + {"mode: `test`,", "test"}, + {"mode: `exported`,", "exported"}, +} + +func TestCCCodegenMode(t *testing.T) { + for _, testData := range ccCodegenModeTestData { + testCCCodegenModeHelper(t, testData.setting, testData.expected) + } +} + +func testCCCodegenModeHelper(t *testing.T, bpMode string, ruleMode string) { + t.Helper() + result := android.GroupFixturePreparers( + PrepareForTestWithAconfigBuildComponents, + cc.PrepareForTestWithCcDefaultModules). + ExtendWithErrorHandler(android.FixtureExpectsNoErrors). + RunTestWithBp(t, fmt.Sprintf(` + aconfig_declarations { + name: "my_aconfig_declarations", + package: "com.example.package", + srcs: ["foo.aconfig"], + } + + cc_library { + name: "server_configurable_flags", + srcs: ["server_configurable_flags.cc"], + } + + cc_aconfig_library { + name: "my_cc_aconfig_library", + aconfig_declarations: "my_aconfig_declarations", + %s + } + `, bpMode)) + + module := result.ModuleForTests("my_cc_aconfig_library", "android_arm64_armv8-a_shared") + rule := module.Rule("cc_aconfig_library") + android.AssertStringEquals(t, "rule must contain test mode", rule.Args["mode"], ruleMode) +} + +var incorrectCCCodegenModeTestData = []struct { + setting, expectedErr string +}{ + {"mode: `unsupported`,", "mode: \"unsupported\" is not a supported mode"}, +} + +func TestIncorrectCCCodegenMode(t *testing.T) { + for _, testData := range incorrectCCCodegenModeTestData { + testIncorrectCCCodegenModeHelper(t, testData.setting, testData.expectedErr) + } +} + +func testIncorrectCCCodegenModeHelper(t *testing.T, bpMode string, err string) { + t.Helper() + android.GroupFixturePreparers( + PrepareForTestWithAconfigBuildComponents, + cc.PrepareForTestWithCcDefaultModules). + ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern(err)). + RunTestWithBp(t, fmt.Sprintf(` + aconfig_declarations { + name: "my_aconfig_declarations", + package: "com.example.package", + srcs: ["foo.aconfig"], + } + + cc_library { + name: "server_configurable_flags", + srcs: ["server_configurable_flags.cc"], + } + + cc_aconfig_library { + name: "my_cc_aconfig_library", + aconfig_declarations: "my_aconfig_declarations", + %s + } + `, bpMode)) +} diff --git a/aconfig/codegen/init.go b/aconfig/codegen/init.go new file mode 100644 index 0000000000..0bff9d2af6 --- /dev/null +++ b/aconfig/codegen/init.go @@ -0,0 +1,83 @@ +// Copyright 2023 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 codegen + +import ( + "android/soong/android" + + "github.com/google/blueprint" +) + +var ( + pctx = android.NewPackageContext("android/soong/aconfig/codegen") + + // For java_aconfig_library: Generate java library + javaRule = pctx.AndroidStaticRule("java_aconfig_library", + blueprint.RuleParams{ + Command: `rm -rf ${out}.tmp` + + ` && mkdir -p ${out}.tmp` + + ` && ${aconfig} create-java-lib` + + ` --mode ${mode}` + + ` --cache ${in}` + + ` --out ${out}.tmp` + + ` && $soong_zip -write_if_changed -jar -o ${out} -C ${out}.tmp -D ${out}.tmp` + + ` && rm -rf ${out}.tmp`, + CommandDeps: []string{ + "$aconfig", + "$soong_zip", + }, + Restat: true, + }, "mode") + + // For cc_aconfig_library: Generate C++ library + cppRule = pctx.AndroidStaticRule("cc_aconfig_library", + blueprint.RuleParams{ + Command: `rm -rf ${gendir}` + + ` && mkdir -p ${gendir}` + + ` && ${aconfig} create-cpp-lib` + + ` --mode ${mode}` + + ` --cache ${in}` + + ` --out ${gendir}`, + CommandDeps: []string{ + "$aconfig", + }, + }, "gendir", "mode") + + // For rust_aconfig_library: Generate Rust library + rustRule = pctx.AndroidStaticRule("rust_aconfig_library", + blueprint.RuleParams{ + Command: `rm -rf ${gendir}` + + ` && mkdir -p ${gendir}` + + ` && ${aconfig} create-rust-lib` + + ` --mode ${mode}` + + ` --cache ${in}` + + ` --out ${gendir}`, + CommandDeps: []string{ + "$aconfig", + }, + }, "gendir", "mode") +) + +func init() { + RegisterBuildComponents(android.InitRegistrationContext) + pctx.HostBinToolVariable("aconfig", "aconfig") + pctx.HostBinToolVariable("soong_zip", "soong_zip") +} + +func RegisterBuildComponents(ctx android.RegistrationContext) { + ctx.RegisterModuleType("cc_aconfig_library", CcAconfigLibraryFactory) + ctx.RegisterModuleType("java_aconfig_library", JavaDeclarationsLibraryFactory) + ctx.RegisterModuleType("rust_aconfig_library", RustAconfigLibraryFactory) +} diff --git a/aconfig/codegen/java_aconfig_library.go b/aconfig/codegen/java_aconfig_library.go new file mode 100644 index 0000000000..202e358ee7 --- /dev/null +++ b/aconfig/codegen/java_aconfig_library.go @@ -0,0 +1,102 @@ +// Copyright 2023 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 codegen + +import ( + "fmt" + + "android/soong/aconfig" + "android/soong/android" + "android/soong/java" + + "github.com/google/blueprint" + "github.com/google/blueprint/proptools" +) + +type declarationsTagType struct { + blueprint.BaseDependencyTag +} + +var declarationsTag = declarationsTagType{} + +var aconfigSupportedModes = []string{"production", "test", "exported"} + +type JavaAconfigDeclarationsLibraryProperties struct { + // name of the aconfig_declarations module to generate a library for + Aconfig_declarations string + + // default mode is "production", the other accepted modes are: + // "test": to generate test mode version of the library + // "exported": to generate exported mode version of the library + // an error will be thrown if the mode is not supported + Mode *string +} + +type JavaAconfigDeclarationsLibraryCallbacks struct { + properties JavaAconfigDeclarationsLibraryProperties +} + +func JavaDeclarationsLibraryFactory() android.Module { + callbacks := &JavaAconfigDeclarationsLibraryCallbacks{} + return java.GeneratedJavaLibraryModuleFactory("java_aconfig_library", callbacks, &callbacks.properties) +} + +func (callbacks *JavaAconfigDeclarationsLibraryCallbacks) DepsMutator(module *java.GeneratedJavaLibraryModule, ctx android.BottomUpMutatorContext) { + declarations := callbacks.properties.Aconfig_declarations + if len(declarations) == 0 { + // TODO: Add test for this case + ctx.PropertyErrorf("aconfig_declarations", "aconfig_declarations property required") + } else { + ctx.AddDependency(ctx.Module(), declarationsTag, declarations) + } + + // Add aconfig-annotations-lib as a dependency for the optimization / code stripping annotations + module.AddSharedLibrary("aconfig-annotations-lib") + // TODO(b/303773055): Remove the annotation after access issue is resolved. + module.AddSharedLibrary("unsupportedappusage") +} + +func (callbacks *JavaAconfigDeclarationsLibraryCallbacks) GenerateSourceJarBuildActions(module *java.GeneratedJavaLibraryModule, ctx android.ModuleContext) android.Path { + // Get the values that came from the global RELEASE_ACONFIG_VALUE_SETS flag + declarationsModules := ctx.GetDirectDepsWithTag(declarationsTag) + if len(declarationsModules) != 1 { + panic(fmt.Errorf("Exactly one aconfig_declarations property required")) + } + declarations := ctx.OtherModuleProvider(declarationsModules[0], aconfig.DeclarationsProviderKey).(aconfig.DeclarationsProviderData) + + // Generate the action to build the srcjar + srcJarPath := android.PathForModuleGen(ctx, ctx.ModuleName()+".srcjar") + + mode := proptools.StringDefault(callbacks.properties.Mode, "production") + if !isModeSupported(mode) { + ctx.PropertyErrorf("mode", "%q is not a supported mode", mode) + } + + ctx.Build(pctx, android.BuildParams{ + Rule: javaRule, + Input: declarations.IntermediateCacheOutputPath, + Output: srcJarPath, + Description: "aconfig.srcjar", + Args: map[string]string{ + "mode": mode, + }, + }) + + return srcJarPath +} + +func isModeSupported(mode string) bool { + return android.InList(mode, aconfigSupportedModes) +} diff --git a/aconfig/codegen/java_aconfig_library_test.go b/aconfig/codegen/java_aconfig_library_test.go new file mode 100644 index 0000000000..2523abcbc9 --- /dev/null +++ b/aconfig/codegen/java_aconfig_library_test.go @@ -0,0 +1,232 @@ +// Copyright 2023 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 codegen + +import ( + "fmt" + "testing" + + "android/soong/android" + "android/soong/java" +) + +// Note: These tests cover the code in the java package. It'd be ideal of that code could +// be in the aconfig package. + +// With the bp parameter that defines a my_module, make sure it has the LOCAL_ACONFIG_FILES entries +func runJavaAndroidMkTest(t *testing.T, bp string) { + result := android.GroupFixturePreparers( + PrepareForTestWithAconfigBuildComponents, + java.PrepareForTestWithJavaDefaultModules). + ExtendWithErrorHandler(android.FixtureExpectsNoErrors). + RunTestWithBp(t, bp+` + aconfig_declarations { + name: "my_aconfig_declarations_foo", + package: "com.example.package", + srcs: ["foo.aconfig"], + } + + java_aconfig_library { + name: "my_java_aconfig_library_foo", + aconfig_declarations: "my_aconfig_declarations_foo", + } + + aconfig_declarations { + name: "my_aconfig_declarations_bar", + package: "com.example.package", + srcs: ["bar.aconfig"], + } + + java_aconfig_library { + name: "my_java_aconfig_library_bar", + aconfig_declarations: "my_aconfig_declarations_bar", + } + `) + + module := result.ModuleForTests("my_module", "android_common").Module() + + entry := android.AndroidMkEntriesForTest(t, result.TestContext, module)[0] + + makeVar := entry.EntryMap["LOCAL_ACONFIG_FILES"] + android.AssertIntEquals(t, "len(LOCAL_ACONFIG_FILES)", 1, len(makeVar)) + android.EnsureListContainsSuffix(t, makeVar, "android_common/aconfig_merged.pb") +} + +func TestAndroidMkJavaLibrary(t *testing.T) { + bp := ` + java_library { + name: "my_module", + srcs: [ + "src/foo.java", + ], + static_libs: [ + "my_java_aconfig_library_foo", + "my_java_aconfig_library_bar", + ], + platform_apis: true, + } + ` + + runJavaAndroidMkTest(t, bp) +} + +func TestAndroidMkAndroidApp(t *testing.T) { + bp := ` + android_app { + name: "my_module", + srcs: [ + "src/foo.java", + ], + static_libs: [ + "my_java_aconfig_library_foo", + "my_java_aconfig_library_bar", + ], + platform_apis: true, + } + ` + + runJavaAndroidMkTest(t, bp) +} + +func TestAndroidMkBinary(t *testing.T) { + bp := ` + java_binary { + name: "my_module", + srcs: [ + "src/foo.java", + ], + static_libs: [ + "my_java_aconfig_library_foo", + "my_java_aconfig_library_bar", + ], + platform_apis: true, + main_class: "foo", + } + ` + + runJavaAndroidMkTest(t, bp) +} + +func TestAndroidMkAndroidLibrary(t *testing.T) { + bp := ` + android_library { + name: "my_module", + srcs: [ + "src/foo.java", + ], + static_libs: [ + "my_java_aconfig_library_foo", + "my_java_aconfig_library_bar", + ], + platform_apis: true, + } + ` + + runJavaAndroidMkTest(t, bp) +} + +func TestAndroidMkBinaryThatLinksAgainstAar(t *testing.T) { + // Tests AndroidLibrary's propagation of flags through JavaInfo + bp := ` + android_library { + name: "some_library", + srcs: [ + "src/foo.java", + ], + static_libs: [ + "my_java_aconfig_library_foo", + "my_java_aconfig_library_bar", + ], + platform_apis: true, + } + java_binary { + name: "my_module", + srcs: [ + "src/bar.java", + ], + static_libs: [ + "some_library", + ], + platform_apis: true, + main_class: "foo", + } + ` + + runJavaAndroidMkTest(t, bp) +} + +func testCodegenMode(t *testing.T, bpMode string, ruleMode string) { + result := android.GroupFixturePreparers( + PrepareForTestWithAconfigBuildComponents, + java.PrepareForTestWithJavaDefaultModules). + ExtendWithErrorHandler(android.FixtureExpectsNoErrors). + RunTestWithBp(t, fmt.Sprintf(` + aconfig_declarations { + name: "my_aconfig_declarations", + package: "com.example.package", + srcs: ["foo.aconfig"], + } + + java_aconfig_library { + name: "my_java_aconfig_library", + aconfig_declarations: "my_aconfig_declarations", + %s + } + `, bpMode)) + + module := result.ModuleForTests("my_java_aconfig_library", "android_common") + rule := module.Rule("java_aconfig_library") + android.AssertStringEquals(t, "rule must contain test mode", rule.Args["mode"], ruleMode) +} + +func testCodegenModeWithError(t *testing.T, bpMode string, err string) { + android.GroupFixturePreparers( + PrepareForTestWithAconfigBuildComponents, + java.PrepareForTestWithJavaDefaultModules). + ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern(err)). + RunTestWithBp(t, fmt.Sprintf(` + aconfig_declarations { + name: "my_aconfig_declarations", + package: "com.example.package", + srcs: ["foo.aconfig"], + } + + java_aconfig_library { + name: "my_java_aconfig_library", + aconfig_declarations: "my_aconfig_declarations", + %s + } + `, bpMode)) +} + +func TestDefaultProdMode(t *testing.T) { + testCodegenMode(t, "", "production") +} + +func TestProdMode(t *testing.T) { + testCodegenMode(t, "mode: `production`,", "production") +} + +func TestTestMode(t *testing.T) { + testCodegenMode(t, "mode: `test`,", "test") +} + +func TestExportedMode(t *testing.T) { + testCodegenMode(t, "mode: `exported`,", "exported") +} + +func TestUnsupportedMode(t *testing.T) { + testCodegenModeWithError(t, "mode: `unsupported`,", "mode: \"unsupported\" is not a supported mode") +} diff --git a/aconfig/codegen/rust_aconfig_library.go b/aconfig/codegen/rust_aconfig_library.go new file mode 100644 index 0000000000..e5870562c2 --- /dev/null +++ b/aconfig/codegen/rust_aconfig_library.go @@ -0,0 +1,97 @@ +package codegen + +import ( + "fmt" + + "android/soong/aconfig" + "android/soong/android" + "android/soong/rust" + + "github.com/google/blueprint" + "github.com/google/blueprint/proptools" +) + +type rustDeclarationsTagType struct { + blueprint.BaseDependencyTag +} + +var rustDeclarationsTag = rustDeclarationsTagType{} + +type RustAconfigLibraryProperties struct { + // name of the aconfig_declarations module to generate a library for + Aconfig_declarations string + + // default mode is "production", the other accepted modes are: + // "test": to generate test mode version of the library + // "exported": to generate exported mode version of the library + // an error will be thrown if the mode is not supported + Mode *string +} + +type aconfigDecorator struct { + *rust.BaseSourceProvider + + Properties RustAconfigLibraryProperties +} + +func NewRustAconfigLibrary(hod android.HostOrDeviceSupported) (*rust.Module, *aconfigDecorator) { + aconfig := &aconfigDecorator{ + BaseSourceProvider: rust.NewSourceProvider(), + Properties: RustAconfigLibraryProperties{}, + } + + module := rust.NewSourceProviderModule(android.HostAndDeviceSupported, aconfig, false, false) + return module, aconfig +} + +// rust_aconfig_library generates aconfig rust code from the provided aconfig declaration. This module type will +// create library variants that can be used as a crate dependency by adding it to the rlibs, dylibs, and rustlibs +// properties of other modules. +func RustAconfigLibraryFactory() android.Module { + module, _ := NewRustAconfigLibrary(android.HostAndDeviceSupported) + return module.Init() +} + +func (a *aconfigDecorator) SourceProviderProps() []interface{} { + return append(a.BaseSourceProvider.SourceProviderProps(), &a.Properties) +} + +func (a *aconfigDecorator) GenerateSource(ctx rust.ModuleContext, deps rust.PathDeps) android.Path { + generatedDir := android.PathForModuleGen(ctx) + generatedSource := android.PathForModuleGen(ctx, "src", "lib.rs") + + declarationsModules := ctx.GetDirectDepsWithTag(rustDeclarationsTag) + + if len(declarationsModules) != 1 { + panic(fmt.Errorf("Exactly one aconfig_declarations property required")) + } + declarations := ctx.OtherModuleProvider(declarationsModules[0], aconfig.DeclarationsProviderKey).(aconfig.DeclarationsProviderData) + + mode := proptools.StringDefault(a.Properties.Mode, "production") + if !isModeSupported(mode) { + ctx.PropertyErrorf("mode", "%q is not a supported mode", mode) + } + + ctx.Build(pctx, android.BuildParams{ + Rule: rustRule, + Input: declarations.IntermediateCacheOutputPath, + Outputs: []android.WritablePath{ + generatedSource, + }, + Description: "rust_aconfig_library", + Args: map[string]string{ + "gendir": generatedDir.String(), + "mode": mode, + }, + }) + a.BaseSourceProvider.OutputFiles = android.Paths{generatedSource} + return generatedSource +} + +func (a *aconfigDecorator) SourceProviderDeps(ctx rust.DepsContext, deps rust.Deps) rust.Deps { + deps = a.BaseSourceProvider.SourceProviderDeps(ctx, deps) + deps.Rustlibs = append(deps.Rustlibs, "libflags_rust") + deps.Rustlibs = append(deps.Rustlibs, "liblazy_static") + ctx.AddDependency(ctx.Module(), rustDeclarationsTag, a.Properties.Aconfig_declarations) + return deps +} diff --git a/aconfig/codegen/rust_aconfig_library_test.go b/aconfig/codegen/rust_aconfig_library_test.go new file mode 100644 index 0000000000..c09f701498 --- /dev/null +++ b/aconfig/codegen/rust_aconfig_library_test.go @@ -0,0 +1,159 @@ +package codegen + +import ( + "fmt" + "testing" + + "android/soong/android" + "android/soong/rust" +) + +func TestRustAconfigLibrary(t *testing.T) { + result := android.GroupFixturePreparers( + PrepareForTestWithAconfigBuildComponents, + rust.PrepareForTestWithRustIncludeVndk, + android.PrepareForTestWithArchMutator, + android.PrepareForTestWithDefaults, + android.PrepareForTestWithPrebuilts, + ). + ExtendWithErrorHandler(android.FixtureExpectsNoErrors). + RunTestWithBp(t, fmt.Sprintf(` + rust_library { + name: "libflags_rust", // test mock + crate_name: "flags_rust", + srcs: ["lib.rs"], + } + rust_library { + name: "liblazy_static", // test mock + crate_name: "lazy_static", + srcs: ["src/lib.rs"], + } + aconfig_declarations { + name: "my_aconfig_declarations", + package: "com.example.package", + srcs: ["foo.aconfig"], + } + + rust_aconfig_library { + name: "libmy_rust_aconfig_library", + crate_name: "my_rust_aconfig_library", + aconfig_declarations: "my_aconfig_declarations", + } + `)) + + sourceVariant := result.ModuleForTests("libmy_rust_aconfig_library", "android_arm64_armv8-a_source") + rule := sourceVariant.Rule("rust_aconfig_library") + android.AssertStringEquals(t, "rule must contain production mode", rule.Args["mode"], "production") + + dylibVariant := result.ModuleForTests("libmy_rust_aconfig_library", "android_arm64_armv8-a_dylib") + rlibRlibStdVariant := result.ModuleForTests("libmy_rust_aconfig_library", "android_arm64_armv8-a_rlib_rlib-std") + rlibDylibStdVariant := result.ModuleForTests("libmy_rust_aconfig_library", "android_arm64_armv8-a_rlib_dylib-std") + + variants := []android.TestingModule{ + dylibVariant, + rlibDylibStdVariant, + rlibRlibStdVariant, + } + + for _, variant := range variants { + android.AssertStringEquals( + t, + "dylib variant builds from generated rust code", + "out/soong/.intermediates/libmy_rust_aconfig_library/android_arm64_armv8-a_source/gen/src/lib.rs", + variant.Rule("rustc").Inputs[0].RelativeToTop().String(), + ) + } +} + +var rustCodegenModeTestData = []struct { + setting, expected string +}{ + {"", "production"}, + {"mode: `production`,", "production"}, + {"mode: `test`,", "test"}, + {"mode: `exported`,", "exported"}, +} + +func TestRustCodegenMode(t *testing.T) { + for _, testData := range rustCodegenModeTestData { + testRustCodegenModeHelper(t, testData.setting, testData.expected) + } +} + +func testRustCodegenModeHelper(t *testing.T, bpMode string, ruleMode string) { + t.Helper() + result := android.GroupFixturePreparers( + PrepareForTestWithAconfigBuildComponents, + rust.PrepareForTestWithRustIncludeVndk). + ExtendWithErrorHandler(android.FixtureExpectsNoErrors). + RunTestWithBp(t, fmt.Sprintf(` + rust_library { + name: "libflags_rust", // test mock + crate_name: "flags_rust", + srcs: ["lib.rs"], + } + rust_library { + name: "liblazy_static", // test mock + crate_name: "lazy_static", + srcs: ["src/lib.rs"], + } + aconfig_declarations { + name: "my_aconfig_declarations", + package: "com.example.package", + srcs: ["foo.aconfig"], + } + rust_aconfig_library { + name: "libmy_rust_aconfig_library", + crate_name: "my_rust_aconfig_library", + aconfig_declarations: "my_aconfig_declarations", + %s + } + `, bpMode)) + + module := result.ModuleForTests("libmy_rust_aconfig_library", "android_arm64_armv8-a_source") + rule := module.Rule("rust_aconfig_library") + android.AssertStringEquals(t, "rule must contain test mode", rule.Args["mode"], ruleMode) +} + +var incorrectRustCodegenModeTestData = []struct { + setting, expectedErr string +}{ + {"mode: `unsupported`,", "mode: \"unsupported\" is not a supported mode"}, +} + +func TestIncorrectRustCodegenMode(t *testing.T) { + for _, testData := range incorrectRustCodegenModeTestData { + testIncorrectRustCodegenModeHelper(t, testData.setting, testData.expectedErr) + } +} + +func testIncorrectRustCodegenModeHelper(t *testing.T, bpMode string, err string) { + t.Helper() + android.GroupFixturePreparers( + PrepareForTestWithAconfigBuildComponents, + rust.PrepareForTestWithRustIncludeVndk). + ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern(err)). + RunTestWithBp(t, fmt.Sprintf(` + rust_library { + name: "libflags_rust", // test mock + crate_name: "flags_rust", + srcs: ["lib.rs"], + } + rust_library { + name: "liblazy_static", // test mock + crate_name: "lazy_static", + srcs: ["src/lib.rs"], + } + aconfig_declarations { + name: "my_aconfig_declarations", + package: "com.example.package", + srcs: ["foo.aconfig"], + } + rust_aconfig_library { + name: "libmy_rust_aconfig_library", + crate_name: "my_rust_aconfig_library", + aconfig_declarations: "my_aconfig_declarations", + %s + } + `, bpMode)) +} diff --git a/aconfig/codegen/testing.go b/aconfig/codegen/testing.go new file mode 100644 index 0000000000..3e1c22eb90 --- /dev/null +++ b/aconfig/codegen/testing.go @@ -0,0 +1,25 @@ +// Copyright (C) 2021 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 codegen + +import ( + "android/soong/aconfig" + "android/soong/android" +) + +var PrepareForTestWithAconfigBuildComponents = android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) { + ctx.RegisterModuleType("aconfig_declarations", aconfig.DeclarationsFactory) + RegisterBuildComponents(ctx) +}) diff --git a/aconfig/exported_java_aconfig_library.go b/aconfig/exported_java_aconfig_library.go new file mode 100644 index 0000000000..45c5e39903 --- /dev/null +++ b/aconfig/exported_java_aconfig_library.go @@ -0,0 +1,56 @@ +// Copyright 2023 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 aconfig + +import ( + "android/soong/android" +) + +func ExportedJavaDeclarationsLibraryFactory() android.Singleton { + return &exportedJavaDeclarationsLibrarySingleton{} +} + +type exportedJavaDeclarationsLibrarySingleton struct { + intermediatePath android.OutputPath +} + +func (this *exportedJavaDeclarationsLibrarySingleton) GenerateBuildActions(ctx android.SingletonContext) { + // Find all of the aconfig_declarations modules + var cacheFiles android.Paths + ctx.VisitAllModules(func(module android.Module) { + if !ctx.ModuleHasProvider(module, DeclarationsProviderKey) { + return + } + decl := ctx.ModuleProvider(module, DeclarationsProviderKey).(DeclarationsProviderData) + cacheFiles = append(cacheFiles, decl.IntermediateCacheOutputPath) + }) + + // Generate build action for aconfig + this.intermediatePath = android.PathForIntermediates(ctx, "exported_java_aconfig_library.jar") + ctx.Build(pctx, android.BuildParams{ + Rule: exportedJavaRule, + Inputs: cacheFiles, + Output: this.intermediatePath, + Description: "exported_java_aconfig_library", + Args: map[string]string{ + "cache_files": android.JoinPathsWithPrefix(cacheFiles, " "), + }, + }) + ctx.Phony("exported_java_aconfig_library", this.intermediatePath) +} + +func (this *exportedJavaDeclarationsLibrarySingleton) MakeVars(ctx android.MakeVarsContext) { + ctx.DistForGoalWithFilename("sdk", this.intermediatePath, "android-flags.jar") +} diff --git a/aconfig/init.go b/aconfig/init.go new file mode 100644 index 0000000000..05fab4ce54 --- /dev/null +++ b/aconfig/init.go @@ -0,0 +1,98 @@ +// Copyright 2023 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 aconfig + +import ( + "android/soong/android" + + "github.com/google/blueprint" +) + +var ( + pctx = android.NewPackageContext("android/soong/aconfig") + + // For aconfig_declarations: Generate cache file + aconfigRule = pctx.AndroidStaticRule("aconfig", + blueprint.RuleParams{ + Command: `${aconfig} create-cache` + + ` --package ${package}` + + ` ${declarations}` + + ` ${values}` + + ` ${default-permission}` + + ` --cache ${out}.tmp` + + ` && ( if cmp -s ${out}.tmp ${out} ; then rm ${out}.tmp ; else mv ${out}.tmp ${out} ; fi )`, + // ` --build-id ${release_version}` + + CommandDeps: []string{ + "${aconfig}", + }, + Restat: true, + }, "release_version", "package", "declarations", "values", "default-permission") + + // For create-device-config-sysprops: Generate aconfig flag value map text file + aconfigTextRule = pctx.AndroidStaticRule("aconfig_text", + blueprint.RuleParams{ + Command: `${aconfig} dump --format bool` + + ` --cache ${in}` + + ` --out ${out}.tmp` + + ` && ( if cmp -s ${out}.tmp ${out} ; then rm ${out}.tmp ; else mv ${out}.tmp ${out} ; fi )`, + CommandDeps: []string{ + "${aconfig}", + }, + Restat: true, + }) + + // For all_aconfig_declarations: Combine all parsed_flags proto files + AllDeclarationsRule = pctx.AndroidStaticRule("All_aconfig_declarations_dump", + blueprint.RuleParams{ + Command: `${aconfig} dump --format protobuf --out ${out} ${cache_files}`, + CommandDeps: []string{ + "${aconfig}", + }, + }, "cache_files") + + mergeAconfigFilesRule = pctx.AndroidStaticRule("mergeAconfigFilesRule", + blueprint.RuleParams{ + Command: `${aconfig} dump --dedup --format protobuf --out $out $flags`, + CommandDeps: []string{"${aconfig}"}, + }, "flags") + // For exported_java_aconfig_library: Generate a JAR from all + // java_aconfig_libraries to be consumed by apps built outside the + // platform + exportedJavaRule = pctx.AndroidStaticRule("exported_java_aconfig_library", + blueprint.RuleParams{ + Command: `rm -rf ${out}.tmp` + + `&& for cache in ${cache_files}; do ${aconfig} create-java-lib --cache $$cache --out ${out}.tmp; done` + + `&& $soong_zip -write_if_changed -jar -o ${out} -C ${out}.tmp -D ${out}.tmp` + + `&& rm -rf ${out}.tmp`, + CommandDeps: []string{ + "$aconfig", + "$soong_zip", + }, + }, "cache_files") +) + +func init() { + RegisterBuildComponents(android.InitRegistrationContext) + pctx.HostBinToolVariable("aconfig", "aconfig") + pctx.HostBinToolVariable("soong_zip", "soong_zip") +} + +func RegisterBuildComponents(ctx android.RegistrationContext) { + ctx.RegisterModuleType("aconfig_declarations", DeclarationsFactory) + ctx.RegisterModuleType("aconfig_values", ValuesFactory) + ctx.RegisterModuleType("aconfig_value_set", ValueSetFactory) + ctx.RegisterParallelSingletonType("all_aconfig_declarations", AllAconfigDeclarationsFactory) + ctx.RegisterParallelSingletonType("exported_java_aconfig_library", ExportedJavaDeclarationsLibraryFactory) +} diff --git a/aconfig/testing.go b/aconfig/testing.go new file mode 100644 index 0000000000..f6489ec3f9 --- /dev/null +++ b/aconfig/testing.go @@ -0,0 +1,29 @@ +// Copyright (C) 2021 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 aconfig + +import ( + "testing" + + "android/soong/android" +) + +var PrepareForTestWithAconfigBuildComponents = android.FixtureRegisterWithContext(RegisterBuildComponents) + +func runTest(t *testing.T, errorHandler android.FixtureErrorHandler, bp string) *android.TestResult { + return android.GroupFixturePreparers(PrepareForTestWithAconfigBuildComponents). + ExtendWithErrorHandler(errorHandler). + RunTestWithBp(t, bp) +} diff --git a/aidl_library/Android.bp b/aidl_library/Android.bp new file mode 100644 index 0000000000..ec21504271 --- /dev/null +++ b/aidl_library/Android.bp @@ -0,0 +1,32 @@ +// Copyright 2023 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 { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +bootstrap_go_package { + name: "soong-aidl-library", + pkgPath: "android/soong/aidl_library", + deps: [ + "soong-android", + ], + srcs: [ + "aidl_library.go", + ], + testSrcs: [ + "aidl_library_test.go", + ], + pluginFor: ["soong_build"], +} diff --git a/aidl_library/aidl_library.go b/aidl_library/aidl_library.go new file mode 100644 index 0000000000..b49c51619a --- /dev/null +++ b/aidl_library/aidl_library.go @@ -0,0 +1,136 @@ +// Copyright 2023 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 aidl_library + +import ( + "android/soong/android" + "github.com/google/blueprint" + "github.com/google/blueprint/proptools" +) + +var PrepareForTestWithAidlLibrary = android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) { + registerAidlLibraryBuildComponents(ctx) +}) + +func init() { + registerAidlLibraryBuildComponents(android.InitRegistrationContext) +} + +func registerAidlLibraryBuildComponents(ctx android.RegistrationContext) { + ctx.RegisterModuleType("aidl_library", AidlLibraryFactory) +} + +type aidlLibraryProperties struct { + // srcs lists files that are included in this module for aidl compilation + Srcs []string `android:"path"` + + // hdrs lists the headers that are imported by srcs but are not compiled by aidl to language binding code + // hdrs is provided to support Bazel migration. It is a no-op until + // we enable input sandbox in aidl compilation action + Hdrs []string `android:"path"` + + // The prefix to strip from the paths of the .aidl files + // The remaining path is the package path of the aidl interface + Strip_import_prefix *string + + // List of aidl files or aidl_library depended on by the module + Deps []string `android:"arch_variant"` +} + +type AidlLibrary struct { + android.ModuleBase + properties aidlLibraryProperties +} + +type AidlLibraryInfo struct { + // The direct aidl files of the module + Srcs android.Paths + // The include dirs to the direct aidl files and those provided from transitive aidl_library deps + IncludeDirs android.DepSet[android.Path] + // The direct hdrs and hdrs from transitive deps + Hdrs android.DepSet[android.Path] +} + +// AidlLibraryProvider provides the srcs and the transitive include dirs +var AidlLibraryProvider = blueprint.NewProvider(AidlLibraryInfo{}) + +func (lib *AidlLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) { + includeDirsDepSetBuilder := android.NewDepSetBuilder[android.Path](android.PREORDER) + hdrsDepSetBuilder := android.NewDepSetBuilder[android.Path](android.PREORDER) + + if len(lib.properties.Srcs) == 0 && len(lib.properties.Hdrs) == 0 { + ctx.ModuleErrorf("at least srcs or hdrs prop must be non-empty") + } + + srcs := android.PathsForModuleSrc(ctx, lib.properties.Srcs) + hdrs := android.PathsForModuleSrc(ctx, lib.properties.Hdrs) + + if lib.properties.Strip_import_prefix != nil { + srcs = android.PathsWithModuleSrcSubDir( + ctx, + srcs, + android.String(lib.properties.Strip_import_prefix), + ) + + hdrs = android.PathsWithModuleSrcSubDir( + ctx, + hdrs, + android.String(lib.properties.Strip_import_prefix), + ) + } + hdrsDepSetBuilder.Direct(hdrs...) + + includeDir := android.PathForModuleSrc( + ctx, + proptools.StringDefault(lib.properties.Strip_import_prefix, ""), + ) + includeDirsDepSetBuilder.Direct(includeDir) + + for _, dep := range ctx.GetDirectDepsWithTag(aidlLibraryTag) { + if ctx.OtherModuleHasProvider(dep, AidlLibraryProvider) { + info := ctx.OtherModuleProvider(dep, AidlLibraryProvider).(AidlLibraryInfo) + includeDirsDepSetBuilder.Transitive(&info.IncludeDirs) + hdrsDepSetBuilder.Transitive(&info.Hdrs) + } + } + + ctx.SetProvider(AidlLibraryProvider, AidlLibraryInfo{ + Srcs: srcs, + IncludeDirs: *includeDirsDepSetBuilder.Build(), + Hdrs: *hdrsDepSetBuilder.Build(), + }) +} + +// aidl_library contains a list of .aidl files and the strip_import_prefix to +// to strip from the paths of the .aidl files. The sub-path left-over after stripping +// corresponds to the aidl package path the aidl interfaces are scoped in +func AidlLibraryFactory() android.Module { + module := &AidlLibrary{} + module.AddProperties(&module.properties) + android.InitAndroidModule(module) + return module +} + +type aidlDependencyTag struct { + blueprint.BaseDependencyTag +} + +var aidlLibraryTag = aidlDependencyTag{} + +func (lib *AidlLibrary) DepsMutator(ctx android.BottomUpMutatorContext) { + for _, dep := range lib.properties.Deps { + ctx.AddDependency(lib, aidlLibraryTag, dep) + } +} diff --git a/aidl_library/aidl_library_test.go b/aidl_library/aidl_library_test.go new file mode 100644 index 0000000000..020562904a --- /dev/null +++ b/aidl_library/aidl_library_test.go @@ -0,0 +1,136 @@ +// Copyright 2023 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 aidl_library + +import ( + "android/soong/android" + "testing" +) + +func TestAidlLibrary(t *testing.T) { + t.Parallel() + ctx := android.GroupFixturePreparers( + PrepareForTestWithAidlLibrary, + android.MockFS{ + "package_bar/Android.bp": []byte(` + aidl_library { + name: "bar", + srcs: ["x/y/Bar.aidl"], + strip_import_prefix: "x", + } + `), + }.AddToFixture(), + android.MockFS{ + "package_foo/Android.bp": []byte(` + aidl_library { + name: "foo", + srcs: ["a/b/Foo.aidl"], + hdrs: ["a/Header.aidl"], + strip_import_prefix: "a", + deps: ["bar"], + } + `), + }.AddToFixture(), + ).RunTest(t).TestContext + + foo := ctx.ModuleForTests("foo", "").Module().(*AidlLibrary) + actualInfo := ctx.ModuleProvider(foo, AidlLibraryProvider).(AidlLibraryInfo) + + android.AssertArrayString( + t, + "aidl include dirs", + []string{"package_foo/a", "package_bar/x"}, + android.Paths(actualInfo.IncludeDirs.ToList()).Strings(), + ) + + android.AssertPathsRelativeToTopEquals( + t, + "aidl srcs paths", + []string{"package_foo/a/b/Foo.aidl"}, + actualInfo.Srcs, + ) + + android.AssertPathsRelativeToTopEquals( + t, + "aidl hdrs paths", + []string{"package_foo/a/Header.aidl"}, + actualInfo.Hdrs.ToList(), + ) +} + +func TestAidlLibraryWithoutStripImportPrefix(t *testing.T) { + t.Parallel() + ctx := android.GroupFixturePreparers( + PrepareForTestWithAidlLibrary, + android.MockFS{ + "package_bar/Android.bp": []byte(` + aidl_library { + name: "bar", + srcs: ["x/y/Bar.aidl"], + hdrs: ["BarHeader.aidl"], + } + `), + }.AddToFixture(), + android.MockFS{ + "package_foo/Android.bp": []byte(` + aidl_library { + name: "foo", + srcs: ["a/b/Foo.aidl"], + deps: ["bar"], + } + `), + }.AddToFixture(), + ).RunTest(t).TestContext + + foo := ctx.ModuleForTests("foo", "").Module().(*AidlLibrary) + actualInfo := ctx.ModuleProvider(foo, AidlLibraryProvider).(AidlLibraryInfo) + + android.AssertArrayString( + t, + "aidl include dirs", + []string{"package_foo", "package_bar"}, + android.Paths(actualInfo.IncludeDirs.ToList()).Strings(), + ) + + android.AssertPathsRelativeToTopEquals( + t, + "aidl srcs paths", + []string{"package_foo/a/b/Foo.aidl"}, + actualInfo.Srcs, + ) + + android.AssertPathsRelativeToTopEquals( + t, + "aidl hdrs paths", + []string{"package_bar/BarHeader.aidl"}, + actualInfo.Hdrs.ToList(), + ) +} + +func TestAidlLibraryWithNoSrcsHdrsDeps(t *testing.T) { + t.Parallel() + android.GroupFixturePreparers( + PrepareForTestWithAidlLibrary, + android.MockFS{ + "package_bar/Android.bp": []byte(` + aidl_library { + name: "bar", + } + `), + }.AddToFixture(), + ). + ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern("at least srcs or hdrs prop must be non-empty")). + RunTest(t) +} diff --git a/android/Android.bp b/android/Android.bp index 641c438f1f..2ac1d5fbe9 100644 --- a/android/Android.bp +++ b/android/Android.bp @@ -12,11 +12,10 @@ bootstrap_go_package { "sbox_proto", "soong", "soong-android-soongconfig", - "soong-bazel", - "soong-cquery", "soong-remoteexec", "soong-response", "soong-shared", + "soong-starlark", "soong-starlark-format", "soong-ui-metrics_proto", "soong-android-allowlists", @@ -30,13 +29,12 @@ bootstrap_go_package { srcs: [ "androidmk.go", "apex.go", + "apex_contributions.go", "api_domain.go", "api_levels.go", "arch.go", "arch_list.go", - "bazel.go", - "bazel_handler.go", - "bazel_paths.go", + "base_module_context.go", "buildinfo_prop.go", "config.go", "test_config.go", @@ -47,8 +45,8 @@ bootstrap_go_package { "defaults.go", "defs.go", "depset_generic.go", - "depset_paths.go", "deptag.go", + "early_module_context.go", "expand.go", "filegroup.go", "fixture.go", @@ -60,10 +58,10 @@ bootstrap_go_package { "license_metadata.go", "license_sdk_member.go", "licenses.go", - "makefile_goal.go", "makevars.go", "metrics.go", "module.go", + "module_context.go", "mutator.go", "namespace.go", "neverallow.go", @@ -77,6 +75,7 @@ bootstrap_go_package { "path_properties.go", "paths.go", "phony.go", + "plugin.go", "prebuilt.go", "prebuilt_build_tool.go", "proto.go", @@ -101,11 +100,9 @@ bootstrap_go_package { "androidmk_test.go", "apex_test.go", "arch_test.go", - "bazel_handler_test.go", - "bazel_paths_test.go", - "bazel_test.go", "config_test.go", "config_bp2build_test.go", + "configured_jars_test.go", "csuite_config_test.go", "defaults_test.go", "depset_test.go", diff --git a/android/allowlists/allowlists.go b/android/allowlists/allowlists.go index 4c04a41310..b9d26f8846 100644 --- a/android/allowlists/allowlists.go +++ b/android/allowlists/allowlists.go @@ -38,7 +38,13 @@ const ( // allows modules to opt-in. Bp2BuildDefaultFalseRecursively - DEFAULT_NINJA_WEIGHT = 1000 + // Modules with build time of more than half a minute should have high priority. + DEFAULT_PRIORITIZED_WEIGHT = 1000 + // Modules with build time of more than a few minute should have higher priority. + HIGH_PRIORITIZED_WEIGHT = 10 * DEFAULT_PRIORITIZED_WEIGHT + // Modules with inputs greater than the threshold should have high priority. + // Adjust this threshold if there are lots of wrong predictions. + INPUT_SIZE_THRESHOLD = 50 ) var ( @@ -59,6 +65,7 @@ var ( "build/bazel": Bp2BuildDefaultTrueRecursively, "build/make/target/product/security": Bp2BuildDefaultTrue, + "build/make/tools": Bp2BuildDefaultTrue, "build/make/tools/protos": Bp2BuildDefaultTrue, "build/make/tools/releasetools": Bp2BuildDefaultTrue, "build/make/tools/sbom": Bp2BuildDefaultTrue, @@ -68,17 +75,24 @@ var ( "build/soong/cc/libbuildversion": Bp2BuildDefaultTrue, // Skip tests subdir "build/soong/cc/ndkstubgen": Bp2BuildDefaultTrue, "build/soong/cc/symbolfile": Bp2BuildDefaultTrue, + "build/soong/jar": Bp2BuildDefaultTrue, "build/soong/licenses": Bp2BuildDefaultTrue, "build/soong/linkerconfig": Bp2BuildDefaultTrueRecursively, + "build/soong/response": Bp2BuildDefaultTrue, "build/soong/scripts": Bp2BuildDefaultTrueRecursively, + "build/soong/third_party/zip": Bp2BuildDefaultTrue, "cts/common/device-side/nativetesthelper/jni": Bp2BuildDefaultTrueRecursively, + "cts/flags/cc_tests": Bp2BuildDefaultTrueRecursively, + "cts/libs/json": Bp2BuildDefaultTrueRecursively, + "cts/tests/tests/gesture": Bp2BuildDefaultTrueRecursively, "dalvik/tools/dexdeps": Bp2BuildDefaultTrueRecursively, "development/apps/DevelopmentSettings": Bp2BuildDefaultTrue, "development/apps/Fallback": Bp2BuildDefaultTrue, "development/apps/WidgetPreview": Bp2BuildDefaultTrue, + "development/python-packages/adb": Bp2BuildDefaultTrueRecursively, "development/samples/BasicGLSurfaceView": Bp2BuildDefaultTrue, "development/samples/BluetoothChat": Bp2BuildDefaultTrue, "development/samples/BrokenKeyDerivation": Bp2BuildDefaultTrue, @@ -113,129 +127,174 @@ var ( "development/samples/WiFiDirectDemo": Bp2BuildDefaultTrue, "development/sdk": Bp2BuildDefaultTrueRecursively, - "external/aac": Bp2BuildDefaultTrueRecursively, - "external/arm-optimized-routines": Bp2BuildDefaultTrueRecursively, - "external/auto": Bp2BuildDefaultTrue, - "external/auto/android-annotation-stubs": Bp2BuildDefaultTrueRecursively, - "external/auto/common": Bp2BuildDefaultTrueRecursively, - "external/auto/service": Bp2BuildDefaultTrueRecursively, - "external/boringssl": Bp2BuildDefaultTrueRecursively, - "external/bouncycastle": Bp2BuildDefaultTrue, - "external/brotli": Bp2BuildDefaultTrue, - "external/bsdiff": Bp2BuildDefaultTrueRecursively, - "external/bzip2": Bp2BuildDefaultTrueRecursively, - "external/conscrypt": Bp2BuildDefaultTrue, - "external/e2fsprogs": Bp2BuildDefaultTrueRecursively, - "external/eigen": Bp2BuildDefaultTrueRecursively, - "external/erofs-utils": Bp2BuildDefaultTrueRecursively, - "external/error_prone": Bp2BuildDefaultTrueRecursively, - "external/escapevelocity": Bp2BuildDefaultTrueRecursively, - "external/expat": Bp2BuildDefaultTrueRecursively, - "external/f2fs-tools": Bp2BuildDefaultTrue, - "external/flac": Bp2BuildDefaultTrueRecursively, - "external/fmtlib": Bp2BuildDefaultTrueRecursively, - "external/guava": Bp2BuildDefaultTrueRecursively, - "external/google-benchmark": Bp2BuildDefaultTrueRecursively, - "external/googletest": Bp2BuildDefaultTrueRecursively, - "external/gwp_asan": Bp2BuildDefaultTrueRecursively, - "external/hamcrest": Bp2BuildDefaultTrueRecursively, - "external/icu": Bp2BuildDefaultTrueRecursively, - "external/icu/android_icu4j": Bp2BuildDefaultFalse, // java rules incomplete - "external/icu/icu4j": Bp2BuildDefaultFalse, // java rules incomplete - "external/jacoco": Bp2BuildDefaultTrueRecursively, - "external/jarjar": Bp2BuildDefaultTrueRecursively, - "external/javaparser": Bp2BuildDefaultTrueRecursively, - "external/javapoet": Bp2BuildDefaultTrueRecursively, - "external/javassist": Bp2BuildDefaultTrueRecursively, - "external/jemalloc_new": Bp2BuildDefaultTrueRecursively, - "external/jsoncpp": Bp2BuildDefaultTrueRecursively, - "external/jsr305": Bp2BuildDefaultTrueRecursively, - "external/jsr330": Bp2BuildDefaultTrueRecursively, - "external/junit": Bp2BuildDefaultTrueRecursively, - "external/kotlinc": Bp2BuildDefaultTrueRecursively, - "external/libaom": Bp2BuildDefaultTrueRecursively, - "external/libavc": Bp2BuildDefaultTrueRecursively, - "external/libcap": Bp2BuildDefaultTrueRecursively, - "external/libcxx": Bp2BuildDefaultTrueRecursively, - "external/libcxxabi": Bp2BuildDefaultTrueRecursively, - "external/libdivsufsort": Bp2BuildDefaultTrueRecursively, - "external/libdrm": Bp2BuildDefaultTrue, - "external/libevent": Bp2BuildDefaultTrueRecursively, - "external/libgav1": Bp2BuildDefaultTrueRecursively, - "external/libhevc": Bp2BuildDefaultTrueRecursively, - "external/libjpeg-turbo": Bp2BuildDefaultTrueRecursively, - "external/libmpeg2": Bp2BuildDefaultTrueRecursively, - "external/libpng": Bp2BuildDefaultTrueRecursively, - "external/libvpx": Bp2BuildDefaultTrueRecursively, - "external/libyuv": Bp2BuildDefaultTrueRecursively, - "external/lz4/lib": Bp2BuildDefaultTrue, - "external/lz4/programs": Bp2BuildDefaultTrue, - "external/lzma/C": Bp2BuildDefaultTrueRecursively, - "external/mdnsresponder": Bp2BuildDefaultTrueRecursively, - "external/minijail": Bp2BuildDefaultTrueRecursively, - "external/musl": Bp2BuildDefaultTrueRecursively, - "external/objenesis": Bp2BuildDefaultTrueRecursively, - "external/openscreen": Bp2BuildDefaultTrueRecursively, - "external/ow2-asm": Bp2BuildDefaultTrueRecursively, - "external/pcre": Bp2BuildDefaultTrueRecursively, - "external/protobuf": Bp2BuildDefaultTrueRecursively, - "external/python/six": Bp2BuildDefaultTrueRecursively, - "external/rappor": Bp2BuildDefaultTrueRecursively, - "external/scudo": Bp2BuildDefaultTrueRecursively, - "external/selinux/libselinux": Bp2BuildDefaultTrueRecursively, - "external/selinux/libsepol": Bp2BuildDefaultTrueRecursively, - "external/speex": Bp2BuildDefaultTrueRecursively, - "external/tinyalsa": Bp2BuildDefaultTrueRecursively, - "external/tinyalsa_new": Bp2BuildDefaultTrueRecursively, - "external/toybox": Bp2BuildDefaultTrueRecursively, - "external/zlib": Bp2BuildDefaultTrueRecursively, - "external/zopfli": Bp2BuildDefaultTrueRecursively, - "external/zstd": Bp2BuildDefaultTrueRecursively, + "external/aac": Bp2BuildDefaultTrueRecursively, + "external/abseil-cpp": Bp2BuildDefaultTrueRecursively, + "external/arm-optimized-routines": Bp2BuildDefaultTrueRecursively, + "external/auto": Bp2BuildDefaultTrue, + "external/auto/android-annotation-stubs": Bp2BuildDefaultTrueRecursively, + "external/auto/common": Bp2BuildDefaultTrueRecursively, + "external/auto/service": Bp2BuildDefaultTrueRecursively, + "external/auto/value": Bp2BuildDefaultTrueRecursively, + "external/boringssl": Bp2BuildDefaultTrueRecursively, + "external/bouncycastle": Bp2BuildDefaultTrue, + "external/brotli": Bp2BuildDefaultTrue, + "external/bsdiff": Bp2BuildDefaultTrueRecursively, + "external/bzip2": Bp2BuildDefaultTrueRecursively, + "external/clang/lib": Bp2BuildDefaultTrue, + "external/conscrypt": Bp2BuildDefaultTrue, + "external/dexmaker": Bp2BuildDefaultTrueRecursively, + "external/e2fsprogs": Bp2BuildDefaultTrueRecursively, + "external/eigen": Bp2BuildDefaultTrueRecursively, + "external/erofs-utils": Bp2BuildDefaultTrueRecursively, + "external/error_prone": Bp2BuildDefaultTrueRecursively, + "external/escapevelocity": Bp2BuildDefaultTrueRecursively, + "external/expat": Bp2BuildDefaultTrueRecursively, + "external/f2fs-tools": Bp2BuildDefaultTrue, + "external/flac": Bp2BuildDefaultTrueRecursively, + "external/flatbuffers": Bp2BuildDefaultTrueRecursively, + "external/fmtlib": Bp2BuildDefaultTrueRecursively, + "external/fsverity-utils": Bp2BuildDefaultTrueRecursively, + "external/gflags": Bp2BuildDefaultTrueRecursively, + "external/google-benchmark": Bp2BuildDefaultTrueRecursively, + "external/googletest": Bp2BuildDefaultTrueRecursively, + "external/guava": Bp2BuildDefaultTrueRecursively, + "external/gwp_asan": Bp2BuildDefaultTrueRecursively, + "external/hamcrest": Bp2BuildDefaultTrueRecursively, + "external/icu": Bp2BuildDefaultTrueRecursively, + "external/icu/android_icu4j": Bp2BuildDefaultFalse, // java rules incomplete + "external/icu/icu4j": Bp2BuildDefaultFalse, // java rules incomplete + "external/jacoco": Bp2BuildDefaultTrueRecursively, + "external/jarjar": Bp2BuildDefaultTrueRecursively, + "external/javaparser": Bp2BuildDefaultTrueRecursively, + "external/javapoet": Bp2BuildDefaultTrueRecursively, + "external/javassist": Bp2BuildDefaultTrueRecursively, + "external/jemalloc_new": Bp2BuildDefaultTrueRecursively, + "external/jsoncpp": Bp2BuildDefaultTrueRecursively, + "external/jsr305": Bp2BuildDefaultTrueRecursively, + "external/jsr330": Bp2BuildDefaultTrueRecursively, + "external/junit": Bp2BuildDefaultTrueRecursively, + "external/kotlinc": Bp2BuildDefaultTrueRecursively, + "external/kotlinx.coroutines": Bp2BuildDefaultTrueRecursively, + "external/libaom": Bp2BuildDefaultTrueRecursively, + "external/libavc": Bp2BuildDefaultTrueRecursively, + "external/libcap": Bp2BuildDefaultTrueRecursively, + "external/libcxx": Bp2BuildDefaultTrueRecursively, + "external/libcxxabi": Bp2BuildDefaultTrueRecursively, + "external/libdivsufsort": Bp2BuildDefaultTrueRecursively, + "external/libdrm": Bp2BuildDefaultTrue, + "external/libevent": Bp2BuildDefaultTrueRecursively, + "external/libgav1": Bp2BuildDefaultTrueRecursively, + "external/libdav1d": Bp2BuildDefaultTrueRecursively, + "external/libhevc": Bp2BuildDefaultTrueRecursively, + "external/libjpeg-turbo": Bp2BuildDefaultTrueRecursively, + "external/libmpeg2": Bp2BuildDefaultTrueRecursively, + "external/libphonenumber": Bp2BuildDefaultTrueRecursively, + "external/libpng": Bp2BuildDefaultTrueRecursively, + "external/libvpx": Bp2BuildDefaultTrueRecursively, + "external/libyuv": Bp2BuildDefaultTrueRecursively, + "external/lz4/lib": Bp2BuildDefaultTrue, + "external/lz4/programs": Bp2BuildDefaultTrue, + "external/lzma/C": Bp2BuildDefaultTrueRecursively, + "external/mdnsresponder": Bp2BuildDefaultTrueRecursively, + "external/minijail": Bp2BuildDefaultTrueRecursively, + "external/mockito": Bp2BuildDefaultTrueRecursively, + "external/musl": Bp2BuildDefaultTrueRecursively, + "external/objenesis": Bp2BuildDefaultTrueRecursively, + "external/openscreen": Bp2BuildDefaultTrueRecursively, + "external/ow2-asm": Bp2BuildDefaultTrueRecursively, + "external/pcre": Bp2BuildDefaultTrueRecursively, + "external/perfmark/api": Bp2BuildDefaultTrueRecursively, + "external/perfetto": Bp2BuildDefaultTrue, + "external/protobuf": Bp2BuildDefaultTrueRecursively, + "external/python/jinja/src": Bp2BuildDefaultTrueRecursively, + "external/python/markupsafe/src": Bp2BuildDefaultTrueRecursively, + "external/python/pyfakefs/pyfakefs": Bp2BuildDefaultTrueRecursively, + "external/python/pyyaml/lib/yaml": Bp2BuildDefaultTrueRecursively, + "external/python/setuptools": Bp2BuildDefaultTrueRecursively, + "external/python/six": Bp2BuildDefaultTrueRecursively, + "external/rappor": Bp2BuildDefaultTrueRecursively, + "external/rust/crates/rustc-demangle": Bp2BuildDefaultTrueRecursively, + "external/rust/crates/rustc-demangle-capi": Bp2BuildDefaultTrueRecursively, + "external/scudo": Bp2BuildDefaultTrueRecursively, + "external/selinux/checkpolicy": Bp2BuildDefaultTrueRecursively, + "external/selinux/libselinux": Bp2BuildDefaultTrueRecursively, + "external/selinux/libsepol": Bp2BuildDefaultTrueRecursively, + "external/speex": Bp2BuildDefaultTrueRecursively, + "external/sqlite": Bp2BuildDefaultTrueRecursively, + "external/tinyalsa": Bp2BuildDefaultTrueRecursively, + "external/tinyalsa_new": Bp2BuildDefaultTrueRecursively, + "external/toybox": Bp2BuildDefaultTrueRecursively, + "external/truth": Bp2BuildDefaultTrueRecursively, + "external/xz-java": Bp2BuildDefaultTrueRecursively, + "external/zlib": Bp2BuildDefaultTrueRecursively, + "external/zopfli": Bp2BuildDefaultTrueRecursively, + "external/zstd": Bp2BuildDefaultTrueRecursively, "frameworks/av": Bp2BuildDefaultTrue, - "frameworks/av/media/codec2/components/aom": Bp2BuildDefaultTrueRecursively, - "frameworks/av/media/codecs": Bp2BuildDefaultTrueRecursively, - "frameworks/av/media/liberror": Bp2BuildDefaultTrueRecursively, - "frameworks/av/media/libshmem": Bp2BuildDefaultTrueRecursively, - "frameworks/av/media/audioaidlconversion": Bp2BuildDefaultTrueRecursively, - "frameworks/av/media/module/minijail": Bp2BuildDefaultTrueRecursively, - "frameworks/av/services/minijail": Bp2BuildDefaultTrueRecursively, - "frameworks/base/libs/androidfw": Bp2BuildDefaultTrue, - "frameworks/base/media/tests/MediaDump": Bp2BuildDefaultTrue, - "frameworks/base/services/tests/servicestests/aidl": Bp2BuildDefaultTrue, - "frameworks/base/proto": Bp2BuildDefaultTrue, - "frameworks/base/startop/apps/test": Bp2BuildDefaultTrue, - "frameworks/base/tests/appwidgets/AppWidgetHostTest": Bp2BuildDefaultTrueRecursively, - "frameworks/base/tools/aapt2": Bp2BuildDefaultTrue, - "frameworks/base/tools/codegen": Bp2BuildDefaultTrueRecursively, - "frameworks/base/tools/streaming_proto": Bp2BuildDefaultTrueRecursively, - "frameworks/hardware/interfaces/stats/aidl": Bp2BuildDefaultTrue, - "frameworks/native/libs/adbd_auth": Bp2BuildDefaultTrueRecursively, - "frameworks/native/libs/arect": Bp2BuildDefaultTrueRecursively, - "frameworks/native/libs/gui": Bp2BuildDefaultTrue, - "frameworks/native/libs/math": Bp2BuildDefaultTrueRecursively, - "frameworks/native/libs/nativebase": Bp2BuildDefaultTrueRecursively, - "frameworks/native/libs/vr": Bp2BuildDefaultTrueRecursively, - "frameworks/native/opengl/tests/gl2_cameraeye": Bp2BuildDefaultTrue, - "frameworks/native/opengl/tests/gl2_java": Bp2BuildDefaultTrue, - "frameworks/native/opengl/tests/testLatency": Bp2BuildDefaultTrue, - "frameworks/native/opengl/tests/testPauseResume": Bp2BuildDefaultTrue, - "frameworks/native/opengl/tests/testViewport": Bp2BuildDefaultTrue, - "frameworks/native/libs/permission": Bp2BuildDefaultTrue, - "frameworks/native/services/batteryservice": Bp2BuildDefaultTrue, - "frameworks/proto_logging/stats": Bp2BuildDefaultTrueRecursively, + "frameworks/av/media/audioaidlconversion": Bp2BuildDefaultTrueRecursively, + "frameworks/av/media/codec2/components/aom": Bp2BuildDefaultTrueRecursively, + "frameworks/av/media/codecs": Bp2BuildDefaultTrueRecursively, + "frameworks/av/media/liberror": Bp2BuildDefaultTrueRecursively, + "frameworks/av/media/libmediahelper": Bp2BuildDefaultTrue, + "frameworks/av/media/libshmem": Bp2BuildDefaultTrueRecursively, + "frameworks/av/media/module/codecs": Bp2BuildDefaultTrueRecursively, + "frameworks/av/media/module/foundation": Bp2BuildDefaultTrueRecursively, + "frameworks/av/media/module/minijail": Bp2BuildDefaultTrueRecursively, + "frameworks/av/services/minijail": Bp2BuildDefaultTrueRecursively, + "frameworks/base/apex/jobscheduler/service/jni": Bp2BuildDefaultTrueRecursively, + "frameworks/base/core/java": Bp2BuildDefaultTrue, + "frameworks/base/core/res": Bp2BuildDefaultTrueRecursively, + "frameworks/base/errorprone": Bp2BuildDefaultTrueRecursively, + "frameworks/base/libs/androidfw": Bp2BuildDefaultTrue, + "frameworks/base/libs/services": Bp2BuildDefaultTrue, + "frameworks/base/media/tests/MediaDump": Bp2BuildDefaultTrue, + "frameworks/base/mime": Bp2BuildDefaultTrueRecursively, + "frameworks/base/proto": Bp2BuildDefaultTrue, + "frameworks/base/services/tests/servicestests/aidl": Bp2BuildDefaultTrue, + "frameworks/base/startop/apps/test": Bp2BuildDefaultTrue, + "frameworks/base/tests/appwidgets/AppWidgetHostTest": Bp2BuildDefaultTrueRecursively, + "frameworks/base/tools/aapt": Bp2BuildDefaultTrue, + "frameworks/base/tools/aapt2": Bp2BuildDefaultTrue, + "frameworks/base/tools/codegen": Bp2BuildDefaultTrueRecursively, + "frameworks/base/tools/locked_region_code_injection": Bp2BuildDefaultTrueRecursively, + "frameworks/base/tools/streaming_proto": Bp2BuildDefaultTrueRecursively, + "frameworks/hardware/interfaces": Bp2BuildDefaultTrue, + "frameworks/hardware/interfaces/displayservice": Bp2BuildDefaultTrueRecursively, + "frameworks/hardware/interfaces/stats/aidl": Bp2BuildDefaultTrue, + "frameworks/libs/modules-utils/build": Bp2BuildDefaultTrueRecursively, + "frameworks/libs/modules-utils/java": Bp2BuildDefaultTrueRecursively, + "frameworks/libs/modules-utils/java/com/android/modules/utils/testing": Bp2BuildDefaultFalseRecursively, + "frameworks/native": Bp2BuildDefaultTrue, + "frameworks/native/libs/adbd_auth": Bp2BuildDefaultTrueRecursively, + "frameworks/native/libs/arect": Bp2BuildDefaultTrueRecursively, + "frameworks/native/libs/binder": Bp2BuildDefaultTrue, + "frameworks/native/libs/gui": Bp2BuildDefaultTrue, + "frameworks/native/libs/math": Bp2BuildDefaultTrueRecursively, + "frameworks/native/libs/nativebase": Bp2BuildDefaultTrueRecursively, + "frameworks/native/libs/permission": Bp2BuildDefaultTrueRecursively, + "frameworks/native/libs/ui": Bp2BuildDefaultTrue, + "frameworks/native/libs/vr": Bp2BuildDefaultTrueRecursively, + "frameworks/native/opengl/tests/gl2_cameraeye": Bp2BuildDefaultTrue, + "frameworks/native/opengl/tests/gl2_java": Bp2BuildDefaultTrue, + "frameworks/native/opengl/tests/testLatency": Bp2BuildDefaultTrue, + "frameworks/native/opengl/tests/testPauseResume": Bp2BuildDefaultTrue, + "frameworks/native/opengl/tests/testViewport": Bp2BuildDefaultTrue, + "frameworks/native/services/batteryservice": Bp2BuildDefaultTrue, + "frameworks/proto_logging/stats": Bp2BuildDefaultTrueRecursively, "hardware/interfaces": Bp2BuildDefaultTrue, "hardware/interfaces/audio/aidl": Bp2BuildDefaultTrue, "hardware/interfaces/audio/aidl/common": Bp2BuildDefaultTrue, "hardware/interfaces/audio/aidl/default": Bp2BuildDefaultTrue, "hardware/interfaces/audio/aidl/sounddose": Bp2BuildDefaultTrue, + "hardware/interfaces/camera/metadata/aidl": Bp2BuildDefaultTrueRecursively, "hardware/interfaces/common/aidl": Bp2BuildDefaultTrue, "hardware/interfaces/common/fmq/aidl": Bp2BuildDefaultTrue, "hardware/interfaces/common/support": Bp2BuildDefaultTrue, "hardware/interfaces/configstore/1.0": Bp2BuildDefaultTrue, "hardware/interfaces/configstore/1.1": Bp2BuildDefaultTrue, "hardware/interfaces/configstore/utils": Bp2BuildDefaultTrue, + "hardware/interfaces/contexthub/aidl": Bp2BuildDefaultTrue, "hardware/interfaces/graphics/allocator/2.0": Bp2BuildDefaultTrue, "hardware/interfaces/graphics/allocator/3.0": Bp2BuildDefaultTrue, "hardware/interfaces/graphics/allocator/4.0": Bp2BuildDefaultTrue, @@ -258,13 +317,9 @@ var ( "hardware/interfaces/health/2.1": Bp2BuildDefaultTrue, "hardware/interfaces/health/aidl": Bp2BuildDefaultTrue, "hardware/interfaces/health/utils": Bp2BuildDefaultTrueRecursively, - "hardware/interfaces/media/1.0": Bp2BuildDefaultTrue, - "hardware/interfaces/media/bufferpool": Bp2BuildDefaultTrueRecursively, + "hardware/interfaces/media": Bp2BuildDefaultTrueRecursively, "hardware/interfaces/media/bufferpool/aidl/default/tests": Bp2BuildDefaultFalseRecursively, - "hardware/interfaces/media/c2/1.0": Bp2BuildDefaultTrue, - "hardware/interfaces/media/c2/1.1": Bp2BuildDefaultTrue, - "hardware/interfaces/media/c2/1.2": Bp2BuildDefaultTrue, - "hardware/interfaces/media/omx/1.0": Bp2BuildDefaultTrue, + "hardware/interfaces/media/omx/1.0/vts": Bp2BuildDefaultFalseRecursively, "hardware/interfaces/neuralnetworks": Bp2BuildDefaultTrueRecursively, "hardware/interfaces/neuralnetworks/aidl/vts": Bp2BuildDefaultFalseRecursively, "hardware/interfaces/neuralnetworks/1.0/vts": Bp2BuildDefaultFalseRecursively, @@ -272,55 +327,77 @@ var ( "hardware/interfaces/neuralnetworks/1.2/vts": Bp2BuildDefaultFalseRecursively, "hardware/interfaces/neuralnetworks/1.3/vts": Bp2BuildDefaultFalseRecursively, "hardware/interfaces/neuralnetworks/1.4/vts": Bp2BuildDefaultFalseRecursively, + "hardware/interfaces/tests": Bp2BuildDefaultTrueRecursively, + "hardware/interfaces/tests/extension": Bp2BuildDefaultFalseRecursively, // missing deps + "hardware/interfaces/tests/msgq": Bp2BuildDefaultFalseRecursively, // missing deps "libnativehelper": Bp2BuildDefaultTrueRecursively, - "packages/apps/DevCamera": Bp2BuildDefaultTrue, - "packages/apps/HTMLViewer": Bp2BuildDefaultTrue, - "packages/apps/Protips": Bp2BuildDefaultTrue, - "packages/apps/SafetyRegulatoryInfo": Bp2BuildDefaultTrue, - "packages/apps/WallpaperPicker": Bp2BuildDefaultTrue, - "packages/modules/NeuralNetworks/driver/cache": Bp2BuildDefaultTrueRecursively, - "packages/modules/StatsD/lib/libstatssocket": Bp2BuildDefaultTrueRecursively, - "packages/modules/adb": Bp2BuildDefaultTrue, - "packages/modules/adb/apex": Bp2BuildDefaultTrue, - "packages/modules/adb/crypto": Bp2BuildDefaultTrueRecursively, - "packages/modules/adb/libs": Bp2BuildDefaultTrueRecursively, - "packages/modules/adb/pairing_auth": Bp2BuildDefaultTrueRecursively, - "packages/modules/adb/pairing_connection": Bp2BuildDefaultTrueRecursively, - "packages/modules/adb/proto": Bp2BuildDefaultTrueRecursively, - "packages/modules/adb/tls": Bp2BuildDefaultTrueRecursively, - "packages/modules/Gki/libkver": Bp2BuildDefaultTrue, - "packages/modules/NetworkStack/common/captiveportal": Bp2BuildDefaultTrue, - "packages/modules/NeuralNetworks/apex": Bp2BuildDefaultTrue, - "packages/providers/MediaProvider/tools/dialogs": Bp2BuildDefaultFalse, // TODO(b/242834374) - "packages/screensavers/Basic": Bp2BuildDefaultTrue, - "packages/services/Car/tests/SampleRearViewCamera": Bp2BuildDefaultFalse, // TODO(b/242834321) - - "platform_testing/tests/example": Bp2BuildDefaultTrueRecursively, + "packages/apps/DevCamera": Bp2BuildDefaultTrue, + "packages/apps/HTMLViewer": Bp2BuildDefaultTrue, + "packages/apps/Protips": Bp2BuildDefaultTrue, + "packages/apps/SafetyRegulatoryInfo": Bp2BuildDefaultTrue, + "packages/apps/WallpaperPicker": Bp2BuildDefaultTrue, + "packages/modules/Connectivity/bpf_progs": Bp2BuildDefaultTrueRecursively, + "packages/modules/Connectivity/service-t": Bp2BuildDefaultTrueRecursively, + "packages/modules/Connectivity/service/native": Bp2BuildDefaultTrueRecursively, + "packages/modules/Connectivity/staticlibs/native": Bp2BuildDefaultTrueRecursively, + "packages/modules/Connectivity/staticlibs/netd": Bp2BuildDefaultTrueRecursively, + "packages/modules/Connectivity/staticlibs/netd/libnetdutils": Bp2BuildDefaultTrueRecursively, + "packages/modules/Connectivity/tests/unit/jni": Bp2BuildDefaultTrueRecursively, + "packages/modules/Gki/libkver": Bp2BuildDefaultTrue, + "packages/modules/NetworkStack/common/captiveportal": Bp2BuildDefaultTrue, + "packages/modules/NeuralNetworks/apex": Bp2BuildDefaultTrue, + "packages/modules/NeuralNetworks/apex/testing": Bp2BuildDefaultTrue, + "packages/modules/NeuralNetworks/driver/cache": Bp2BuildDefaultTrueRecursively, + "packages/modules/SdkExtensions/gen_sdk": Bp2BuildDefaultTrue, + "packages/modules/StatsD/lib/libstatssocket": Bp2BuildDefaultTrueRecursively, + "packages/modules/adb": Bp2BuildDefaultTrue, + "packages/modules/adb/apex": Bp2BuildDefaultTrue, + "packages/modules/adb/crypto": Bp2BuildDefaultTrueRecursively, + "packages/modules/adb/fastdeploy": Bp2BuildDefaultTrue, + "packages/modules/adb/libs": Bp2BuildDefaultTrueRecursively, + "packages/modules/adb/pairing_auth": Bp2BuildDefaultTrueRecursively, + "packages/modules/adb/pairing_connection": Bp2BuildDefaultTrueRecursively, + "packages/modules/adb/proto": Bp2BuildDefaultTrueRecursively, + "packages/modules/adb/tls": Bp2BuildDefaultTrueRecursively, + "packages/modules/common/proto": Bp2BuildDefaultTrue, + "packages/providers/MediaProvider/tools/dialogs": Bp2BuildDefaultFalse, // TODO(b/242834374) + "packages/screensavers/Basic": Bp2BuildDefaultTrue, + "packages/services/Car/tests/SampleRearViewCamera": Bp2BuildDefaultFalse, // TODO(b/242834321) + + "platform_testing/libraries/annotations": Bp2BuildDefaultTrueRecursively, + "platform_testing/libraries/flag-helpers/libflagtest": Bp2BuildDefaultTrueRecursively, + "platform_testing/tests/example": Bp2BuildDefaultTrueRecursively, "prebuilts/clang/host/linux-x86": Bp2BuildDefaultTrueRecursively, "prebuilts/gradle-plugin": Bp2BuildDefaultTrueRecursively, + "prebuilts/module_sdk": Bp2BuildDefaultTrueRecursively, "prebuilts/runtime/mainline/platform/sdk": Bp2BuildDefaultTrueRecursively, + "prebuilts/sdk": Bp2BuildDefaultTrue, "prebuilts/sdk/current/androidx": Bp2BuildDefaultTrue, "prebuilts/sdk/current/androidx-legacy": Bp2BuildDefaultTrue, + "prebuilts/sdk/current/extras/app-toolkit": Bp2BuildDefaultTrue, "prebuilts/sdk/current/extras/constraint-layout-x": Bp2BuildDefaultTrue, "prebuilts/sdk/current/extras/material-design-x": Bp2BuildDefaultTrue, - "prebuilts/sdk/current/extras/app-toolkit": Bp2BuildDefaultTrue, "prebuilts/sdk/current/support": Bp2BuildDefaultTrue, "prebuilts/tools": Bp2BuildDefaultTrue, "prebuilts/tools/common/m2": Bp2BuildDefaultTrue, + "prebuilts/r8": Bp2BuildDefaultTrueRecursively, + "sdk/annotations": Bp2BuildDefaultTrueRecursively, "sdk/dumpeventlog": Bp2BuildDefaultTrue, "sdk/eventanalyzer": Bp2BuildDefaultTrue, "system/apex": Bp2BuildDefaultFalse, // TODO(b/207466993): flaky failures "system/apex/apexer": Bp2BuildDefaultTrue, "system/apex/libs": Bp2BuildDefaultTrueRecursively, + "system/apex/libs/libapexsupport": Bp2BuildDefaultFalseRecursively, // TODO(b/267572288): depends on rust "system/apex/proto": Bp2BuildDefaultTrueRecursively, "system/apex/tools": Bp2BuildDefaultTrueRecursively, "system/core/debuggerd": Bp2BuildDefaultTrueRecursively, "system/core/diagnose_usb": Bp2BuildDefaultTrueRecursively, + "system/core/fs_mgr": Bp2BuildDefaultTrueRecursively, "system/core/healthd": Bp2BuildDefaultTrue, "system/core/healthd/testdata": Bp2BuildDefaultTrue, "system/core/libasyncio": Bp2BuildDefaultTrue, @@ -330,6 +407,8 @@ var ( "system/core/libprocessgroup": Bp2BuildDefaultTrue, "system/core/libprocessgroup/cgrouprc": Bp2BuildDefaultTrue, "system/core/libprocessgroup/cgrouprc_format": Bp2BuildDefaultTrue, + "system/core/libsparse": Bp2BuildDefaultTrueRecursively, + "system/core/libstats/expresslog": Bp2BuildDefaultTrueRecursively, "system/core/libsuspend": Bp2BuildDefaultTrue, "system/core/libsystem": Bp2BuildDefaultTrueRecursively, "system/core/libsysutils": Bp2BuildDefaultTrueRecursively, @@ -338,12 +417,16 @@ var ( "system/core/mkbootfs": Bp2BuildDefaultTrueRecursively, "system/core/property_service/libpropertyinfoparser": Bp2BuildDefaultTrueRecursively, "system/core/property_service/libpropertyinfoserializer": Bp2BuildDefaultTrueRecursively, + "system/core/trusty/libtrusty": Bp2BuildDefaultTrue, + "system/extras/f2fs_utils": Bp2BuildDefaultTrueRecursively, "system/extras/toolchain-extras": Bp2BuildDefaultTrue, + "system/extras/verity": Bp2BuildDefaultTrueRecursively, "system/hardware/interfaces/media": Bp2BuildDefaultTrueRecursively, "system/incremental_delivery/incfs": Bp2BuildDefaultTrue, "system/libartpalette": Bp2BuildDefaultTrueRecursively, "system/libbase": Bp2BuildDefaultTrueRecursively, "system/libfmq": Bp2BuildDefaultTrue, + "system/libhidl": Bp2BuildDefaultTrue, "system/libhidl/libhidlmemory": Bp2BuildDefaultTrue, "system/libhidl/transport": Bp2BuildDefaultTrue, "system/libhidl/transport/allocator/1.0": Bp2BuildDefaultTrue, @@ -351,8 +434,7 @@ var ( "system/libhidl/transport/manager/1.0": Bp2BuildDefaultTrue, "system/libhidl/transport/manager/1.1": Bp2BuildDefaultTrue, "system/libhidl/transport/manager/1.2": Bp2BuildDefaultTrue, - "system/libhidl/transport/memory/1.0": Bp2BuildDefaultTrue, - "system/libhidl/transport/memory/token/1.0": Bp2BuildDefaultTrue, + "system/libhidl/transport/memory": Bp2BuildDefaultTrueRecursively, "system/libhidl/transport/safe_union/1.0": Bp2BuildDefaultTrue, "system/libhidl/transport/token/1.0": Bp2BuildDefaultTrue, "system/libhidl/transport/token/1.0/utils": Bp2BuildDefaultTrue, @@ -360,13 +442,17 @@ var ( "system/libprocinfo": Bp2BuildDefaultTrue, "system/libvintf": Bp2BuildDefaultTrue, "system/libziparchive": Bp2BuildDefaultTrueRecursively, + "system/linkerconfig": Bp2BuildDefaultTrueRecursively, "system/logging": Bp2BuildDefaultTrueRecursively, "system/media": Bp2BuildDefaultTrue, - "system/media/audio": Bp2BuildDefaultTrueRecursively, "system/media/alsa_utils": Bp2BuildDefaultTrueRecursively, + "system/media/audio": Bp2BuildDefaultTrueRecursively, "system/media/audio_utils": Bp2BuildDefaultTrueRecursively, + "system/media/camera": Bp2BuildDefaultTrueRecursively, "system/memory/libion": Bp2BuildDefaultTrueRecursively, "system/memory/libmemunreachable": Bp2BuildDefaultTrueRecursively, + "system/netd": Bp2BuildDefaultTrue, + "system/security/fsverity": Bp2BuildDefaultTrueRecursively, "system/sepolicy/apex": Bp2BuildDefaultTrueRecursively, "system/testing/gtest_extras": Bp2BuildDefaultTrueRecursively, "system/timezone/apex": Bp2BuildDefaultTrueRecursively, @@ -375,18 +461,24 @@ var ( "system/timezone/testing": Bp2BuildDefaultTrueRecursively, "system/tools/aidl/build/tests_bp2build": Bp2BuildDefaultTrue, "system/tools/aidl/metadata": Bp2BuildDefaultTrue, - "system/tools/hidl/metadata": Bp2BuildDefaultTrue, + "system/tools/hidl": Bp2BuildDefaultTrueRecursively, "system/tools/mkbootimg": Bp2BuildDefaultTrueRecursively, "system/tools/sysprop": Bp2BuildDefaultTrue, "system/tools/xsdc/utils": Bp2BuildDefaultTrueRecursively, "system/unwinding/libunwindstack": Bp2BuildDefaultTrueRecursively, - "tools/apifinder": Bp2BuildDefaultTrue, - "tools/apksig": Bp2BuildDefaultTrue, - "tools/external_updater": Bp2BuildDefaultTrueRecursively, - "tools/metalava": Bp2BuildDefaultTrue, - "tools/platform-compat/java/android/compat": Bp2BuildDefaultTrueRecursively, - "tools/tradefederation/prebuilts/filegroups": Bp2BuildDefaultTrueRecursively, + "test/vts/vts_hal_hidl_target": Bp2BuildDefaultTrueRecursively, + + "toolchain/pgo-profiles": Bp2BuildDefaultTrueRecursively, + "tools/apifinder": Bp2BuildDefaultTrue, + "tools/apksig": Bp2BuildDefaultTrue, + "tools/dexter/slicer": Bp2BuildDefaultTrueRecursively, + "tools/external_updater": Bp2BuildDefaultTrueRecursively, + "tools/metalava": Bp2BuildDefaultTrueRecursively, + "tools/platform-compat/java/android/compat": Bp2BuildDefaultTrueRecursively, + "tools/platform-compat/java/androidprocessor": Bp2BuildDefaultTrueRecursively, + "tools/tradefederation/core/util_apps": Bp2BuildDefaultTrueRecursively, + "tools/tradefederation/prebuilts/filegroups": Bp2BuildDefaultTrueRecursively, } Bp2buildKeepExistingBuildFile = map[string]bool{ @@ -405,7 +497,13 @@ var ( // external/bazelbuild-rules_android/... is needed by mixed builds, otherwise mixed builds analysis fails // e.g. ERROR: Analysis of target '@soong_injection//mixed_builds:buildroot' failed "external/bazelbuild-rules_android":/* recursive = */ true, + "external/bazelbuild-rules_cc":/* recursive = */ true, + "external/bazelbuild-rules_java":/* recursive = */ true, "external/bazelbuild-rules_license":/* recursive = */ true, + "external/bazelbuild-rules_go":/* recursive = */ true, + "external/bazelbuild-rules_python":/* recursive = */ true, + "external/bazelbuild-rules_rust":/* recursive = */ true, + "external/bazelbuild-rules_testing":/* recursive = */ true, "external/bazelbuild-kotlin-rules":/* recursive = */ true, "external/bazel-skylib":/* recursive = */ true, "external/protobuf":/* recursive = */ false, @@ -427,31 +525,104 @@ var ( "prebuilts/clang-tools":/* recursive = */ true, "prebuilts/gcc":/* recursive = */ true, "prebuilts/build-tools":/* recursive = */ true, + "prebuilts/jdk/jdk8":/* recursive = */ true, "prebuilts/jdk/jdk17":/* recursive = */ true, "prebuilts/misc":/* recursive = */ false, // not recursive because we need bp2build converted build files in prebuilts/misc/common/asm "prebuilts/sdk":/* recursive = */ false, "prebuilts/sdk/tools":/* recursive = */ false, "prebuilts/r8":/* recursive = */ false, "prebuilts/runtime":/* recursive = */ false, + "prebuilts/rust":/* recursive = */ true, // not recursive due to conflicting workspace paths in tools/atest/bazel/rules "tools/asuite/atest":/* recursive = */ false, "tools/asuite/atest/bazel/reporter":/* recursive = */ true, - // TODO(b/266459895): remove this and the placeholder BUILD file after re-enabling libunwindstack - "external/rust/crates/rustc-demangle-capi":/* recursive = */ false, + // Used for testing purposes only. Should not actually exist in the real source tree. + "testpkg/keep_build_file":/* recursive = */ false, "vendor/lineage-priv/keys":/* recursive = */ false, } Bp2buildModuleAlwaysConvertList = []string{ + "aconfig.test.cpp", + "AconfigJavaHostTest", + // aconfig + "libonce_cell", + "libanyhow", + "libunicode_segmentation", + "libmemchr", + "libbitflags-1.3.2", + "libryu", + "libitoa", + "libos_str_bytes", + "libheck", + "libclap_lex", + "libsyn", + "libquote", + "libunicode_ident", + "libproc_macro2", + "libthiserror_impl", + "libserde_derive", + "libclap_derive", + "libthiserror", + "libserde", + "libclap", + "libbytes", + "libprotobuf_support", + "libtinytemplate", + "libserde_json", + "libprotobuf", + + "protoc-gen-rust", + "libprotobuf_codegen", + "libprotobuf_parse", + "libregex", + "libtempfile", + "libwhich", + "libregex_syntax", + "libfastrand", + "libeither", + "libaho_corasick", + "liblibc", + "libcfg_if", + "liblog_rust", + "libgetrandom", + "libremove_dir_all", + "libahash", + "libhashbrown", + "libindexmap", + "libaconfig_protos", + "libpaste", + "aconfig", + + // ext + "tagsoup", + + // framework-minus-apex + "AndroidFrameworkLintChecker", + "ImmutabilityAnnotationProcessor", + "debian.mime.types.minimized", + "framework-javastream-protos", + "libview-inspector-annotation-processor", + + // services + "apache-commons-math", + "cbor-java", + "icu4j_calendar_astronomer", + "statslog-art-java-gen", + + "AndroidCommonLint", + "ImmutabilityAnnotation", + "ImmutabilityAnnotationProcessorHostLibrary", + "libidmap2_policies", "libSurfaceFlingerProp", + "toolbox_input_labels", + // cc mainline modules - "code_coverage.policy", - "code_coverage.policy.other", - "codec2_soft_exports", - "codecs_g711dec", + + // com.android.media.swcodec "com.android.media.swcodec", "com.android.media.swcodec-androidManifest", "com.android.media.swcodec-ld.config.txt", @@ -459,17 +630,20 @@ var ( "com.android.media.swcodec-mediaswcodec.rc", "com.android.media.swcodec.certificate", "com.android.media.swcodec.key", - "flatbuffer_headers", + "test_com.android.media.swcodec", + + // deps + "code_coverage.policy", + "code_coverage.policy.other", + "codec2_soft_exports", + "compatibility_matrix_schema", "framework-connectivity-protos", + "framework-connectivity-javastream-protos", "gemmlowp_headers", "gl_headers", "libandroid_runtime_lazy", "libandroid_runtime_vm_headers", "libaudioclient_aidl_conversion_util", - "libbinder", - "libbinder_device_interface_sources", - "libbinder_aidl", - "libbinder_headers", "libbinder_headers_platform_shared", "libbinderthreadstateutils", "libbluetooth-types-header", @@ -484,7 +658,6 @@ var ( "libneuralnetworks", "libneuralnetworks_static", "libgraphicsenv", - "libhardware", "libhardware_headers", "libnativeloader-headers", "libnativewindow_headers", @@ -496,29 +669,12 @@ var ( "libandroidio", "libandroidio_srcs", "libserviceutils", - "libstagefright_amrnbenc", - "libstagefright_amrnbdec", - "libstagefright_amrwbdec", - "libstagefright_amrwbenc", - "libstagefright_amrnb_common", - "libstagefright_enc_common", - "libstagefright_flacdec", - "libstagefright_foundation", - "libstagefright_foundation_headers", - "libstagefright_headers", - "libstagefright_m4vh263dec", - "libstagefright_m4vh263enc", - "libstagefright_mp3dec", - "libstagefright_mp3dec_headers", "libsurfaceflinger_headers", "libsync", "libtextclassifier_hash_headers", "libtextclassifier_hash_static", "libtflite_kernel_utils", "libtinyxml2", - "libui", - "libui-types", - "libui_headers", "libvorbisidec", "media_ndk_headers", "media_plugin_headers", @@ -526,8 +682,6 @@ var ( "mediaswcodec.xml", "neuralnetworks_types", "libneuralnetworks_common", - // packagemanager_aidl_interface is created implicitly in packagemanager_aidl module - "packagemanager_aidl_interface", "philox_random", "philox_random_headers", "server_configurable_flags", @@ -542,12 +696,19 @@ var ( // prebuilts "prebuilt_stats-log-api-gen", + "prebuilt_aapt2", // fastboot "fastboot", "libfastboot", - "liblp", - "libstorage_literals_headers", + + "PluginCoreLib", + "dagger2", + "dagger2-android-annotation-stubs", + "dagger2-bootstrap-compiler", + "dagger2-producers", + "okio-lib", + "setupdesign-strings", //external/avb "avbtool", @@ -561,12 +722,11 @@ var ( //external/fec "libfec_rs", - //system/core/libsparse - "libsparse", - //system/extras/ext4_utils "libext4_utils", "mke2fs_conf", + "mkuserimg_mke2fs", + "blk_alloc_to_base_fs", //system/extras/libfec "libfec", @@ -574,10 +734,6 @@ var ( //system/extras/squashfs_utils "libsquashfs_utils", - //system/extras/verity/fec - "fec", - "boot_signer", - //packages/apps/Car/libs/car-ui-lib/car-ui-androidx // genrule dependencies for java_imports "car-ui-androidx-annotation-nodeps", @@ -586,14 +742,6 @@ var ( "car-ui-androidx-lifecycle-common-nodeps", "car-ui-androidx-constraintlayout-solver-nodeps", - //system/libhidl - "libhidlbase", // needed by cc_hidl_library - "libhidl_gtest_helper", - - //frameworks/native - "framework_native_aidl_binder", - "framework_native_aidl_gui", - //frameworks/native/libs/input "inputconstants_aidl", @@ -602,21 +750,11 @@ var ( "libusb", - // needed by liblogd - "ILogcatManagerService_aidl", - "libincremental_aidl-cpp", - "incremental_aidl", - //frameworks/native/cmds/cmd "libcmd", - //system/core/fs_mgr/libdm - "libdm", - - //system/core/fs_mgr/libfiemap - "libfiemap_headers", - "libfiemap_passthrough_srcs", - "libfiemap_srcs", + //system/chre + "chre_api", //system/gsid "libgsi", @@ -634,23 +772,16 @@ var ( //system/extras/libfscrypt "libfscrypt", - //system/core/fs_mgr - "libfstab", - //bootable/recovery/fuse_sideload "libfusesideload", - //system/core/fs_mgr/libfs_avb - "libfs_avb", - - //system/core/fs_mgr - "libfs_mgr", - + "libcodec2_aidl", "libcodec2_hidl@1.0", "libcodec2_hidl@1.1", "libcodec2_hidl@1.2", "libcodec2_hidl_plugin_stub", "libcodec2_hidl_plugin", + "libcodec2_hal_common", "libstagefright_bufferqueue_helper_novndk", "libGLESv2", "libEGL", @@ -685,6 +816,7 @@ var ( "libcodec2_soft_vp8dec", "libcodec2_soft_vp9dec", "libcodec2_soft_av1dec_gav1", + "libcodec2_soft_av1dec_dav1d", "libcodec2_soft_vp8enc", "libcodec2_soft_vp9enc", "libcodec2_soft_rawdec", @@ -721,13 +853,6 @@ var ( // for api_fingerprint.txt generation "api_fingerprint", - // allowlisting for kotlinx_coroutines - "kotlinx_coroutines", - "kotlinx_coroutines-device", - "kotlinx_coroutines-host", - "annotations", - "kotlinx-coroutines-android-annotation-stubs", - // for building com.android.neuralnetworks "libimapper_stablec", "libimapper_providerutils", @@ -735,20 +860,171 @@ var ( // min_sdk_version in android_app "CtsShimUpgrade", + "art_cmdlineparser_headers", + // Mainline Module Apps "CaptivePortalLogin", + "ModuleMetadata", + + "libstagefright_headers", + + // Apps with JNI libs + "SimpleJNI", + "libsimplejni", + + // aidl + "aidl", + "libaidl-common", + + // Used by xsd_config + "xsdc", + + // cc_test that can be run by b test + "binderRpcWireProtocolTest", + "binderUnitTest", + "cpu_features-bit_utils_test", + "android.hardware.audio.common.test.utility_tests", + "HalAudioStreamWorkerTest", + "libjavacore-unit-tests", + "NeuralNetworksTest_utils", + "NeuralNetworksTest_logtag", + "NeuralNetworksTest_operations", + "nanoapp_chqts_shared_tests", + "fakeservicemanager_test", + "tristate_test", + "binderUtilsHostTest", + "run_dex2oat_test", + "bluetooth-address-unit-tests", + + // for platform_compat_config + "process-compat-config", + + // cc_* modules with rscript srcs + "rstest-latency", + "libRScpp_static", + "rs-headers", + "rs_script_api", + "libRSDispatch", + + // hal_unit_tests and deps + "chre_flatbuffers", + "event_logger", + "hal_unit_tests", + + "merge_annotation_zips_test", + + // java_resources with multiple resource_dirs + "emma", + + // NDK STL + "ndk_libc++abi", + "ndk_libunwind", + "ndk_libc++_static", + "ndk_libc++_shared", + "ndk_system", + + // allowlist //prebuilts/common/misc/androidx-test/... + "androidx.test.runner", + "androidx.test.runner-nodeps", + "androidx.test.services.storage", + "androidx.test.services.storage-nodeps", + "androidx.test.monitor", + "androidx.test.monitor-nodeps", + "androidx.test.annotation", + "androidx.test.annotation-nodeps", + + // jni deps of an internal android_test (b/297405812) + "libopenjdkjvmti_headers", + + // tradefed deps + "apache-commons-compress", + "tradefed-protos", + "grpc-java", + "grpc-java-api", + "grpc-java-auth", + "grpc-java-context", + "grpc-java-core", + "grpc-java-core-inprocess", + "grpc-java-core-internal", + "grpc-java-core-util", + "grpc-java-protobuf", + "grpc-java-protobuf-lite", + "grpc-java-stub", + "grpc-java-annotation-stubs", + "grpc-java-annotation-stubs-srcjar", + "gen_annotations", + "opencensus-java-contrib-grpc-metrics", + "opencensus-java-api", + "gson", + "GsonBuildConfig.java", + "gson_version_generator", + "lab-resource-grpc", + "blueprint-deptools", + "protoc-gen-grpc-java-plugin", + "tf-remote-client", + "tradefed-lite", + "tradefed-isolation-protos", + "snakeyaml_patched_src_files", + "asuite_proto_java", + "tradefed-service-grpc-lib", + "tradefed-invocation-grpc", + "tradefed-external-dependencies", + "tradefed-dynamic-sharding-grpc", + "tradefed-device-manager-grpc", + "statsd_internal_protos", + "snakeyaml", + "loganalysis", + "junit-params", + "grpc-java-testing", + "grpc-java-netty-shaded", + "aoa-helper", + "test-services.apk", + "test-composers", + "py3-stdlib-prebuilt-srcs", + "platformprotos", + "test-services-normalized.apk", + "tradefed-common-util", + "tradefed-clearcut-client", + "tradefed-result-interfaces", + "tradefed-device-build-interfaces", + "tradefed-invocation-interfaces", + "tradefed-lib-core", + + "libandroid_net_connectivity_com_android_net_module_util_jni", + "libservice-connectivity", + + "mainline_modules_sdks_test", + + "fake_device_config", } Bp2buildModuleTypeAlwaysConvertList = []string{ + // go/keep-sorted start + "aconfig_declarations", + "aconfig_value_set", + "aconfig_values", "aidl_interface_headers", "bpf", + "cc_aconfig_library", + "cc_prebuilt_library", + "cc_prebuilt_library_headers", + "cc_prebuilt_library_shared", + "cc_prebuilt_library_static", "combined_apis", - "license", - "linker_config", + "droiddoc_exported_dir", + "java_aconfig_library", "java_import", "java_import_host", "java_sdk_library", + "java_sdk_library_import", + "license", + "linker_config", + "ndk_headers", + "ndk_library", "sysprop_library", + "versioned_ndk_headers", + "xsd_config", + // go/keep-sorted end } // Add the names of modules that bp2build should never convert, if it is @@ -759,16 +1035,26 @@ var ( // the "prebuilt_" prefix to the name, so that it's differentiable from // the source versions within Soong's module graph. Bp2buildModuleDoNotConvertList = []string{ - // Depends on unconverted libandroid, libgui - "dvr_buffer_queue-test", - "dvr_display-test", - // Depends on unconverted libchrome - "pdx_benchmarks", - "buffer_hub_queue-test", - "buffer_hub_queue_producer-test", + + // rust modules that have cc deps + "liblogger", + "libbssl_ffi", + "libbssl_ffi_nostd", + "pull_rust", + "libstatslog_rust", + "libstatslog_rust_header", + "libflatbuffers", + "liblog_event_list", + "libminijail_rust", + "libminijail_sys", + "libfsverity_rs", + "libtombstoned_client_rust", + + // TODO(b/263326760): Failed already. + "minijail_compiler_unittest", + "minijail_parser_unittest", // cc bugs - "libactivitymanager_aidl", // TODO(b/207426160): Unsupported use of aidl sources (via Dactivity_manager_procstate_aidl) in a cc_library // TODO(b/198619163) module has same name as source "logtagd.rc", @@ -781,20 +1067,16 @@ var ( "libcutils_test_static", "KernelLibcutilsTest", - "linker", // TODO(b/228316882): cc_binary uses link_crt - "versioner", // TODO(b/228313961): depends on prebuilt shared library libclang-cpp_host as a shared library, which does not supply expected providers for a shared library - "art_libartbase_headers", // TODO(b/236268577): Header libraries do not support export_shared_libs_headers - "apexer_test", // Requires aapt2 - "apexer_test_host_tools", - "host_apex_verifier", - "tjbench", // TODO(b/240563612): Stem property + "linker", // TODO(b/228316882): cc_binary uses link_crt + "versioner", // TODO(b/228313961): depends on prebuilt shared library libclang-cpp_host as a shared library, which does not supply expected providers for a shared library - // java bugs - "libbase_ndk", // TODO(b/186826477): fails to link libctscamera2_jni for device (required for CtsCameraTestCases) - "bouncycastle", // TODO(b/274474005): Need support for custom system_modules. + // requires host tools for apexer + "apexer_test", "apexer_test_host_tools", "host_apex_verifier", "host-apex-verifier", - // python protos - "libprotobuf-python", // Has a handcrafted alternative + // java bugs + "libbase_ndk", // TODO(b/186826477): fails to link libctscamera2_jni for device (required for CtsCameraTestCases) + "bouncycastle", // TODO(b/274474005): Need support for custom system_modules. + "bouncycastle-test-lib", // TODO(b/274474005): Reverse dependency of bouncycastle // genrule incompatibilities "brotli-fuzzer-corpus", // TODO(b/202015218): outputs are in location incompatible with bazel genrule handling. @@ -811,120 +1093,32 @@ var ( "conscrypt-for-host", // TODO(b/210751803), we don't handle path property for filegroups "host-libprotobuf-java-full", // TODO(b/210751803), we don't handle path property for filegroups "libprotobuf-internal-python-srcs", // TODO(b/210751803), we don't handle path property for filegroups - "libprotobuf-java-full", // TODO(b/210751803), we don't handle path property for filegroups - "libprotobuf-java-util-full", // TODO(b/210751803), we don't handle path property for filegroups - "auto_value_plugin_resources", // TODO(b/210751803), we don't handle path property for filegroups - // go deps: + // go deps. + // TODO: b/305091740 - Rely on bp2build_deps to remove these dependencies. "analyze_bcpf", // depends on bpmodify a blueprint_go_binary. "analyze_bcpf_test", // depends on bpmodify a blueprint_go_binary. "host_bionic_linker_asm", // depends on extract_linker, a go binary. "host_bionic_linker_script", // depends on extract_linker, a go binary. - // in cmd attribute of genrule rule //system/timezone/output_data:robolectric_tzdata: label '//system/timezone/output_data:iana/tzdata' in $(location) expression is not a declared prerequisite of this rule - "robolectric_tzdata", - // rust support "libtombstoned_client_rust_bridge_code", "libtombstoned_client_wrapper", // rust conversions are not supported - // unconverted deps - "apexer_with_DCLA_preprocessing_test", // depends on unconverted modules: apexer_test_host_tools, com.android.example.apex - "adb", // depends on unconverted modules: AdbWinApi, libandroidfw, libopenscreen-discovery, libopenscreen-platform-impl, libusb, bin2c_fastdeployagent, AdbWinUsbApi - "android_icu4j_srcgen", // depends on unconverted modules: currysrc - "android_icu4j_srcgen_binary", // depends on unconverted modules: android_icu4j_srcgen, currysrc - "apex_compression_test", // depends on unconverted modules: soong_zip, com.android.example.apex - "apex_manifest_proto_java", // b/210751803, depends on libprotobuf-java-full - "art-script", // depends on unconverted modules: dalvikvm, dex2oat - "bin2c_fastdeployagent", // depends on unconverted modules: deployagent - "CarHTMLViewer", // depends on unconverted modules android.car-stubs, car-ui-lib - "com.android.runtime", // depends on unconverted modules: bionic-linker-config, linkerconfig - "currysrc", // depends on unconverted modules: currysrc_org.eclipse, guavalib, jopt-simple-4.9 - "dex2oat-script", // depends on unconverted modules: dex2oat - "generated_android_icu4j_resources", // depends on unconverted modules: android_icu4j_srcgen_binary - "generated_android_icu4j_test_resources", // depends on unconverted modules: android_icu4j_srcgen_binary - "host-libprotobuf-java-nano", // b/220869005, depends on libprotobuf-java-nano - "jacoco-stubs", // b/245767077, depends on droidstubs - "libapexutil", // depends on unconverted modules: apex-info-list-tinyxml - "libart", // depends on unconverted modules: apex-info-list-tinyxml, libtinyxml2, libnativeloader-headers, heapprofd_client_api, art_operator_srcs, libcpu_features, libodrstatslog, libelffile, art_cmdlineparser_headers, cpp-define-generator-definitions, libdexfile, libnativebridge, libnativeloader, libsigchain, libartbase, libprofile, cpp-define-generator-asm-support - "libart-runtime-gtest", // depends on unconverted modules: libgtest_isolated, libart-compiler, libdexfile, libprofile, libartbase, libartbase-art-gtest - "libart_headers", // depends on unconverted modules: art_libartbase_headers - "libartbase-art-gtest", // depends on unconverted modules: libgtest_isolated, libart, libart-compiler, libdexfile, libprofile - "libartbased-art-gtest", // depends on unconverted modules: libgtest_isolated, libartd, libartd-compiler, libdexfiled, libprofiled - "libartd", // depends on unconverted modules: art_operator_srcs, libcpu_features, libodrstatslog, libelffiled, art_cmdlineparser_headers, cpp-define-generator-definitions, libdexfiled, libnativebridge, libnativeloader, libsigchain, libartbased, libprofiled, cpp-define-generator-asm-support, apex-info-list-tinyxml, libtinyxml2, libnativeloader-headers, heapprofd_client_api - "libartd-runtime-gtest", // depends on unconverted modules: libgtest_isolated, libartd-compiler, libdexfiled, libprofiled, libartbased, libartbased-art-gtest - "libdebuggerd", // depends on unconverted module: libdexfile - "libdebuggerd_handler", // depends on unconverted module libdebuggerd_handler_core - "libdebuggerd_handler_core", "libdebuggerd_handler_fallback", // depends on unconverted module libdebuggerd - "libdexfiled", // depends on unconverted modules: dexfile_operator_srcs, libartbased, libartpalette - "libfastdeploy_host", // depends on unconverted modules: libandroidfw, libusb, AdbWinApi - "libgmock_main_ndk", // depends on unconverted modules: libgtest_ndk_c++ - "libgmock_ndk", // depends on unconverted modules: libgtest_ndk_c++ - "libnativehelper_lazy_mts_jni", "libnativehelper_mts_jni", // depends on unconverted modules: libnativetesthelper_jni, libgmock_ndk - "libnativetesthelper_jni", // depends on unconverted modules: libgtest_ndk_c++ - "libstatslog", // depends on unconverted modules: libstatspull, statsd-aidl-ndk - "libstatslog_art", // depends on unconverted modules: statslog_art.cpp, statslog_art.h - "linker_reloc_bench_main", // depends on unconverted modules: liblinker_reloc_bench_* - "malloc-rss-benchmark", // depends on unconverted modules: libmeminfo - "pbtombstone", "crash_dump", // depends on libdebuggerd, libunwindstack - "releasetools_test", // depends on unconverted modules: com.android.apex.compressed.v1 - "robolectric-sqlite4java-0.282", // depends on unconverted modules: robolectric-sqlite4java-import, robolectric-sqlite4java-native - "static_crasher", // depends on unconverted modules: libdebuggerd_handler - "test_fips", // depends on unconverted modules: adb - "timezone-host", // depends on unconverted modules: art.module.api.annotations - "truth-host-prebuilt", // depends on unconverted modules: truth-prebuilt - "truth-prebuilt", // depends on unconverted modules: asm-7.0, guava - - // '//bionic/libc:libc_bp2build_cc_library_static' is duplicated in the 'deps' attribute of rule - "toybox-static", + // TODO: b/303474748 - aidl rules for java are incompatible with parcelable declarations + "modules-utils-list-slice", + "modules-utils-os", + "modules-utils-synchronous-result-receiver", // aidl files not created "overlayable_policy_aidl_interface", - //prebuilts/tools/common/m2 - // depends on //external/okio:okio-lib, which uses kotlin - "wire-runtime", - - // depends on adbd_system_api_recovery, which is a unconverted `phony` module type - "minadbd", - - // depends on android.hardware.health-V2.0-java - "android.hardware.health-translate-java", - - //system/libvintf - // depends on apex-info-list-tinyxml, unconverted xsd_config Soong module type. - "libvintf", - "vintf", - "libassemblevintf", - "assemble_vintf", - "libvintffm", - "vintffm", - "checkvintf", - - // depends on audio_policy_configuration_aidl_default, xsd_config module. - "libaudioserviceexampleimpl", - "android.hardware.audio.service-aidl.example", - - // depends on //system/tools/aidl/build:aidl_metadata_json, which is an aidl_interfaces_metadata custom Soong type. - "aidl_metadata_in_cpp", - "libaidlmetadata", - "libaidlmetadata_test", - - // depends on //system/tools/hidl/build:hidl_metadata_json, which is an hidl_interfaces_metadata custom Soong type. - "hidl_metadata_in_cpp", - "libhidlmetadata", - "hidl_metadata_test", - // cc_test related. // b/274164834 "Could not open Configuration file test.cfg" "svcenc", "svcdec", // Failing host cc_tests - "memunreachable_unit_test", - "libprocinfo_test", - "ziparchive-tests", "gtest_isolated_tests", "libunwindstack_unit_test", - "task_profiles_test", "power_tests", // failing test on server, but not on host // reflect: call of reflect.Value.NumField on interface Value @@ -941,45 +1135,27 @@ var ( "libnativebridge6-test-case", "libnativebridge6prezygotefork", - "libandroidfw_tests", "aapt2_tests", // failing due to data path issues + "libandroidfw_tests", // failing due to data path issues + + // error: overriding commands for target + // `out/host/linux-x86/nativetest64/gmock_tests/gmock_tests__cc_runner_test', + // previously defined at out/soong/installs-aosp_arm.mk:64919` + "gmock_tests", // cc_test with unconverted deps, or are device-only (and not verified to pass yet) "AMRWBEncTest", - "AmrnbDecoderTest", // depends on unconverted modules: libaudioutils, libsndfile - "AmrnbEncoderTest", // depends on unconverted modules: libaudioutils, libsndfile - "AmrwbDecoderTest", // depends on unconverted modules: libsndfile, libaudioutils - "AmrwbEncoderTest", // depends on unconverted modules: libaudioutils, libsndfile - "Mp3DecoderTest", // depends on unconverted modules: libsndfile, libaudioutils - "Mpeg4H263DecoderTest", // depends on unconverted modules: libstagefright_foundation - "Mpeg4H263EncoderTest", "avcdec", "avcenc", - "bionic-benchmarks-tests", - "bionic-fortify-runtime-asan-test", - "bionic-stress-tests", - "bionic-unit-tests", - "bionic-unit-tests-glibc", - "bionic-unit-tests-static", - "boringssl_crypto_test", - "boringssl_ssl_test", + "boringssl_test_support", //b/244431896 "cfi_test_helper", - "cfi_test_helper2", "cintltst32", "cintltst64", "compare", "cpuid", - "debuggerd_test", // depends on unconverted modules: libdebuggerd "elftls_dlopen_ie_error_helper", - "exec_linker_helper", - "fastdeploy_test", // depends on unconverted modules: AdbWinApi, libadb_host, libandroidfw, libfastdeploy_host, libopenscreen-discovery, libopenscreen-platform-impl, libusb "fdtrack_test", "google-benchmark-test", - "googletest-param-test-test_ndk", // depends on unconverted modules: libgtest_ndk_c++ "gtest-typed-test_test", - "gtest-typed-test_test_ndk", // depends on unconverted modules: libgtest_ndk_c++, libgtest_main_ndk_c++ - "gtest_ndk_tests", // depends on unconverted modules: libgtest_ndk_c++, libgtest_main_ndk_c++ - "gtest_ndk_tests_no_main", // depends on unconverted modules: libgtest_ndk_c++ - "gtest_prod_test_ndk", // depends on unconverted modules: libgtest_ndk_c++, libgtest_main_ndk_c++ "gtest_tests", "gtest_tests_no_main", "gwp_asan_unittest", @@ -987,7 +1163,6 @@ var ( "hashcombine_test", "hevcdec", "hevcenc", - "hwbinderThroughputTest", // depends on unconverted modules: android.hardware.tests.libhwbinder@1.0-impl.test, android.hardware.tests.libhwbinder@1.0 "i444tonv12_eg", "icu4c_sample_break", "intltest32", @@ -995,25 +1170,17 @@ var ( "ion-unit-tests", "jemalloc5_integrationtests", "jemalloc5_unittests", - "ld_config_test_helper", - "ld_preload_test_helper", - "libBionicCtsGtestMain", // depends on unconverted modules: libgtest_isolated - "libBionicLoaderTests", // depends on unconverted modules: libmeminfo - "libapexutil_tests", // depends on unconverted modules: apex-info-list-tinyxml, libapexutil + "jemalloc5_stresstests", // run by run_jemalloc_tests.sh and will be deleted after V "libcutils_sockets_test", - "libexpectedutils_test", "libhwbinder_latency", "liblog-host-test", // failing tests "libminijail_test", "libminijail_unittest_gtest", "libpackagelistparser_test", "libprotobuf_vendor_suffix_test", - "libstagefright_amrnbdec_test", // depends on unconverted modules: libsndfile, libaudioutils "libstagefright_amrnbenc_test", - "libstagefright_amrwbdec_test", // depends on unconverted modules: libsndfile, libaudioutils + "libstagefright_amrwbdec_test", // error: did not report any run "libstagefright_m4vh263enc_test", - "libstagefright_mp3dec_test", // depends on unconverted modules: libsndfile, libaudioutils - "libstatssocket_test", "libvndksupport-tests", "libyuv_unittest", "linker-unit-tests", @@ -1022,30 +1189,21 @@ var ( "malloc_hooks_system_tests", "mat_test", "mathtest", - "memunreachable_binder_test", // depends on unconverted modules: libbinder "memunreachable_test", "metadata_tests", - "minijail0_cli_unittest_gtest", "mpeg2dec", "mvcdec", - "ns_hidden_child_helper", "pngtest", "preinit_getauxval_test_helper", "preinit_syscall_test_helper", "psnr", "quat_test", - "rappor-tests", // depends on unconverted modules: jsr305, guava "scudo_unit_tests", - "stats-log-api-gen-test", // depends on unconverted modules: libstats_proto_host - "syscall_filter_unittest_gtest", - "sysprop_test", // depends on unconverted modules: libcom.android.sysprop.tests "thread_exit_cb_helper", - "tls_properties_helper", "ulp", "vec_test", "yuvconstants", "yuvconvert", - "zipalign_tests", // cc_test_library "clang_diagnostic_tests", @@ -1069,7 +1227,7 @@ var ( "libcfi-test", "libcfi-test-bad", "libcrash_test", - // "libcrypto_fuzz_unsafe", + "libcrypto_fuzz_unsafe", "libdl_preempt_test_1", "libdl_preempt_test_2", "libdl_test_df_1_global", @@ -1280,7 +1438,7 @@ var ( "librelocations-fat", "libsegment_gap_inner", "libsegment_gap_outer", - // "libssl_fuzz_unsafe", + "libssl_fuzz_unsafe", "libstatssocket_private", "libsysv-hash-table-library", "libtest_atexit", @@ -1373,25 +1531,13 @@ var ( "libtest_with_dependency_loop_c", "libtestshared", - // depends on unconverted libprotobuf-java-nano - "dnsresolverprotosnano", - "launcherprotosnano", - "datastallprotosnano", - "devicepolicyprotosnano", - "ota_metadata_proto_java", - "merge_ota", - // releasetools - "releasetools_fsverity_metadata_generator", "verity_utils", "check_ota_package_signature", "check_target_files_vintf", "releasetools_check_target_files_vintf", - "releasetools_verity_utils", - "build_image", "ota_from_target_files", "releasetools_ota_from_target_files", - "releasetools_build_image", "add_img_to_target_files", "releasetools_add_img_to_target_files", "fsverity_metadata_generator", @@ -1434,76 +1580,48 @@ var ( // python_test_host with test data "sbom_writers_test", - } + "hidl_test", - MixedBuildsDisabledList = []string{ - "libruy_static", "libtflite_kernel_utils", // TODO(b/237315968); Depend on prebuilt stl, not from source - - "art_libdexfile_dex_instruction_list_header", // breaks libart_mterp.armng, header not found - - "libbrotli", // http://b/198585397, ld.lld: error: bionic/libc/arch-arm64/generic/bionic/memmove.S:95:(.text+0x10): relocation R_AARCH64_CONDBR19 out of range: -1404176 is not in [-1048576, 1048575]; references __memcpy - "minijail_constants_json", // http://b/200899432, bazel-built cc_genrule does not work in mixed build when it is a dependency of another soong module. - - "cap_names.h", // TODO(b/204913827) runfiles need to be handled in mixed builds - "libcap", // TODO(b/204913827) runfiles need to be handled in mixed builds - "libprotobuf-cpp-full", "libprotobuf-cpp-lite", // Unsupported product&vendor suffix. b/204811222 and b/204810610. - - // Depends on libprotobuf-cpp-* - "libadb_pairing_connection", - "libadb_pairing_connection_static", - "libadb_pairing_server", "libadb_pairing_server_static", - - // TODO(b/240563612) Needing `stem` selection support for cc_binary - "crasher", - - // java_import[_host] issues - // tradefed prebuilts depend on libprotobuf - "prebuilt_tradefed", - "prebuilt_tradefed-test-framework", - // handcrafted BUILD.bazel files in //prebuilts/... - "prebuilt_r8lib-prebuilt", - "prebuilt_sdk-core-lambda-stubs", - "prebuilt_android-support-collections-nodeps", - "prebuilt_android-arch-core-common-nodeps", - "prebuilt_android-arch-lifecycle-common-java8-nodeps", - "prebuilt_android-arch-lifecycle-common-nodeps", - "prebuilt_android-support-annotations-nodeps", - "prebuilt_android-arch-paging-common-nodeps", - "prebuilt_android-arch-room-common-nodeps", - // TODO(b/217750501) exclude_dirs property not supported - "prebuilt_kotlin-reflect", - "prebuilt_kotlin-stdlib", - "prebuilt_kotlin-stdlib-jdk7", - "prebuilt_kotlin-stdlib-jdk8", - "prebuilt_kotlin-test", - // TODO(b/217750501) exclude_files property not supported - "prebuilt_currysrc_org.eclipse", - - // TODO(b/266459895): re-enable libunwindstack - "libunwindstack", - "libunwindstack_stdout_log", - "libunwindstack_no_dex", - "libunwindstack_utils", - "unwind_reg_info", - "libunwindstack_local", - "unwind_for_offline", - "unwind", - "unwind_info", - "unwind_symbols", - "libEGL", - "libGLESv2", - "libc_malloc_debug", - "libcodec2_hidl@1.0", - "libcodec2_hidl@1.1", - "libcodec2_hidl@1.2", - "libfdtrack", - "libgui", - "libgui_bufferqueue_static", - "libmedia_codecserviceregistrant", - "libstagefright_bufferqueue_helper_novndk", - "libutils_test", - "libutilscallstack", - "mediaswcodec", + // TODO(B/283193845): Remove tradefed from this list. + "tradefed", + + "libprotobuf-full-test", // TODO(b/246997908): cannot convert proto_libraries which implicitly include other srcs in the same directory + "libprotobuf-lite-test", // TODO(b/246997908): cannot convert proto_libraries which implicitly include other srcs in the same directory + + "logcat", // TODO(b/246997908): cannot convert proto_libraries which implicitly include other srcs in the same directory + + "expresscatalogvalidator", // TODO(b/246997908): cannot convert proto_libraries which implicitly include other srcs in the same directory + + // r8 is a java_binary, which creates an implicit "r8.jar" target, but the + // same package contains a "r8.jar" file which gets overshadowed by the implicit target. + // We don't need this target as we're not using the Soong wrapper for now + "r8", + + // TODO(b/299924782): Fix linking error + "libbinder_on_trusty_mock", + + // TODO(b/299943581): Depends on aidl filegroups with implicit headers + "libdataloader_aidl-cpp", + "libincremental_manager_aidl-cpp", + + // TODO(b/299974637) Fix linking error + "libbinder_rpc_unstable", + + // TODO(b/297356704) sdk_version is unset. + "VendorAtomCodeGenJavaTest", + + // TODO: b/305223367 - Missing dep on android.test.base-neverlink + "ObjenesisTck", + + // TODO - b/306197073: Sets different STL for host and device variants + "trace_processor_shell", + + // TODO - b/303713102: duplicate deps added by cc_lite_proto_library + "perfetto_unittests", + "perfetto_integrationtests", + + // TODO - b/306194966: Depends on an empty filegroup + "libperfetto_c", } // Bazel prod-mode allowlist. Modules in this list are built by Bazel @@ -1523,6 +1641,17 @@ var ( "adb_tls_connection_test", // M9: mixed builds for mainline trains launch "api_fingerprint", + // M11: neuralnetworks launch + "com.android.neuralnetworks", + "test_com.android.neuralnetworks", + "libneuralnetworks", + "libneuralnetworks_static", + // M13: media.swcodec launch + // TODO(b/307389608) Relaunch swcodec after fixing rust dependencies + // "com.android.media.swcodec", + // "test_com.android.media.swcodec", + // "libstagefright_foundation", + // "libcodec2_hidl@1.0", } // Staging-mode allowlist. Modules in this list are only built @@ -1530,11 +1659,7 @@ var ( // which will soon be added to the prod allowlist. // It is implicit that all modules in ProdMixedBuildsEnabledList will // also be built - do not add them to this list. - StagingMixedBuildsEnabledList = []string{ - "com.android.neuralnetworks", - "libneuralnetworks", - "libneuralnetworks_static", - } + StagingMixedBuildsEnabledList = []string{} // These should be the libs that are included by the apexes in the ProdMixedBuildsEnabledList ProdDclaMixedBuildsEnabledList = []string{ @@ -1542,6 +1667,8 @@ var ( "libc++", "libcrypto", "libcutils", + "libstagefright_flacdec", + "libutils", } // These should be the libs that are included by the apexes in the StagingMixedBuildsEnabledList @@ -1549,270 +1676,13 @@ var ( // TODO(b/269342245): Enable the rest of the DCLA libs // "libssl", - // "libstagefright_flacdec", - // "libutils", - - // TODO(b/273282046): Make this list customizable to support various targets. - // The list of modules which are expected to spend lots of build time. - // With `--ninja_weight_source=soong`, ninja builds these modules and deps first. - HugeModulesMap = map[string]int{ - "AccountManagementApp": DEFAULT_NINJA_WEIGHT, - "ActivityManagerPerfTestsStubApp1": DEFAULT_NINJA_WEIGHT, - "ActivityManagerPerfTestsStubApp2": DEFAULT_NINJA_WEIGHT, - "ActivityManagerPerfTestsStubApp3": DEFAULT_NINJA_WEIGHT, - "api-stubs-docs-non-updatable": DEFAULT_NINJA_WEIGHT, - "AppCompatibilityTest": DEFAULT_NINJA_WEIGHT, - "AppTransitionTests": DEFAULT_NINJA_WEIGHT, - "art_compiler_tests": DEFAULT_NINJA_WEIGHT, - "art.module.intra.core.api.stubs.source": DEFAULT_NINJA_WEIGHT, - "art.module.public.api.stubs.source": DEFAULT_NINJA_WEIGHT, - "AttestationVerificationTest": DEFAULT_NINJA_WEIGHT, - "BatteryUsageStatsProtoTests": DEFAULT_NINJA_WEIGHT, - "bluetooth_test_gd_unit": DEFAULT_NINJA_WEIGHT, - "Bluetooth": DEFAULT_NINJA_WEIGHT, - "BluetoothInstrumentationTests": DEFAULT_NINJA_WEIGHT, - "Calendar": DEFAULT_NINJA_WEIGHT, - "CalendarProvider": DEFAULT_NINJA_WEIGHT, - "Camera2": DEFAULT_NINJA_WEIGHT, - "CarRotaryControllerUnitTests": DEFAULT_NINJA_WEIGHT, - "CarSettingsForUnitTesting": DEFAULT_NINJA_WEIGHT, - "CarSettingsUnitTests": DEFAULT_NINJA_WEIGHT, - "CarSystemUI-tests": DEFAULT_NINJA_WEIGHT, - "CellBroadcastApp": DEFAULT_NINJA_WEIGHT, - "CellBroadcastLegacyApp": DEFAULT_NINJA_WEIGHT, - "CellBroadcastReceiverOemUnitTests": DEFAULT_NINJA_WEIGHT, - "CellBroadcastServiceModule": DEFAULT_NINJA_WEIGHT, - "CompanionDeviceManager": DEFAULT_NINJA_WEIGHT, - "ConnectivityChecker": DEFAULT_NINJA_WEIGHT, - "Contacts": DEFAULT_NINJA_WEIGHT, - "ContactsProvider": DEFAULT_NINJA_WEIGHT, - "ContentCapturePerfTests": DEFAULT_NINJA_WEIGHT, - "CorePerfTests": DEFAULT_NINJA_WEIGHT, - "crosvm": DEFAULT_NINJA_WEIGHT, - "CtsDomainVerificationDeviceMultiUserTestCases": DEFAULT_NINJA_WEIGHT, - "CtsLogdTestCases": DEFAULT_NINJA_WEIGHT, - "CtsMediaProviderTranscodeTests": DEFAULT_NINJA_WEIGHT, - "CtsRollbackManagerHostTestHelperApp": DEFAULT_NINJA_WEIGHT, - "CtsRollbackManagerHostTestHelperApp2": DEFAULT_NINJA_WEIGHT, - "CtsRootPackageInstallerTestCases": DEFAULT_NINJA_WEIGHT, - "CtsRootRollbackManagerHostTestHelperApp": DEFAULT_NINJA_WEIGHT, - "CtsTranscodeTestAppSupportsHevc": DEFAULT_NINJA_WEIGHT, - "CtsTranscodeTestAppSupportsSlowMotion": DEFAULT_NINJA_WEIGHT, - "CuttlefishDisplayHotplugHelperApp": DEFAULT_NINJA_WEIGHT, - "cvd-host_package": DEFAULT_NINJA_WEIGHT, - "DelegateTestApp": DEFAULT_NINJA_WEIGHT, - "DeskClock": DEFAULT_NINJA_WEIGHT, - "Development": DEFAULT_NINJA_WEIGHT, - "DeviceAdminTestApp": DEFAULT_NINJA_WEIGHT, - "DevicePolicyManagementRoleHolderTestApp": DEFAULT_NINJA_WEIGHT, - "dex2oatd": DEFAULT_NINJA_WEIGHT, - "DocumentsUI": DEFAULT_NINJA_WEIGHT, - "EasterEgg": DEFAULT_NINJA_WEIGHT, - "EffectProxyTest": DEFAULT_NINJA_WEIGHT, - "EmergencyInfo": DEFAULT_NINJA_WEIGHT, - "EmptyTestApp": DEFAULT_NINJA_WEIGHT, - "ExtServices": DEFAULT_NINJA_WEIGHT, - "FacebookAppsScenarioTests": DEFAULT_NINJA_WEIGHT, - "flickerlib-core": DEFAULT_NINJA_WEIGHT, - "flickerlib": DEFAULT_NINJA_WEIGHT, - "FlickerLibTest": DEFAULT_NINJA_WEIGHT, - "FlickerTests": DEFAULT_NINJA_WEIGHT, - "framework-minus-apex": DEFAULT_NINJA_WEIGHT, - "framework-res": DEFAULT_NINJA_WEIGHT, - "FrameworksCoreTests": DEFAULT_NINJA_WEIGHT, - "FrameworksMockingCoreTests": DEFAULT_NINJA_WEIGHT, - "FrameworksMockingServicesTests": DEFAULT_NINJA_WEIGHT, - "FrameworksNetSmokeTests": DEFAULT_NINJA_WEIGHT, - "FrameworksNetTests": DEFAULT_NINJA_WEIGHT, - "FrameworksServicesTests": DEFAULT_NINJA_WEIGHT, - "FrameworksTelephonyTests": DEFAULT_NINJA_WEIGHT, - "FrameworksUiServicesTests": DEFAULT_NINJA_WEIGHT, - "FrameworksVcnTests": DEFAULT_NINJA_WEIGHT, - "Gallery2": DEFAULT_NINJA_WEIGHT, - "GameCoreDevice": DEFAULT_NINJA_WEIGHT, - "GoogleBluetoothInstrumentationTests": DEFAULT_NINJA_WEIGHT, - "guice_munged_srcs": DEFAULT_NINJA_WEIGHT, - "HalfSheetUX": DEFAULT_NINJA_WEIGHT, - "ImePerfTests": DEFAULT_NINJA_WEIGHT, - "imgdiag": DEFAULT_NINJA_WEIGHT, - "ImsServiceEntitlement": DEFAULT_NINJA_WEIGHT, - "ImsServiceEntitlementUnitTests": DEFAULT_NINJA_WEIGHT, - "InputTests": DEFAULT_NINJA_WEIGHT, - "InstallTest": DEFAULT_NINJA_WEIGHT, - "IntentResolver": DEFAULT_NINJA_WEIGHT, - "JankBench": DEFAULT_NINJA_WEIGHT, - "jsilver": DEFAULT_NINJA_WEIGHT, - "KeyChain": DEFAULT_NINJA_WEIGHT, - "KeyChainTests": DEFAULT_NINJA_WEIGHT, - "keystore2": DEFAULT_NINJA_WEIGHT, - "LargeResourcesCompressed": DEFAULT_NINJA_WEIGHT, - "LatinIME": DEFAULT_NINJA_WEIGHT, - "Launcher3QuickStepLib": DEFAULT_NINJA_WEIGHT, - "libaom": DEFAULT_NINJA_WEIGHT, - "libart-broken": DEFAULT_NINJA_WEIGHT, - "libart-compiler": DEFAULT_NINJA_WEIGHT, - "libart-disassembler": DEFAULT_NINJA_WEIGHT, - "libart": DEFAULT_NINJA_WEIGHT, - "libartd": DEFAULT_NINJA_WEIGHT, - "libaudiohal@7.1": DEFAULT_NINJA_WEIGHT, - "libbluetooth_core_rs": DEFAULT_NINJA_WEIGHT, - "libbluetooth_gd_unit_tests": DEFAULT_NINJA_WEIGHT, - "libbluetooth_gd": DEFAULT_NINJA_WEIGHT, - "libbluetooth_rs": DEFAULT_NINJA_WEIGHT, - "libbluetooth-for-tests": DEFAULT_NINJA_WEIGHT, - "libbt_common": DEFAULT_NINJA_WEIGHT, - "libbt_packets_nonapex": DEFAULT_NINJA_WEIGHT, - "libbt_packets": DEFAULT_NINJA_WEIGHT, - "libbt_shim_ffi": DEFAULT_NINJA_WEIGHT, - "libbt_shim": DEFAULT_NINJA_WEIGHT, - "libbt-audio-hal-interface": DEFAULT_NINJA_WEIGHT, - "libbt-bta-core": DEFAULT_NINJA_WEIGHT, - "libbt-bta": DEFAULT_NINJA_WEIGHT, - "libbt-common": DEFAULT_NINJA_WEIGHT, - "libbt-hci": DEFAULT_NINJA_WEIGHT, - "libbt-platform-protos-lite": DEFAULT_NINJA_WEIGHT, - "libbt-protos-lite": DEFAULT_NINJA_WEIGHT, - "libbt-sbc-decoder": DEFAULT_NINJA_WEIGHT, - "libc": DEFAULT_NINJA_WEIGHT, - "libclap": DEFAULT_NINJA_WEIGHT, - "libcodec2_soft_av1dec_gav1": DEFAULT_NINJA_WEIGHT, - "libcompositionengine_test": DEFAULT_NINJA_WEIGHT, - "libdevices": DEFAULT_NINJA_WEIGHT, - "libfrontend_proto": DEFAULT_NINJA_WEIGHT, - "libhwtrust": DEFAULT_NINJA_WEIGHT, - "libjni": DEFAULT_NINJA_WEIGHT, - "libkeystore2": DEFAULT_NINJA_WEIGHT, - "libkmr_ta": DEFAULT_NINJA_WEIGHT, - "liblmp": DEFAULT_NINJA_WEIGHT, - "libopenjdkjvmtid": DEFAULT_NINJA_WEIGHT, - "libprotobuf_deprecated": DEFAULT_NINJA_WEIGHT, - "libprotobuf": DEFAULT_NINJA_WEIGHT, - "libregex": DEFAULT_NINJA_WEIGHT, - "LibStatsPullTests": DEFAULT_NINJA_WEIGHT, - "libstd": DEFAULT_NINJA_WEIGHT, - "libsurfaceflinger_unittest": DEFAULT_NINJA_WEIGHT, - "libsyn": DEFAULT_NINJA_WEIGHT, - "libtokio": DEFAULT_NINJA_WEIGHT, - "libuwb_core": DEFAULT_NINJA_WEIGHT, - "libuwb_uci_jni_rust": DEFAULT_NINJA_WEIGHT, - "libuwb_uci_packets": DEFAULT_NINJA_WEIGHT, - "libvpx": DEFAULT_NINJA_WEIGHT, - "libvulkan_enc": DEFAULT_NINJA_WEIGHT, - "libwebrtc": DEFAULT_NINJA_WEIGHT, - "LiveWallpapersPicker": DEFAULT_NINJA_WEIGHT, - "LockTaskApp": DEFAULT_NINJA_WEIGHT, - "LongevityPlatformLibTests": DEFAULT_NINJA_WEIGHT, - "ManagedProvisioning": DEFAULT_NINJA_WEIGHT, - "ManagedProvisioningTests": DEFAULT_NINJA_WEIGHT, - "MediaProvider": DEFAULT_NINJA_WEIGHT, - "MediaProviderClientTests": DEFAULT_NINJA_WEIGHT, - "MediaProviderLegacy": DEFAULT_NINJA_WEIGHT, - "messaging": DEFAULT_NINJA_WEIGHT, - "metalava": DEFAULT_NINJA_WEIGHT, - "MicrobenchmarkRunnerTests": DEFAULT_NINJA_WEIGHT, - "microdroid_manager": DEFAULT_NINJA_WEIGHT, - "minikin_tests": DEFAULT_NINJA_WEIGHT, - "MLCTestApp": DEFAULT_NINJA_WEIGHT, - "MmsService": DEFAULT_NINJA_WEIGHT, - "MmsServiceTests": DEFAULT_NINJA_WEIGHT, - "module-lib-api-stubs-docs-non-updatable": DEFAULT_NINJA_WEIGHT, - "motion_tool_lib_tests": DEFAULT_NINJA_WEIGHT, - "MtpService": DEFAULT_NINJA_WEIGHT, - "MultiUserTests": DEFAULT_NINJA_WEIGHT, - "NearbyIntegrationUiTests": DEFAULT_NINJA_WEIGHT, - "net_test_bluetooth": DEFAULT_NINJA_WEIGHT, - "net_test_btif": DEFAULT_NINJA_WEIGHT, - "net_test_main_shim": DEFAULT_NINJA_WEIGHT, - "net_test_stack": DEFAULT_NINJA_WEIGHT, - "net-tests-utils": DEFAULT_NINJA_WEIGHT, - "NetworkStackCoverageTests": DEFAULT_NINJA_WEIGHT, - "NetworkStackIntegrationTests": DEFAULT_NINJA_WEIGHT, - "NetworkStackNext": DEFAULT_NINJA_WEIGHT, - "NfcNci": DEFAULT_NINJA_WEIGHT, - "NfcNciUnitTests": DEFAULT_NINJA_WEIGHT, - "NotEmptyTestApp": DEFAULT_NINJA_WEIGHT, - "NotificationFunctionalTests": DEFAULT_NINJA_WEIGHT, - "oatdumpd": DEFAULT_NINJA_WEIGHT, - "OsuLogin": DEFAULT_NINJA_WEIGHT, - "PackageInstaller": DEFAULT_NINJA_WEIGHT, - "PackageManagerComponentOverrideTests": DEFAULT_NINJA_WEIGHT, - "PackageManagerPerfTests": DEFAULT_NINJA_WEIGHT, - "PackageManagerServiceServerTests": DEFAULT_NINJA_WEIGHT, - "PackageManagerServiceUnitTests": DEFAULT_NINJA_WEIGHT, - "PackageWatchdogTest": DEFAULT_NINJA_WEIGHT, - "PandoraServerLib": DEFAULT_NINJA_WEIGHT, - "pdl": DEFAULT_NINJA_WEIGHT, - "perfetto_trace_java_protos": DEFAULT_NINJA_WEIGHT, - "perfetto_trace-full": DEFAULT_NINJA_WEIGHT, - "PermissionController": DEFAULT_NINJA_WEIGHT, - "PermissionControllerMockingTests": DEFAULT_NINJA_WEIGHT, - "PixelAppCompTests": DEFAULT_NINJA_WEIGHT, - "platform-bootclasspath": DEFAULT_NINJA_WEIGHT, - "PlatformCommonScenarioTests": DEFAULT_NINJA_WEIGHT, - "PlatformComposeCoreTests": DEFAULT_NINJA_WEIGHT, - "platformprotoslite": DEFAULT_NINJA_WEIGHT, - "PlatformRuleTests": DEFAULT_NINJA_WEIGHT, - "precompiled_sepolicy-without_apex": DEFAULT_NINJA_WEIGHT, - "PresencePolling": DEFAULT_NINJA_WEIGHT, - "PrintSpooler": DEFAULT_NINJA_WEIGHT, - "QuickSearchBox": DEFAULT_NINJA_WEIGHT, - "RemoteDPCTestApp": DEFAULT_NINJA_WEIGHT, - "RemoteProvisioningServiceTests": DEFAULT_NINJA_WEIGHT, - "RkpdAppUnitTests": DEFAULT_NINJA_WEIGHT, - "Robolectric_shadows_framework": DEFAULT_NINJA_WEIGHT, - "RoleHolderApp": DEFAULT_NINJA_WEIGHT, - "SdkSandbox": DEFAULT_NINJA_WEIGHT, - "service-appsearch": DEFAULT_NINJA_WEIGHT, - "service-connectivity": DEFAULT_NINJA_WEIGHT, - "service-uwb": DEFAULT_NINJA_WEIGHT, - "service-wifi": DEFAULT_NINJA_WEIGHT, - "services-non-updatable-stubs": DEFAULT_NINJA_WEIGHT, - "services": DEFAULT_NINJA_WEIGHT, - "Settings-core": DEFAULT_NINJA_WEIGHT, - "Settings": DEFAULT_NINJA_WEIGHT, - "SettingsIntelligence": DEFAULT_NINJA_WEIGHT, - "SettingsLibTests": DEFAULT_NINJA_WEIGHT, - "SettingsProvider": DEFAULT_NINJA_WEIGHT, - "Shell": DEFAULT_NINJA_WEIGHT, - "SimAppDialog": DEFAULT_NINJA_WEIGHT, - "sl4a": DEFAULT_NINJA_WEIGHT, - "SmsApp": DEFAULT_NINJA_WEIGHT, - "SoundPicker": DEFAULT_NINJA_WEIGHT, - "StagedInstallTest": DEFAULT_NINJA_WEIGHT, - "StatementService": DEFAULT_NINJA_WEIGHT, - "StatsdFrameworkTestApp": DEFAULT_NINJA_WEIGHT, - "StatsdFrameworkTestAppNoPermission": DEFAULT_NINJA_WEIGHT, - "statsdprotolite": DEFAULT_NINJA_WEIGHT, - "Stk": DEFAULT_NINJA_WEIGHT, - "StorageManager": DEFAULT_NINJA_WEIGHT, - "system-api-stubs-docs-non-updatable": DEFAULT_NINJA_WEIGHT, - "SystemUI-core": DEFAULT_NINJA_WEIGHT, - "SystemUI-tests-base": DEFAULT_NINJA_WEIGHT, - "SystemUI-tests": DEFAULT_NINJA_WEIGHT, - "SystemUI": DEFAULT_NINJA_WEIGHT, - "SystemUIComposeFeatures": DEFAULT_NINJA_WEIGHT, - "SystemUIComposeFeaturesTests": DEFAULT_NINJA_WEIGHT, - "SystemUITests": DEFAULT_NINJA_WEIGHT, - "Tag": DEFAULT_NINJA_WEIGHT, - "Telecom": DEFAULT_NINJA_WEIGHT, - "TelecomUnitTests": DEFAULT_NINJA_WEIGHT, - "telephony-common": DEFAULT_NINJA_WEIGHT, - "TelephonyProvider": DEFAULT_NINJA_WEIGHT, - "TeleService": DEFAULT_NINJA_WEIGHT, - "test-api-stubs-docs-non-updatable": DEFAULT_NINJA_WEIGHT, - "TetheringIntegrationTests": DEFAULT_NINJA_WEIGHT, - "TetheringNext": DEFAULT_NINJA_WEIGHT, - "ThemePickerTests": DEFAULT_NINJA_WEIGHT, - "Traceur": DEFAULT_NINJA_WEIGHT, - "UsbManagerTests": DEFAULT_NINJA_WEIGHT, - "UsbTests": DEFAULT_NINJA_WEIGHT, - "virtmgr": DEFAULT_NINJA_WEIGHT, - "WallpaperPicker2TestLib": DEFAULT_NINJA_WEIGHT, - "WallpaperPicker2Tests": DEFAULT_NINJA_WEIGHT, - "WifiDialog": DEFAULT_NINJA_WEIGHT, - "wm-proto-parsers": DEFAULT_NINJA_WEIGHT, - "WMShellFlickerTests": DEFAULT_NINJA_WEIGHT, - "WmTests": DEFAULT_NINJA_WEIGHT, - "wpa_supplicant": DEFAULT_NINJA_WEIGHT, + + // The list of module types which are expected to spend lots of build time. + // With `--ninja_weight_source=soong`, ninja builds these module types and deps first. + HugeModuleTypePrefixMap = map[string]int{ + "rust_": HIGH_PRIORITIZED_WEIGHT, + "droidstubs": DEFAULT_PRIORITIZED_WEIGHT, + "art_": DEFAULT_PRIORITIZED_WEIGHT, + "ndk_library": DEFAULT_PRIORITIZED_WEIGHT, } ) diff --git a/android/androidmk.go b/android/androidmk.go index aa411d1161..c4b93c77f2 100644 --- a/android/androidmk.go +++ b/android/androidmk.go @@ -42,7 +42,7 @@ func init() { } func RegisterAndroidMkBuildComponents(ctx RegistrationContext) { - ctx.RegisterSingletonType("androidmk", AndroidMkSingleton) + ctx.RegisterParallelSingletonType("androidmk", AndroidMkSingleton) } // Enable androidmk support. @@ -486,17 +486,6 @@ func (a *AndroidMkEntries) GetDistForGoals(mod blueprint.Module) []string { 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) { - AndroidMkEmitAssignList(w, "LOCAL_LICENSE_KINDS", a.EntryMap["LOCAL_LICENSE_KINDS"]) - AndroidMkEmitAssignList(w, "LOCAL_LICENSE_CONDITIONS", a.EntryMap["LOCAL_LICENSE_CONDITIONS"]) - AndroidMkEmitAssignList(w, "LOCAL_NOTICE_FILE", a.EntryMap["LOCAL_NOTICE_FILE"]) - if pn, ok := a.EntryMap["LOCAL_LICENSE_PACKAGE_NAME"]; ok { - AndroidMkEmitAssignList(w, "LOCAL_LICENSE_PACKAGE_NAME", 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. type fillInEntriesContext interface { @@ -534,15 +523,6 @@ func (a *AndroidMkEntries) fillInEntries(ctx fillInEntriesContext, mod blueprint // Collect make variable assignment entries. a.SetString("LOCAL_PATH", ctx.ModuleDir(mod)) a.SetString("LOCAL_MODULE", name+a.SubName) - a.AddStrings("LOCAL_LICENSE_KINDS", base.commonProperties.Effective_license_kinds...) - a.AddStrings("LOCAL_LICENSE_CONDITIONS", base.commonProperties.Effective_license_conditions...) - a.AddStrings("LOCAL_NOTICE_FILE", base.commonProperties.Effective_license_text.Strings()...) - // TODO(b/151177513): Does this code need to set LOCAL_MODULE_IS_CONTAINER ? - if base.commonProperties.Effective_package_name != nil { - a.SetString("LOCAL_LICENSE_PACKAGE_NAME", *base.commonProperties.Effective_package_name) - } else if len(base.commonProperties.Effective_licenses) > 0 { - a.SetString("LOCAL_LICENSE_PACKAGE_NAME", strings.Join(base.commonProperties.Effective_licenses, " ")) - } a.SetString("LOCAL_MODULE_CLASS", a.Class) a.SetString("LOCAL_PREBUILT_MODULE_FILE", a.OutputFile.String()) a.AddStrings("LOCAL_REQUIRED_MODULES", a.Required...) @@ -560,6 +540,10 @@ func (a *AndroidMkEntries) fillInEntries(ctx fillInEntriesContext, mod blueprint a.SetPaths("LOCAL_SOONG_INSTALL_SYMLINKS", base.katiSymlinks.InstallPaths().Paths()) } + if len(base.testData) > 0 { + a.AddStrings("LOCAL_TEST_DATA", androidMkDataPaths(base.testData)...) + } + if am, ok := mod.(ApexModule); ok { a.SetBoolIfTrue("LOCAL_NOT_AVAILABLE_FOR_PLATFORM", am.NotAvailableForPlatform()) } @@ -956,10 +940,13 @@ func shouldSkipAndroidMkProcessing(module *ModuleBase) bool { // A utility func to format LOCAL_TEST_DATA outputs. See the comments on DataPath to understand how // to use this func. -func AndroidMkDataPaths(data []DataPath) []string { +func androidMkDataPaths(data []DataPath) []string { var testFiles []string for _, d := range data { rel := d.SrcPath.Rel() + if d.WithoutRel { + rel = d.SrcPath.Base() + } path := d.SrcPath.String() // LOCAL_TEST_DATA requires the rel portion of the path to be removed from the path. if !strings.HasSuffix(path, rel) { diff --git a/android/apex.go b/android/apex.go index c9b4a0b7c1..c6d9940336 100644 --- a/android/apex.go +++ b/android/apex.go @@ -84,6 +84,9 @@ type ApexInfo struct { // // See Prebuilt.ApexInfoMutator for more information. ForPrebuiltApex bool + + // Returns the name of the test apexes that this module is included in. + TestApexes []string } var ApexInfoProvider = blueprint.NewMutatorProvider(ApexInfo{}, "apex") @@ -287,6 +290,9 @@ type ApexProperties struct { // See ApexModule.UniqueApexVariants() UniqueApexVariationsForDeps bool `blueprint:"mutated"` + + // The test apexes that includes this apex variant + TestApexes []string `blueprint:"mutated"` } // Marker interface that identifies dependencies that are excluded from APEX contents. @@ -429,6 +435,11 @@ func (m *ApexModuleBase) TestFor() []string { return nil } +// Returns the test apexes that this module is included in. +func (m *ApexModuleBase) TestApexes() []string { + return m.ApexProperties.TestApexes +} + // Implements ApexModule func (m *ApexModuleBase) UniqueApexVariations() bool { // If needed, this will bel overridden by concrete types inheriting @@ -451,6 +462,14 @@ const ( AvailableToGkiApex = "com.android.gki.*" ) +var ( + AvailableToRecognziedWildcards = []string{ + AvailableToPlatform, + AvailableToAnyApex, + AvailableToGkiApex, + } +) + // CheckAvailableForApex provides the default algorithm for checking the apex availability. When the // availability is empty, it defaults to ["//apex_available:platform"] which means "available to the // platform but not available to any APEX". When the list is not empty, `what` is matched against @@ -551,12 +570,14 @@ func mergeApexVariations(ctx PathContext, apexInfos []ApexInfo) (merged []ApexIn // Platform APIs is allowed for this module only when all APEXes containing // the module are with `use_platform_apis: true`. merged[index].UsePlatformApis = merged[index].UsePlatformApis && apexInfo.UsePlatformApis + merged[index].TestApexes = append(merged[index].TestApexes, apexInfo.TestApexes...) } else { seen[mergedName] = len(merged) apexInfo.ApexVariationName = mergedName apexInfo.InApexVariants = CopyOf(apexInfo.InApexVariants) apexInfo.InApexModules = CopyOf(apexInfo.InApexModules) apexInfo.ApexContents = append([]*ApexContents(nil), apexInfo.ApexContents...) + apexInfo.TestApexes = CopyOf(apexInfo.TestApexes) merged = append(merged, apexInfo) } aliases = append(aliases, [2]string{variantName, mergedName}) @@ -604,8 +625,10 @@ func CreateApexVariations(mctx BottomUpMutatorContext, module ApexModule) []Modu mctx.SetDefaultDependencyVariation(&defaultVariation) variations := []string{defaultVariation} + testApexes := []string{} for _, a := range apexInfos { variations = append(variations, a.ApexVariationName) + testApexes = append(testApexes, a.TestApexes...) } modules := mctx.CreateVariations(variations...) for i, mod := range modules { @@ -619,6 +642,9 @@ func CreateApexVariations(mctx BottomUpMutatorContext, module ApexModule) []Modu if !platformVariation { mctx.SetVariationProvider(mod, ApexInfoProvider, apexInfos[i-1]) } + // Set the value of TestApexes in every single apex variant. + // This allows each apex variant to be aware of the test apexes in the user provided apex_available. + mod.(ApexModule).apexModuleBase().ApexProperties.TestApexes = testApexes } for _, alias := range aliases { @@ -909,3 +935,22 @@ func CheckMinSdkVersion(ctx ModuleContext, minSdkVersion ApiLevel, walk WalkPayl return true }) } + +// Construct ApiLevel object from min_sdk_version string value +func MinSdkVersionFromValue(ctx EarlyModuleContext, value string) ApiLevel { + if value == "" { + return NoneApiLevel + } + apiLevel, err := ApiLevelFromUser(ctx, value) + if err != nil { + ctx.PropertyErrorf("min_sdk_version", "%s", err.Error()) + return NoneApiLevel + } + return apiLevel +} + +// Implemented by apexBundle. +type ApexTestInterface interface { + // Return true if the apex bundle is an apex_test + IsTestApex() bool +} diff --git a/android/apex_contributions.go b/android/apex_contributions.go new file mode 100644 index 0000000000..f13659a6d7 --- /dev/null +++ b/android/apex_contributions.go @@ -0,0 +1,172 @@ +// Copyright 2023 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" + "github.com/google/blueprint/proptools" +) + +func init() { + RegisterApexContributionsBuildComponents(InitRegistrationContext) +} + +func RegisterApexContributionsBuildComponents(ctx RegistrationContext) { + ctx.RegisterModuleType("apex_contributions", apexContributionsFactory) + ctx.RegisterSingletonModuleType("all_apex_contributions", allApexContributionsFactory) +} + +type apexContributions struct { + ModuleBase + properties contributionProps +} + +type contributionProps struct { + // Name of the mainline module + Api_domain *string + // A list of module names that should be used when this contribution + // is selected via product_config + // The name should be explicit (foo or prebuilt_foo) + Contents []string +} + +func (m *apexContributions) ApiDomain() string { + return proptools.String(m.properties.Api_domain) +} + +func (m *apexContributions) Contents() []string { + return m.properties.Contents +} + +// apex_contributions contains a list of module names (source or +// prebuilt) belonging to the mainline module +// An apex can have multiple apex_contributions modules +// with different combinations of source or prebuilts, but only one can be +// selected via product_config. +func apexContributionsFactory() Module { + module := &apexContributions{} + module.AddProperties(&module.properties) + InitAndroidModule(module) + return module +} + +// This module type does not have any build actions. +// It provides metadata that is used in post-deps mutator phase for source vs +// prebuilts selection. +func (m *apexContributions) GenerateAndroidBuildActions(ctx ModuleContext) { +} + +// A container for apex_contributions. +// Based on product_config, it will create a dependency on the selected +// apex_contributions per mainline module +type allApexContributions struct { + SingletonModuleBase +} + +func allApexContributionsFactory() SingletonModule { + module := &allApexContributions{} + InitAndroidModule(module) + return module +} + +type apexContributionsDepTag struct { + blueprint.BaseDependencyTag +} + +var ( + acDepTag = apexContributionsDepTag{} +) + +// Creates a dep to each selected apex_contributions +func (a *allApexContributions) DepsMutator(ctx BottomUpMutatorContext) { + ctx.AddDependency(ctx.Module(), acDepTag, ctx.Config().AllApexContributions()...) +} + +// Set PrebuiltSelectionInfoProvider in post deps phase +func (a *allApexContributions) SetPrebuiltSelectionInfoProvider(ctx BaseModuleContext) { + addContentsToProvider := func(p *PrebuiltSelectionInfoMap, m *apexContributions) { + for _, content := range m.Contents() { + if !ctx.OtherModuleExists(content) && !ctx.Config().AllowMissingDependencies() { + ctx.ModuleErrorf("%s listed in apex_contributions %s does not exist\n", content, m.Name()) + } + pi := &PrebuiltSelectionInfo{ + baseModuleName: RemoveOptionalPrebuiltPrefix(content), + selectedModuleName: content, + metadataModuleName: m.Name(), + apiDomain: m.ApiDomain(), + } + p.Add(ctx, pi) + } + } + + p := PrebuiltSelectionInfoMap{} + ctx.VisitDirectDepsWithTag(acDepTag, func(child Module) { + if m, ok := child.(*apexContributions); ok { + addContentsToProvider(&p, m) + } else { + ctx.ModuleErrorf("%s is not an apex_contributions module\n", child.Name()) + } + }) + ctx.SetProvider(PrebuiltSelectionInfoProvider, p) +} + +// A provider containing metadata about whether source or prebuilt should be used +// This provider will be used in prebuilt_select mutator to redirect deps +var PrebuiltSelectionInfoProvider = blueprint.NewMutatorProvider(PrebuiltSelectionInfoMap{}, "prebuilt_select") + +// Map of baseModuleName to the selected source or prebuilt +type PrebuiltSelectionInfoMap map[string]PrebuiltSelectionInfo + +// Add a new entry to the map with some validations +func (pm *PrebuiltSelectionInfoMap) Add(ctx BaseModuleContext, p *PrebuiltSelectionInfo) { + if p == nil { + return + } + // Do not allow dups. If the base module (without the prebuilt_) has been added before, raise an exception. + if old, exists := (*pm)[p.baseModuleName]; exists { + ctx.ModuleErrorf("Cannot use Soong module: %s from apex_contributions: %s because it has been added previously as: %s from apex_contributions: %s\n", + p.selectedModuleName, p.metadataModuleName, old.selectedModuleName, old.metadataModuleName, + ) + } + (*pm)[p.baseModuleName] = *p +} + +type PrebuiltSelectionInfo struct { + // e.g. libc + baseModuleName string + // e.g. (libc|prebuilt_libc) + selectedModuleName string + // Name of the apex_contributions module + metadataModuleName string + // e.g. com.android.runtime + apiDomain string +} + +// Returns true if `name` is explicitly requested using one of the selected +// apex_contributions metadata modules. +func (p *PrebuiltSelectionInfoMap) IsSelected(baseModuleName, name string) bool { + if i, exists := (*p)[baseModuleName]; exists { + return i.selectedModuleName == name + } else { + return false + } +} + +// This module type does not have any build actions. +func (a *allApexContributions) GenerateAndroidBuildActions(ctx ModuleContext) { +} + +func (a *allApexContributions) GenerateSingletonBuildActions(ctx SingletonContext) { +} diff --git a/android/apex_test.go b/android/apex_test.go index 0bf4c9c367..ddc730d05a 100644 --- a/android/apex_test.go +++ b/android/apex_test.go @@ -33,10 +33,10 @@ func Test_mergeApexVariations(t *testing.T) { { name: "single", in: []ApexInfo{ - {"foo", FutureApiLevel, false, false, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, + {"foo", FutureApiLevel, false, false, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex, nil}, }, wantMerged: []ApexInfo{ - {"apex10000", FutureApiLevel, false, false, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, + {"apex10000", FutureApiLevel, false, false, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex, nil}, }, wantAliases: [][2]string{ {"foo", "apex10000"}, @@ -45,11 +45,11 @@ func Test_mergeApexVariations(t *testing.T) { { name: "merge", in: []ApexInfo{ - {"foo", FutureApiLevel, false, false, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, - {"bar", FutureApiLevel, false, false, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex}, + {"foo", FutureApiLevel, false, false, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex, nil}, + {"bar", FutureApiLevel, false, false, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex, nil}, }, wantMerged: []ApexInfo{ - {"apex10000", FutureApiLevel, false, false, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, false}}, + {"apex10000", FutureApiLevel, false, false, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, false, nil}}, wantAliases: [][2]string{ {"bar", "apex10000"}, {"foo", "apex10000"}, @@ -58,12 +58,12 @@ func Test_mergeApexVariations(t *testing.T) { { name: "don't merge version", in: []ApexInfo{ - {"foo", FutureApiLevel, false, false, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, - {"bar", uncheckedFinalApiLevel(30), false, false, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex}, + {"foo", FutureApiLevel, false, false, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex, nil}, + {"bar", uncheckedFinalApiLevel(30), false, false, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex, nil}, }, wantMerged: []ApexInfo{ - {"apex30", uncheckedFinalApiLevel(30), false, false, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex}, - {"apex10000", FutureApiLevel, false, false, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, + {"apex30", uncheckedFinalApiLevel(30), false, false, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex, nil}, + {"apex10000", FutureApiLevel, false, false, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex, nil}, }, wantAliases: [][2]string{ {"bar", "apex30"}, @@ -73,11 +73,11 @@ func Test_mergeApexVariations(t *testing.T) { { name: "merge updatable", in: []ApexInfo{ - {"foo", FutureApiLevel, false, false, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, - {"bar", FutureApiLevel, true, false, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex}, + {"foo", FutureApiLevel, false, false, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex, nil}, + {"bar", FutureApiLevel, true, false, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex, nil}, }, wantMerged: []ApexInfo{ - {"apex10000", FutureApiLevel, true, false, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, NotForPrebuiltApex}, + {"apex10000", FutureApiLevel, true, false, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, NotForPrebuiltApex, nil}, }, wantAliases: [][2]string{ {"bar", "apex10000"}, @@ -87,15 +87,15 @@ func Test_mergeApexVariations(t *testing.T) { { name: "don't merge when for prebuilt_apex", in: []ApexInfo{ - {"foo", FutureApiLevel, false, false, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, - {"bar", FutureApiLevel, true, false, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex}, + {"foo", FutureApiLevel, false, false, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex, nil}, + {"bar", FutureApiLevel, true, false, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex, nil}, // This one should not be merged in with the others because it is for // a prebuilt_apex. - {"baz", FutureApiLevel, true, false, []string{"baz"}, []string{"baz"}, nil, ForPrebuiltApex}, + {"baz", FutureApiLevel, true, false, []string{"baz"}, []string{"baz"}, nil, ForPrebuiltApex, nil}, }, wantMerged: []ApexInfo{ - {"apex10000", FutureApiLevel, true, false, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, NotForPrebuiltApex}, - {"baz", FutureApiLevel, true, false, []string{"baz"}, []string{"baz"}, nil, ForPrebuiltApex}, + {"apex10000", FutureApiLevel, true, false, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, NotForPrebuiltApex, nil}, + {"baz", FutureApiLevel, true, false, []string{"baz"}, []string{"baz"}, nil, ForPrebuiltApex, nil}, }, wantAliases: [][2]string{ {"bar", "apex10000"}, @@ -105,11 +105,11 @@ func Test_mergeApexVariations(t *testing.T) { { name: "merge different UsePlatformApis but don't allow using platform api", in: []ApexInfo{ - {"foo", FutureApiLevel, false, false, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, - {"bar", FutureApiLevel, false, true, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex}, + {"foo", FutureApiLevel, false, false, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex, nil}, + {"bar", FutureApiLevel, false, true, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex, nil}, }, wantMerged: []ApexInfo{ - {"apex10000", FutureApiLevel, false, false, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, NotForPrebuiltApex}, + {"apex10000", FutureApiLevel, false, false, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, NotForPrebuiltApex, nil}, }, wantAliases: [][2]string{ {"bar", "apex10000"}, @@ -119,11 +119,11 @@ func Test_mergeApexVariations(t *testing.T) { { name: "merge same UsePlatformApis and allow using platform api", in: []ApexInfo{ - {"foo", FutureApiLevel, false, true, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, - {"bar", FutureApiLevel, false, true, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex}, + {"foo", FutureApiLevel, false, true, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex, nil}, + {"bar", FutureApiLevel, false, true, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex, nil}, }, wantMerged: []ApexInfo{ - {"apex10000", FutureApiLevel, false, true, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, NotForPrebuiltApex}, + {"apex10000", FutureApiLevel, false, true, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, NotForPrebuiltApex, nil}, }, wantAliases: [][2]string{ {"bar", "apex10000"}, diff --git a/android/api_domain.go b/android/api_domain.go index 587ceaefa1..0a66c3d735 100644 --- a/android/api_domain.go +++ b/android/api_domain.go @@ -14,20 +14,6 @@ package android -import ( - "github.com/google/blueprint" - - "android/soong/bazel" -) - -func init() { - RegisterApiDomainBuildComponents(InitRegistrationContext) -} - -func RegisterApiDomainBuildComponents(ctx RegistrationContext) { - ctx.RegisterModuleType("api_domain", ApiDomainFactory) -} - type ApiSurface int // TODO(b/246656800): Reconcile with android.SdkKind @@ -50,81 +36,3 @@ func (a ApiSurface) String() string { return "invalid" } } - -type apiDomain struct { - ModuleBase - BazelModuleBase - - properties apiDomainProperties -} - -type apiDomainProperties struct { - // cc library contributions (.h files/.map.txt) of this API domain - // This dependency is a no-op in Soong, but the corresponding Bazel target in the api_bp2build workspace - // will provide a `CcApiContributionInfo` provider - Cc_api_contributions []string - - // java library contributions (as .txt) of this API domain - // This dependency is a no-op in Soong, but the corresponding Bazel target in the api_bp2build workspace - // will provide a `JavaApiContributionInfo` provider - Java_api_contributions []string -} - -func ApiDomainFactory() Module { - m := &apiDomain{} - m.AddProperties(&m.properties) - InitAndroidArchModule(m, DeviceSupported, MultilibBoth) - return m -} - -// Do not create any dependency edges in Soong for now to skip visibility checks for some systemapi libraries. -// Currently, all api_domain modules reside in build/orchestrator/apis/Android.bp -// However, cc libraries like libsigchain (com.android.art) restrict their visibility to art/* -// When the api_domain module types are collocated with their contributions, this dependency edge can be restored -func (a *apiDomain) DepsMutator(ctx BottomUpMutatorContext) { -} - -// API domain does not have any builld actions yet -func (a *apiDomain) GenerateAndroidBuildActions(ctx ModuleContext) { -} - -const ( - apiContributionSuffix = ".contribution" -) - -// ApiContributionTargetName returns the name of the bp2build target (e.g. cc_api_contribution) of contribution modules (e.g. ndk_library) -// A suffix is necessary to prevent a name collision with the base target in the same bp2build bazel package -func ApiContributionTargetName(moduleName string) string { - return moduleName + apiContributionSuffix -} - -// For each contributing cc_library, format the name to its corresponding contribution bazel target in the bp2build workspace -func contributionBazelAttributes(ctx TopDownMutatorContext, contributions []string) bazel.LabelListAttribute { - addSuffix := func(ctx BazelConversionPathContext, module blueprint.Module) string { - baseLabel := BazelModuleLabel(ctx, module) - return ApiContributionTargetName(baseLabel) - } - bazelLabels := BazelLabelForModuleDepsWithFn(ctx, contributions, addSuffix) - return bazel.MakeLabelListAttribute(bazelLabels) -} - -type bazelApiDomainAttributes struct { - Cc_api_contributions bazel.LabelListAttribute - Java_api_contributions bazel.LabelListAttribute -} - -var _ ApiProvider = (*apiDomain)(nil) - -func (a *apiDomain) ConvertWithApiBp2build(ctx TopDownMutatorContext) { - props := bazel.BazelTargetModuleProperties{ - Rule_class: "api_domain", - Bzl_load_location: "//build/bazel/rules/apis:api_domain.bzl", - } - attrs := &bazelApiDomainAttributes{ - Cc_api_contributions: contributionBazelAttributes(ctx, a.properties.Cc_api_contributions), - Java_api_contributions: contributionBazelAttributes(ctx, a.properties.Java_api_contributions), - } - ctx.CreateBazelTargetModule(props, CommonAttributes{ - Name: ctx.ModuleName(), - }, attrs) -} diff --git a/android/api_levels.go b/android/api_levels.go index 137fd9dada..3f538c0381 100644 --- a/android/api_levels.go +++ b/android/api_levels.go @@ -15,16 +15,15 @@ package android import ( + "android/soong/starlark_import" "encoding/json" "fmt" "strconv" - - "android/soong/bazel" - "android/soong/starlark_fmt" + "strings" ) func init() { - RegisterSingletonType("api_levels", ApiLevelsSingleton) + RegisterParallelSingletonType("api_levels", ApiLevelsSingleton) } const previewAPILevelBase = 9000 @@ -239,6 +238,14 @@ func uncheckedFinalApiLevel(num int) ApiLevel { } } +func uncheckedFinalIncrementalApiLevel(num int, increment int) ApiLevel { + return ApiLevel{ + value: strconv.Itoa(num) + "." + strconv.Itoa(increment), + number: num, + isPreview: false, + } +} + var NoneApiLevel = ApiLevel{ value: "(no version)", // Not 0 because we don't want this to compare equal with the first preview. @@ -277,10 +284,6 @@ var FirstAndroidRelrVersion = uncheckedFinalApiLevel(28) // relocations itself. var FirstPackedRelocationsVersion = uncheckedFinalApiLevel(23) -// The first API level that does not require NDK code to link -// libandroid_support. -var FirstNonLibAndroidSupportVersion = uncheckedFinalApiLevel(21) - // LastWithoutModuleLibCoreSystemModules is the last API level where prebuilts/sdk does not contain // a core-for-system-modules.jar for the module-lib API scope. var LastWithoutModuleLibCoreSystemModules = uncheckedFinalApiLevel(31) @@ -288,13 +291,17 @@ var LastWithoutModuleLibCoreSystemModules = uncheckedFinalApiLevel(31) // ReplaceFinalizedCodenames returns the API level number associated with that API level // if the `raw` input is the codename of an API level has been finalized. // If the input is *not* a finalized codename, the input is returned unmodified. -func ReplaceFinalizedCodenames(config Config, raw string) string { - num, ok := getFinalCodenamesMap(config)[raw] +func ReplaceFinalizedCodenames(config Config, raw string) (string, error) { + finalCodenamesMap, err := getFinalCodenamesMap(config) + if err != nil { + return raw, err + } + num, ok := finalCodenamesMap[raw] if !ok { - return raw + return raw, nil } - return strconv.Itoa(num) + return strconv.Itoa(num), nil } // ApiLevelFrom converts the given string `raw` to an ApiLevel. @@ -344,7 +351,11 @@ func ApiLevelFromUserWithConfig(config Config, raw string) (ApiLevel, error) { } } - canonical, ok := getApiLevelsMapReleasedVersions()[raw] + apiLevelsReleasedVersions, err := getApiLevelsMapReleasedVersions() + if err != nil { + return NoneApiLevel, err + } + canonical, ok := apiLevelsReleasedVersions[raw] if !ok { asInt, err := strconv.Atoi(raw) if err != nil { @@ -369,6 +380,22 @@ func ApiLevelForTest(raw string) ApiLevel { return FutureApiLevel } + if strings.Contains(raw, ".") { + // Check prebuilt incremental API format MM.m for major (API level) and minor (incremental) revisions + parts := strings.Split(raw, ".") + if len(parts) != 2 { + panic(fmt.Errorf("Found unexpected version '%s' for incremental API - expect MM.m format for incremental API with both major (MM) an minor (m) revision.", raw)) + } + sdk, sdk_err := strconv.Atoi(parts[0]) + qpr, qpr_err := strconv.Atoi(parts[1]) + if sdk_err != nil || qpr_err != nil { + panic(fmt.Errorf("Unable to read version number for incremental api '%s'", raw)) + } + + apiLevel := uncheckedFinalIncrementalApiLevel(sdk, qpr) + return apiLevel + } + asInt, err := strconv.Atoi(raw) if err != nil { panic(fmt.Errorf("%q could not be parsed as an integer and is not a recognized codename", raw)) @@ -410,38 +437,21 @@ func GetApiLevelsJson(ctx PathContext) WritablePath { return PathForOutput(ctx, "api_levels.json") } -func getApiLevelsMapReleasedVersions() map[string]int { - return map[string]int{ - "G": 9, - "I": 14, - "J": 16, - "J-MR1": 17, - "J-MR2": 18, - "K": 19, - "L": 21, - "L-MR1": 22, - "M": 23, - "N": 24, - "N-MR1": 25, - "O": 26, - "O-MR1": 27, - "P": 28, - "Q": 29, - "R": 30, - "S": 31, - "S-V2": 32, - "Tiramisu": 33, - "UpsideDownCake": 34, - } +func getApiLevelsMapReleasedVersions() (map[string]int, error) { + return starlark_import.GetStarlarkValue[map[string]int]("api_levels_released_versions") } var finalCodenamesMapKey = NewOnceKey("FinalCodenamesMap") -func getFinalCodenamesMap(config Config) map[string]int { +func getFinalCodenamesMap(config Config) (map[string]int, error) { + type resultStruct struct { + result map[string]int + err error + } // This logic is replicated in starlark, if changing logic here update starlark code too // https://cs.android.com/android/platform/superproject/+/master:build/bazel/rules/common/api.bzl;l=30;drc=231c7e8c8038fd478a79eb68aa5b9f5c64e0e061 - return config.Once(finalCodenamesMapKey, func() interface{} { - apiLevelsMap := getApiLevelsMapReleasedVersions() + result := config.Once(finalCodenamesMapKey, func() interface{} { + apiLevelsMap, err := getApiLevelsMapReleasedVersions() // TODO: Differentiate "current" and "future". // The code base calls it FutureApiLevel, but the spelling is "current", @@ -454,41 +464,44 @@ func getFinalCodenamesMap(config Config) map[string]int { // added in S, both of these are usable when building for "current" when // neither R nor S are final, but the S APIs stop being available in a // final R build. - if Bool(config.productVariables.Platform_sdk_final) { + if err == nil && Bool(config.productVariables.Platform_sdk_final) { apiLevelsMap["current"] = config.PlatformSdkVersion().FinalOrFutureInt() } - return apiLevelsMap - }).(map[string]int) + return resultStruct{apiLevelsMap, err} + }).(resultStruct) + return result.result, result.err } var apiLevelsMapKey = NewOnceKey("ApiLevelsMap") // ApiLevelsMap has entries for preview API levels -func GetApiLevelsMap(config Config) map[string]int { +func GetApiLevelsMap(config Config) (map[string]int, error) { + type resultStruct struct { + result map[string]int + err error + } // This logic is replicated in starlark, if changing logic here update starlark code too // https://cs.android.com/android/platform/superproject/+/master:build/bazel/rules/common/api.bzl;l=23;drc=231c7e8c8038fd478a79eb68aa5b9f5c64e0e061 - return config.Once(apiLevelsMapKey, func() interface{} { - apiLevelsMap := getApiLevelsMapReleasedVersions() - for i, codename := range config.PlatformVersionAllPreviewCodenames() { - apiLevelsMap[codename] = previewAPILevelBase + i + result := config.Once(apiLevelsMapKey, func() interface{} { + apiLevelsMap, err := getApiLevelsMapReleasedVersions() + if err == nil { + for i, codename := range config.PlatformVersionAllPreviewCodenames() { + apiLevelsMap[codename] = previewAPILevelBase + i + } } - return apiLevelsMap - }).(map[string]int) + return resultStruct{apiLevelsMap, err} + }).(resultStruct) + return result.result, result.err } func (a *apiLevelsSingleton) GenerateBuildActions(ctx SingletonContext) { - apiLevelsMap := GetApiLevelsMap(ctx.Config()) + apiLevelsMap, err := GetApiLevelsMap(ctx.Config()) + if err != nil { + ctx.Errorf("%s\n", err) + return + } apiLevelsJson := GetApiLevelsJson(ctx) createApiLevelsJson(ctx, apiLevelsJson, apiLevelsMap) } - -func StarlarkApiLevelConfigs(config Config) string { - return fmt.Sprintf(bazel.GeneratedBazelFileWarning+` -_api_levels_released_versions = %s - -api_levels_released_versions = _api_levels_released_versions -`, starlark_fmt.PrintStringIntDict(getApiLevelsMapReleasedVersions(), 0), - ) -} diff --git a/android/arch.go b/android/arch.go index 4b4691b3d2..7436660ac2 100644 --- a/android/arch.go +++ b/android/arch.go @@ -425,7 +425,7 @@ func osMutator(bpctx blueprint.BottomUpMutatorContext) { // blueprint.BottomUpMutatorContext because android.BottomUpMutatorContext // filters out non-Soong modules. Now that we've handled them, create a // normal android.BottomUpMutatorContext. - mctx := bottomUpMutatorContextFactory(bpctx, module, false, false) + mctx := bottomUpMutatorContextFactory(bpctx, module, false) base := module.base() @@ -570,7 +570,7 @@ func archMutator(bpctx blueprint.BottomUpMutatorContext) { // blueprint.BottomUpMutatorContext because android.BottomUpMutatorContext // filters out non-Soong modules. Now that we've handled them, create a // normal android.BottomUpMutatorContext. - mctx := bottomUpMutatorContextFactory(bpctx, module, false, false) + mctx := bottomUpMutatorContextFactory(bpctx, module, false) base := module.base() @@ -1884,10 +1884,10 @@ func decodeMultilibTargets(multilib string, targets []Target, prefer32 bool) ([] buildTargets = filterMultilibTargets(targets, "lib64") // Reverse the targets so that the first architecture can depend on the second // architecture module in order to merge the outputs. - reverseSliceInPlace(buildTargets) + ReverseSliceInPlace(buildTargets) case "darwin_universal_common_first": archTargets := filterMultilibTargets(targets, "lib64") - reverseSliceInPlace(archTargets) + ReverseSliceInPlace(archTargets) buildTargets = append(getCommonTargets(targets), archTargets...) default: return nil, fmt.Errorf(`compile_multilib must be "both", "first", "32", "64", "prefer32" or "first_prefer32" found %q`, diff --git a/android/base_module_context.go b/android/base_module_context.go new file mode 100644 index 0000000000..4312e9bd47 --- /dev/null +++ b/android/base_module_context.go @@ -0,0 +1,611 @@ +// Copyright 2015 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" + "github.com/google/blueprint" + "regexp" + "strings" +) + +// BaseModuleContext is the same as blueprint.BaseModuleContext except that Config() returns +// a Config instead of an interface{}, and some methods have been wrapped to use an android.Module +// instead of a blueprint.Module, plus some extra methods that return Android-specific information +// about the current module. +type BaseModuleContext interface { + EarlyModuleContext + + blueprintBaseModuleContext() blueprint.BaseModuleContext + + // OtherModuleName returns the name of another Module. See BaseModuleContext.ModuleName for more information. + // It is intended for use inside the visit functions of Visit* and WalkDeps. + OtherModuleName(m blueprint.Module) string + + // OtherModuleDir returns the directory of another Module. See BaseModuleContext.ModuleDir for more information. + // It is intended for use inside the visit functions of Visit* and WalkDeps. + OtherModuleDir(m blueprint.Module) string + + // OtherModuleErrorf reports an error on another Module. See BaseModuleContext.ModuleErrorf for more information. + // It is intended for use inside the visit functions of Visit* and WalkDeps. + OtherModuleErrorf(m blueprint.Module, fmt string, args ...interface{}) + + // OtherModuleDependencyTag returns the dependency tag used to depend on a module, or nil if there is no dependency + // on the module. When called inside a Visit* method with current module being visited, and there are multiple + // dependencies on the module being visited, it returns the dependency tag used for the current dependency. + OtherModuleDependencyTag(m blueprint.Module) blueprint.DependencyTag + + // OtherModuleExists returns true if a module with the specified name exists, as determined by the NameInterface + // passed to Context.SetNameInterface, or SimpleNameInterface if it was not called. + OtherModuleExists(name string) bool + + // OtherModuleDependencyVariantExists returns true if a module with the + // specified name and variant exists. The variant must match the given + // variations. It must also match all the non-local variations of the current + // module. In other words, it checks for the module that AddVariationDependencies + // would add a dependency on with the same arguments. + OtherModuleDependencyVariantExists(variations []blueprint.Variation, name string) bool + + // OtherModuleFarDependencyVariantExists returns true if a module with the + // specified name and variant exists. The variant must match the given + // variations, but not the non-local variations of the current module. In + // other words, it checks for the module that AddFarVariationDependencies + // would add a dependency on with the same arguments. + OtherModuleFarDependencyVariantExists(variations []blueprint.Variation, name string) bool + + // OtherModuleReverseDependencyVariantExists returns true if a module with the + // specified name exists with the same variations as the current module. In + // other words, it checks for the module that AddReverseDependency would add a + // dependency on with the same argument. + OtherModuleReverseDependencyVariantExists(name string) bool + + // OtherModuleType returns the type of another Module. See BaseModuleContext.ModuleType for more information. + // It is intended for use inside the visit functions of Visit* and WalkDeps. + OtherModuleType(m blueprint.Module) string + + // OtherModuleProvider returns the value for a provider for the given module. If the value is + // not set it returns the zero value of the type of the provider, so the return value can always + // be type asserted to the type of the provider. The value returned may be a deep copy of the + // value originally passed to SetProvider. + OtherModuleProvider(m blueprint.Module, provider blueprint.ProviderKey) interface{} + + // OtherModuleHasProvider returns true if the provider for the given module has been set. + OtherModuleHasProvider(m blueprint.Module, provider blueprint.ProviderKey) bool + + // Provider returns the value for a provider for the current module. If the value is + // not set it returns the zero value of the type of the provider, so the return value can always + // be type asserted to the type of the provider. It panics if called before the appropriate + // mutator or GenerateBuildActions pass for the provider. The value returned may be a deep + // copy of the value originally passed to SetProvider. + Provider(provider blueprint.ProviderKey) interface{} + + // HasProvider returns true if the provider for the current module has been set. + HasProvider(provider blueprint.ProviderKey) bool + + // SetProvider sets the value for a provider for the current module. It panics if not called + // during the appropriate mutator or GenerateBuildActions pass for the provider, if the value + // is not of the appropriate type, or if the value has already been set. The value should not + // be modified after being passed to SetProvider. + SetProvider(provider blueprint.ProviderKey, value interface{}) + + GetDirectDepsWithTag(tag blueprint.DependencyTag) []Module + + // GetDirectDepWithTag returns the Module the direct dependency with the specified name, or nil if + // none exists. It panics if the dependency does not have the specified tag. It skips any + // dependencies that are not an android.Module. + GetDirectDepWithTag(name string, tag blueprint.DependencyTag) blueprint.Module + + // GetDirectDep returns the Module and DependencyTag for the direct dependency with the specified + // name, or nil if none exists. If there are multiple dependencies on the same module it returns + // the first DependencyTag. + GetDirectDep(name string) (blueprint.Module, blueprint.DependencyTag) + + // VisitDirectDepsBlueprint calls visit for each direct dependency. If there are multiple + // direct dependencies on the same module visit will be called multiple times on that module + // and OtherModuleDependencyTag will return a different tag for each. + // + // The Module passed to the visit function should not be retained outside of the visit + // function, it may be invalidated by future mutators. + VisitDirectDepsBlueprint(visit func(blueprint.Module)) + + // VisitDirectDeps calls visit for each direct dependency. If there are multiple + // direct dependencies on the same module visit will be called multiple times on that module + // and OtherModuleDependencyTag will return a different tag for each. It raises an error if any of the + // dependencies are not an android.Module. + // + // The Module passed to the visit function should not be retained outside of the visit + // function, it may be invalidated by future mutators. + VisitDirectDeps(visit func(Module)) + + VisitDirectDepsWithTag(tag blueprint.DependencyTag, visit func(Module)) + + // VisitDirectDepsIf calls pred for each direct dependency, and if pred returns true calls visit. If there are + // multiple direct dependencies on the same module pred and visit will be called multiple times on that module and + // OtherModuleDependencyTag will return a different tag for each. It skips any + // dependencies that are not an android.Module. + // + // The Module passed to the visit function should not be retained outside of the visit function, it may be + // invalidated by future mutators. + VisitDirectDepsIf(pred func(Module) bool, visit func(Module)) + // Deprecated: use WalkDeps instead to support multiple dependency tags on the same module + VisitDepsDepthFirst(visit func(Module)) + // Deprecated: use WalkDeps instead to support multiple dependency tags on the same module + VisitDepsDepthFirstIf(pred func(Module) bool, visit func(Module)) + + // WalkDeps calls visit for each transitive dependency, traversing the dependency tree in top down order. visit may + // be called multiple times for the same (child, parent) pair if there are multiple direct dependencies between the + // child and parent with different tags. OtherModuleDependencyTag will return the tag for the currently visited + // (child, parent) pair. If visit returns false WalkDeps will not continue recursing down to child. It skips + // any dependencies that are not an android.Module. + // + // The Modules passed to the visit function should not be retained outside of the visit function, they may be + // invalidated by future mutators. + WalkDeps(visit func(child, parent Module) bool) + + // WalkDepsBlueprint calls visit for each transitive dependency, traversing the dependency + // tree in top down order. visit may be called multiple times for the same (child, parent) + // pair if there are multiple direct dependencies between the child and parent with different + // tags. OtherModuleDependencyTag will return the tag for the currently visited + // (child, parent) pair. If visit returns false WalkDeps will not continue recursing down + // to child. + // + // The Modules passed to the visit function should not be retained outside of the visit function, they may be + // invalidated by future mutators. + WalkDepsBlueprint(visit func(blueprint.Module, blueprint.Module) bool) + + // GetWalkPath is supposed to be called in visit function passed in WalkDeps() + // and returns a top-down dependency path from a start module to current child module. + GetWalkPath() []Module + + // PrimaryModule returns the first variant of the current module. Variants of a module are always visited in + // order by mutators and GenerateBuildActions, so the data created by the current mutator can be read from the + // Module returned by PrimaryModule without data races. This can be used to perform singleton actions that are + // only done once for all variants of a module. + PrimaryModule() Module + + // FinalModule returns the last variant of the current module. Variants of a module are always visited in + // order by mutators and GenerateBuildActions, so the data created by the current mutator can be read from all + // variants using VisitAllModuleVariants if the current module == FinalModule(). This can be used to perform + // singleton actions that are only done once for all variants of a module. + FinalModule() Module + + // VisitAllModuleVariants calls visit for each variant of the current module. Variants of a module are always + // visited in order by mutators and GenerateBuildActions, so the data created by the current mutator can be read + // from all variants if the current module == FinalModule(). Otherwise, care must be taken to not access any + // data modified by the current mutator. + VisitAllModuleVariants(visit func(Module)) + + // GetTagPath is supposed to be called in visit function passed in WalkDeps() + // and returns a top-down dependency tags path from a start module to current child module. + // It has one less entry than GetWalkPath() as it contains the dependency tags that + // exist between each adjacent pair of modules in the GetWalkPath(). + // GetTagPath()[i] is the tag between GetWalkPath()[i] and GetWalkPath()[i+1] + GetTagPath() []blueprint.DependencyTag + + // GetPathString is supposed to be called in visit function passed in WalkDeps() + // and returns a multi-line string showing the modules and dependency tags + // among them along the top-down dependency path from a start module to current child module. + // skipFirst when set to true, the output doesn't include the start module, + // which is already printed when this function is used along with ModuleErrorf(). + GetPathString(skipFirst bool) string + + AddMissingDependencies(missingDeps []string) + + // getMissingDependencies returns the list of missing dependencies. + // Calling this function prevents adding new dependencies. + getMissingDependencies() []string + + Target() Target + TargetPrimary() bool + + // The additional arch specific targets (e.g. 32/64 bit) that this module variant is + // responsible for creating. + MultiTargets() []Target + Arch() Arch + Os() OsType + Host() bool + Device() bool + Darwin() bool + Windows() bool + PrimaryArch() bool +} + +type baseModuleContext struct { + bp blueprint.BaseModuleContext + earlyModuleContext + os OsType + target Target + multiTargets []Target + targetPrimary bool + + walkPath []Module + tagPath []blueprint.DependencyTag + + strictVisitDeps bool // If true, enforce that all dependencies are enabled + +} + +func (b *baseModuleContext) OtherModuleName(m blueprint.Module) string { + return b.bp.OtherModuleName(m) +} +func (b *baseModuleContext) OtherModuleDir(m blueprint.Module) string { return b.bp.OtherModuleDir(m) } +func (b *baseModuleContext) OtherModuleErrorf(m blueprint.Module, fmt string, args ...interface{}) { + b.bp.OtherModuleErrorf(m, fmt, args...) +} +func (b *baseModuleContext) OtherModuleDependencyTag(m blueprint.Module) blueprint.DependencyTag { + return b.bp.OtherModuleDependencyTag(m) +} +func (b *baseModuleContext) OtherModuleExists(name string) bool { return b.bp.OtherModuleExists(name) } +func (b *baseModuleContext) OtherModuleDependencyVariantExists(variations []blueprint.Variation, name string) bool { + return b.bp.OtherModuleDependencyVariantExists(variations, name) +} +func (b *baseModuleContext) OtherModuleFarDependencyVariantExists(variations []blueprint.Variation, name string) bool { + return b.bp.OtherModuleFarDependencyVariantExists(variations, name) +} +func (b *baseModuleContext) OtherModuleReverseDependencyVariantExists(name string) bool { + return b.bp.OtherModuleReverseDependencyVariantExists(name) +} +func (b *baseModuleContext) OtherModuleType(m blueprint.Module) string { + return b.bp.OtherModuleType(m) +} +func (b *baseModuleContext) OtherModuleProvider(m blueprint.Module, provider blueprint.ProviderKey) interface{} { + return b.bp.OtherModuleProvider(m, provider) +} +func (b *baseModuleContext) OtherModuleHasProvider(m blueprint.Module, provider blueprint.ProviderKey) bool { + return b.bp.OtherModuleHasProvider(m, provider) +} +func (b *baseModuleContext) Provider(provider blueprint.ProviderKey) interface{} { + return b.bp.Provider(provider) +} +func (b *baseModuleContext) HasProvider(provider blueprint.ProviderKey) bool { + return b.bp.HasProvider(provider) +} +func (b *baseModuleContext) SetProvider(provider blueprint.ProviderKey, value interface{}) { + b.bp.SetProvider(provider, value) +} + +func (b *baseModuleContext) GetDirectDepWithTag(name string, tag blueprint.DependencyTag) blueprint.Module { + return b.bp.GetDirectDepWithTag(name, tag) +} + +func (b *baseModuleContext) blueprintBaseModuleContext() blueprint.BaseModuleContext { + return b.bp +} + +func (b *baseModuleContext) AddMissingDependencies(deps []string) { + if deps != nil { + missingDeps := &b.Module().base().commonProperties.MissingDeps + *missingDeps = append(*missingDeps, deps...) + *missingDeps = FirstUniqueStrings(*missingDeps) + } +} + +func (b *baseModuleContext) checkedMissingDeps() bool { + return b.Module().base().commonProperties.CheckedMissingDeps +} + +func (b *baseModuleContext) getMissingDependencies() []string { + checked := &b.Module().base().commonProperties.CheckedMissingDeps + *checked = true + var missingDeps []string + missingDeps = append(missingDeps, b.Module().base().commonProperties.MissingDeps...) + missingDeps = append(missingDeps, b.bp.EarlyGetMissingDependencies()...) + missingDeps = FirstUniqueStrings(missingDeps) + return missingDeps +} + +type AllowDisabledModuleDependency interface { + blueprint.DependencyTag + AllowDisabledModuleDependency(target Module) bool +} + +func (b *baseModuleContext) validateAndroidModule(module blueprint.Module, tag blueprint.DependencyTag, strict bool) Module { + aModule, _ := module.(Module) + + if !strict { + return aModule + } + + if aModule == nil { + b.ModuleErrorf("module %q (%#v) not an android module", b.OtherModuleName(module), tag) + return nil + } + + if !aModule.Enabled() { + if t, ok := tag.(AllowDisabledModuleDependency); !ok || !t.AllowDisabledModuleDependency(aModule) { + if b.Config().AllowMissingDependencies() { + b.AddMissingDependencies([]string{b.OtherModuleName(aModule)}) + } else { + b.ModuleErrorf("depends on disabled module %q", b.OtherModuleName(aModule)) + } + } + return nil + } + return aModule +} + +type dep struct { + mod blueprint.Module + tag blueprint.DependencyTag +} + +func (b *baseModuleContext) getDirectDepsInternal(name string, tag blueprint.DependencyTag) []dep { + var deps []dep + b.VisitDirectDepsBlueprint(func(module blueprint.Module) { + if aModule, _ := module.(Module); aModule != nil { + if aModule.base().BaseModuleName() == name { + returnedTag := b.bp.OtherModuleDependencyTag(aModule) + if tag == nil || returnedTag == tag { + deps = append(deps, dep{aModule, returnedTag}) + } + } + } else if b.bp.OtherModuleName(module) == name { + returnedTag := b.bp.OtherModuleDependencyTag(module) + if tag == nil || returnedTag == tag { + deps = append(deps, dep{module, returnedTag}) + } + } + }) + return deps +} + +func (b *baseModuleContext) getDirectDepInternal(name string, tag blueprint.DependencyTag) (blueprint.Module, blueprint.DependencyTag) { + deps := b.getDirectDepsInternal(name, tag) + if len(deps) == 1 { + return deps[0].mod, deps[0].tag + } else if len(deps) >= 2 { + panic(fmt.Errorf("Multiple dependencies having same BaseModuleName() %q found from %q", + name, b.ModuleName())) + } else { + return nil, nil + } +} + +func (b *baseModuleContext) getDirectDepFirstTag(name string) (blueprint.Module, blueprint.DependencyTag) { + foundDeps := b.getDirectDepsInternal(name, nil) + deps := map[blueprint.Module]bool{} + for _, dep := range foundDeps { + deps[dep.mod] = true + } + if len(deps) == 1 { + return foundDeps[0].mod, foundDeps[0].tag + } else if len(deps) >= 2 { + // this could happen if two dependencies have the same name in different namespaces + // TODO(b/186554727): this should not occur if namespaces are handled within + // getDirectDepsInternal. + panic(fmt.Errorf("Multiple dependencies having same BaseModuleName() %q found from %q", + name, b.ModuleName())) + } else { + return nil, nil + } +} + +func (b *baseModuleContext) GetDirectDepsWithTag(tag blueprint.DependencyTag) []Module { + var deps []Module + b.VisitDirectDepsBlueprint(func(module blueprint.Module) { + if aModule, _ := module.(Module); aModule != nil { + if b.bp.OtherModuleDependencyTag(aModule) == tag { + deps = append(deps, aModule) + } + } + }) + return deps +} + +// GetDirectDep returns the Module and DependencyTag for the direct dependency with the specified +// name, or nil if none exists. If there are multiple dependencies on the same module it returns the +// first DependencyTag. +func (b *baseModuleContext) GetDirectDep(name string) (blueprint.Module, blueprint.DependencyTag) { + return b.getDirectDepFirstTag(name) +} + +func (b *baseModuleContext) VisitDirectDepsBlueprint(visit func(blueprint.Module)) { + b.bp.VisitDirectDeps(visit) +} + +func (b *baseModuleContext) VisitDirectDeps(visit func(Module)) { + b.bp.VisitDirectDeps(func(module blueprint.Module) { + if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil { + visit(aModule) + } + }) +} + +func (b *baseModuleContext) VisitDirectDepsWithTag(tag blueprint.DependencyTag, visit func(Module)) { + b.bp.VisitDirectDeps(func(module blueprint.Module) { + if b.bp.OtherModuleDependencyTag(module) == tag { + if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil { + visit(aModule) + } + } + }) +} + +func (b *baseModuleContext) VisitDirectDepsIf(pred func(Module) bool, visit func(Module)) { + b.bp.VisitDirectDepsIf( + // pred + func(module blueprint.Module) bool { + if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil { + return pred(aModule) + } else { + return false + } + }, + // visit + func(module blueprint.Module) { + visit(module.(Module)) + }) +} + +func (b *baseModuleContext) VisitDepsDepthFirst(visit func(Module)) { + b.bp.VisitDepsDepthFirst(func(module blueprint.Module) { + if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil { + visit(aModule) + } + }) +} + +func (b *baseModuleContext) VisitDepsDepthFirstIf(pred func(Module) bool, visit func(Module)) { + b.bp.VisitDepsDepthFirstIf( + // pred + func(module blueprint.Module) bool { + if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil { + return pred(aModule) + } else { + return false + } + }, + // visit + func(module blueprint.Module) { + visit(module.(Module)) + }) +} + +func (b *baseModuleContext) WalkDepsBlueprint(visit func(blueprint.Module, blueprint.Module) bool) { + b.bp.WalkDeps(visit) +} + +func (b *baseModuleContext) WalkDeps(visit func(Module, Module) bool) { + b.walkPath = []Module{b.Module()} + b.tagPath = []blueprint.DependencyTag{} + b.bp.WalkDeps(func(child, parent blueprint.Module) bool { + childAndroidModule, _ := child.(Module) + parentAndroidModule, _ := parent.(Module) + if childAndroidModule != nil && parentAndroidModule != nil { + // record walkPath before visit + for b.walkPath[len(b.walkPath)-1] != parentAndroidModule { + b.walkPath = b.walkPath[0 : len(b.walkPath)-1] + b.tagPath = b.tagPath[0 : len(b.tagPath)-1] + } + b.walkPath = append(b.walkPath, childAndroidModule) + b.tagPath = append(b.tagPath, b.OtherModuleDependencyTag(childAndroidModule)) + return visit(childAndroidModule, parentAndroidModule) + } else { + return false + } + }) +} + +func (b *baseModuleContext) GetWalkPath() []Module { + return b.walkPath +} + +func (b *baseModuleContext) GetTagPath() []blueprint.DependencyTag { + return b.tagPath +} + +func (b *baseModuleContext) VisitAllModuleVariants(visit func(Module)) { + b.bp.VisitAllModuleVariants(func(module blueprint.Module) { + visit(module.(Module)) + }) +} + +func (b *baseModuleContext) PrimaryModule() Module { + return b.bp.PrimaryModule().(Module) +} + +func (b *baseModuleContext) FinalModule() Module { + return b.bp.FinalModule().(Module) +} + +// IsMetaDependencyTag returns true for cross-cutting metadata dependencies. +func IsMetaDependencyTag(tag blueprint.DependencyTag) bool { + if tag == licenseKindTag { + return true + } else if tag == licensesTag { + return true + } else if tag == acDepTag { + return true + } + return false +} + +// A regexp for removing boilerplate from BaseDependencyTag from the string representation of +// a dependency tag. +var tagCleaner = regexp.MustCompile(`\QBaseDependencyTag:{}\E(, )?`) + +// PrettyPrintTag returns string representation of the tag, but prefers +// custom String() method if available. +func PrettyPrintTag(tag blueprint.DependencyTag) string { + // Use tag's custom String() method if available. + if stringer, ok := tag.(fmt.Stringer); ok { + return stringer.String() + } + + // Otherwise, get a default string representation of the tag's struct. + tagString := fmt.Sprintf("%T: %+v", tag, tag) + + // Remove the boilerplate from BaseDependencyTag as it adds no value. + tagString = tagCleaner.ReplaceAllString(tagString, "") + return tagString +} + +func (b *baseModuleContext) GetPathString(skipFirst bool) string { + sb := strings.Builder{} + tagPath := b.GetTagPath() + walkPath := b.GetWalkPath() + if !skipFirst { + sb.WriteString(walkPath[0].String()) + } + for i, m := range walkPath[1:] { + sb.WriteString("\n") + sb.WriteString(fmt.Sprintf(" via tag %s\n", PrettyPrintTag(tagPath[i]))) + sb.WriteString(fmt.Sprintf(" -> %s", m.String())) + } + return sb.String() +} + +func (b *baseModuleContext) Target() Target { + return b.target +} + +func (b *baseModuleContext) TargetPrimary() bool { + return b.targetPrimary +} + +func (b *baseModuleContext) MultiTargets() []Target { + return b.multiTargets +} + +func (b *baseModuleContext) Arch() Arch { + return b.target.Arch +} + +func (b *baseModuleContext) Os() OsType { + return b.os +} + +func (b *baseModuleContext) Host() bool { + return b.os.Class == Host +} + +func (b *baseModuleContext) Device() bool { + return b.os.Class == Device +} + +func (b *baseModuleContext) Darwin() bool { + return b.os == Darwin +} + +func (b *baseModuleContext) Windows() bool { + return b.os == Windows +} + +func (b *baseModuleContext) PrimaryArch() bool { + if len(b.config.Targets[b.target.Os]) <= 1 { + return true + } + return b.target.Arch.ArchType == b.config.Targets[b.target.Os][0].Arch.ArchType +} diff --git a/android/bazel.go b/android/bazel.go deleted file mode 100644 index 1646883965..0000000000 --- a/android/bazel.go +++ /dev/null @@ -1,545 +0,0 @@ -// 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 ( - "bufio" - "errors" - "strings" - - "github.com/google/blueprint" - "github.com/google/blueprint/proptools" - - "android/soong/android/allowlists" -) - -const ( - // A sentinel value to be used as a key in Bp2BuildConfig for modules with - // no package path. This is also the module dir for top level Android.bp - // modules. - Bp2BuildTopLevel = "." -) - -// FileGroupAsLibrary describes a filegroup module that is converted to some library -// such as aidl_library or proto_library. -type FileGroupAsLibrary interface { - ShouldConvertToAidlLibrary(ctx BazelConversionPathContext) bool - ShouldConvertToProtoLibrary(ctx BazelConversionPathContext) bool - GetAidlLibraryLabel(ctx BazelConversionPathContext) string - GetProtoLibraryLabel(ctx BazelConversionPathContext) string -} - -type BazelConversionStatus struct { - // Information about _all_ bp2build targets generated by this module. Multiple targets are - // supported as Soong handles some things within a single target that we may choose to split into - // multiple targets, e.g. renderscript, protos, yacc within a cc module. - Bp2buildInfo []bp2buildInfo `blueprint:"mutated"` - - // UnconvertedBp2buildDep stores the module names of direct dependency that were not converted to - // Bazel - UnconvertedDeps []string `blueprint:"mutated"` - - // MissingBp2buildDep stores the module names of direct dependency that were not found - MissingDeps []string `blueprint:"mutated"` -} - -type bazelModuleProperties struct { - // The label of the Bazel target replacing this Soong module. When run in conversion mode, this - // will import the handcrafted build target into the autogenerated file. Note: this may result in - // a conflict due to duplicate targets if bp2build_available is also set. - Label *string - - // If true, bp2build will generate the converted Bazel target for this module. Note: this may - // cause a conflict due to the duplicate targets if label is also set. - // - // This is a bool pointer to support tristates: true, false, not set. - // - // To opt in a module, set bazel_module: { bp2build_available: true } - // To opt out a module, set bazel_module: { bp2build_available: false } - // To defer the default setting for the directory, do not set the value. - Bp2build_available *bool - - // CanConvertToBazel is set via InitBazelModule to indicate that a module type can be converted to - // Bazel with Bp2build. - CanConvertToBazel bool `blueprint:"mutated"` -} - -// Properties contains common module properties for Bazel migration purposes. -type properties struct { - // In "Bazel mixed build" mode, this represents the Bazel target replacing - // this Soong module. - Bazel_module bazelModuleProperties -} - -// namespacedVariableProperties is a map from a string representing a Soong -// config variable namespace, like "android" or "vendor_name" to a slice of -// pointer to a struct containing a single field called Soong_config_variables -// whose value mirrors the structure in the Blueprint file. -type namespacedVariableProperties map[string][]interface{} - -// BazelModuleBase contains the property structs with metadata for modules which can be converted to -// Bazel. -type BazelModuleBase struct { - bazelProperties properties - - // namespacedVariableProperties is used for soong_config_module_type support - // in bp2build. Soong config modules allow users to set module properties - // based on custom product variables defined in Android.bp files. These - // variables are namespaced to prevent clobbering, especially when set from - // Makefiles. - namespacedVariableProperties namespacedVariableProperties - - // baseModuleType is set when this module was created from a module type - // defined by a soong_config_module_type. Every soong_config_module_type - // "wraps" another module type, e.g. a soong_config_module_type can wrap a - // cc_defaults to a custom_cc_defaults, or cc_binary to a custom_cc_binary. - // This baseModuleType is set to the wrapped module type. - baseModuleType string -} - -// Bazelable is specifies the interface for modules that can be converted to Bazel. -type Bazelable interface { - bazelProps() *properties - HasHandcraftedLabel() bool - HandcraftedLabel() string - GetBazelLabel(ctx BazelConversionPathContext, module blueprint.Module) string - ShouldConvertWithBp2build(ctx BazelConversionContext) bool - shouldConvertWithBp2build(ctx bazelOtherModuleContext, module blueprint.Module) bool - ConvertWithBp2build(ctx TopDownMutatorContext) - - // namespacedVariableProps is a map from a soong config variable namespace - // (e.g. acme, android) to a map of interfaces{}, which are really - // reflect.Struct pointers, representing the value of the - // soong_config_variables property of a module. The struct pointer is the - // one with the single member called Soong_config_variables, which itself is - // a struct containing fields for each supported feature in that namespace. - // - // The reason for using a slice of interface{} is to support defaults - // propagation of the struct pointers. - namespacedVariableProps() namespacedVariableProperties - setNamespacedVariableProps(props namespacedVariableProperties) - BaseModuleType() string - SetBaseModuleType(baseModuleType string) -} - -// ApiProvider is implemented by modules that contribute to an API surface -type ApiProvider interface { - ConvertWithApiBp2build(ctx TopDownMutatorContext) -} - -// MixedBuildBuildable is an interface that module types should implement in order -// to be "handled by Bazel" in a mixed build. -type MixedBuildBuildable interface { - // IsMixedBuildSupported returns true if and only if this module should be - // "handled by Bazel" in a mixed build. - // This "escape hatch" allows modules with corner-case scenarios to opt out - // of being built with Bazel. - IsMixedBuildSupported(ctx BaseModuleContext) bool - - // QueueBazelCall invokes request-queueing functions on the BazelContext - // so that these requests are handled when Bazel's cquery is invoked. - QueueBazelCall(ctx BaseModuleContext) - - // ProcessBazelQueryResponse uses Bazel information (obtained from the BazelContext) - // to set module fields and providers to propagate this module's metadata upstream. - // This effectively "bridges the gap" between Bazel and Soong in a mixed build. - // Soong modules depending on this module should be oblivious to the fact that - // this module was handled by Bazel. - ProcessBazelQueryResponse(ctx ModuleContext) -} - -// BazelModule is a lightweight wrapper interface around Module for Bazel-convertible modules. -type BazelModule interface { - Module - Bazelable -} - -// InitBazelModule is a wrapper function that decorates a BazelModule with Bazel-conversion -// properties. -func InitBazelModule(module BazelModule) { - module.AddProperties(module.bazelProps()) - module.bazelProps().Bazel_module.CanConvertToBazel = true -} - -// bazelProps returns the Bazel properties for the given BazelModuleBase. -func (b *BazelModuleBase) bazelProps() *properties { - return &b.bazelProperties -} - -func (b *BazelModuleBase) namespacedVariableProps() namespacedVariableProperties { - return b.namespacedVariableProperties -} - -func (b *BazelModuleBase) setNamespacedVariableProps(props namespacedVariableProperties) { - b.namespacedVariableProperties = props -} - -func (b *BazelModuleBase) BaseModuleType() string { - return b.baseModuleType -} - -func (b *BazelModuleBase) SetBaseModuleType(baseModuleType string) { - b.baseModuleType = baseModuleType -} - -// HasHandcraftedLabel returns whether this module has a handcrafted Bazel label. -func (b *BazelModuleBase) HasHandcraftedLabel() bool { - return b.bazelProperties.Bazel_module.Label != nil -} - -// HandcraftedLabel returns the handcrafted label for this module, or empty string if there is none -func (b *BazelModuleBase) HandcraftedLabel() string { - return proptools.String(b.bazelProperties.Bazel_module.Label) -} - -// GetBazelLabel returns the Bazel label for the given BazelModuleBase. -func (b *BazelModuleBase) GetBazelLabel(ctx BazelConversionPathContext, module blueprint.Module) string { - if b.HasHandcraftedLabel() { - return b.HandcraftedLabel() - } - if b.ShouldConvertWithBp2build(ctx) { - return bp2buildModuleLabel(ctx, module) - } - return "" // no label for unconverted module -} - -type Bp2BuildConversionAllowlist struct { - // Configure modules in these directories to enable bp2build_available: true or false by default. - defaultConfig allowlists.Bp2BuildConfig - - // Keep any existing BUILD files (and do not generate new BUILD files) for these directories - // in the synthetic Bazel workspace. - keepExistingBuildFile map[string]bool - - // Per-module allowlist to always opt modules into both bp2build and Bazel Dev Mode mixed - // builds. These modules are usually in directories with many other modules that are not ready - // for conversion. - // - // A module can either be in this list or its directory allowlisted entirely - // in bp2buildDefaultConfig, but not both at the same time. - moduleAlwaysConvert map[string]bool - - // Per-module-type allowlist to always opt modules in to both bp2build and - // Bazel Dev Mode mixed builds when they have the same type as one listed. - moduleTypeAlwaysConvert map[string]bool - - // Per-module denylist to always opt modules out of bp2build conversion. - moduleDoNotConvert map[string]bool -} - -// NewBp2BuildAllowlist creates a new, empty Bp2BuildConversionAllowlist -// which can be populated using builder pattern Set* methods -func NewBp2BuildAllowlist() Bp2BuildConversionAllowlist { - return Bp2BuildConversionAllowlist{ - allowlists.Bp2BuildConfig{}, - map[string]bool{}, - map[string]bool{}, - map[string]bool{}, - map[string]bool{}, - } -} - -// SetDefaultConfig copies the entries from defaultConfig into the allowlist -func (a Bp2BuildConversionAllowlist) SetDefaultConfig(defaultConfig allowlists.Bp2BuildConfig) Bp2BuildConversionAllowlist { - if a.defaultConfig == nil { - a.defaultConfig = allowlists.Bp2BuildConfig{} - } - for k, v := range defaultConfig { - a.defaultConfig[k] = v - } - - return a -} - -// SetKeepExistingBuildFile copies the entries from keepExistingBuildFile into the allowlist -func (a Bp2BuildConversionAllowlist) SetKeepExistingBuildFile(keepExistingBuildFile map[string]bool) Bp2BuildConversionAllowlist { - if a.keepExistingBuildFile == nil { - a.keepExistingBuildFile = map[string]bool{} - } - for k, v := range keepExistingBuildFile { - a.keepExistingBuildFile[k] = v - } - - return a -} - -// SetModuleAlwaysConvertList copies the entries from moduleAlwaysConvert into the allowlist -func (a Bp2BuildConversionAllowlist) SetModuleAlwaysConvertList(moduleAlwaysConvert []string) Bp2BuildConversionAllowlist { - if a.moduleAlwaysConvert == nil { - a.moduleAlwaysConvert = map[string]bool{} - } - for _, m := range moduleAlwaysConvert { - a.moduleAlwaysConvert[m] = true - } - - return a -} - -// SetModuleTypeAlwaysConvertList copies the entries from moduleTypeAlwaysConvert into the allowlist -func (a Bp2BuildConversionAllowlist) SetModuleTypeAlwaysConvertList(moduleTypeAlwaysConvert []string) Bp2BuildConversionAllowlist { - if a.moduleTypeAlwaysConvert == nil { - a.moduleTypeAlwaysConvert = map[string]bool{} - } - for _, m := range moduleTypeAlwaysConvert { - a.moduleTypeAlwaysConvert[m] = true - } - - return a -} - -// SetModuleDoNotConvertList copies the entries from moduleDoNotConvert into the allowlist -func (a Bp2BuildConversionAllowlist) SetModuleDoNotConvertList(moduleDoNotConvert []string) Bp2BuildConversionAllowlist { - if a.moduleDoNotConvert == nil { - a.moduleDoNotConvert = map[string]bool{} - } - for _, m := range moduleDoNotConvert { - a.moduleDoNotConvert[m] = true - } - - return a -} - -// ShouldKeepExistingBuildFileForDir returns whether an existing BUILD file should be -// added to the build symlink forest based on the current global configuration. -func (a Bp2BuildConversionAllowlist) ShouldKeepExistingBuildFileForDir(dir string) bool { - if _, ok := a.keepExistingBuildFile[dir]; ok { - // Exact dir match - return true - } - var i int - // Check if subtree match - for { - j := strings.Index(dir[i:], "/") - if j == -1 { - return false //default - } - prefix := dir[0 : i+j] - i = i + j + 1 // skip the "/" - if recursive, ok := a.keepExistingBuildFile[prefix]; ok && recursive { - return true - } - } -} - -var bp2BuildAllowListKey = NewOnceKey("Bp2BuildAllowlist") -var bp2buildAllowlist OncePer - -func GetBp2BuildAllowList() Bp2BuildConversionAllowlist { - return bp2buildAllowlist.Once(bp2BuildAllowListKey, func() interface{} { - return NewBp2BuildAllowlist().SetDefaultConfig(allowlists.Bp2buildDefaultConfig). - SetKeepExistingBuildFile(allowlists.Bp2buildKeepExistingBuildFile). - SetModuleAlwaysConvertList(allowlists.Bp2buildModuleAlwaysConvertList). - SetModuleTypeAlwaysConvertList(allowlists.Bp2buildModuleTypeAlwaysConvertList). - SetModuleDoNotConvertList(allowlists.Bp2buildModuleDoNotConvertList) - }).(Bp2BuildConversionAllowlist) -} - -// MixedBuildsEnabled returns true if a module is ready to be replaced by a -// converted or handcrafted Bazel target. As a side effect, calling this -// method will also log whether this module is mixed build enabled for -// metrics reporting. -func MixedBuildsEnabled(ctx BaseModuleContext) bool { - module := ctx.Module() - apexInfo := ctx.Provider(ApexInfoProvider).(ApexInfo) - withinApex := !apexInfo.IsForPlatform() - mixedBuildEnabled := ctx.Config().IsMixedBuildsEnabled() && - ctx.Os() != Windows && // Windows toolchains are not currently supported. - ctx.Os() != LinuxBionic && // Linux Bionic toolchains are not currently supported. - ctx.Os() != LinuxMusl && // Linux musl toolchains are not currently supported (b/259266326). - ctx.Arch().ArchType != Riscv64 && // TODO(b/262192655) Riscv64 toolchains are not currently supported. - module.Enabled() && - convertedToBazel(ctx, module) && - ctx.Config().BazelContext.IsModuleNameAllowed(module.Name(), withinApex) - ctx.Config().LogMixedBuild(ctx, mixedBuildEnabled) - return mixedBuildEnabled -} - -// ConvertedToBazel returns whether this module has been converted (with bp2build or manually) to Bazel. -func convertedToBazel(ctx BazelConversionContext, module blueprint.Module) bool { - b, ok := module.(Bazelable) - if !ok { - return false - } - return b.shouldConvertWithBp2build(ctx, module) || b.HasHandcraftedLabel() -} - -// ShouldConvertWithBp2build returns whether the given BazelModuleBase should be converted with bp2build -func (b *BazelModuleBase) ShouldConvertWithBp2build(ctx BazelConversionContext) bool { - return b.shouldConvertWithBp2build(ctx, ctx.Module()) -} - -type bazelOtherModuleContext interface { - ModuleErrorf(format string, args ...interface{}) - Config() Config - OtherModuleType(m blueprint.Module) string - OtherModuleName(m blueprint.Module) string - OtherModuleDir(m blueprint.Module) string -} - -func (b *BazelModuleBase) shouldConvertWithBp2build(ctx bazelOtherModuleContext, module blueprint.Module) bool { - if !b.bazelProps().Bazel_module.CanConvertToBazel { - return false - } - - // In api_bp2build mode, all soong modules that can provide API contributions should be converted - // This is irrespective of its presence/absence in bp2build allowlists - if ctx.Config().BuildMode == ApiBp2build { - _, providesApis := module.(ApiProvider) - return providesApis - } - - propValue := b.bazelProperties.Bazel_module.Bp2build_available - packagePath := ctx.OtherModuleDir(module) - - // Modules in unit tests which are enabled in the allowlist by type or name - // trigger this conditional because unit tests run under the "." package path - isTestModule := packagePath == Bp2BuildTopLevel && proptools.BoolDefault(propValue, false) - if isTestModule { - return true - } - - moduleName := module.Name() - allowlist := ctx.Config().Bp2buildPackageConfig - moduleNameAllowed := allowlist.moduleAlwaysConvert[moduleName] - moduleTypeAllowed := allowlist.moduleTypeAlwaysConvert[ctx.OtherModuleType(module)] - allowlistConvert := moduleNameAllowed || moduleTypeAllowed - if moduleNameAllowed && moduleTypeAllowed { - ctx.ModuleErrorf("A module cannot be in moduleAlwaysConvert and also be in moduleTypeAlwaysConvert") - return false - } - - if allowlist.moduleDoNotConvert[moduleName] { - if moduleNameAllowed { - ctx.ModuleErrorf("a module cannot be in moduleDoNotConvert and also be in moduleAlwaysConvert") - } - return false - } - - // This is a tristate value: true, false, or unset. - if ok, directoryPath := bp2buildDefaultTrueRecursively(packagePath, allowlist.defaultConfig); ok { - if moduleNameAllowed { - ctx.ModuleErrorf("A module cannot be in a directory marked Bp2BuildDefaultTrue"+ - " or Bp2BuildDefaultTrueRecursively and also be in moduleAlwaysConvert. Directory: '%s'"+ - " Module: '%s'", directoryPath, moduleName) - return false - } - - // Allow modules to explicitly opt-out. - return proptools.BoolDefault(propValue, true) - } - - // Allow modules to explicitly opt-in. - return proptools.BoolDefault(propValue, allowlistConvert) -} - -// bp2buildDefaultTrueRecursively checks that the package contains a prefix from the -// set of package prefixes where all modules must be converted. That is, if the -// package is x/y/z, and the list contains either x, x/y, or x/y/z, this function will -// return true. -// -// However, if the package is x/y, and it matches a Bp2BuildDefaultFalse "x/y" entry -// exactly, this module will return false early. -// -// This function will also return false if the package doesn't match anything in -// the config. -// -// This function will also return the allowlist entry which caused a particular -// package to be enabled. Since packages can be enabled via a recursive declaration, -// the path returned will not always be the same as the one provided. -func bp2buildDefaultTrueRecursively(packagePath string, config allowlists.Bp2BuildConfig) (bool, string) { - // Check if the package path has an exact match in the config. - if config[packagePath] == allowlists.Bp2BuildDefaultTrue || config[packagePath] == allowlists.Bp2BuildDefaultTrueRecursively { - return true, packagePath - } else if config[packagePath] == allowlists.Bp2BuildDefaultFalse || config[packagePath] == allowlists.Bp2BuildDefaultFalseRecursively { - return false, packagePath - } - - // If not, check for the config recursively. - packagePrefix := packagePath - - // e.g. for x/y/z, iterate over x/y, then x, taking the most-specific value from the allowlist. - for strings.Contains(packagePrefix, "/") { - dirIndex := strings.LastIndex(packagePrefix, "/") - packagePrefix = packagePrefix[:dirIndex] - switch value := config[packagePrefix]; value { - case allowlists.Bp2BuildDefaultTrueRecursively: - // package contains this prefix and this prefix should convert all modules - return true, packagePrefix - case allowlists.Bp2BuildDefaultFalseRecursively: - //package contains this prefix and this prefix should NOT convert any modules - return false, packagePrefix - } - // Continue to the next part of the package dir. - - } - - return false, packagePath -} - -func registerBp2buildConversionMutator(ctx RegisterMutatorsContext) { - ctx.TopDown("bp2build_conversion", convertWithBp2build).Parallel() -} - -func convertWithBp2build(ctx TopDownMutatorContext) { - bModule, ok := ctx.Module().(Bazelable) - if !ok || !bModule.shouldConvertWithBp2build(ctx, ctx.Module()) { - return - } - - bModule.ConvertWithBp2build(ctx) -} - -func registerApiBp2buildConversionMutator(ctx RegisterMutatorsContext) { - ctx.TopDown("apiBp2build_conversion", convertWithApiBp2build).Parallel() -} - -// Generate API contribution targets if the Soong module provides APIs -func convertWithApiBp2build(ctx TopDownMutatorContext) { - if m, ok := ctx.Module().(ApiProvider); ok { - m.ConvertWithApiBp2build(ctx) - } -} - -// GetMainClassInManifest scans the manifest file specified in filepath and returns -// the value of attribute Main-Class in the manifest file if it exists, or returns error. -// WARNING: this is for bp2build converters of java_* modules only. -func GetMainClassInManifest(c Config, filepath string) (string, error) { - file, err := c.fs.Open(filepath) - if err != nil { - return "", err - } - defer file.Close() - scanner := bufio.NewScanner(file) - for scanner.Scan() { - line := scanner.Text() - if strings.HasPrefix(line, "Main-Class:") { - return strings.TrimSpace(line[len("Main-Class:"):]), nil - } - } - - return "", errors.New("Main-Class is not found.") -} - -func AttachValidationActions(ctx ModuleContext, outputFilePath Path, validations Paths) ModuleOutPath { - validatedOutputFilePath := PathForModuleOut(ctx, "validated", outputFilePath.Base()) - ctx.Build(pctx, BuildParams{ - Rule: CpNoPreserveSymlink, - Description: "run validations " + outputFilePath.Base(), - Output: validatedOutputFilePath, - Input: outputFilePath, - Validations: validations, - }) - return validatedOutputFilePath -} diff --git a/android/bazel_handler.go b/android/bazel_handler.go deleted file mode 100644 index 9c273d9a33..0000000000 --- a/android/bazel_handler.go +++ /dev/null @@ -1,1498 +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 android - -import ( - "bytes" - "fmt" - "os" - "os/exec" - "path" - "path/filepath" - "runtime" - "sort" - "strings" - "sync" - - "android/soong/android/allowlists" - "android/soong/bazel/cquery" - "android/soong/shared" - "android/soong/starlark_fmt" - - "github.com/google/blueprint" - "github.com/google/blueprint/metrics" - - "android/soong/bazel" -) - -var ( - _ = pctx.HostBinToolVariable("bazelBuildRunfilesTool", "build-runfiles") - buildRunfilesRule = pctx.AndroidStaticRule("bazelBuildRunfiles", blueprint.RuleParams{ - Command: "${bazelBuildRunfilesTool} ${in} ${outDir}", - Depfile: "", - Description: "", - CommandDeps: []string{"${bazelBuildRunfilesTool}"}, - }, "outDir") - allowedBazelEnvironmentVars = []string{ - // clang-tidy - "ALLOW_LOCAL_TIDY_TRUE", - "DEFAULT_TIDY_HEADER_DIRS", - "TIDY_TIMEOUT", - "WITH_TIDY", - "WITH_TIDY_FLAGS", - "TIDY_EXTERNAL_VENDOR", - - "SKIP_ABI_CHECKS", - "UNSAFE_DISABLE_APEX_ALLOWED_DEPS_CHECK", - "AUTO_ZERO_INITIALIZE", - "AUTO_PATTERN_INITIALIZE", - "AUTO_UNINITIALIZE", - "USE_CCACHE", - "LLVM_NEXT", - "ALLOW_UNKNOWN_WARNING_OPTION", - - "UNBUNDLED_BUILD_TARGET_SDK_WITH_API_FINGERPRINT", - - // Overrides the version in the apex_manifest.json. The version is unique for - // each branch (internal, aosp, mainline releases, dessert releases). This - // enables modules built on an older branch to be installed against a newer - // device for development purposes. - "OVERRIDE_APEX_MANIFEST_DEFAULT_VERSION", - } -) - -func init() { - RegisterMixedBuildsMutator(InitRegistrationContext) -} - -func RegisterMixedBuildsMutator(ctx RegistrationContext) { - ctx.FinalDepsMutators(func(ctx RegisterMutatorsContext) { - ctx.BottomUp("mixed_builds_prep", mixedBuildsPrepareMutator).Parallel() - }) -} - -func mixedBuildsPrepareMutator(ctx BottomUpMutatorContext) { - if m := ctx.Module(); m.Enabled() { - if mixedBuildMod, ok := m.(MixedBuildBuildable); ok { - queueMixedBuild := mixedBuildMod.IsMixedBuildSupported(ctx) && MixedBuildsEnabled(ctx) - if queueMixedBuild { - mixedBuildMod.QueueBazelCall(ctx) - } else if _, ok := ctx.Config().bazelForceEnabledModules[m.Name()]; ok { - // TODO(b/273910287) - remove this once --ensure_allowlist_integrity is added - ctx.ModuleErrorf("Attempted to force enable an unready module: %s. Did you forget to Bp2BuildDefaultTrue its directory?\n", m.Name()) - } - } - } -} - -type cqueryRequest interface { - // Name returns a string name for this request type. Such request type names must be unique, - // and must only consist of alphanumeric characters. - Name() string - - // StarlarkFunctionBody returns a starlark function body to process this request type. - // The returned string is the body of a Starlark function which obtains - // all request-relevant information about a target and returns a string containing - // this information. - // The function should have the following properties: - // - The arguments are `target` (a configured target) and `id_string` (the label + configuration). - // - The return value must be a string. - // - The function body should not be indented outside of its own scope. - StarlarkFunctionBody() string -} - -// Portion of cquery map key to describe target configuration. -type configKey struct { - arch string - osType OsType - apexKey ApexConfigKey -} - -type ApexConfigKey struct { - WithinApex bool - ApexSdkVersion string -} - -func (c ApexConfigKey) String() string { - return fmt.Sprintf("%s_%s", withinApexToString(c.WithinApex), c.ApexSdkVersion) -} - -func withinApexToString(withinApex bool) string { - if withinApex { - return "within_apex" - } - return "" -} - -func (c configKey) String() string { - return fmt.Sprintf("%s::%s::%s", c.arch, c.osType, c.apexKey) -} - -// Map key to describe bazel cquery requests. -type cqueryKey struct { - label string - requestType cqueryRequest - configKey configKey -} - -func makeCqueryKey(label string, cqueryRequest cqueryRequest, cfgKey configKey) cqueryKey { - if strings.HasPrefix(label, "//") { - // Normalize Bazel labels to specify main repository explicitly. - label = "@" + label - } - return cqueryKey{label, cqueryRequest, cfgKey} -} - -func (c cqueryKey) String() string { - return fmt.Sprintf("cquery(%s,%s,%s)", c.label, c.requestType.Name(), c.configKey) -} - -type invokeBazelContext interface { - GetEventHandler() *metrics.EventHandler -} - -// BazelContext is a context object useful for interacting with Bazel during -// the course of a build. Use of Bazel to evaluate part of the build graph -// is referred to as a "mixed build". (Some modules are managed by Soong, -// some are managed by Bazel). To facilitate interop between these build -// subgraphs, Soong may make requests to Bazel and evaluate their responses -// so that Soong modules may accurately depend on Bazel targets. -type BazelContext interface { - // Add a cquery request to the bazel request queue. All queued requests - // will be sent to Bazel on a subsequent invocation of InvokeBazel. - QueueBazelRequest(label string, requestType cqueryRequest, cfgKey configKey) - - // ** Cquery Results Retrieval Functions - // The below functions pertain to retrieving cquery results from a prior - // InvokeBazel function call and parsing the results. - - // Returns result files built by building the given bazel target label. - GetOutputFiles(label string, cfgKey configKey) ([]string, error) - - // Returns the results of GetOutputFiles and GetCcObjectFiles in a single query (in that order). - GetCcInfo(label string, cfgKey configKey) (cquery.CcInfo, error) - - // Returns the executable binary resultant from building together the python sources - // TODO(b/232976601): Remove. - GetPythonBinary(label string, cfgKey configKey) (string, error) - - // Returns the results of the GetApexInfo query (including output files) - GetApexInfo(label string, cfgkey configKey) (cquery.ApexInfo, error) - - // Returns the results of the GetCcUnstrippedInfo query - GetCcUnstrippedInfo(label string, cfgkey configKey) (cquery.CcUnstrippedInfo, error) - - // ** end Cquery Results Retrieval Functions - - // Issues commands to Bazel to receive results for all cquery requests - // queued in the BazelContext. The ctx argument is optional and is only - // used for performance data collection - InvokeBazel(config Config, ctx invokeBazelContext) error - - // Returns true if Bazel handling is enabled for the module with the given name. - // Note that this only implies "bazel mixed build" allowlisting. The caller - // should independently verify the module is eligible for Bazel handling - // (for example, that it is MixedBuildBuildable). - IsModuleNameAllowed(moduleName string, withinApex bool) bool - - IsModuleDclaAllowed(moduleName string) bool - - // Returns the bazel output base (the root directory for all bazel intermediate outputs). - OutputBase() string - - // Returns build statements which should get registered to reflect Bazel's outputs. - BuildStatementsToRegister() []*bazel.BuildStatement - - // Returns the depsets defined in Bazel's aquery response. - AqueryDepsets() []bazel.AqueryDepset -} - -type bazelRunner interface { - createBazelCommand(config Config, paths *bazelPaths, runName bazel.RunName, command bazelCommand, extraFlags ...string) *exec.Cmd - issueBazelCommand(bazelCmd *exec.Cmd, eventHandler *metrics.EventHandler) (output string, errorMessage string, error error) -} - -type bazelPaths struct { - homeDir string - bazelPath string - outputBase string - workspaceDir string - soongOutDir string - metricsDir string - bazelDepsFile string -} - -// A context object which tracks queued requests that need to be made to Bazel, -// and their results after the requests have been made. -type mixedBuildBazelContext struct { - bazelRunner - paths *bazelPaths - // cquery requests that have not yet been issued to Bazel. This list is maintained - // in a sorted state, and is guaranteed to have no duplicates. - requests []cqueryKey - requestMutex sync.Mutex // requests can be written in parallel - - results map[cqueryKey]string // Results of cquery requests after Bazel invocations - - // Build statements which should get registered to reflect Bazel's outputs. - buildStatements []*bazel.BuildStatement - - // Depsets which should be used for Bazel's build statements. - depsets []bazel.AqueryDepset - - // Per-module allowlist/denylist functionality to control whether analysis of - // modules are handled by Bazel. For modules which do not have a Bazel definition - // (or do not sufficiently support bazel handling via MixedBuildBuildable), - // this allowlist will have no effect, even if the module is explicitly allowlisted here. - // Per-module denylist to opt modules out of bazel handling. - bazelDisabledModules map[string]bool - // Per-module allowlist to opt modules in to bazel handling. - bazelEnabledModules map[string]bool - // DCLA modules are enabled when used in apex. - bazelDclaEnabledModules map[string]bool - // If true, modules are bazel-enabled by default, unless present in bazelDisabledModules. - modulesDefaultToBazel bool - - targetProduct string - targetBuildVariant string -} - -var _ BazelContext = &mixedBuildBazelContext{} - -// A bazel context to use when Bazel is disabled. -type noopBazelContext struct{} - -var _ BazelContext = noopBazelContext{} - -// A bazel context to use for tests. -type MockBazelContext struct { - OutputBaseDir string - - LabelToOutputFiles map[string][]string - LabelToCcInfo map[string]cquery.CcInfo - LabelToPythonBinary map[string]string - LabelToApexInfo map[string]cquery.ApexInfo - LabelToCcBinary map[string]cquery.CcUnstrippedInfo - - BazelRequests map[string]bool -} - -func (m MockBazelContext) QueueBazelRequest(label string, requestType cqueryRequest, cfgKey configKey) { - key := BuildMockBazelContextRequestKey(label, requestType, cfgKey.arch, cfgKey.osType, cfgKey.apexKey) - if m.BazelRequests == nil { - m.BazelRequests = make(map[string]bool) - } - m.BazelRequests[key] = true -} - -func (m MockBazelContext) GetOutputFiles(label string, _ configKey) ([]string, error) { - result, ok := m.LabelToOutputFiles[label] - if !ok { - return []string{}, fmt.Errorf("no target with label %q in LabelToOutputFiles", label) - } - return result, nil -} - -func (m MockBazelContext) GetCcInfo(label string, cfgKey configKey) (cquery.CcInfo, error) { - result, ok := m.LabelToCcInfo[label] - if !ok { - key := BuildMockBazelContextResultKey(label, cfgKey.arch, cfgKey.osType, cfgKey.apexKey) - result, ok = m.LabelToCcInfo[key] - if !ok { - return cquery.CcInfo{}, fmt.Errorf("no target with label %q in LabelToCcInfo", label) - } - } - return result, nil -} - -func (m MockBazelContext) GetPythonBinary(label string, _ configKey) (string, error) { - result, ok := m.LabelToPythonBinary[label] - if !ok { - return "", fmt.Errorf("no target with label %q in LabelToPythonBinary", label) - } - return result, nil -} - -func (m MockBazelContext) GetApexInfo(label string, _ configKey) (cquery.ApexInfo, error) { - result, ok := m.LabelToApexInfo[label] - if !ok { - return cquery.ApexInfo{}, fmt.Errorf("no target with label %q in LabelToApexInfo", label) - } - return result, nil -} - -func (m MockBazelContext) GetCcUnstrippedInfo(label string, _ configKey) (cquery.CcUnstrippedInfo, error) { - result, ok := m.LabelToCcBinary[label] - if !ok { - return cquery.CcUnstrippedInfo{}, fmt.Errorf("no target with label %q in LabelToCcBinary", label) - } - return result, nil -} - -func (m MockBazelContext) InvokeBazel(_ Config, _ invokeBazelContext) error { - panic("unimplemented") -} - -func (m MockBazelContext) IsModuleNameAllowed(_ string, _ bool) bool { - return true -} - -func (m MockBazelContext) IsModuleDclaAllowed(_ string) bool { - return true -} - -func (m MockBazelContext) OutputBase() string { return m.OutputBaseDir } - -func (m MockBazelContext) BuildStatementsToRegister() []*bazel.BuildStatement { - return []*bazel.BuildStatement{} -} - -func (m MockBazelContext) AqueryDepsets() []bazel.AqueryDepset { - return []bazel.AqueryDepset{} -} - -var _ BazelContext = MockBazelContext{} - -func BuildMockBazelContextRequestKey(label string, request cqueryRequest, arch string, osType OsType, apexKey ApexConfigKey) string { - cfgKey := configKey{ - arch: arch, - osType: osType, - apexKey: apexKey, - } - - return strings.Join([]string{label, request.Name(), cfgKey.String()}, "_") -} - -func BuildMockBazelContextResultKey(label string, arch string, osType OsType, apexKey ApexConfigKey) string { - cfgKey := configKey{ - arch: arch, - osType: osType, - apexKey: apexKey, - } - - return strings.Join([]string{label, cfgKey.String()}, "_") -} - -func (bazelCtx *mixedBuildBazelContext) QueueBazelRequest(label string, requestType cqueryRequest, cfgKey configKey) { - key := makeCqueryKey(label, requestType, cfgKey) - bazelCtx.requestMutex.Lock() - defer bazelCtx.requestMutex.Unlock() - - // Insert key into requests, maintaining the sort, and only if it's not duplicate. - keyString := key.String() - foundEqual := false - notLessThanKeyString := func(i int) bool { - s := bazelCtx.requests[i].String() - v := strings.Compare(s, keyString) - if v == 0 { - foundEqual = true - } - return v >= 0 - } - targetIndex := sort.Search(len(bazelCtx.requests), notLessThanKeyString) - if foundEqual { - return - } - - if targetIndex == len(bazelCtx.requests) { - bazelCtx.requests = append(bazelCtx.requests, key) - } else { - bazelCtx.requests = append(bazelCtx.requests[:targetIndex+1], bazelCtx.requests[targetIndex:]...) - bazelCtx.requests[targetIndex] = key - } -} - -func (bazelCtx *mixedBuildBazelContext) GetOutputFiles(label string, cfgKey configKey) ([]string, error) { - key := makeCqueryKey(label, cquery.GetOutputFiles, cfgKey) - if rawString, ok := bazelCtx.results[key]; ok { - bazelOutput := strings.TrimSpace(rawString) - - return cquery.GetOutputFiles.ParseResult(bazelOutput), nil - } - return nil, fmt.Errorf("no bazel response found for %v", key) -} - -func (bazelCtx *mixedBuildBazelContext) GetCcInfo(label string, cfgKey configKey) (cquery.CcInfo, error) { - key := makeCqueryKey(label, cquery.GetCcInfo, cfgKey) - if rawString, ok := bazelCtx.results[key]; ok { - bazelOutput := strings.TrimSpace(rawString) - return cquery.GetCcInfo.ParseResult(bazelOutput) - } - return cquery.CcInfo{}, fmt.Errorf("no bazel response found for %v", key) -} - -func (bazelCtx *mixedBuildBazelContext) GetPythonBinary(label string, cfgKey configKey) (string, error) { - key := makeCqueryKey(label, cquery.GetPythonBinary, cfgKey) - if rawString, ok := bazelCtx.results[key]; ok { - bazelOutput := strings.TrimSpace(rawString) - return cquery.GetPythonBinary.ParseResult(bazelOutput), nil - } - return "", fmt.Errorf("no bazel response found for %v", key) -} - -func (bazelCtx *mixedBuildBazelContext) GetApexInfo(label string, cfgKey configKey) (cquery.ApexInfo, error) { - key := makeCqueryKey(label, cquery.GetApexInfo, cfgKey) - if rawString, ok := bazelCtx.results[key]; ok { - return cquery.GetApexInfo.ParseResult(strings.TrimSpace(rawString)) - } - return cquery.ApexInfo{}, fmt.Errorf("no bazel response found for %v", key) -} - -func (bazelCtx *mixedBuildBazelContext) GetCcUnstrippedInfo(label string, cfgKey configKey) (cquery.CcUnstrippedInfo, error) { - key := makeCqueryKey(label, cquery.GetCcUnstrippedInfo, cfgKey) - if rawString, ok := bazelCtx.results[key]; ok { - return cquery.GetCcUnstrippedInfo.ParseResult(strings.TrimSpace(rawString)) - } - return cquery.CcUnstrippedInfo{}, fmt.Errorf("no bazel response for %s", key) -} - -func (n noopBazelContext) QueueBazelRequest(_ string, _ cqueryRequest, _ configKey) { - panic("unimplemented") -} - -func (n noopBazelContext) GetOutputFiles(_ string, _ configKey) ([]string, error) { - panic("unimplemented") -} - -func (n noopBazelContext) GetCcInfo(_ string, _ configKey) (cquery.CcInfo, error) { - panic("unimplemented") -} - -func (n noopBazelContext) GetPythonBinary(_ string, _ configKey) (string, error) { - panic("unimplemented") -} - -func (n noopBazelContext) GetApexInfo(_ string, _ configKey) (cquery.ApexInfo, error) { - panic("unimplemented") -} - -func (n noopBazelContext) GetCcUnstrippedInfo(_ string, _ configKey) (cquery.CcUnstrippedInfo, error) { - //TODO implement me - panic("implement me") -} - -func (n noopBazelContext) InvokeBazel(_ Config, _ invokeBazelContext) error { - panic("unimplemented") -} - -func (m noopBazelContext) OutputBase() string { - return "" -} - -func (n noopBazelContext) IsModuleNameAllowed(_ string, _ bool) bool { - return false -} - -func (n noopBazelContext) IsModuleDclaAllowed(_ string) bool { - return false -} - -func (m noopBazelContext) BuildStatementsToRegister() []*bazel.BuildStatement { - return []*bazel.BuildStatement{} -} - -func (m noopBazelContext) AqueryDepsets() []bazel.AqueryDepset { - return []bazel.AqueryDepset{} -} - -func addToStringSet(set map[string]bool, items []string) { - for _, item := range items { - set[item] = true - } -} - -func GetBazelEnabledAndDisabledModules(buildMode SoongBuildMode, forceEnabled map[string]struct{}) (map[string]bool, map[string]bool) { - disabledModules := map[string]bool{} - enabledModules := map[string]bool{} - - switch buildMode { - case BazelProdMode: - addToStringSet(enabledModules, allowlists.ProdMixedBuildsEnabledList) - for enabledAdHocModule := range forceEnabled { - enabledModules[enabledAdHocModule] = true - } - case BazelStagingMode: - // Staging mode includes all prod modules plus all staging modules. - addToStringSet(enabledModules, allowlists.ProdMixedBuildsEnabledList) - addToStringSet(enabledModules, allowlists.StagingMixedBuildsEnabledList) - for enabledAdHocModule := range forceEnabled { - enabledModules[enabledAdHocModule] = true - } - case BazelDevMode: - addToStringSet(disabledModules, allowlists.MixedBuildsDisabledList) - default: - panic("Expected BazelProdMode, BazelStagingMode, or BazelDevMode") - } - return enabledModules, disabledModules -} - -func GetBazelEnabledModules(buildMode SoongBuildMode) []string { - enabledModules, disabledModules := GetBazelEnabledAndDisabledModules(buildMode, nil) - enabledList := make([]string, 0, len(enabledModules)) - for module := range enabledModules { - if !disabledModules[module] { - enabledList = append(enabledList, module) - } - } - sort.Strings(enabledList) - return enabledList -} - -func NewBazelContext(c *config) (BazelContext, error) { - if c.BuildMode != BazelProdMode && c.BuildMode != BazelStagingMode && c.BuildMode != BazelDevMode { - return noopBazelContext{}, nil - } - - enabledModules, disabledModules := GetBazelEnabledAndDisabledModules(c.BuildMode, c.BazelModulesForceEnabledByFlag()) - - paths := bazelPaths{ - soongOutDir: c.soongOutDir, - } - var missing []string - vars := []struct { - name string - ptr *string - - // True if the environment variable needs to be tracked so that changes to the variable - // cause the ninja file to be regenerated, false otherwise. False should only be set for - // environment variables that have no effect on the generated ninja file. - track bool - }{ - {"BAZEL_HOME", &paths.homeDir, true}, - {"BAZEL_PATH", &paths.bazelPath, true}, - {"BAZEL_OUTPUT_BASE", &paths.outputBase, true}, - {"BAZEL_WORKSPACE", &paths.workspaceDir, true}, - {"BAZEL_METRICS_DIR", &paths.metricsDir, false}, - {"BAZEL_DEPS_FILE", &paths.bazelDepsFile, true}, - } - for _, v := range vars { - if v.track { - if s := c.Getenv(v.name); len(s) > 1 { - *v.ptr = s - continue - } - } else if s, ok := c.env[v.name]; ok { - *v.ptr = s - } else { - missing = append(missing, v.name) - } - } - if len(missing) > 0 { - return nil, fmt.Errorf("missing required env vars to use bazel: %s", missing) - } - - targetBuildVariant := "user" - if c.Eng() { - targetBuildVariant = "eng" - } else if c.Debuggable() { - targetBuildVariant = "userdebug" - } - targetProduct := "unknown" - if c.HasDeviceProduct() { - targetProduct = c.DeviceProduct() - } - dclaMixedBuildsEnabledList := []string{} - if c.BuildMode == BazelProdMode { - dclaMixedBuildsEnabledList = allowlists.ProdDclaMixedBuildsEnabledList - } else if c.BuildMode == BazelStagingMode { - dclaMixedBuildsEnabledList = append(allowlists.ProdDclaMixedBuildsEnabledList, - allowlists.StagingDclaMixedBuildsEnabledList...) - } - dclaEnabledModules := map[string]bool{} - addToStringSet(dclaEnabledModules, dclaMixedBuildsEnabledList) - return &mixedBuildBazelContext{ - bazelRunner: &builtinBazelRunner{c.UseBazelProxy, absolutePath(c.outDir)}, - paths: &paths, - modulesDefaultToBazel: c.BuildMode == BazelDevMode, - bazelEnabledModules: enabledModules, - bazelDisabledModules: disabledModules, - bazelDclaEnabledModules: dclaEnabledModules, - targetProduct: targetProduct, - targetBuildVariant: targetBuildVariant, - }, nil -} - -func (p *bazelPaths) BazelMetricsDir() string { - return p.metricsDir -} - -func (context *mixedBuildBazelContext) IsModuleNameAllowed(moduleName string, withinApex bool) bool { - if context.bazelDisabledModules[moduleName] { - return false - } - if context.bazelEnabledModules[moduleName] { - return true - } - if withinApex && context.IsModuleDclaAllowed(moduleName) { - return true - } - - return context.modulesDefaultToBazel -} - -func (context *mixedBuildBazelContext) IsModuleDclaAllowed(moduleName string) bool { - return context.bazelDclaEnabledModules[moduleName] -} - -func pwdPrefix() string { - // Darwin doesn't have /proc - if runtime.GOOS != "darwin" { - return "PWD=/proc/self/cwd" - } - return "" -} - -type bazelCommand struct { - command string - // query or label - expression string -} - -type mockBazelRunner struct { - bazelCommandResults map[bazelCommand]string - // use *exec.Cmd as a key to get the bazelCommand, the map will be used in issueBazelCommand() - // Register createBazelCommand() invocations. Later, an - // issueBazelCommand() invocation can be mapped to the *exec.Cmd instance - // and then to the expected result via bazelCommandResults - tokens map[*exec.Cmd]bazelCommand - commands []bazelCommand - extraFlags []string -} - -func (r *mockBazelRunner) createBazelCommand(_ Config, _ *bazelPaths, _ bazel.RunName, - command bazelCommand, extraFlags ...string) *exec.Cmd { - r.commands = append(r.commands, command) - r.extraFlags = append(r.extraFlags, strings.Join(extraFlags, " ")) - cmd := &exec.Cmd{} - if r.tokens == nil { - r.tokens = make(map[*exec.Cmd]bazelCommand) - } - r.tokens[cmd] = command - return cmd -} - -func (r *mockBazelRunner) issueBazelCommand(bazelCmd *exec.Cmd, _ *metrics.EventHandler) (string, string, error) { - if command, ok := r.tokens[bazelCmd]; ok { - return r.bazelCommandResults[command], "", nil - } - return "", "", nil -} - -type builtinBazelRunner struct { - useBazelProxy bool - outDir string -} - -// Issues the given bazel command with given build label and additional flags. -// Returns (stdout, stderr, error). The first and second return values are strings -// containing the stdout and stderr of the run command, and an error is returned if -// the invocation returned an error code. -func (r *builtinBazelRunner) issueBazelCommand(bazelCmd *exec.Cmd, eventHandler *metrics.EventHandler) (string, string, error) { - if r.useBazelProxy { - eventHandler.Begin("client_proxy") - defer eventHandler.End("client_proxy") - proxyClient := bazel.NewProxyClient(r.outDir) - // Omit the arg containing the Bazel binary, as that is handled by the proxy - // server. - bazelFlags := bazelCmd.Args[1:] - // TODO(b/270989498): Refactor these functions to not take exec.Cmd, as its - // not actually executed for client proxying. - resp, err := proxyClient.IssueCommand(bazel.CmdRequest{bazelFlags, bazelCmd.Env}) - - if err != nil { - return "", "", err - } - if len(resp.ErrorString) > 0 { - return "", "", fmt.Errorf(resp.ErrorString) - } - return resp.Stdout, resp.Stderr, nil - } else { - eventHandler.Begin("bazel command") - defer eventHandler.End("bazel command") - stderr := &bytes.Buffer{} - bazelCmd.Stderr = stderr - if output, err := bazelCmd.Output(); err != nil { - return "", string(stderr.Bytes()), - fmt.Errorf("bazel command failed: %s\n---command---\n%s\n---env---\n%s\n---stderr---\n%s---", - err, bazelCmd, strings.Join(bazelCmd.Env, "\n"), stderr) - } else { - return string(output), string(stderr.Bytes()), nil - } - } -} - -func (r *builtinBazelRunner) createBazelCommand(config Config, paths *bazelPaths, runName bazel.RunName, command bazelCommand, - extraFlags ...string) *exec.Cmd { - cmdFlags := []string{ - "--output_base=" + absolutePath(paths.outputBase), - command.command, - command.expression, - // TODO(asmundak): is it needed in every build? - "--profile=" + shared.BazelMetricsFilename(paths, runName), - - // We don't need to set --host_platforms because it's set in bazelrc files - // that the bazel shell script wrapper passes - - // Explicitly disable downloading rules (such as canonical C++ and Java rules) from the network. - "--experimental_repository_disable_download", - - // Suppress noise - "--ui_event_filters=-INFO", - "--noshow_progress", - "--norun_validations", - } - cmdFlags = append(cmdFlags, extraFlags...) - - bazelCmd := exec.Command(paths.bazelPath, cmdFlags...) - bazelCmd.Dir = absolutePath(paths.syntheticWorkspaceDir()) - extraEnv := []string{ - "HOME=" + paths.homeDir, - pwdPrefix(), - "BUILD_DIR=" + absolutePath(paths.soongOutDir), - // Make OUT_DIR absolute here so build/bazel/bin/bazel uses the correct - // OUT_DIR at /out, instead of /out/soong/workspace/out. - "OUT_DIR=" + absolutePath(paths.outDir()), - // Disables local host detection of gcc; toolchain information is defined - // explicitly in BUILD files. - "BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1", - } - for _, envvar := range allowedBazelEnvironmentVars { - val := config.Getenv(envvar) - if val == "" { - continue - } - extraEnv = append(extraEnv, fmt.Sprintf("%s=%s", envvar, val)) - } - bazelCmd.Env = append(os.Environ(), extraEnv...) - - return bazelCmd -} - -func printableCqueryCommand(bazelCmd *exec.Cmd) string { - outputString := strings.Join(bazelCmd.Env, " ") + " \"" + strings.Join(bazelCmd.Args, "\" \"") + "\"" - return outputString - -} - -func (context *mixedBuildBazelContext) mainBzlFileContents() []byte { - // TODO(cparsons): Define configuration transitions programmatically based - // on available archs. - contents := ` -##################################################### -# This file is generated by soong_build. Do not edit. -##################################################### -def _config_node_transition_impl(settings, attr): - if attr.os == "android" and attr.arch == "target": - target = "{PRODUCT}-{VARIANT}" - else: - target = "{PRODUCT}-{VARIANT}_%s_%s" % (attr.os, attr.arch) - apex_name = "" - if attr.within_apex: - # //build/bazel/rules/apex:apex_name has to be set to a non_empty value, - # otherwise //build/bazel/rules/apex:non_apex will be true and the - # "-D__ANDROID_APEX__" compiler flag will be missing. Apex_name is used - # in some validation on bazel side which don't really apply in mixed - # build because soong will do the work, so we just set it to a fixed - # value here. - apex_name = "dcla_apex" - outputs = { - "//command_line_option:platforms": "@soong_injection//product_config_platforms/products/{PRODUCT}-{VARIANT}:%s" % target, - "@//build/bazel/rules/apex:within_apex": attr.within_apex, - "@//build/bazel/rules/apex:min_sdk_version": attr.apex_sdk_version, - "@//build/bazel/rules/apex:apex_name": apex_name, - } - - return outputs - -_config_node_transition = transition( - implementation = _config_node_transition_impl, - inputs = [], - outputs = [ - "//command_line_option:platforms", - "@//build/bazel/rules/apex:within_apex", - "@//build/bazel/rules/apex:min_sdk_version", - "@//build/bazel/rules/apex:apex_name", - ], -) - -def _passthrough_rule_impl(ctx): - return [DefaultInfo(files = depset(ctx.files.deps))] - -config_node = rule( - implementation = _passthrough_rule_impl, - attrs = { - "arch" : attr.string(mandatory = True), - "os" : attr.string(mandatory = True), - "within_apex" : attr.bool(default = False), - "apex_sdk_version" : attr.string(mandatory = True), - "deps" : attr.label_list(cfg = _config_node_transition, allow_files = True), - "_allowlist_function_transition": attr.label(default = "@bazel_tools//tools/allowlists/function_transition_allowlist"), - }, -) - - -# Rule representing the root of the build, to depend on all Bazel targets that -# are required for the build. Building this target will build the entire Bazel -# build tree. -mixed_build_root = rule( - implementation = _passthrough_rule_impl, - attrs = { - "deps" : attr.label_list(), - }, -) - -def _phony_root_impl(ctx): - return [] - -# Rule to depend on other targets but build nothing. -# This is useful as follows: building a target of this rule will generate -# symlink forests for all dependencies of the target, without executing any -# actions of the build. -phony_root = rule( - implementation = _phony_root_impl, - attrs = {"deps" : attr.label_list()}, -) -` - - productReplacer := strings.NewReplacer( - "{PRODUCT}", context.targetProduct, - "{VARIANT}", context.targetBuildVariant) - - return []byte(productReplacer.Replace(contents)) -} - -func (context *mixedBuildBazelContext) mainBuildFileContents() []byte { - // TODO(cparsons): Map label to attribute programmatically; don't use hard-coded - // architecture mapping. - formatString := ` -# This file is generated by soong_build. Do not edit. -load(":main.bzl", "config_node", "mixed_build_root", "phony_root") - -%s - -mixed_build_root(name = "buildroot", - deps = [%s], - testonly = True, # Unblocks testonly deps. -) - -phony_root(name = "phonyroot", - deps = [":buildroot"], - testonly = True, # Unblocks testonly deps. -) -` - configNodeFormatString := ` -config_node(name = "%s", - arch = "%s", - os = "%s", - within_apex = %s, - apex_sdk_version = "%s", - deps = [%s], - testonly = True, # Unblocks testonly deps. -) -` - - configNodesSection := "" - - labelsByConfig := map[string][]string{} - - for _, val := range context.requests { - labelString := fmt.Sprintf("\"@%s\"", val.label) - configString := getConfigString(val) - labelsByConfig[configString] = append(labelsByConfig[configString], labelString) - } - - // Configs need to be sorted to maintain determinism of the BUILD file. - sortedConfigs := make([]string, 0, len(labelsByConfig)) - for val := range labelsByConfig { - sortedConfigs = append(sortedConfigs, val) - } - sort.Slice(sortedConfigs, func(i, j int) bool { return sortedConfigs[i] < sortedConfigs[j] }) - - allLabels := []string{} - for _, configString := range sortedConfigs { - labels := labelsByConfig[configString] - configTokens := strings.Split(configString, "|") - if len(configTokens) < 2 { - panic(fmt.Errorf("Unexpected config string format: %s", configString)) - } - archString := configTokens[0] - osString := configTokens[1] - withinApex := "False" - apexSdkVerString := "" - targetString := fmt.Sprintf("%s_%s", osString, archString) - if len(configTokens) > 2 { - targetString += "_" + configTokens[2] - if configTokens[2] == withinApexToString(true) { - withinApex = "True" - } - } - if len(configTokens) > 3 { - targetString += "_" + configTokens[3] - apexSdkVerString = configTokens[3] - } - allLabels = append(allLabels, fmt.Sprintf("\":%s\"", targetString)) - labelsString := strings.Join(labels, ",\n ") - configNodesSection += fmt.Sprintf(configNodeFormatString, targetString, archString, osString, withinApex, apexSdkVerString, - labelsString) - } - - return []byte(fmt.Sprintf(formatString, configNodesSection, strings.Join(allLabels, ",\n "))) -} - -func indent(original string) string { - result := "" - for _, line := range strings.Split(original, "\n") { - result += " " + line + "\n" - } - return result -} - -// Returns the file contents of the buildroot.cquery file that should be used for the cquery -// expression in order to obtain information about buildroot and its dependencies. -// The contents of this file depend on the mixedBuildBazelContext's requests; requests are enumerated -// and grouped by their request type. The data retrieved for each label depends on its -// request type. -func (context *mixedBuildBazelContext) cqueryStarlarkFileContents() []byte { - requestTypeToCqueryIdEntries := map[cqueryRequest][]string{} - requestTypes := []cqueryRequest{} - for _, val := range context.requests { - cqueryId := getCqueryId(val) - mapEntryString := fmt.Sprintf("%q : True", cqueryId) - if _, seenKey := requestTypeToCqueryIdEntries[val.requestType]; !seenKey { - requestTypes = append(requestTypes, val.requestType) - } - requestTypeToCqueryIdEntries[val.requestType] = - append(requestTypeToCqueryIdEntries[val.requestType], mapEntryString) - } - labelRegistrationMapSection := "" - functionDefSection := "" - mainSwitchSection := "" - - mapDeclarationFormatString := ` -%s = { - %s -} -` - functionDefFormatString := ` -def %s(target, id_string): -%s -` - mainSwitchSectionFormatString := ` - if id_string in %s: - return id_string + ">>" + %s(target, id_string) -` - - for _, requestType := range requestTypes { - labelMapName := requestType.Name() + "_Labels" - functionName := requestType.Name() + "_Fn" - labelRegistrationMapSection += fmt.Sprintf(mapDeclarationFormatString, - labelMapName, - strings.Join(requestTypeToCqueryIdEntries[requestType], ",\n ")) - functionDefSection += fmt.Sprintf(functionDefFormatString, - functionName, - indent(requestType.StarlarkFunctionBody())) - mainSwitchSection += fmt.Sprintf(mainSwitchSectionFormatString, - labelMapName, functionName) - } - - formatString := ` -# This file is generated by soong_build. Do not edit. - -{LABEL_REGISTRATION_MAP_SECTION} - -{FUNCTION_DEF_SECTION} - -def get_arch(target): - # TODO(b/199363072): filegroups and file targets aren't associated with any - # specific platform architecture in mixed builds. This is consistent with how - # Soong treats filegroups, but it may not be the case with manually-written - # filegroup BUILD targets. - buildoptions = build_options(target) - - if buildoptions == None: - # File targets do not have buildoptions. File targets aren't associated with - # any specific platform architecture in mixed builds, so use the host. - return "x86_64|linux" - platforms = buildoptions["//command_line_option:platforms"] - if len(platforms) != 1: - # An individual configured target should have only one platform architecture. - # Note that it's fine for there to be multiple architectures for the same label, - # but each is its own configured target. - fail("expected exactly 1 platform for " + str(target.label) + " but got " + str(platforms)) - platform_name = platforms[0].name - if platform_name == "host": - return "HOST" - if not platform_name.startswith("{TARGET_PRODUCT}-{TARGET_BUILD_VARIANT}"): - fail("expected platform name of the form '{TARGET_PRODUCT}-{TARGET_BUILD_VARIANT}_android_' or '{TARGET_PRODUCT}-{TARGET_BUILD_VARIANT}_linux_', but was " + str(platforms)) - platform_name = platform_name.removeprefix("{TARGET_PRODUCT}-{TARGET_BUILD_VARIANT}").removeprefix("_") - config_key = "" - if not platform_name: - config_key = "target|android" - elif platform_name.startswith("android_"): - config_key = platform_name.removeprefix("android_") + "|android" - elif platform_name.startswith("linux_"): - config_key = platform_name.removeprefix("linux_") + "|linux" - else: - fail("expected platform name of the form '{TARGET_PRODUCT}-{TARGET_BUILD_VARIANT}_android_' or '{TARGET_PRODUCT}-{TARGET_BUILD_VARIANT}_linux_', but was " + str(platforms)) - - within_apex = buildoptions.get("//build/bazel/rules/apex:within_apex") - apex_sdk_version = buildoptions.get("//build/bazel/rules/apex:min_sdk_version") - - if within_apex: - config_key += "|within_apex" - if apex_sdk_version != None and len(apex_sdk_version) > 0: - config_key += "|" + apex_sdk_version - - return config_key - -def format(target): - id_string = str(target.label) + "|" + get_arch(target) - - # TODO(b/248106697): Remove once Bazel is updated to always normalize labels. - if id_string.startswith("//"): - id_string = "@" + id_string - - {MAIN_SWITCH_SECTION} - - # This target was not requested via cquery, and thus must be a dependency - # of a requested target. - return id_string + ">>NONE" -` - replacer := strings.NewReplacer( - "{TARGET_PRODUCT}", context.targetProduct, - "{TARGET_BUILD_VARIANT}", context.targetBuildVariant, - "{LABEL_REGISTRATION_MAP_SECTION}", labelRegistrationMapSection, - "{FUNCTION_DEF_SECTION}", functionDefSection, - "{MAIN_SWITCH_SECTION}", mainSwitchSection) - - return []byte(replacer.Replace(formatString)) -} - -// Returns a path containing build-related metadata required for interfacing -// with Bazel. Example: out/soong/bazel. -func (p *bazelPaths) intermediatesDir() string { - return filepath.Join(p.soongOutDir, "bazel") -} - -// Returns the path where the contents of the @soong_injection repository live. -// It is used by Soong to tell Bazel things it cannot over the command line. -func (p *bazelPaths) injectedFilesDir() string { - return filepath.Join(p.soongOutDir, bazel.SoongInjectionDirName) -} - -// Returns the path of the synthetic Bazel workspace that contains a symlink -// forest composed the whole source tree and BUILD files generated by bp2build. -func (p *bazelPaths) syntheticWorkspaceDir() string { - return filepath.Join(p.soongOutDir, "workspace") -} - -// Returns the path to the top level out dir ($OUT_DIR). -func (p *bazelPaths) outDir() string { - return filepath.Dir(p.soongOutDir) -} - -const buildrootLabel = "@soong_injection//mixed_builds:buildroot" - -var ( - cqueryCmd = bazelCommand{"cquery", fmt.Sprintf("deps(%s, 2)", buildrootLabel)} - aqueryCmd = bazelCommand{"aquery", fmt.Sprintf("deps(%s)", buildrootLabel)} - buildCmd = bazelCommand{"build", "@soong_injection//mixed_builds:phonyroot"} -) - -// Issues commands to Bazel to receive results for all cquery requests -// queued in the BazelContext. -func (context *mixedBuildBazelContext) InvokeBazel(config Config, ctx invokeBazelContext) error { - eventHandler := ctx.GetEventHandler() - eventHandler.Begin("bazel") - defer eventHandler.End("bazel") - - if metricsDir := context.paths.BazelMetricsDir(); metricsDir != "" { - if err := os.MkdirAll(metricsDir, 0777); err != nil { - return err - } - } - context.results = make(map[cqueryKey]string) - if err := context.runCquery(config, ctx); err != nil { - return err - } - if err := context.runAquery(config, ctx); err != nil { - return err - } - if err := context.generateBazelSymlinks(config, ctx); err != nil { - return err - } - - // Clear requests. - context.requests = []cqueryKey{} - return nil -} - -func (context *mixedBuildBazelContext) runCquery(config Config, ctx invokeBazelContext) error { - eventHandler := ctx.GetEventHandler() - eventHandler.Begin("cquery") - defer eventHandler.End("cquery") - soongInjectionPath := absolutePath(context.paths.injectedFilesDir()) - mixedBuildsPath := filepath.Join(soongInjectionPath, "mixed_builds") - if _, err := os.Stat(mixedBuildsPath); os.IsNotExist(err) { - err = os.MkdirAll(mixedBuildsPath, 0777) - if err != nil { - return err - } - } - if err := writeFileBytesIfChanged(filepath.Join(soongInjectionPath, "WORKSPACE.bazel"), []byte{}, 0666); err != nil { - return err - } - if err := writeFileBytesIfChanged(filepath.Join(mixedBuildsPath, "main.bzl"), context.mainBzlFileContents(), 0666); err != nil { - return err - } - if err := writeFileBytesIfChanged(filepath.Join(mixedBuildsPath, "BUILD.bazel"), context.mainBuildFileContents(), 0666); err != nil { - return err - } - cqueryFileRelpath := filepath.Join(context.paths.injectedFilesDir(), "buildroot.cquery") - if err := writeFileBytesIfChanged(absolutePath(cqueryFileRelpath), context.cqueryStarlarkFileContents(), 0666); err != nil { - return err - } - - extraFlags := []string{"--output=starlark", "--starlark:file=" + absolutePath(cqueryFileRelpath)} - if Bool(config.productVariables.ClangCoverage) { - extraFlags = append(extraFlags, "--collect_code_coverage") - } - - cqueryCommandWithFlag := context.createBazelCommand(config, context.paths, bazel.CqueryBuildRootRunName, cqueryCmd, extraFlags...) - cqueryOutput, cqueryErrorMessage, cqueryErr := context.issueBazelCommand(cqueryCommandWithFlag, eventHandler) - if cqueryErr != nil { - return cqueryErr - } - cqueryCommandPrint := fmt.Sprintf("cquery command line:\n %s \n\n\n", printableCqueryCommand(cqueryCommandWithFlag)) - if err := os.WriteFile(filepath.Join(soongInjectionPath, "cquery.out"), []byte(cqueryCommandPrint+cqueryOutput), 0666); err != nil { - return err - } - cqueryResults := map[string]string{} - for _, outputLine := range strings.Split(cqueryOutput, "\n") { - if strings.Contains(outputLine, ">>") { - splitLine := strings.SplitN(outputLine, ">>", 2) - cqueryResults[splitLine[0]] = splitLine[1] - } - } - for _, val := range context.requests { - if cqueryResult, ok := cqueryResults[getCqueryId(val)]; ok { - context.results[val] = cqueryResult - } else { - return fmt.Errorf("missing result for bazel target %s. query output: [%s], cquery err: [%s]", - getCqueryId(val), cqueryOutput, cqueryErrorMessage) - } - } - return nil -} - -func writeFileBytesIfChanged(path string, contents []byte, perm os.FileMode) error { - oldContents, err := os.ReadFile(path) - if err != nil || !bytes.Equal(contents, oldContents) { - err = os.WriteFile(path, contents, perm) - } - return nil -} - -func (context *mixedBuildBazelContext) runAquery(config Config, ctx invokeBazelContext) error { - eventHandler := ctx.GetEventHandler() - eventHandler.Begin("aquery") - defer eventHandler.End("aquery") - // Issue an aquery command to retrieve action information about the bazel build tree. - // - // Use jsonproto instead of proto; actual proto parsing would require a dependency on Bazel's - // proto sources, which would add a number of unnecessary dependencies. - extraFlags := []string{"--output=proto", "--include_file_write_contents"} - if Bool(config.productVariables.ClangCoverage) { - extraFlags = append(extraFlags, "--collect_code_coverage") - paths := make([]string, 0, 2) - if p := config.productVariables.NativeCoveragePaths; len(p) > 0 { - for i := range p { - // TODO(b/259404593) convert path wildcard to regex values - if p[i] == "*" { - p[i] = ".*" - } - } - paths = append(paths, JoinWithPrefixAndSeparator(p, "+", ",")) - } - if p := config.productVariables.NativeCoverageExcludePaths; len(p) > 0 { - paths = append(paths, JoinWithPrefixAndSeparator(p, "-", ",")) - } - if len(paths) > 0 { - extraFlags = append(extraFlags, "--instrumentation_filter="+strings.Join(paths, ",")) - } - } - aqueryOutput, _, err := context.issueBazelCommand(context.createBazelCommand(config, context.paths, bazel.AqueryBuildRootRunName, aqueryCmd, - extraFlags...), eventHandler) - if err != nil { - return err - } - context.buildStatements, context.depsets, err = bazel.AqueryBuildStatements([]byte(aqueryOutput), eventHandler) - return err -} - -func (context *mixedBuildBazelContext) generateBazelSymlinks(config Config, ctx invokeBazelContext) error { - eventHandler := ctx.GetEventHandler() - eventHandler.Begin("symlinks") - defer eventHandler.End("symlinks") - // Issue a build command of the phony root to generate symlink forests for dependencies of the - // Bazel build. This is necessary because aquery invocations do not generate this symlink forest, - // but some of symlinks may be required to resolve source dependencies of the build. - _, _, err := context.issueBazelCommand(context.createBazelCommand(config, context.paths, bazel.BazelBuildPhonyRootRunName, buildCmd), eventHandler) - return err -} - -func (context *mixedBuildBazelContext) BuildStatementsToRegister() []*bazel.BuildStatement { - return context.buildStatements -} - -func (context *mixedBuildBazelContext) AqueryDepsets() []bazel.AqueryDepset { - return context.depsets -} - -func (context *mixedBuildBazelContext) OutputBase() string { - return context.paths.outputBase -} - -// Singleton used for registering BUILD file ninja dependencies (needed -// for correctness of builds which use Bazel. -func BazelSingleton() Singleton { - return &bazelSingleton{} -} - -type bazelSingleton struct{} - -func (c *bazelSingleton) GenerateBuildActions(ctx SingletonContext) { - // bazelSingleton is a no-op if mixed-soong-bazel-builds are disabled. - if !ctx.Config().IsMixedBuildsEnabled() { - return - } - - // Add ninja file dependencies for files which all bazel invocations require. - bazelBuildList := absolutePath(filepath.Join( - filepath.Dir(ctx.Config().moduleListFile), "bazel.list")) - ctx.AddNinjaFileDeps(bazelBuildList) - - data, err := os.ReadFile(bazelBuildList) - if err != nil { - ctx.Errorf(err.Error()) - } - files := strings.Split(strings.TrimSpace(string(data)), "\n") - for _, file := range files { - ctx.AddNinjaFileDeps(file) - } - - for _, depset := range ctx.Config().BazelContext.AqueryDepsets() { - var outputs []Path - var orderOnlies []Path - for _, depsetDepHash := range depset.TransitiveDepSetHashes { - otherDepsetName := bazelDepsetName(depsetDepHash) - outputs = append(outputs, PathForPhony(ctx, otherDepsetName)) - } - for _, artifactPath := range depset.DirectArtifacts { - pathInBazelOut := PathForBazelOut(ctx, artifactPath) - if artifactPath == "bazel-out/volatile-status.txt" { - // See https://bazel.build/docs/user-manual#workspace-status - orderOnlies = append(orderOnlies, pathInBazelOut) - } else { - outputs = append(outputs, pathInBazelOut) - } - } - thisDepsetName := bazelDepsetName(depset.ContentHash) - ctx.Build(pctx, BuildParams{ - Rule: blueprint.Phony, - Outputs: []WritablePath{PathForPhony(ctx, thisDepsetName)}, - Implicits: outputs, - OrderOnly: orderOnlies, - }) - } - - executionRoot := path.Join(ctx.Config().BazelContext.OutputBase(), "execroot", "__main__") - bazelOutDir := path.Join(executionRoot, "bazel-out") - for index, buildStatement := range ctx.Config().BazelContext.BuildStatementsToRegister() { - // nil build statements are a valid case where we do not create an action because it is - // unnecessary or handled by other processing - if buildStatement == nil { - continue - } - if len(buildStatement.Command) > 0 { - rule := NewRuleBuilder(pctx, ctx) - createCommand(rule.Command(), buildStatement, executionRoot, bazelOutDir, ctx) - desc := fmt.Sprintf("%s: %s", buildStatement.Mnemonic, buildStatement.OutputPaths) - rule.Build(fmt.Sprintf("bazel %d", index), desc) - continue - } - // Certain actions returned by aquery (for instance FileWrite) do not contain a command - // and thus require special treatment. If BuildStatement were an interface implementing - // buildRule(ctx) function, the code here would just call it. - // Unfortunately, the BuildStatement is defined in - // the 'bazel' package, which cannot depend on 'android' package where ctx is defined, - // because this would cause circular dependency. So, until we move aquery processing - // to the 'android' package, we need to handle special cases here. - switch buildStatement.Mnemonic { - case "FileWrite", "SourceSymlinkManifest": - out := PathForBazelOut(ctx, buildStatement.OutputPaths[0]) - WriteFileRuleVerbatim(ctx, out, buildStatement.FileContents) - case "SymlinkTree": - // build-runfiles arguments are the manifest file and the target directory - // where it creates the symlink tree according to this manifest (and then - // writes the MANIFEST file to it). - outManifest := PathForBazelOut(ctx, buildStatement.OutputPaths[0]) - outManifestPath := outManifest.String() - if !strings.HasSuffix(outManifestPath, "MANIFEST") { - panic("the base name of the symlink tree action should be MANIFEST, got " + outManifestPath) - } - outDir := filepath.Dir(outManifestPath) - ctx.Build(pctx, BuildParams{ - Rule: buildRunfilesRule, - Output: outManifest, - Inputs: []Path{PathForBazelOut(ctx, buildStatement.InputPaths[0])}, - Description: "symlink tree for " + outDir, - Args: map[string]string{ - "outDir": outDir, - }, - }) - default: - panic(fmt.Sprintf("unhandled build statement: %v", buildStatement)) - } - } -} - -// Register bazel-owned build statements (obtained from the aquery invocation). -func createCommand(cmd *RuleBuilderCommand, buildStatement *bazel.BuildStatement, executionRoot string, bazelOutDir string, ctx BuilderContext) { - // executionRoot is the action cwd. - cmd.Text(fmt.Sprintf("cd '%s' &&", executionRoot)) - - // Remove old outputs, as some actions might not rerun if the outputs are detected. - if len(buildStatement.OutputPaths) > 0 { - cmd.Text("rm -rf") // -r because outputs can be Bazel dir/tree artifacts. - for _, outputPath := range buildStatement.OutputPaths { - cmd.Text(fmt.Sprintf("'%s'", outputPath)) - } - cmd.Text("&&") - } - - for _, pair := range buildStatement.Env { - // Set per-action env variables, if any. - cmd.Flag(pair.Key + "=" + pair.Value) - } - - // The actual Bazel action. - if len(buildStatement.Command) > 16*1024 { - commandFile := PathForBazelOut(ctx, buildStatement.OutputPaths[0]+".sh") - WriteFileRule(ctx, commandFile, buildStatement.Command) - - cmd.Text("bash").Text(buildStatement.OutputPaths[0] + ".sh").Implicit(commandFile) - } else { - cmd.Text(buildStatement.Command) - } - - for _, outputPath := range buildStatement.OutputPaths { - cmd.ImplicitOutput(PathForBazelOut(ctx, outputPath)) - } - for _, inputPath := range buildStatement.InputPaths { - cmd.Implicit(PathForBazelOut(ctx, inputPath)) - } - for _, inputDepsetHash := range buildStatement.InputDepsetHashes { - otherDepsetName := bazelDepsetName(inputDepsetHash) - cmd.Implicit(PathForPhony(ctx, otherDepsetName)) - } - - if depfile := buildStatement.Depfile; depfile != nil { - // The paths in depfile are relative to `executionRoot`. - // Hence, they need to be corrected by replacing "bazel-out" - // with the full `bazelOutDir`. - // Otherwise, implicit outputs and implicit inputs under "bazel-out/" - // would be deemed missing. - // (Note: The regexp uses a capture group because the version of sed - // does not support a look-behind pattern.) - replacement := fmt.Sprintf(`&& sed -i'' -E 's@(^|\s|")bazel-out/@\1%s/@g' '%s'`, - bazelOutDir, *depfile) - cmd.Text(replacement) - cmd.ImplicitDepFile(PathForBazelOut(ctx, *depfile)) - } - - for _, symlinkPath := range buildStatement.SymlinkPaths { - cmd.ImplicitSymlinkOutput(PathForBazelOut(ctx, symlinkPath)) - } -} - -func getCqueryId(key cqueryKey) string { - return key.label + "|" + getConfigString(key) -} - -func getConfigString(key cqueryKey) string { - arch := key.configKey.arch - if len(arch) == 0 || arch == "common" { - if key.configKey.osType.Class == Device { - // For the generic Android, the expected result is "target|android", which - // corresponds to the product_variable_config named "android_target" in - // build/bazel/platforms/BUILD.bazel. - arch = "target" - } else { - // Use host platform, which is currently hardcoded to be x86_64. - arch = "x86_64" - } - } - osName := key.configKey.osType.Name - if len(osName) == 0 || osName == "common_os" || osName == "linux_glibc" || osName == "linux_musl" { - // Use host OS, which is currently hardcoded to be linux. - osName = "linux" - } - keyString := arch + "|" + osName - if key.configKey.apexKey.WithinApex { - keyString += "|" + withinApexToString(key.configKey.apexKey.WithinApex) - } - - if len(key.configKey.apexKey.ApexSdkVersion) > 0 { - keyString += "|" + key.configKey.apexKey.ApexSdkVersion - } - - return keyString -} - -func GetConfigKey(ctx BaseModuleContext) configKey { - return configKey{ - // use string because Arch is not a valid key in go - arch: ctx.Arch().String(), - osType: ctx.Os(), - } -} - -func GetConfigKeyApexVariant(ctx BaseModuleContext, apexKey *ApexConfigKey) configKey { - configKey := GetConfigKey(ctx) - - if apexKey != nil { - configKey.apexKey = ApexConfigKey{ - WithinApex: apexKey.WithinApex, - ApexSdkVersion: apexKey.ApexSdkVersion, - } - } - - return configKey -} - -func bazelDepsetName(contentHash string) string { - return fmt.Sprintf("bazel_depset_%s", contentHash) -} - -func EnvironmentVarsFile(config Config) string { - return fmt.Sprintf(bazel.GeneratedBazelFileWarning+` -_env = %s - -env = _env -`, - starlark_fmt.PrintStringList(allowedBazelEnvironmentVars, 0), - ) -} diff --git a/android/bazel_handler_test.go b/android/bazel_handler_test.go deleted file mode 100644 index c67d7fb66a..0000000000 --- a/android/bazel_handler_test.go +++ /dev/null @@ -1,319 +0,0 @@ -package android - -import ( - "encoding/json" - "os" - "path/filepath" - "reflect" - "strings" - "testing" - - "android/soong/bazel/cquery" - analysis_v2_proto "prebuilts/bazel/common/proto/analysis_v2" - - "github.com/google/blueprint/metrics" - "google.golang.org/protobuf/proto" -) - -var testConfig = TestConfig("out", nil, "", nil) - -type testInvokeBazelContext struct{} - -func (t *testInvokeBazelContext) GetEventHandler() *metrics.EventHandler { - return &metrics.EventHandler{} -} - -func TestRequestResultsAfterInvokeBazel(t *testing.T) { - label_foo := "@//foo:foo" - label_bar := "@//foo:bar" - apexKey := ApexConfigKey{ - WithinApex: true, - ApexSdkVersion: "29", - } - cfg_foo := configKey{"arm64_armv8-a", Android, apexKey} - cfg_bar := configKey{arch: "arm64_armv8-a", osType: Android} - cmd_results := []string{ - `@//foo:foo|arm64_armv8-a|android|within_apex|29>>out/foo/foo.txt`, - `@//foo:bar|arm64_armv8-a|android>>out/foo/bar.txt`, - } - bazelContext, _ := testBazelContext(t, map[bazelCommand]string{ - bazelCommand{command: "cquery", expression: "deps(@soong_injection//mixed_builds:buildroot, 2)"}: strings.Join(cmd_results, "\n"), - }) - - bazelContext.QueueBazelRequest(label_foo, cquery.GetOutputFiles, cfg_foo) - bazelContext.QueueBazelRequest(label_bar, cquery.GetOutputFiles, cfg_bar) - err := bazelContext.InvokeBazel(testConfig, &testInvokeBazelContext{}) - if err != nil { - t.Fatalf("Did not expect error invoking Bazel, but got %s", err) - } - verifyCqueryResult(t, bazelContext, label_foo, cfg_foo, "out/foo/foo.txt") - verifyCqueryResult(t, bazelContext, label_bar, cfg_bar, "out/foo/bar.txt") -} - -func verifyCqueryResult(t *testing.T, ctx *mixedBuildBazelContext, label string, cfg configKey, result string) { - g, err := ctx.GetOutputFiles(label, cfg) - if err != nil { - t.Errorf("Expected cquery results after running InvokeBazel(), but got err %v", err) - } else if w := []string{result}; !reflect.DeepEqual(w, g) { - t.Errorf("Expected output %s, got %s", w, g) - } -} - -func TestInvokeBazelWritesBazelFiles(t *testing.T) { - bazelContext, baseDir := testBazelContext(t, map[bazelCommand]string{}) - err := bazelContext.InvokeBazel(testConfig, &testInvokeBazelContext{}) - if err != nil { - t.Fatalf("Did not expect error invoking Bazel, but got %s", err) - } - if _, err := os.Stat(filepath.Join(baseDir, "soong_injection", "mixed_builds", "main.bzl")); os.IsNotExist(err) { - t.Errorf("Expected main.bzl to exist, but it does not") - } else if err != nil { - t.Errorf("Unexpected error stating main.bzl %s", err) - } - - if _, err := os.Stat(filepath.Join(baseDir, "soong_injection", "mixed_builds", "BUILD.bazel")); os.IsNotExist(err) { - t.Errorf("Expected BUILD.bazel to exist, but it does not") - } else if err != nil { - t.Errorf("Unexpected error stating BUILD.bazel %s", err) - } - - if _, err := os.Stat(filepath.Join(baseDir, "soong_injection", "WORKSPACE.bazel")); os.IsNotExist(err) { - t.Errorf("Expected WORKSPACE.bazel to exist, but it does not") - } else if err != nil { - t.Errorf("Unexpected error stating WORKSPACE.bazel %s", err) - } -} - -func TestInvokeBazelPopulatesBuildStatements(t *testing.T) { - type testCase struct { - input string - command string - } - - var testCases = []testCase{ - {` -{ - "artifacts": [ - { "id": 1, "path_fragment_id": 1 }, - { "id": 2, "path_fragment_id": 2 }], - "actions": [{ - "target_Id": 1, - "action_Key": "x", - "mnemonic": "x", - "arguments": ["touch", "foo"], - "input_dep_set_ids": [1], - "output_Ids": [1], - "primary_output_id": 1 - }], - "dep_set_of_files": [ - { "id": 1, "direct_artifact_ids": [1, 2] }], - "path_fragments": [ - { "id": 1, "label": "one" }, - { "id": 2, "label": "two" }] -}`, - "cd 'test/exec_root' && rm -rf 'one' && touch foo", - }, {` -{ - "artifacts": [ - { "id": 1, "path_fragment_id": 10 }, - { "id": 2, "path_fragment_id": 20 }], - "actions": [{ - "target_Id": 100, - "action_Key": "x", - "mnemonic": "x", - "arguments": ["bogus", "command"], - "output_Ids": [1, 2], - "primary_output_id": 1 - }], - "path_fragments": [ - { "id": 10, "label": "one", "parent_id": 30 }, - { "id": 20, "label": "one.d", "parent_id": 30 }, - { "id": 30, "label": "parent" }] -}`, - `cd 'test/exec_root' && rm -rf 'parent/one' && bogus command && sed -i'' -E 's@(^|\s|")bazel-out/@\1test/bazel_out/@g' 'parent/one.d'`, - }, - } - - for i, testCase := range testCases { - data, err := JsonToActionGraphContainer(testCase.input) - if err != nil { - t.Error(err) - } - bazelContext, _ := testBazelContext(t, map[bazelCommand]string{ - bazelCommand{command: "aquery", expression: "deps(@soong_injection//mixed_builds:buildroot)"}: string(data)}) - - err = bazelContext.InvokeBazel(testConfig, &testInvokeBazelContext{}) - if err != nil { - t.Fatalf("testCase #%d: did not expect error invoking Bazel, but got %s", i+1, err) - } - - got := bazelContext.BuildStatementsToRegister() - if want := 1; len(got) != want { - t.Fatalf("expected %d registered build statements, but got %#v", want, got) - } - - cmd := RuleBuilderCommand{} - ctx := builderContextForTests{PathContextForTesting(TestConfig("out", nil, "", nil))} - createCommand(&cmd, got[0], "test/exec_root", "test/bazel_out", ctx) - if actual, expected := cmd.buf.String(), testCase.command; expected != actual { - t.Errorf("expected: [%s], actual: [%s]", expected, actual) - } - } -} - -func TestCoverageFlagsAfterInvokeBazel(t *testing.T) { - testConfig.productVariables.ClangCoverage = boolPtr(true) - - testConfig.productVariables.NativeCoveragePaths = []string{"foo1", "foo2"} - testConfig.productVariables.NativeCoverageExcludePaths = []string{"bar1", "bar2"} - verifyExtraFlags(t, testConfig, `--collect_code_coverage --instrumentation_filter=+foo1,+foo2,-bar1,-bar2`) - - testConfig.productVariables.NativeCoveragePaths = []string{"foo1"} - testConfig.productVariables.NativeCoverageExcludePaths = []string{"bar1"} - verifyExtraFlags(t, testConfig, `--collect_code_coverage --instrumentation_filter=+foo1,-bar1`) - - testConfig.productVariables.NativeCoveragePaths = []string{"foo1"} - testConfig.productVariables.NativeCoverageExcludePaths = nil - verifyExtraFlags(t, testConfig, `--collect_code_coverage --instrumentation_filter=+foo1`) - - testConfig.productVariables.NativeCoveragePaths = nil - testConfig.productVariables.NativeCoverageExcludePaths = []string{"bar1"} - verifyExtraFlags(t, testConfig, `--collect_code_coverage --instrumentation_filter=-bar1`) - - testConfig.productVariables.NativeCoveragePaths = []string{"*"} - testConfig.productVariables.NativeCoverageExcludePaths = nil - verifyExtraFlags(t, testConfig, `--collect_code_coverage --instrumentation_filter=+.*`) - - testConfig.productVariables.ClangCoverage = boolPtr(false) - actual := verifyExtraFlags(t, testConfig, ``) - if strings.Contains(actual, "--collect_code_coverage") || - strings.Contains(actual, "--instrumentation_filter=") { - t.Errorf("Expected code coverage disabled, but got %#v", actual) - } -} - -func TestBazelRequestsSorted(t *testing.T) { - bazelContext, _ := testBazelContext(t, map[bazelCommand]string{}) - - cfgKeyArm64Android := configKey{arch: "arm64_armv8-a", osType: Android} - cfgKeyArm64Linux := configKey{arch: "arm64_armv8-a", osType: Linux} - cfgKeyOtherAndroid := configKey{arch: "otherarch", osType: Android} - - bazelContext.QueueBazelRequest("zzz", cquery.GetOutputFiles, cfgKeyArm64Android) - bazelContext.QueueBazelRequest("ccc", cquery.GetApexInfo, cfgKeyArm64Android) - bazelContext.QueueBazelRequest("duplicate", cquery.GetOutputFiles, cfgKeyArm64Android) - bazelContext.QueueBazelRequest("duplicate", cquery.GetOutputFiles, cfgKeyArm64Android) - bazelContext.QueueBazelRequest("xxx", cquery.GetOutputFiles, cfgKeyArm64Linux) - bazelContext.QueueBazelRequest("aaa", cquery.GetOutputFiles, cfgKeyArm64Android) - bazelContext.QueueBazelRequest("aaa", cquery.GetOutputFiles, cfgKeyOtherAndroid) - bazelContext.QueueBazelRequest("bbb", cquery.GetOutputFiles, cfgKeyOtherAndroid) - - if len(bazelContext.requests) != 7 { - t.Error("Expected 7 request elements, but got", len(bazelContext.requests)) - } - - lastString := "" - for _, val := range bazelContext.requests { - thisString := val.String() - if thisString <= lastString { - t.Errorf("Requests are not ordered correctly. '%s' came before '%s'", lastString, thisString) - } - lastString = thisString - } -} - -func TestIsModuleNameAllowed(t *testing.T) { - libDisabled := "lib_disabled" - libEnabled := "lib_enabled" - libDclaWithinApex := "lib_dcla_within_apex" - libDclaNonApex := "lib_dcla_non_apex" - libNotConverted := "lib_not_converted" - - disabledModules := map[string]bool{ - libDisabled: true, - } - enabledModules := map[string]bool{ - libEnabled: true, - } - dclaEnabledModules := map[string]bool{ - libDclaWithinApex: true, - libDclaNonApex: true, - } - - bazelContext := &mixedBuildBazelContext{ - modulesDefaultToBazel: false, - bazelEnabledModules: enabledModules, - bazelDisabledModules: disabledModules, - bazelDclaEnabledModules: dclaEnabledModules, - } - - if bazelContext.IsModuleNameAllowed(libDisabled, true) { - t.Fatalf("%s shouldn't be allowed for mixed build", libDisabled) - } - - if !bazelContext.IsModuleNameAllowed(libEnabled, true) { - t.Fatalf("%s should be allowed for mixed build", libEnabled) - } - - if !bazelContext.IsModuleNameAllowed(libDclaWithinApex, true) { - t.Fatalf("%s should be allowed for mixed build", libDclaWithinApex) - } - - if bazelContext.IsModuleNameAllowed(libDclaNonApex, false) { - t.Fatalf("%s shouldn't be allowed for mixed build", libDclaNonApex) - } - - if bazelContext.IsModuleNameAllowed(libNotConverted, true) { - t.Fatalf("%s shouldn't be allowed for mixed build", libNotConverted) - } -} - -func verifyExtraFlags(t *testing.T, config Config, expected string) string { - bazelContext, _ := testBazelContext(t, map[bazelCommand]string{}) - - err := bazelContext.InvokeBazel(config, &testInvokeBazelContext{}) - if err != nil { - t.Fatalf("Did not expect error invoking Bazel, but got %s", err) - } - - flags := bazelContext.bazelRunner.(*mockBazelRunner).extraFlags - if expected := 3; len(flags) != expected { - t.Errorf("Expected %d extra flags got %#v", expected, flags) - } - - actual := flags[1] - if !strings.Contains(actual, expected) { - t.Errorf("Expected %#v got %#v", expected, actual) - } - - return actual -} - -func testBazelContext(t *testing.T, bazelCommandResults map[bazelCommand]string) (*mixedBuildBazelContext, string) { - t.Helper() - p := bazelPaths{ - soongOutDir: t.TempDir(), - outputBase: "outputbase", - workspaceDir: "workspace_dir", - } - aqueryCommand := bazelCommand{command: "aquery", expression: "deps(@soong_injection//mixed_builds:buildroot)"} - if _, exists := bazelCommandResults[aqueryCommand]; !exists { - bazelCommandResults[aqueryCommand] = "" - } - runner := &mockBazelRunner{bazelCommandResults: bazelCommandResults} - return &mixedBuildBazelContext{ - bazelRunner: runner, - paths: &p, - }, p.soongOutDir -} - -// Transform the json format to ActionGraphContainer -func JsonToActionGraphContainer(inputString string) ([]byte, error) { - var aqueryProtoResult analysis_v2_proto.ActionGraphContainer - err := json.Unmarshal([]byte(inputString), &aqueryProtoResult) - if err != nil { - return []byte(""), err - } - data, _ := proto.Marshal(&aqueryProtoResult) - return data, err -} diff --git a/android/bazel_paths.go b/android/bazel_paths.go deleted file mode 100644 index bad7baf06a..0000000000 --- a/android/bazel_paths.go +++ /dev/null @@ -1,593 +0,0 @@ -// Copyright 2015 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" - "path/filepath" - "strings" - - "android/soong/bazel" - - "github.com/google/blueprint" - "github.com/google/blueprint/pathtools" -) - -// bazel_paths contains methods to: -// * resolve Soong path and module references into bazel.LabelList -// * resolve Bazel path references into Soong-compatible paths -// -// There is often a similar method for Bazel as there is for Soong path handling and should be used -// in similar circumstances -// -// Bazel Soong -// ============================================================== -// BazelLabelForModuleSrc PathForModuleSrc -// BazelLabelForModuleSrcExcludes PathForModuleSrcExcludes -// BazelLabelForModuleDeps n/a -// tbd PathForSource -// tbd ExistentPathsForSources -// PathForBazelOut PathForModuleOut -// -// Use cases: -// * Module contains a property (often tagged `android:"path"`) that expects paths *relative to the -// module directory*: -// * BazelLabelForModuleSrcExcludes, if the module also contains an excludes_ property -// * BazelLabelForModuleSrc, otherwise -// * Converting references to other modules to Bazel Labels: -// BazelLabelForModuleDeps -// * Converting a path obtained from bazel_handler cquery results: -// PathForBazelOut -// -// NOTE: all Soong globs are expanded within Soong rather than being converted to a Bazel glob -// syntax. This occurs because Soong does not have a concept of crossing package boundaries, -// so the glob as computed by Soong may contain paths that cross package-boundaries. These -// would be unknowingly omitted if the glob were handled by Bazel. By expanding globs within -// Soong, we support identification and detection (within Bazel) use of paths that cross -// package boundaries. -// -// Path resolution: -// * filepath/globs: resolves as itself or is converted to an absolute Bazel label (e.g. -// //path/to/dir:) if path exists in a separate package or subpackage. -// * references to other modules (using the ":name{.tag}" syntax). These resolve as a Bazel label -// for a target. If the Bazel target is in the local module directory, it will be returned -// relative to the current package (e.g. ":"). Otherwise, it will be returned as an -// absolute Bazel label (e.g. "//path/to/dir:"). If the reference to another module -// cannot be resolved,the function will panic. This is often due to the dependency not being added -// via an AddDependency* method. - -// BazelConversionContext is a minimal context interface to check if a module should be converted by bp2build, -// with functions containing information to match against allowlists and denylists. -// If a module is deemed to be convertible by bp2build, then it should rely on a -// BazelConversionPathContext for more functions for dep/path features. -type BazelConversionContext interface { - Config() Config - - Module() Module - OtherModuleType(m blueprint.Module) string - OtherModuleName(m blueprint.Module) string - OtherModuleDir(m blueprint.Module) string - ModuleErrorf(format string, args ...interface{}) -} - -// A subset of the ModuleContext methods which are sufficient to resolve references to paths/deps in -// order to form a Bazel-compatible label for conversion. -type BazelConversionPathContext interface { - EarlyModulePathContext - BazelConversionContext - - ModuleErrorf(fmt string, args ...interface{}) - PropertyErrorf(property, fmt string, args ...interface{}) - GetDirectDep(name string) (blueprint.Module, blueprint.DependencyTag) - ModuleFromName(name string) (blueprint.Module, bool) - AddUnconvertedBp2buildDep(string) - AddMissingBp2buildDep(dep string) -} - -// BazelLabelForModuleDeps expects a list of reference to other modules, ("" -// or ":") and returns a Bazel-compatible label which corresponds to dependencies on the -// module within the given ctx. -func BazelLabelForModuleDeps(ctx BazelConversionPathContext, modules []string) bazel.LabelList { - return BazelLabelForModuleDepsWithFn(ctx, modules, BazelModuleLabel) -} - -// BazelLabelForModuleWholeDepsExcludes expects two lists: modules (containing modules to include in -// the list), and excludes (modules to exclude from the list). Both of these should contain -// references to other modules, ("" or ":"). It returns a Bazel-compatible label -// list which corresponds to dependencies on the module within the given ctx, and the excluded -// dependencies. Prebuilt dependencies will be appended with _alwayslink so they can be handled as -// whole static libraries. -func BazelLabelForModuleDepsExcludes(ctx BazelConversionPathContext, modules, excludes []string) bazel.LabelList { - return BazelLabelForModuleDepsExcludesWithFn(ctx, modules, excludes, BazelModuleLabel) -} - -// BazelLabelForModuleDepsWithFn expects a list of reference to other modules, ("" -// or ":") and applies moduleToLabelFn to determine and return a Bazel-compatible label -// which corresponds to dependencies on the module within the given ctx. -func BazelLabelForModuleDepsWithFn(ctx BazelConversionPathContext, modules []string, - moduleToLabelFn func(BazelConversionPathContext, blueprint.Module) string) bazel.LabelList { - var labels bazel.LabelList - // In some cases, a nil string list is different than an explicitly empty list. - if len(modules) == 0 && modules != nil { - labels.Includes = []bazel.Label{} - return labels - } - for _, module := range modules { - bpText := module - if m := SrcIsModule(module); m == "" { - module = ":" + module - } - if m, t := SrcIsModuleWithTag(module); m != "" { - l := getOtherModuleLabel(ctx, m, t, moduleToLabelFn) - if l != nil { - l.OriginalModuleName = bpText - labels.Includes = append(labels.Includes, *l) - } - } else { - ctx.ModuleErrorf("%q, is not a module reference", module) - } - } - return labels -} - -// BazelLabelForModuleDepsExcludesWithFn expects two lists: modules (containing modules to include in the -// list), and excludes (modules to exclude from the list). Both of these should contain references -// to other modules, ("" or ":"). It applies moduleToLabelFn to determine and return a -// Bazel-compatible label list which corresponds to dependencies on the module within the given ctx, and -// the excluded dependencies. -func BazelLabelForModuleDepsExcludesWithFn(ctx BazelConversionPathContext, modules, excludes []string, - moduleToLabelFn func(BazelConversionPathContext, blueprint.Module) string) bazel.LabelList { - moduleLabels := BazelLabelForModuleDepsWithFn(ctx, RemoveListFromList(modules, excludes), moduleToLabelFn) - if len(excludes) == 0 { - return moduleLabels - } - excludeLabels := BazelLabelForModuleDepsWithFn(ctx, excludes, moduleToLabelFn) - return bazel.LabelList{ - Includes: moduleLabels.Includes, - Excludes: excludeLabels.Includes, - } -} - -func BazelLabelForModuleSrcSingle(ctx BazelConversionPathContext, path string) bazel.Label { - if srcs := BazelLabelForModuleSrcExcludes(ctx, []string{path}, []string(nil)).Includes; len(srcs) > 0 { - return srcs[0] - } - return bazel.Label{} -} - -func BazelLabelForModuleDepSingle(ctx BazelConversionPathContext, path string) bazel.Label { - if srcs := BazelLabelForModuleDepsExcludes(ctx, []string{path}, []string(nil)).Includes; len(srcs) > 0 { - return srcs[0] - } - return bazel.Label{} -} - -// BazelLabelForModuleSrc expects a list of path (relative to local module directory) and module -// references (":") and returns a bazel.LabelList{} containing the resolved references in -// paths, relative to the local module, or Bazel-labels (absolute if in a different package or -// relative if within the same package). -// Properties must have been annotated with struct tag `android:"path"` so that dependencies modules -// will have already been handled by the path_deps mutator. -func BazelLabelForModuleSrc(ctx BazelConversionPathContext, paths []string) bazel.LabelList { - return BazelLabelForModuleSrcExcludes(ctx, paths, []string(nil)) -} - -// BazelLabelForModuleSrc expects lists of path and excludes (relative to local module directory) -// and module references (":") and returns a bazel.LabelList{} containing the resolved -// references in paths, minus those in excludes, relative to the local module, or Bazel-labels -// (absolute if in a different package or relative if within the same package). -// Properties must have been annotated with struct tag `android:"path"` so that dependencies modules -// will have already been handled by the path_deps mutator. -func BazelLabelForModuleSrcExcludes(ctx BazelConversionPathContext, paths, excludes []string) bazel.LabelList { - excludeLabels := expandSrcsForBazel(ctx, excludes, []string(nil)) - excluded := make([]string, 0, len(excludeLabels.Includes)) - for _, e := range excludeLabels.Includes { - excluded = append(excluded, e.Label) - } - labels := expandSrcsForBazel(ctx, paths, excluded) - labels.Excludes = excludeLabels.Includes - labels = transformSubpackagePaths(ctx, labels) - return labels -} - -// Returns true if a prefix + components[:i] is a package boundary. -// -// A package boundary is determined by a BUILD file in the directory. This can happen in 2 cases: -// -// 1. An Android.bp exists, which bp2build will always convert to a sibling BUILD file. -// 2. An Android.bp doesn't exist, but a checked-in BUILD/BUILD.bazel file exists, and that file -// is allowlisted by the bp2build configuration to be merged into the symlink forest workspace. -func isPackageBoundary(config Config, prefix string, components []string, componentIndex int) bool { - prefix = filepath.Join(prefix, filepath.Join(components[:componentIndex+1]...)) - if exists, _, _ := config.fs.Exists(filepath.Join(prefix, "Android.bp")); exists { - return true - } else if config.Bp2buildPackageConfig.ShouldKeepExistingBuildFileForDir(prefix) { - if exists, _, _ := config.fs.Exists(filepath.Join(prefix, "BUILD")); exists { - return true - } else if exists, _, _ := config.fs.Exists(filepath.Join(prefix, "BUILD.bazel")); exists { - return true - } - } - - return false -} - -// Transform a path (if necessary) to acknowledge package boundaries -// -// e.g. something like -// -// async_safe/include/async_safe/CHECK.h -// -// might become -// -// //bionic/libc/async_safe:include/async_safe/CHECK.h -// -// if the "async_safe" directory is actually a package and not just a directory. -// -// In particular, paths that extend into packages are transformed into absolute labels beginning with //. -func transformSubpackagePath(ctx BazelConversionPathContext, path bazel.Label) bazel.Label { - var newPath bazel.Label - - // Don't transform OriginalModuleName - newPath.OriginalModuleName = path.OriginalModuleName - - if strings.HasPrefix(path.Label, "//") { - // Assume absolute labels are already correct (e.g. //path/to/some/package:foo.h) - newPath.Label = path.Label - return newPath - } - if strings.HasPrefix(path.Label, "./") { - // Drop "./" for consistent handling of paths. - // Specifically, to not let "." be considered a package boundary. - // Say `inputPath` is `x/Android.bp` and that file has some module - // with `srcs=["y/a.c", "z/b.c"]`. - // And say the directory tree is: - // x - // ├── Android.bp - // ├── y - // │ ├── a.c - // │ └── Android.bp - // └── z - // └── b.c - // Then bazel equivalent labels in srcs should be: - // //x/y:a.c, x/z/b.c - // The above should still be the case if `x/Android.bp` had - // srcs=["./y/a.c", "./z/b.c"] - // However, if we didn't strip "./", we'd get - // //x/./y:a.c, //x/.:z/b.c - path.Label = strings.TrimPrefix(path.Label, "./") - } - pathComponents := strings.Split(path.Label, "/") - newLabel := "" - foundPackageBoundary := false - // Check the deepest subdirectory first and work upwards - for i := len(pathComponents) - 1; i >= 0; i-- { - pathComponent := pathComponents[i] - var sep string - if !foundPackageBoundary && isPackageBoundary(ctx.Config(), ctx.ModuleDir(), pathComponents, i) { - sep = ":" - foundPackageBoundary = true - } else { - sep = "/" - } - if newLabel == "" { - newLabel = pathComponent - } else { - newLabel = pathComponent + sep + newLabel - } - } - if foundPackageBoundary { - // Ensure paths end up looking like //bionic/... instead of //./bionic/... - moduleDir := ctx.ModuleDir() - if strings.HasPrefix(moduleDir, ".") { - moduleDir = moduleDir[1:] - } - // Make the path into an absolute label (e.g. //bionic/libc/foo:bar.h instead of just foo:bar.h) - if moduleDir == "" { - newLabel = "//" + newLabel - } else { - newLabel = "//" + moduleDir + "/" + newLabel - } - } - newPath.Label = newLabel - - return newPath -} - -// Transform paths to acknowledge package boundaries -// See transformSubpackagePath() for more information -func transformSubpackagePaths(ctx BazelConversionPathContext, paths bazel.LabelList) bazel.LabelList { - var newPaths bazel.LabelList - for _, include := range paths.Includes { - newPaths.Includes = append(newPaths.Includes, transformSubpackagePath(ctx, include)) - } - for _, exclude := range paths.Excludes { - newPaths.Excludes = append(newPaths.Excludes, transformSubpackagePath(ctx, exclude)) - } - return newPaths -} - -// Converts root-relative Paths to a list of bazel.Label relative to the module in ctx. -func RootToModuleRelativePaths(ctx BazelConversionPathContext, paths Paths) []bazel.Label { - var newPaths []bazel.Label - for _, path := range PathsWithModuleSrcSubDir(ctx, paths, "") { - s := path.Rel() - newPaths = append(newPaths, bazel.Label{Label: s}) - } - return newPaths -} - -// expandSrcsForBazel returns bazel.LabelList with paths rooted from the module's local source -// directory and Bazel target labels, excluding those included in the excludes argument (which -// should already be expanded to resolve references to Soong-modules). Valid elements of paths -// include: -// - filepath, relative to local module directory, resolves as a filepath relative to the local -// source directory -// - glob, relative to the local module directory, resolves as filepath(s), relative to the local -// module directory. Because Soong does not have a concept of crossing package boundaries, the -// glob as computed by Soong may contain paths that cross package-boundaries that would be -// unknowingly omitted if the glob were handled by Bazel. To allow identification and detect -// (within Bazel) use of paths that cross package boundaries, we expand globs within Soong rather -// than converting Soong glob syntax to Bazel glob syntax. **Invalid for excludes.** -// - other modules using the ":name{.tag}" syntax. These modules must implement SourceFileProducer -// or OutputFileProducer. These resolve as a Bazel label for a target. If the Bazel target is in -// the local module directory, it will be returned relative to the current package (e.g. -// ":"). Otherwise, it will be returned as an absolute Bazel label (e.g. -// "//path/to/dir:"). If the reference to another module cannot be resolved,the function -// will panic. -// -// Properties passed as the paths or excludes argument must have been annotated with struct tag -// `android:"path"` so that dependencies on other modules will have already been handled by the -// path_deps mutator. -func expandSrcsForBazel(ctx BazelConversionPathContext, paths, expandedExcludes []string) bazel.LabelList { - if paths == nil { - return bazel.LabelList{} - } - labels := bazel.LabelList{ - Includes: []bazel.Label{}, - } - - // expandedExcludes contain module-dir relative paths, but root-relative paths - // are needed for GlobFiles later. - var rootRelativeExpandedExcludes []string - for _, e := range expandedExcludes { - rootRelativeExpandedExcludes = append(rootRelativeExpandedExcludes, filepath.Join(ctx.ModuleDir(), e)) - } - - for _, p := range paths { - if m, tag := SrcIsModuleWithTag(p); m != "" { - l := getOtherModuleLabel(ctx, m, tag, BazelModuleLabel) - if l != nil && !InList(l.Label, expandedExcludes) { - l.OriginalModuleName = fmt.Sprintf(":%s", m) - labels.Includes = append(labels.Includes, *l) - } - } else { - var expandedPaths []bazel.Label - if pathtools.IsGlob(p) { - // e.g. turn "math/*.c" in - // external/arm-optimized-routines to external/arm-optimized-routines/math/*.c - rootRelativeGlobPath := pathForModuleSrc(ctx, p).String() - expandedPaths = RootToModuleRelativePaths(ctx, GlobFiles(ctx, rootRelativeGlobPath, rootRelativeExpandedExcludes)) - } else { - if !InList(p, expandedExcludes) { - expandedPaths = append(expandedPaths, bazel.Label{Label: p}) - } - } - labels.Includes = append(labels.Includes, expandedPaths...) - } - } - return labels -} - -// getOtherModuleLabel returns a bazel.Label for the given dependency/tag combination for the -// module. The label will be relative to the current directory if appropriate. The dependency must -// already be resolved by either deps mutator or path deps mutator. -func getOtherModuleLabel(ctx BazelConversionPathContext, dep, tag string, - labelFromModule func(BazelConversionPathContext, blueprint.Module) string) *bazel.Label { - m, _ := ctx.ModuleFromName(dep) - // The module was not found in an Android.bp file, this is often due to: - // * a limited manifest - // * a required module not being converted from Android.mk - if m == nil { - ctx.AddMissingBp2buildDep(dep) - return &bazel.Label{ - Label: ":" + dep + "__BP2BUILD__MISSING__DEP", - } - } - if !convertedToBazel(ctx, m) { - ctx.AddUnconvertedBp2buildDep(dep) - } - label := BazelModuleLabel(ctx, ctx.Module()) - otherLabel := labelFromModule(ctx, m) - - // TODO(b/165114590): Convert tag (":name{.tag}") to corresponding Bazel implicit output targets. - - if samePackage(label, otherLabel) { - otherLabel = bazelShortLabel(otherLabel) - } - - return &bazel.Label{ - Label: otherLabel, - } -} - -func BazelModuleLabel(ctx BazelConversionPathContext, module blueprint.Module) string { - // TODO(b/165114590): Convert tag (":name{.tag}") to corresponding Bazel implicit output targets. - if !convertedToBazel(ctx, module) { - return bp2buildModuleLabel(ctx, module) - } - b, _ := module.(Bazelable) - return b.GetBazelLabel(ctx, module) -} - -func bazelShortLabel(label string) string { - i := strings.Index(label, ":") - if i == -1 { - panic(fmt.Errorf("Could not find the ':' character in '%s', expected a fully qualified label.", label)) - } - return label[i:] -} - -func bazelPackage(label string) string { - i := strings.Index(label, ":") - if i == -1 { - panic(fmt.Errorf("Could not find the ':' character in '%s', expected a fully qualified label.", label)) - } - return label[0:i] -} - -func samePackage(label1, label2 string) bool { - return bazelPackage(label1) == bazelPackage(label2) -} - -func bp2buildModuleLabel(ctx BazelConversionContext, module blueprint.Module) string { - moduleName := ctx.OtherModuleName(module) - moduleDir := ctx.OtherModuleDir(module) - if moduleDir == Bp2BuildTopLevel { - moduleDir = "" - } - return fmt.Sprintf("//%s:%s", moduleDir, moduleName) -} - -// BazelOutPath is a Bazel output path compatible to be used for mixed builds within Soong/Ninja. -type BazelOutPath struct { - OutputPath -} - -// ensure BazelOutPath implements Path -var _ Path = BazelOutPath{} - -// ensure BazelOutPath implements genPathProvider -var _ genPathProvider = BazelOutPath{} - -// ensure BazelOutPath implements objPathProvider -var _ objPathProvider = BazelOutPath{} - -func (p BazelOutPath) genPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleGenPath { - return PathForModuleGen(ctx, subdir, pathtools.ReplaceExtension(p.path, ext)) -} - -func (p BazelOutPath) objPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleObjPath { - return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext)) -} - -// PathForBazelOutRelative returns a BazelOutPath representing the path under an output directory dedicated to -// bazel-owned outputs. Calling .Rel() on the result will give the input path as relative to the given -// relativeRoot. -func PathForBazelOutRelative(ctx PathContext, relativeRoot string, path string) BazelOutPath { - validatedPath, err := validatePath(filepath.Join("execroot", "__main__", path)) - if err != nil { - reportPathError(ctx, err) - } - var relativeRootPath string - if pathComponents := strings.SplitN(path, "/", 4); len(pathComponents) >= 3 && - pathComponents[0] == "bazel-out" && pathComponents[2] == "bin" { - // If the path starts with something like: bazel-out/linux_x86_64-fastbuild-ST-b4ef1c4402f9/bin/ - // make it relative to that folder. bazel-out/volatile-status.txt is an example - // of something that starts with bazel-out but is not relative to the bin folder - relativeRootPath = filepath.Join("execroot", "__main__", pathComponents[0], pathComponents[1], pathComponents[2], relativeRoot) - } else { - relativeRootPath = filepath.Join("execroot", "__main__", relativeRoot) - } - - var relPath string - if relPath, err = filepath.Rel(relativeRootPath, validatedPath); err != nil || strings.HasPrefix(relPath, "../") { - // We failed to make this path relative to execroot/__main__, fall back to a non-relative path - // One case where this happens is when path is ../bazel_tools/something - relativeRootPath = "" - relPath = validatedPath - } - - outputPath := OutputPath{ - basePath{"", ""}, - ctx.Config().soongOutDir, - ctx.Config().BazelContext.OutputBase(), - } - - return BazelOutPath{ - // .withRel() appends its argument onto the current path, and only the most - // recently appended part is returned by outputPath.rel(). - // So outputPath.rel() will return relPath. - OutputPath: outputPath.withRel(relativeRootPath).withRel(relPath), - } -} - -// PathForBazelOut returns a BazelOutPath representing the path under an output directory dedicated to -// bazel-owned outputs. -func PathForBazelOut(ctx PathContext, path string) BazelOutPath { - return PathForBazelOutRelative(ctx, "", path) -} - -// PathsForBazelOut returns a list of paths representing the paths under an output directory -// dedicated to Bazel-owned outputs. -func PathsForBazelOut(ctx PathContext, paths []string) Paths { - outs := make(Paths, 0, len(paths)) - for _, p := range paths { - outs = append(outs, PathForBazelOut(ctx, p)) - } - return outs -} - -// BazelStringOrLabelFromProp splits a Soong module property that can be -// either a string literal, path (with android:path tag) or a module reference -// into separate bazel string or label attributes. Bazel treats string and label -// attributes as distinct types, so this function categorizes a string property -// into either one of them. -// -// e.g. apex.private_key = "foo.pem" can either refer to: -// -// 1. "foo.pem" in the current directory -> file target -// 2. "foo.pem" module -> rule target -// 3. "foo.pem" file in a different directory, prefixed by a product variable handled -// in a bazel macro. -> string literal -// -// For the first two cases, they are defined using the label attribute. For the third case, -// it's defined with the string attribute. -func BazelStringOrLabelFromProp( - ctx TopDownMutatorContext, - propToDistinguish *string) (bazel.LabelAttribute, bazel.StringAttribute) { - - var labelAttr bazel.LabelAttribute - var strAttr bazel.StringAttribute - - if propToDistinguish == nil { - // nil pointer - return labelAttr, strAttr - } - - prop := String(propToDistinguish) - if SrcIsModule(prop) != "" { - // If it's a module (SrcIsModule will return the module name), set the - // resolved label to the label attribute. - labelAttr.SetValue(BazelLabelForModuleDepSingle(ctx, prop)) - } else { - // Not a module name. This could be a string literal or a file target in - // the current dir. Check if the path exists: - path := ExistentPathForSource(ctx, ctx.ModuleDir(), prop) - - if path.Valid() && parentDir(path.String()) == ctx.ModuleDir() { - // If it exists and the path is relative to the current dir, resolve the bazel label - // for the _file target_ and set it to the label attribute. - // - // Resolution is necessary because this could be a file in a subpackage. - labelAttr.SetValue(BazelLabelForModuleSrcSingle(ctx, prop)) - } else { - // Otherwise, treat it as a string literal and assign to the string attribute. - strAttr.Value = propToDistinguish - } - } - - return labelAttr, strAttr -} diff --git a/android/bazel_paths_test.go b/android/bazel_paths_test.go deleted file mode 100644 index 450bf7674f..0000000000 --- a/android/bazel_paths_test.go +++ /dev/null @@ -1,183 +0,0 @@ -// Copyright 2022 Google Inc. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package android - -import ( - "path/filepath" - "testing" - - "android/soong/bazel" - "github.com/google/blueprint" - "github.com/google/blueprint/pathtools" -) - -type TestBazelPathContext struct{} - -func (*TestBazelPathContext) Config() Config { - cfg := NullConfig("out", "out/soong") - cfg.BazelContext = MockBazelContext{ - OutputBaseDir: "out/bazel", - } - return cfg -} - -func (*TestBazelPathContext) AddNinjaFileDeps(...string) { - panic("Unimplemented") -} - -func TestPathForBazelOut(t *testing.T) { - ctx := &TestBazelPathContext{} - out := PathForBazelOut(ctx, "foo/bar/baz/boq.txt") - expectedPath := filepath.Join(ctx.Config().BazelContext.OutputBase(), "execroot/__main__/foo/bar/baz/boq.txt") - if out.String() != expectedPath { - t.Errorf("incorrect OutputPath: expected %q, got %q", expectedPath, out.String()) - } - - expectedRelPath := "foo/bar/baz/boq.txt" - if out.Rel() != expectedRelPath { - t.Errorf("incorrect OutputPath.Rel(): expected %q, got %q", expectedRelPath, out.Rel()) - } -} - -func TestPathForBazelOutRelative(t *testing.T) { - ctx := &TestBazelPathContext{} - out := PathForBazelOutRelative(ctx, "foo/bar", "foo/bar/baz/boq.txt") - - expectedPath := filepath.Join(ctx.Config().BazelContext.OutputBase(), "execroot/__main__/foo/bar/baz/boq.txt") - if out.String() != expectedPath { - t.Errorf("incorrect OutputPath: expected %q, got %q", expectedPath, out.String()) - } - - expectedRelPath := "baz/boq.txt" - if out.Rel() != expectedRelPath { - t.Errorf("incorrect OutputPath.Rel(): expected %q, got %q", expectedRelPath, out.Rel()) - } -} - -func TestPathForBazelOutRelativeUnderBinFolder(t *testing.T) { - ctx := &TestBazelPathContext{} - out := PathForBazelOutRelative(ctx, "foo/bar", "bazel-out/linux_x86_64-fastbuild-ST-b4ef1c4402f9/bin/foo/bar/baz/boq.txt") - - expectedPath := filepath.Join(ctx.Config().BazelContext.OutputBase(), "execroot/__main__/bazel-out/linux_x86_64-fastbuild-ST-b4ef1c4402f9/bin/foo/bar/baz/boq.txt") - if out.String() != expectedPath { - t.Errorf("incorrect OutputPath: expected %q, got %q", expectedPath, out.String()) - } - - expectedRelPath := "baz/boq.txt" - if out.Rel() != expectedRelPath { - t.Errorf("incorrect OutputPath.Rel(): expected %q, got %q", expectedRelPath, out.Rel()) - } -} - -func TestPathForBazelOutOutsideOfExecroot(t *testing.T) { - ctx := &TestBazelPathContext{} - out := PathForBazelOut(ctx, "../bazel_tools/linux_x86_64-fastbuild/bin/tools/android/java_base_extras.jar") - - expectedPath := filepath.Join(ctx.Config().BazelContext.OutputBase(), "execroot/bazel_tools/linux_x86_64-fastbuild/bin/tools/android/java_base_extras.jar") - if out.String() != expectedPath { - t.Errorf("incorrect OutputPath: expected %q, got %q", expectedPath, out.String()) - } - - expectedRelPath := "execroot/bazel_tools/linux_x86_64-fastbuild/bin/tools/android/java_base_extras.jar" - if out.Rel() != expectedRelPath { - t.Errorf("incorrect OutputPath.Rel(): expected %q, got %q", expectedRelPath, out.Rel()) - } -} - -func TestPathForBazelOutRelativeWithParentDirectoryRoot(t *testing.T) { - ctx := &TestBazelPathContext{} - out := PathForBazelOutRelative(ctx, "../bazel_tools", "../bazel_tools/foo/bar/baz.sh") - - expectedPath := filepath.Join(ctx.Config().BazelContext.OutputBase(), "execroot/bazel_tools/foo/bar/baz.sh") - if out.String() != expectedPath { - t.Errorf("incorrect OutputPath: expected %q, got %q", expectedPath, out.String()) - } - - expectedRelPath := "foo/bar/baz.sh" - if out.Rel() != expectedRelPath { - t.Errorf("incorrect OutputPath.Rel(): expected %q, got %q", expectedRelPath, out.Rel()) - } -} - -type TestBazelConversionPathContext struct { - TestBazelConversionContext - moduleDir string - cfg Config -} - -func (ctx *TestBazelConversionPathContext) AddNinjaFileDeps(...string) { - panic("Unimplemented") -} - -func (ctx *TestBazelConversionPathContext) GlobWithDeps(string, []string) ([]string, error) { - panic("Unimplemented") -} - -func (ctx *TestBazelConversionPathContext) PropertyErrorf(string, string, ...interface{}) { - panic("Unimplemented") -} - -func (ctx *TestBazelConversionPathContext) GetDirectDep(string) (blueprint.Module, blueprint.DependencyTag) { - panic("Unimplemented") -} - -func (ctx *TestBazelConversionPathContext) ModuleFromName(string) (blueprint.Module, bool) { - panic("Unimplemented") -} - -func (ctx *TestBazelConversionPathContext) AddUnconvertedBp2buildDep(string) { - panic("Unimplemented") -} - -func (ctx *TestBazelConversionPathContext) AddMissingBp2buildDep(string) { - panic("Unimplemented") -} - -func (ctx *TestBazelConversionPathContext) Module() Module { - panic("Unimplemented") -} - -func (ctx *TestBazelConversionPathContext) Config() Config { - return ctx.cfg -} - -func (ctx *TestBazelConversionPathContext) ModuleDir() string { - return ctx.moduleDir -} - -func TestTransformSubpackagePath(t *testing.T) { - cfg := NullConfig("out", "out/soong") - cfg.fs = pathtools.MockFs(map[string][]byte{ - "x/Android.bp": nil, - "x/y/Android.bp": nil, - }) - - var ctx BazelConversionPathContext = &TestBazelConversionPathContext{ - moduleDir: "x", - cfg: cfg, - } - pairs := map[string]string{ - "y/a.c": "//x/y:a.c", - "./y/a.c": "//x/y:a.c", - "z/b.c": "z/b.c", - "./z/b.c": "z/b.c", - } - for in, out := range pairs { - actual := transformSubpackagePath(ctx, bazel.Label{Label: in}).Label - if actual != out { - t.Errorf("expected:\n%v\nactual:\n%v", out, actual) - } - } -} diff --git a/android/bazel_test.go b/android/bazel_test.go deleted file mode 100644 index 87b2c8f429..0000000000 --- a/android/bazel_test.go +++ /dev/null @@ -1,438 +0,0 @@ -// 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 ( - "fmt" - "testing" - - "android/soong/android/allowlists" - "android/soong/bazel" - - "github.com/google/blueprint" - "github.com/google/blueprint/proptools" -) - -func TestConvertAllModulesInPackage(t *testing.T) { - testCases := []struct { - prefixes allowlists.Bp2BuildConfig - packageDir string - }{ - { - prefixes: allowlists.Bp2BuildConfig{ - "a": allowlists.Bp2BuildDefaultTrueRecursively, - }, - packageDir: "a", - }, - { - prefixes: allowlists.Bp2BuildConfig{ - "a/b": allowlists.Bp2BuildDefaultTrueRecursively, - }, - packageDir: "a/b", - }, - { - prefixes: allowlists.Bp2BuildConfig{ - "a/b": allowlists.Bp2BuildDefaultTrueRecursively, - "a/b/c": allowlists.Bp2BuildDefaultTrueRecursively, - }, - packageDir: "a/b", - }, - { - prefixes: allowlists.Bp2BuildConfig{ - "a": allowlists.Bp2BuildDefaultTrueRecursively, - "d/e/f": allowlists.Bp2BuildDefaultTrueRecursively, - }, - packageDir: "a/b", - }, - { - prefixes: allowlists.Bp2BuildConfig{ - "a": allowlists.Bp2BuildDefaultFalse, - "a/b": allowlists.Bp2BuildDefaultTrueRecursively, - "a/b/c": allowlists.Bp2BuildDefaultFalse, - }, - packageDir: "a/b", - }, - { - prefixes: allowlists.Bp2BuildConfig{ - "a": allowlists.Bp2BuildDefaultTrueRecursively, - "a/b": allowlists.Bp2BuildDefaultFalse, - "a/b/c": allowlists.Bp2BuildDefaultTrueRecursively, - }, - packageDir: "a", - }, - { - prefixes: allowlists.Bp2BuildConfig{ - "a": allowlists.Bp2BuildDefaultFalseRecursively, - "a/b": allowlists.Bp2BuildDefaultTrue, - }, - packageDir: "a/b", - }, - { - prefixes: allowlists.Bp2BuildConfig{ - "a": allowlists.Bp2BuildDefaultFalseRecursively, - "a/b": allowlists.Bp2BuildDefaultTrueRecursively, - }, - packageDir: "a/b/c", - }, - } - - for _, test := range testCases { - if ok, _ := bp2buildDefaultTrueRecursively(test.packageDir, test.prefixes); !ok { - t.Errorf("Expected to convert all modules in %s based on %v, but failed.", test.packageDir, test.prefixes) - } - } -} - -func TestModuleOptIn(t *testing.T) { - testCases := []struct { - prefixes allowlists.Bp2BuildConfig - packageDir string - }{ - { - prefixes: allowlists.Bp2BuildConfig{ - "a/b": allowlists.Bp2BuildDefaultFalse, - }, - packageDir: "a/b", - }, - { - prefixes: allowlists.Bp2BuildConfig{ - "a": allowlists.Bp2BuildDefaultFalse, - "a/b": allowlists.Bp2BuildDefaultTrueRecursively, - }, - packageDir: "a", - }, - { - prefixes: allowlists.Bp2BuildConfig{ - "a/b": allowlists.Bp2BuildDefaultTrueRecursively, - }, - packageDir: "a", // opt-in by default - }, - { - prefixes: allowlists.Bp2BuildConfig{ - "a/b/c": allowlists.Bp2BuildDefaultTrueRecursively, - }, - packageDir: "a/b", - }, - { - prefixes: allowlists.Bp2BuildConfig{ - "a": allowlists.Bp2BuildDefaultTrueRecursively, - "d/e/f": allowlists.Bp2BuildDefaultTrueRecursively, - }, - packageDir: "foo/bar", - }, - { - prefixes: allowlists.Bp2BuildConfig{ - "a": allowlists.Bp2BuildDefaultTrueRecursively, - "a/b": allowlists.Bp2BuildDefaultFalse, - "a/b/c": allowlists.Bp2BuildDefaultTrueRecursively, - }, - packageDir: "a/b", - }, - { - prefixes: allowlists.Bp2BuildConfig{ - "a": allowlists.Bp2BuildDefaultFalse, - "a/b": allowlists.Bp2BuildDefaultTrueRecursively, - "a/b/c": allowlists.Bp2BuildDefaultFalse, - }, - packageDir: "a", - }, - { - prefixes: allowlists.Bp2BuildConfig{ - "a": allowlists.Bp2BuildDefaultFalseRecursively, - "a/b": allowlists.Bp2BuildDefaultTrue, - }, - packageDir: "a/b/c", - }, - { - prefixes: allowlists.Bp2BuildConfig{ - "a": allowlists.Bp2BuildDefaultTrueRecursively, - "a/b": allowlists.Bp2BuildDefaultFalseRecursively, - }, - packageDir: "a/b/c", - }, - } - - for _, test := range testCases { - if ok, _ := bp2buildDefaultTrueRecursively(test.packageDir, test.prefixes); ok { - t.Errorf("Expected to allow module opt-in in %s based on %v, but failed.", test.packageDir, test.prefixes) - } - } -} - -type TestBazelModule struct { - bazel.TestModuleInfo - BazelModuleBase -} - -var _ blueprint.Module = TestBazelModule{} - -func (m TestBazelModule) Name() string { - return m.TestModuleInfo.ModuleName -} - -func (m TestBazelModule) GenerateBuildActions(blueprint.ModuleContext) { -} - -type TestBazelConversionContext struct { - omc bazel.OtherModuleTestContext - allowlist Bp2BuildConversionAllowlist - errors []string -} - -var _ bazelOtherModuleContext = &TestBazelConversionContext{} - -func (bcc *TestBazelConversionContext) OtherModuleType(m blueprint.Module) string { - return bcc.omc.OtherModuleType(m) -} - -func (bcc *TestBazelConversionContext) OtherModuleName(m blueprint.Module) string { - return bcc.omc.OtherModuleName(m) -} - -func (bcc *TestBazelConversionContext) OtherModuleDir(m blueprint.Module) string { - return bcc.omc.OtherModuleDir(m) -} - -func (bcc *TestBazelConversionContext) ModuleErrorf(format string, args ...interface{}) { - bcc.errors = append(bcc.errors, fmt.Sprintf(format, args...)) -} - -func (bcc *TestBazelConversionContext) Config() Config { - return Config{ - &config{ - Bp2buildPackageConfig: bcc.allowlist, - }, - } -} - -var bazelableBazelModuleBase = BazelModuleBase{ - bazelProperties: properties{ - Bazel_module: bazelModuleProperties{ - CanConvertToBazel: true, - }, - }, -} - -func TestBp2BuildAllowlist(t *testing.T) { - testCases := []struct { - description string - shouldConvert bool - expectedErrors []string - module TestBazelModule - allowlist Bp2BuildConversionAllowlist - }{ - { - description: "allowlist enables module", - shouldConvert: true, - module: TestBazelModule{ - TestModuleInfo: bazel.TestModuleInfo{ - ModuleName: "foo", - Typ: "rule1", - Dir: "dir1", - }, - BazelModuleBase: bazelableBazelModuleBase, - }, - allowlist: Bp2BuildConversionAllowlist{ - moduleAlwaysConvert: map[string]bool{ - "foo": true, - }, - }, - }, - { - description: "module in name allowlist and type allowlist fails", - shouldConvert: false, - expectedErrors: []string{"A module cannot be in moduleAlwaysConvert and also be in moduleTypeAlwaysConvert"}, - module: TestBazelModule{ - TestModuleInfo: bazel.TestModuleInfo{ - ModuleName: "foo", - Typ: "rule1", - Dir: "dir1", - }, - BazelModuleBase: bazelableBazelModuleBase, - }, - allowlist: Bp2BuildConversionAllowlist{ - moduleAlwaysConvert: map[string]bool{ - "foo": true, - }, - moduleTypeAlwaysConvert: map[string]bool{ - "rule1": true, - }, - }, - }, - { - description: "module in allowlist and denylist fails", - shouldConvert: false, - expectedErrors: []string{"a module cannot be in moduleDoNotConvert and also be in moduleAlwaysConvert"}, - module: TestBazelModule{ - TestModuleInfo: bazel.TestModuleInfo{ - ModuleName: "foo", - Typ: "rule1", - Dir: "dir1", - }, - BazelModuleBase: bazelableBazelModuleBase, - }, - allowlist: Bp2BuildConversionAllowlist{ - moduleAlwaysConvert: map[string]bool{ - "foo": true, - }, - moduleDoNotConvert: map[string]bool{ - "foo": true, - }, - }, - }, - { - description: "module allowlist and enabled directory", - shouldConvert: false, - expectedErrors: []string{"A module cannot be in a directory marked Bp2BuildDefaultTrue or Bp2BuildDefaultTrueRecursively and also be in moduleAlwaysConvert. Directory: 'existing/build/dir' Module: 'foo'"}, - module: TestBazelModule{ - TestModuleInfo: bazel.TestModuleInfo{ - ModuleName: "foo", - Typ: "rule1", - Dir: "existing/build/dir", - }, - BazelModuleBase: bazelableBazelModuleBase, - }, - allowlist: Bp2BuildConversionAllowlist{ - moduleAlwaysConvert: map[string]bool{ - "foo": true, - }, - defaultConfig: allowlists.Bp2BuildConfig{ - "existing/build/dir": allowlists.Bp2BuildDefaultTrue, - }, - }, - }, - { - description: "module allowlist and enabled subdirectory", - shouldConvert: false, - expectedErrors: []string{"A module cannot be in a directory marked Bp2BuildDefaultTrue or Bp2BuildDefaultTrueRecursively and also be in moduleAlwaysConvert. Directory: 'existing/build/dir' Module: 'foo'"}, - module: TestBazelModule{ - TestModuleInfo: bazel.TestModuleInfo{ - ModuleName: "foo", - Typ: "rule1", - Dir: "existing/build/dir/subdir", - }, - BazelModuleBase: bazelableBazelModuleBase, - }, - allowlist: Bp2BuildConversionAllowlist{ - moduleAlwaysConvert: map[string]bool{ - "foo": true, - }, - defaultConfig: allowlists.Bp2BuildConfig{ - "existing/build/dir": allowlists.Bp2BuildDefaultTrueRecursively, - }, - }, - }, - { - description: "module enabled in unit test short-circuits other allowlists", - shouldConvert: true, - module: TestBazelModule{ - TestModuleInfo: bazel.TestModuleInfo{ - ModuleName: "foo", - Typ: "rule1", - Dir: ".", - }, - BazelModuleBase: BazelModuleBase{ - bazelProperties: properties{ - Bazel_module: bazelModuleProperties{ - CanConvertToBazel: true, - Bp2build_available: proptools.BoolPtr(true), - }, - }, - }, - }, - allowlist: Bp2BuildConversionAllowlist{ - moduleAlwaysConvert: map[string]bool{ - "foo": true, - }, - moduleDoNotConvert: map[string]bool{ - "foo": true, - }, - }, - }, - } - - for _, test := range testCases { - t.Run(test.description, func(t *testing.T) { - bcc := &TestBazelConversionContext{ - omc: bazel.OtherModuleTestContext{ - Modules: []bazel.TestModuleInfo{ - test.module.TestModuleInfo, - }, - }, - allowlist: test.allowlist, - } - - shouldConvert := test.module.shouldConvertWithBp2build(bcc, test.module.TestModuleInfo) - if test.shouldConvert != shouldConvert { - t.Errorf("Module shouldConvert expected to be: %v, but was: %v", test.shouldConvert, shouldConvert) - } - - errorsMatch := true - if len(test.expectedErrors) != len(bcc.errors) { - errorsMatch = false - } else { - for i, err := range test.expectedErrors { - if err != bcc.errors[i] { - errorsMatch = false - } - } - } - if !errorsMatch { - t.Errorf("Expected errors to be: %v, but were: %v", test.expectedErrors, bcc.errors) - } - }) - } -} - -func TestBp2buildAllowList(t *testing.T) { - allowlist := GetBp2BuildAllowList() - for k, v := range allowlists.Bp2buildDefaultConfig { - if allowlist.defaultConfig[k] != v { - t.Errorf("bp2build default config of %s: expected: %v, got: %v", k, v, allowlist.defaultConfig[k]) - } - } - for k, v := range allowlists.Bp2buildKeepExistingBuildFile { - if allowlist.keepExistingBuildFile[k] != v { - t.Errorf("bp2build keep existing build file of %s: expected: %v, got: %v", k, v, allowlist.keepExistingBuildFile[k]) - } - } - for _, k := range allowlists.Bp2buildModuleTypeAlwaysConvertList { - if !allowlist.moduleTypeAlwaysConvert[k] { - t.Errorf("bp2build module type always convert of %s: expected: true, got: %v", k, allowlist.moduleTypeAlwaysConvert[k]) - } - } - for _, k := range allowlists.Bp2buildModuleDoNotConvertList { - if !allowlist.moduleDoNotConvert[k] { - t.Errorf("bp2build module do not convert of %s: expected: true, got: %v", k, allowlist.moduleDoNotConvert[k]) - } - } -} - -func TestShouldKeepExistingBuildFileForDir(t *testing.T) { - allowlist := NewBp2BuildAllowlist() - // entry "a/b2/c2" is moot because of its parent "a/b2" - allowlist.SetKeepExistingBuildFile(map[string]bool{"a": false, "a/b1": false, "a/b2": true, "a/b1/c1": true, "a/b2/c2": false}) - truths := []string{"a", "a/b1", "a/b2", "a/b1/c1", "a/b2/c", "a/b2/c2", "a/b2/c2/d"} - falsities := []string{"a1", "a/b", "a/b1/c"} - for _, dir := range truths { - if !allowlist.ShouldKeepExistingBuildFileForDir(dir) { - t.Errorf("%s expected TRUE but was FALSE", dir) - } - } - for _, dir := range falsities { - if allowlist.ShouldKeepExistingBuildFileForDir(dir) { - t.Errorf("%s expected FALSE but was TRUE", dir) - } - } -} diff --git a/android/buildinfo_prop.go b/android/buildinfo_prop.go index 46f6488020..8e19ad5f4e 100644 --- a/android/buildinfo_prop.go +++ b/android/buildinfo_prop.go @@ -23,7 +23,7 @@ import ( func init() { ctx := InitRegistrationContext - ctx.RegisterSingletonModuleType("buildinfo_prop", buildinfoPropFactory) + ctx.RegisterParallelSingletonModuleType("buildinfo_prop", buildinfoPropFactory) } type buildinfoPropProperties struct { diff --git a/android/config.go b/android/config.go index ba4adbceac..4a081467ca 100644 --- a/android/config.go +++ b/android/config.go @@ -18,7 +18,6 @@ package android // product variables necessary for soong_build's operation. import ( - "bytes" "encoding/json" "fmt" "os" @@ -80,28 +79,21 @@ type SoongBuildMode int type CmdArgs struct { bootstrap.Args - RunGoTests bool - OutDir string - SoongOutDir string - - SymlinkForestMarker string - Bp2buildMarker string - BazelQueryViewDir string - BazelApiBp2buildDir string - ModuleGraphFile string - ModuleActionsFile string - DocFile string + RunGoTests bool + OutDir string + SoongOutDir string + SoongVariables string - MultitreeBuild bool + BazelQueryViewDir string + ModuleGraphFile string + ModuleActionsFile string + DocFile string - BazelMode bool - BazelModeDev bool - BazelModeStaging bool - BazelForceEnabledModules string + MultitreeBuild bool - UseBazelProxy bool + BuildFromSourceStub bool - BuildFromTextStub bool + EnsureAllowlistIntegrity bool } // Build modes that soong_build can run as. @@ -109,39 +101,16 @@ const ( // Don't use bazel at all during module analysis. AnalysisNoBazel SoongBuildMode = iota - // Symlink fores mode: merge two directory trees into a symlink forest - SymlinkForest - - // Bp2build mode: Generate BUILD files from blueprint files and exit. - Bp2build - // Generate BUILD files which faithfully represent the dependency graph of // blueprint modules. Individual BUILD targets will not, however, faitfhully // express build semantics. GenerateQueryView - // Generate BUILD files for API contributions to API surfaces - ApiBp2build - // Create a JSON representation of the module graph and exit. GenerateModuleGraph // Generate a documentation file for module type definitions and exit. GenerateDocFile - - // Use bazel during analysis of many allowlisted build modules. The allowlist - // is considered a "developer mode" allowlist, as some modules may be - // allowlisted on an experimental basis. - BazelDevMode - - // Use bazel during analysis of a few allowlisted build modules. The allowlist - // is considered "staging, as these are modules being prepared to be released - // into prod mode shortly after. - BazelStagingMode - - // Use bazel during analysis of build modules from an allowlist carefully - // curated by the build team to be proven stable. - BazelProdMode ) // SoongOutDir returns the build output directory for the configuration. @@ -174,6 +143,28 @@ func (c Config) RunningInsideUnitTest() bool { return c.config.TestProductVariables != nil } +// DisableHiddenApiChecks returns true if hiddenapi checks have been disabled. +// For 'eng' target variant hiddenapi checks are disabled by default for performance optimisation, +// but can be enabled by setting environment variable ENABLE_HIDDENAPI_FLAGS=true. +// For other target variants hiddenapi check are enabled by default but can be disabled by +// setting environment variable UNSAFE_DISABLE_HIDDENAPI_FLAGS=true. +// If both ENABLE_HIDDENAPI_FLAGS=true and UNSAFE_DISABLE_HIDDENAPI_FLAGS=true, then +// ENABLE_HIDDENAPI_FLAGS=true will be triggered and hiddenapi checks will be considered enabled. +func (c Config) DisableHiddenApiChecks() bool { + return !c.IsEnvTrue("ENABLE_HIDDENAPI_FLAGS") && + (c.IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") || + Bool(c.productVariables.Eng)) +} + +// DisableVerifyOverlaps returns true if verify_overlaps is skipped. +// Mismatch in version of apexes and module SDK is required for mainline prebuilts to work in +// trunk stable. +// Thus, verify_overlaps is disabled when RELEASE_DEFAULT_MODULE_BUILD_FROM_SOURCE is set to false. +// TODO(b/308174018): Re-enable verify_overlaps for both builr from source/mainline prebuilts. +func (c Config) DisableVerifyOverlaps() bool { + return c.IsEnvTrue("DISABLE_VERIFY_OVERLAPS") || !c.ReleaseDefaultModuleBuildFromSource() +} + // MaxPageSizeSupported returns the max page size supported by the device. This // value will define the ELF segment alignment for binaries (executables and // shared libraries). @@ -181,6 +172,42 @@ func (c Config) MaxPageSizeSupported() string { return String(c.config.productVariables.DeviceMaxPageSizeSupported) } +// NoBionicPageSizeMacro returns true when AOSP is page size agnostic. +// This means that the bionic's macro PAGE_SIZE won't be defined. +// Returns false when AOSP is NOT page size agnostic. +// This means that bionic's macro PAGE_SIZE is defined. +func (c Config) NoBionicPageSizeMacro() bool { + return Bool(c.config.productVariables.DeviceNoBionicPageSizeMacro) +} + +// The release version passed to aconfig, derived from RELEASE_VERSION +func (c Config) ReleaseVersion() string { + return c.config.productVariables.ReleaseVersion +} + +// The aconfig value set passed to aconfig, derived from RELEASE_VERSION +func (c Config) ReleaseAconfigValueSets() []string { + return c.config.productVariables.ReleaseAconfigValueSets +} + +// The flag default permission value passed to aconfig +// derived from RELEASE_ACONFIG_FLAG_DEFAULT_PERMISSION +func (c Config) ReleaseAconfigFlagDefaultPermission() string { + return c.config.productVariables.ReleaseAconfigFlagDefaultPermission +} + +// The flag indicating behavior for the tree wrt building modules or using prebuilts +// derived from RELEASE_DEFAULT_MODULE_BUILD_FROM_SOURCE +func (c Config) ReleaseDefaultModuleBuildFromSource() bool { + return c.config.productVariables.ReleaseDefaultModuleBuildFromSource == nil || + Bool(c.config.productVariables.ReleaseDefaultModuleBuildFromSource) +} + +// Enables ABI monitoring of NDK libraries +func (c Config) ReleaseNdkAbiMonitored() bool { + return c.config.productVariables.GetBuildFlagBool("RELEASE_NDK_ABI_MONITORED") +} + // A DeviceConfig object represents the configuration for a particular device // being built. For now there will only be one of these, but in the future there // may be multiple devices being built. @@ -195,14 +222,10 @@ type VendorConfig soongconfig.SoongConfig // product configuration values are read from Kati-generated soong.variables. type config struct { // Options configurable with soong.variables - productVariables productVariables + productVariables ProductVariables // Only available on configs created by TestConfig - TestProductVariables *productVariables - - // A specialized context object for Bazel/Soong mixed builds and migration - // purposes. - BazelContext BazelContext + TestProductVariables *ProductVariables ProductVariablesFileName string @@ -245,9 +268,7 @@ type config struct { fs pathtools.FileSystem mockBpList string - BuildMode SoongBuildMode - Bp2buildPackageConfig Bp2BuildConversionAllowlist - Bp2buildSoongConfigDefinitions soongconfig.Bp2BuildSoongConfigDefinitions + BuildMode SoongBuildMode // If MultitreeBuild is true then this is one inner tree of a multitree // build directed by the multitree orchestrator. @@ -263,28 +284,17 @@ type config struct { OncePer - // These fields are only used for metrics collection. A module should be added - // to these maps only if its implementation supports Bazel handling in mixed - // builds. A module being in the "enabled" list indicates that there is a - // variant of that module for which bazel-handling actually took place. - // A module being in the "disabled" list indicates that there is a variant of - // that module for which bazel-handling was denied. - mixedBuildsLock sync.Mutex - mixedBuildEnabledModules map[string]struct{} - mixedBuildDisabledModules map[string]struct{} - - // These are modules to be built with Bazel beyond the allowlisted/build-mode - // specified modules. They are passed via the command-line flag - // "--bazel-force-enabled-modules" - bazelForceEnabledModules map[string]struct{} + // If buildFromSourceStub is true then the Java API stubs are + // built from the source Java files, not the signature text files. + buildFromSourceStub bool - // If true, for any requests to Bazel, communicate with a Bazel proxy using - // unix sockets, instead of spawning Bazel as a subprocess. - UseBazelProxy bool + // If ensureAllowlistIntegrity is true, then the presence of any allowlisted + // modules that aren't mixed-built for at least one variant will cause a build + // failure + ensureAllowlistIntegrity bool - // If buildFromTextStub is true then the Java API stubs are - // built from the signature text files, not the source Java files. - buildFromTextStub bool + // List of Api libraries that contribute to Api surfaces. + apiLibraries map[string]struct{} } type deviceConfig struct { @@ -302,7 +312,7 @@ func loadConfig(config *config) error { // loadFromConfigFile loads and decodes configuration options from a JSON file // in the current working directory. -func loadFromConfigFile(configurable *productVariables, filename string) error { +func loadFromConfigFile(configurable *ProductVariables, filename string) error { // Try to open the file configFileReader, err := os.Open(filename) defer configFileReader.Close() @@ -354,7 +364,7 @@ func loadFromConfigFile(configurable *productVariables, filename string) error { // atomically writes the config file in case two copies of soong_build are running simultaneously // (for example, docs generation and ninja manifest generation) -func saveToConfigFile(config *productVariables, filename string) error { +func saveToConfigFile(config *ProductVariables, filename string) error { data, err := json.MarshalIndent(&config, "", " ") if err != nil { return fmt.Errorf("cannot marshal config data: %s", err.Error()) @@ -383,65 +393,52 @@ func saveToConfigFile(config *productVariables, filename string) error { return nil } -func saveToBazelConfigFile(config *productVariables, outDir string) error { +type productVariableStarlarkRepresentation struct { + soongType string + selectable bool + archVariant bool +} + +func saveToBazelConfigFile(config *ProductVariables, outDir string) error { dir := filepath.Join(outDir, bazel.SoongInjectionDirName, "product_config") err := createDirIfNonexistent(dir, os.ModePerm) if err != nil { return fmt.Errorf("Could not create dir %s: %s", dir, err) } - nonArchVariantProductVariables := []string{} - archVariantProductVariables := []string{} + allProductVariablesType := reflect.TypeOf((*ProductVariables)(nil)).Elem() + productVariablesInfo := make(map[string]productVariableStarlarkRepresentation) p := variableProperties{} t := reflect.TypeOf(p.Product_variables) for i := 0; i < t.NumField(); i++ { f := t.Field(i) - nonArchVariantProductVariables = append(nonArchVariantProductVariables, strings.ToLower(f.Name)) - if proptools.HasTag(f, "android", "arch_variant") { - archVariantProductVariables = append(archVariantProductVariables, strings.ToLower(f.Name)) + archVariant := proptools.HasTag(f, "android", "arch_variant") + if mainProductVariablesStructField, ok := allProductVariablesType.FieldByName(f.Name); ok { + productVariablesInfo[f.Name] = productVariableStarlarkRepresentation{ + soongType: stringRepresentationOfSimpleType(mainProductVariablesStructField.Type), + selectable: true, + archVariant: archVariant, + } + } else { + panic("Unknown variable " + f.Name) } } - nonArchVariantProductVariablesJson := starlark_fmt.PrintStringList(nonArchVariantProductVariables, 0) - if err != nil { - return fmt.Errorf("cannot marshal product variable data: %s", err.Error()) - } - - archVariantProductVariablesJson := starlark_fmt.PrintStringList(archVariantProductVariables, 0) - if err != nil { - return fmt.Errorf("cannot marshal arch variant product variable data: %s", err.Error()) - } - - configJson, err := json.MarshalIndent(&config, "", " ") - if err != nil { - return fmt.Errorf("cannot marshal config data: %s", err.Error()) - } - // The backslashes need to be escaped because this text is going to be put - // inside a Starlark string literal. - configJson = bytes.ReplaceAll(configJson, []byte("\\"), []byte("\\\\")) - - bzl := []string{ - bazel.GeneratedBazelFileWarning, - fmt.Sprintf(`_product_vars = json.decode("""%s""")`, configJson), - fmt.Sprintf(`_product_var_constraints = %s`, nonArchVariantProductVariablesJson), - fmt.Sprintf(`_arch_variant_product_var_constraints = %s`, archVariantProductVariablesJson), - "\n", ` -product_vars = _product_vars - -# TODO(b/269577299) Remove these when everything switches over to loading them from product_variable_constants.bzl -product_var_constraints = _product_var_constraints -arch_variant_product_var_constraints = _arch_variant_product_var_constraints -`, - } - err = pathtools.WriteFileIfChanged(filepath.Join(dir, "product_variables.bzl"), - []byte(strings.Join(bzl, "\n")), 0644) - if err != nil { - return fmt.Errorf("Could not write .bzl config file %s", err) - } err = pathtools.WriteFileIfChanged(filepath.Join(dir, "product_variable_constants.bzl"), []byte(fmt.Sprintf(` -product_var_constraints = %s -arch_variant_product_var_constraints = %s -`, nonArchVariantProductVariablesJson, archVariantProductVariablesJson)), 0644) +# product_var_constant_info is a map of product variables to information about them. The fields are: +# - soongType: The type of the product variable as it appears in soong's ProductVariables struct. +# examples are string, bool, int, *bool, *string, []string, etc. This may be an overly +# conservative estimation of the type, for example a *bool could oftentimes just be a +# bool that defaults to false. +# - selectable: if this product variable can be selected on in Android.bp/build files. This means +# it's listed in the "variableProperties" soong struct. Currently all variables in +# this list are selectable because we only need the selectable ones at the moment, +# but the list may be expanded later. +# - archVariant: If the variable is tagged as arch variant in the "variableProperties" struct. +product_var_constant_info = %s +product_var_constraints = [k for k, v in product_var_constant_info.items() if v.selectable] +arch_variant_product_var_constraints = [k for k, v in product_var_constant_info.items() if v.selectable and v.archVariant] +`, starlark_fmt.PrintAny(productVariablesInfo, 0))), 0644) if err != nil { return fmt.Errorf("Could not write .bzl config file %s", err) } @@ -454,6 +451,23 @@ arch_variant_product_var_constraints = %s return nil } +func stringRepresentationOfSimpleType(ty reflect.Type) string { + switch ty.Kind() { + case reflect.String: + return "string" + case reflect.Bool: + return "bool" + case reflect.Int: + return "int" + case reflect.Slice: + return "[]" + stringRepresentationOfSimpleType(ty.Elem()) + case reflect.Pointer: + return "*" + stringRepresentationOfSimpleType(ty.Elem()) + default: + panic("unimplemented type: " + ty.Kind().String()) + } +} + // NullConfig returns a mostly empty Config for use by standalone tools like dexpreopt_gen that // use the android package. func NullConfig(outDir, soongOutDir string) Config { @@ -471,7 +485,7 @@ func NullConfig(outDir, soongOutDir string) Config { func NewConfig(cmdArgs CmdArgs, availableEnv map[string]string) (Config, error) { // Make a config with default options. config := &config{ - ProductVariablesFileName: filepath.Join(cmdArgs.SoongOutDir, productVariablesFileName), + ProductVariablesFileName: cmdArgs.SoongVariables, env: availableEnv, @@ -480,16 +494,12 @@ func NewConfig(cmdArgs CmdArgs, availableEnv map[string]string) (Config, error) runGoTests: cmdArgs.RunGoTests, multilibConflicts: make(map[ArchType]bool), - moduleListFile: cmdArgs.ModuleListFile, - fs: pathtools.NewOsFs(absSrcDir), - mixedBuildDisabledModules: make(map[string]struct{}), - mixedBuildEnabledModules: make(map[string]struct{}), - bazelForceEnabledModules: make(map[string]struct{}), + moduleListFile: cmdArgs.ModuleListFile, + fs: pathtools.NewOsFs(absSrcDir), MultitreeBuild: cmdArgs.MultitreeBuild, - UseBazelProxy: cmdArgs.UseBazelProxy, - buildFromTextStub: cmdArgs.BuildFromTextStub, + buildFromSourceStub: cmdArgs.BuildFromSourceStub, } config.deviceConfig = &deviceConfig{ @@ -580,30 +590,44 @@ func NewConfig(cmdArgs CmdArgs, availableEnv map[string]string) (Config, error) config.BuildMode = mode } } - setBazelMode := func(arg bool, argName string, mode SoongBuildMode) { - if arg { - if config.BuildMode != AnalysisNoBazel { - fmt.Fprintf(os.Stderr, "buildMode is already set, illegal argument: %s", argName) - os.Exit(1) - } - config.BuildMode = mode - } - } - setBuildMode(cmdArgs.SymlinkForestMarker, SymlinkForest) - setBuildMode(cmdArgs.Bp2buildMarker, Bp2build) setBuildMode(cmdArgs.BazelQueryViewDir, GenerateQueryView) - setBuildMode(cmdArgs.BazelApiBp2buildDir, ApiBp2build) setBuildMode(cmdArgs.ModuleGraphFile, GenerateModuleGraph) setBuildMode(cmdArgs.DocFile, GenerateDocFile) - setBazelMode(cmdArgs.BazelModeDev, "--bazel-mode-dev", BazelDevMode) - setBazelMode(cmdArgs.BazelMode, "--bazel-mode", BazelProdMode) - setBazelMode(cmdArgs.BazelModeStaging, "--bazel-mode-staging", BazelStagingMode) - for _, module := range strings.Split(cmdArgs.BazelForceEnabledModules, ",") { - config.bazelForceEnabledModules[module] = struct{}{} - } - config.BazelContext, err = NewBazelContext(config) - config.Bp2buildPackageConfig = GetBp2BuildAllowList() + // TODO(b/276958307): Replace the hardcoded list to a sdk_library local prop. + config.apiLibraries = map[string]struct{}{ + "android.net.ipsec.ike": {}, + "art.module.public.api": {}, + "conscrypt.module.public.api": {}, + "framework-adservices": {}, + "framework-appsearch": {}, + "framework-bluetooth": {}, + "framework-configinfrastructure": {}, + "framework-connectivity": {}, + "framework-connectivity-t": {}, + "framework-devicelock": {}, + "framework-graphics": {}, + "framework-healthfitness": {}, + "framework-location": {}, + "framework-media": {}, + "framework-mediaprovider": {}, + "framework-nfc": {}, + "framework-ondevicepersonalization": {}, + "framework-pdf": {}, + "framework-permission": {}, + "framework-permission-s": {}, + "framework-scheduling": {}, + "framework-sdkextensions": {}, + "framework-statsd": {}, + "framework-sdksandbox": {}, + "framework-tethering": {}, + "framework-uwb": {}, + "framework-virtualization": {}, + "framework-wifi": {}, + "i18n.module.public.api": {}, + } + + config.productVariables.Build_from_text_stub = boolPtr(config.BuildFromTextStub()) return Config{config}, err } @@ -638,37 +662,6 @@ func (c *config) mockFileSystem(bp string, fs map[string][]byte) { c.mockBpList = blueprint.MockModuleListFile } -// TODO(b/265062549): Add a field to our collected (and uploaded) metrics which -// describes a reason that we fell back to non-mixed builds. -// Returns true if "Bazel builds" is enabled. In this mode, part of build -// analysis is handled by Bazel. -func (c *config) IsMixedBuildsEnabled() bool { - globalMixedBuildsSupport := c.Once(OnceKey{"globalMixedBuildsSupport"}, func() interface{} { - if c.productVariables.DeviceArch != nil && *c.productVariables.DeviceArch == "riscv64" { - return false - } - if c.IsEnvTrue("GLOBAL_THINLTO") { - return false - } - if len(c.productVariables.SanitizeHost) > 0 { - return false - } - if len(c.productVariables.SanitizeDevice) > 0 { - return false - } - if len(c.productVariables.SanitizeDeviceDiag) > 0 { - return false - } - if len(c.productVariables.SanitizeDeviceArch) > 0 { - return false - } - return true - }).(bool) - - bazelModeEnabled := c.BuildMode == BazelProdMode || c.BuildMode == BazelDevMode || c.BuildMode == BazelStagingMode - return globalMixedBuildsSupport && bazelModeEnabled -} - func (c *config) SetAllowMissingDependencies() { c.productVariables.Allow_missing_dependencies = proptools.BoolPtr(true) } @@ -684,7 +677,7 @@ func (c *config) HostToolDir() string { } func (c *config) HostToolPath(ctx PathContext, tool string) Path { - path := pathForInstall(ctx, ctx.Config().BuildOS, ctx.Config().BuildArch, "bin", false, tool) + path := pathForInstall(ctx, ctx.Config().BuildOS, ctx.Config().BuildArch, "bin", tool) return path } @@ -693,15 +686,23 @@ func (c *config) HostJNIToolPath(ctx PathContext, lib string) Path { if runtime.GOOS == "darwin" { ext = ".dylib" } - path := pathForInstall(ctx, ctx.Config().BuildOS, ctx.Config().BuildArch, "lib64", false, lib+ext) + path := pathForInstall(ctx, ctx.Config().BuildOS, ctx.Config().BuildArch, "lib64", lib+ext) return path } func (c *config) HostJavaToolPath(ctx PathContext, tool string) Path { - path := pathForInstall(ctx, ctx.Config().BuildOS, ctx.Config().BuildArch, "framework", false, tool) + path := pathForInstall(ctx, ctx.Config().BuildOS, ctx.Config().BuildArch, "framework", tool) return path } +func (c *config) HostCcSharedLibPath(ctx PathContext, lib string) Path { + libDir := "lib" + if ctx.Config().BuildArch.Multilib == "lib64" { + libDir = "lib64" + } + return pathForInstall(ctx, ctx.Config().BuildOS, ctx.Config().BuildArch, libDir, lib+".so") +} + // PrebuiltOS returns the name of the host OS used in prebuilts directories. func (c *config) PrebuiltOS() string { switch runtime.GOOS { @@ -787,6 +788,10 @@ func (c *config) KatiEnabled() bool { return c.katiEnabled } +func (c *config) ProductVariables() ProductVariables { + return c.productVariables +} + func (c *config) BuildId() string { return String(c.productVariables.BuildId) } @@ -897,12 +902,18 @@ func (c *config) FinalApiLevels() []ApiLevel { func (c *config) PreviewApiLevels() []ApiLevel { var levels []ApiLevel - for i, codename := range c.PlatformVersionActiveCodenames() { + i := 0 + for _, codename := range c.PlatformVersionActiveCodenames() { + if codename == "REL" { + continue + } + levels = append(levels, ApiLevel{ value: codename, number: i, isPreview: true, }) + i++ } return levels } @@ -926,8 +937,6 @@ func (c *config) AllSupportedApiLevels() []ApiLevel { // DefaultAppTargetSdk returns the API level that platform apps are targeting. // This converts a codename to the exact ApiLevel it represents. func (c *config) DefaultAppTargetSdk(ctx EarlyModuleContext) ApiLevel { - // This logic is replicated in starlark, if changing logic here update starlark code too - // https://cs.android.com/android/platform/superproject/+/master:build/bazel/rules/common/api.bzl;l=72;drc=231c7e8c8038fd478a79eb68aa5b9f5c64e0e061 if Bool(c.productVariables.Platform_sdk_final) { return c.PlatformSdkVersion() } @@ -1285,8 +1294,12 @@ func (c *config) PrebuiltHiddenApiDir(_ PathContext) string { return String(c.productVariables.PrebuiltHiddenApiDir) } -func (c *config) BazelModulesForceEnabledByFlag() map[string]struct{} { - return c.bazelForceEnabledModules +func (c *config) IsVndkDeprecated() bool { + return !Bool(c.productVariables.KeepVndk) +} + +func (c *config) VendorApiLevel() string { + return String(c.productVariables.VendorApiLevel) } func (c *deviceConfig) Arches() []Arch { @@ -1328,16 +1341,12 @@ func (c *deviceConfig) PlatformVndkVersion() string { return String(c.config.productVariables.Platform_vndk_version) } -func (c *deviceConfig) ProductVndkVersion() string { - return String(c.config.productVariables.ProductVndkVersion) -} - func (c *deviceConfig) ExtraVndkVersions() []string { return c.config.productVariables.ExtraVndkVersions } func (c *deviceConfig) VndkUseCoreVariant() bool { - return Bool(c.config.productVariables.VndkUseCoreVariant) + return Bool(c.config.productVariables.VndkUseCoreVariant) && Bool(c.config.productVariables.KeepVndk) } func (c *deviceConfig) SystemSdkVersions() []string { @@ -1568,11 +1577,18 @@ func (c *config) MemtagHeapSyncEnabledForPath(path string) bool { return HasAnyPrefix(path, c.productVariables.MemtagHeapSyncIncludePaths) && !c.MemtagHeapDisabledForPath(path) } +func (c *config) HWASanDisabledForPath(path string) bool { + if len(c.productVariables.HWASanExcludePaths) == 0 { + return false + } + return HasAnyPrefix(path, c.productVariables.HWASanExcludePaths) +} + func (c *config) HWASanEnabledForPath(path string) bool { if len(c.productVariables.HWASanIncludePaths) == 0 { return false } - return HasAnyPrefix(path, c.productVariables.HWASanIncludePaths) + return HasAnyPrefix(path, c.productVariables.HWASanIncludePaths) && !c.HWASanDisabledForPath(path) } func (c *config) VendorConfig(name string) VendorConfig { @@ -1587,10 +1603,6 @@ func (c *config) AmlAbis() bool { return Bool(c.productVariables.Aml_abis) } -func (c *config) FlattenApex() bool { - return Bool(c.productVariables.Flatten_apex) -} - func (c *config) ForceApexSymlinkOptimization() bool { return Bool(c.productVariables.ForceApexSymlinkOptimization) } @@ -1623,10 +1635,6 @@ func (c *config) InterPartitionJavaLibraryAllowList() []string { return c.productVariables.InterPartitionJavaLibraryAllowList } -func (c *config) InstallExtraFlattenedApexes() bool { - return Bool(c.productVariables.InstallExtraFlattenedApexes) -} - func (c *config) ProductHiddenAPIStubs() []string { return c.productVariables.ProductHiddenAPIStubs } @@ -1651,10 +1659,6 @@ func (c *config) ProductPrivateSepolicyDirs() []string { return c.productVariables.ProductPrivateSepolicyDirs } -func (c *config) MissingUsesLibraries() []string { - return c.productVariables.MissingUsesLibraries -} - func (c *config) TargetMultitreeUpdateMeta() bool { return c.productVariables.MultitreeUpdateMeta } @@ -1710,30 +1714,6 @@ func (c *deviceConfig) BoardSepolicyVers() string { return c.PlatformSepolicyVersion() } -func (c *deviceConfig) BoardPlatVendorPolicy() []string { - return c.config.productVariables.BoardPlatVendorPolicy -} - -func (c *deviceConfig) BoardReqdMaskPolicy() []string { - return c.config.productVariables.BoardReqdMaskPolicy -} - -func (c *deviceConfig) BoardSystemExtPublicPrebuiltDirs() []string { - return c.config.productVariables.BoardSystemExtPublicPrebuiltDirs -} - -func (c *deviceConfig) BoardSystemExtPrivatePrebuiltDirs() []string { - return c.config.productVariables.BoardSystemExtPrivatePrebuiltDirs -} - -func (c *deviceConfig) BoardProductPublicPrebuiltDirs() []string { - return c.config.productVariables.BoardProductPublicPrebuiltDirs -} - -func (c *deviceConfig) BoardProductPrivatePrebuiltDirs() []string { - return c.config.productVariables.BoardProductPrivatePrebuiltDirs -} - func (c *deviceConfig) SystemExtSepolicyPrebuiltApiDir() string { return String(c.config.productVariables.SystemExtSepolicyPrebuiltApiDir) } @@ -1830,6 +1810,10 @@ func (c *deviceConfig) ShippingApiLevel() ApiLevel { return uncheckedFinalApiLevel(apiLevel) } +func (c *deviceConfig) BuildBrokenPluginValidation() []string { + return c.config.productVariables.BuildBrokenPluginValidation +} + func (c *deviceConfig) BuildBrokenClangAsFlags() bool { return c.config.productVariables.BuildBrokenClangAsFlags } @@ -1866,20 +1850,24 @@ func (c *deviceConfig) BuildBrokenInputDir(name string) bool { return InList(name, c.config.productVariables.BuildBrokenInputDirModules) } -func (c *deviceConfig) BuildBrokenDepfile() bool { - return Bool(c.config.productVariables.BuildBrokenDepfile) +func (c *config) BuildWarningBadOptionalUsesLibsAllowlist() []string { + return c.productVariables.BuildWarningBadOptionalUsesLibsAllowlist +} + +func (c *deviceConfig) GenruleSandboxing() bool { + return Bool(c.config.productVariables.GenruleSandboxing) } func (c *deviceConfig) RequiresInsecureExecmemForSwiftshader() bool { return c.config.productVariables.RequiresInsecureExecmemForSwiftshader } -func (c *config) SelinuxIgnoreNeverallows() bool { - return c.productVariables.SelinuxIgnoreNeverallows +func (c *deviceConfig) Release_aidl_use_unfrozen() bool { + return Bool(c.config.productVariables.Release_aidl_use_unfrozen) } -func (c *deviceConfig) SepolicySplit() bool { - return c.config.productVariables.SepolicySplit +func (c *config) SelinuxIgnoreNeverallows() bool { + return c.productVariables.SelinuxIgnoreNeverallows } func (c *deviceConfig) SepolicyFreezeTestExtraDirs() []string { @@ -1922,17 +1910,6 @@ func (c *config) UseHostMusl() bool { return Bool(c.productVariables.HostMusl) } -func (c *config) LogMixedBuild(ctx BaseModuleContext, useBazel bool) { - moduleName := ctx.Module().Name() - c.mixedBuildsLock.Lock() - defer c.mixedBuildsLock.Unlock() - if useBazel { - c.mixedBuildEnabledModules[moduleName] = struct{}{} - } else { - c.mixedBuildDisabledModules[moduleName] = struct{}{} - } -} - // ApiSurfaces directory returns the source path inside the api_surfaces repo // (relative to workspace root). func (c *config) ApiSurfacesDir(s ApiSurface, version string) string { @@ -1944,15 +1921,94 @@ func (c *config) ApiSurfacesDir(s ApiSurface, version string) string { version) } +func (c *config) JavaCoverageEnabled() bool { + return c.IsEnvTrue("EMMA_INSTRUMENT") || c.IsEnvTrue("EMMA_INSTRUMENT_STATIC") || c.IsEnvTrue("EMMA_INSTRUMENT_FRAMEWORK") +} + +func (c *deviceConfig) BuildFromSourceStub() bool { + return Bool(c.config.productVariables.BuildFromSourceStub) +} + func (c *config) BuildFromTextStub() bool { - return c.buildFromTextStub + // TODO: b/302320354 - Remove the coverage build specific logic once the + // robust solution for handling native properties in from-text stub build + // is implemented. + return !c.buildFromSourceStub && + !c.JavaCoverageEnabled() && + !c.deviceConfig.BuildFromSourceStub() } func (c *config) SetBuildFromTextStub(b bool) { - c.buildFromTextStub = b + c.buildFromSourceStub = !b + c.productVariables.Build_from_text_stub = boolPtr(b) } -func (c *config) AddForceEnabledModules(forceEnabled []string) { - for _, forceEnabledModule := range forceEnabled { - c.bazelForceEnabledModules[forceEnabledModule] = struct{}{} + +func (c *config) SetApiLibraries(libs []string) { + c.apiLibraries = make(map[string]struct{}) + for _, lib := range libs { + c.apiLibraries[lib] = struct{}{} } } + +func (c *config) GetApiLibraries() map[string]struct{} { + return c.apiLibraries +} + +func (c *deviceConfig) CheckVendorSeappViolations() bool { + return Bool(c.config.productVariables.CheckVendorSeappViolations) +} + +func (c *deviceConfig) NextReleaseHideFlaggedApi() bool { + return Bool(c.config.productVariables.NextReleaseHideFlaggedApi) +} + +func (c *deviceConfig) ReleaseExposeFlaggedApi() bool { + return Bool(c.config.productVariables.Release_expose_flagged_api) +} + +func (c *deviceConfig) HideFlaggedApis() bool { + return c.NextReleaseHideFlaggedApi() && !c.ReleaseExposeFlaggedApi() +} + +func (c *config) GetBuildFlag(name string) (string, bool) { + val, ok := c.productVariables.BuildFlags[name] + return val, ok +} + +var ( + mainlineApexContributionBuildFlags = []string{ + "RELEASE_APEX_CONTRIBUTIONS_ADSERVICES", + "RELEASE_APEX_CONTRIBUTIONS_APPSEARCH", + "RELEASE_APEX_CONTRIBUTIONS_ART", + "RELEASE_APEX_CONTRIBUTIONS_BLUETOOTH", + "RELEASE_APEX_CONTRIBUTIONS_CONFIGINFRASTRUCTURE", + "RELEASE_APEX_CONTRIBUTIONS_CONNECTIVITY", + "RELEASE_APEX_CONTRIBUTIONS_CONSCRYPT", + "RELEASE_APEX_CONTRIBUTIONS_CRASHRECOVERY", + "RELEASE_APEX_CONTRIBUTIONS_DEVICELOCK", + "RELEASE_APEX_CONTRIBUTIONS_HEALTHFITNESS", + "RELEASE_APEX_CONTRIBUTIONS_IPSEC", + "RELEASE_APEX_CONTRIBUTIONS_MEDIA", + "RELEASE_APEX_CONTRIBUTIONS_MEDIAPROVIDER", + "RELEASE_APEX_CONTRIBUTIONS_ONDEVICEPERSONALIZATION", + "RELEASE_APEX_CONTRIBUTIONS_PERMISSION", + "RELEASE_APEX_CONTRIBUTIONS_REMOTEKEYPROVISIONING", + "RELEASE_APEX_CONTRIBUTIONS_SCHEDULING", + "RELEASE_APEX_CONTRIBUTIONS_SDKEXTENSIONS", + "RELEASE_APEX_CONTRIBUTIONS_STATSD", + "RELEASE_APEX_CONTRIBUTIONS_UWB", + "RELEASE_APEX_CONTRIBUTIONS_WIFI", + } +) + +// Returns the list of _selected_ apex_contributions +// Each mainline module will have one entry in the list +func (c *config) AllApexContributions() []string { + ret := []string{} + for _, f := range mainlineApexContributionBuildFlags { + if val, exists := c.GetBuildFlag(f); exists && val != "" { + ret = append(ret, val) + } + } + return ret +} diff --git a/android/config_bp2build.go b/android/config_bp2build.go index 830890d56d..b632e3302f 100644 --- a/android/config_bp2build.go +++ b/android/config_bp2build.go @@ -95,15 +95,6 @@ func (ev ExportedVariables) ExportVariableConfigMethod(name string, method inter return ev.pctx.VariableConfigMethod(name, method) } -func (ev ExportedVariables) ExportStringStaticVariableWithEnvOverride(name, envVar, defaultVal string) { - ev.ExportVariableConfigMethod(name, func(config Config) string { - if override := config.Getenv(envVar); override != "" { - return override - } - return defaultVal - }) -} - // ExportSourcePathVariable declares a static "source path" variable and exports // it to Bazel's toolchain. func (ev ExportedVariables) ExportSourcePathVariable(name string, value string) { @@ -210,7 +201,7 @@ func (m ExportedStringVariables) asBazel(config Config, panic(fmt.Errorf("error expanding config variable %s: %s", k, err)) } if len(expandedVar) > 1 { - panic(fmt.Errorf("%s expands to more than one string value: %s", variableValue, expandedVar)) + panic(fmt.Errorf("%q expands to more than one string value: %q", variableValue, expandedVar)) } ret = append(ret, bazelConstant{ variableName: k, diff --git a/android/config_test.go b/android/config_test.go index 9df5288a13..7d327a27ef 100644 --- a/android/config_test.go +++ b/android/config_test.go @@ -75,7 +75,7 @@ Did you mean to use an annotation of ",omitempty"? // run validateConfigAnnotations against each type that might have json annotations func TestProductConfigAnnotations(t *testing.T) { - err := validateConfigAnnotations(&productVariables{}) + err := validateConfigAnnotations(&ProductVariables{}) if err != nil { t.Errorf(err.Error()) } @@ -88,7 +88,7 @@ func TestMissingVendorConfig(t *testing.T) { } } -func verifyProductVariableMarshaling(t *testing.T, v productVariables) { +func verifyProductVariableMarshaling(t *testing.T, v ProductVariables) { dir := t.TempDir() path := filepath.Join(dir, "test.variables") err := saveToConfigFile(&v, path) @@ -96,20 +96,20 @@ func verifyProductVariableMarshaling(t *testing.T, v productVariables) { t.Errorf("Couldn't save default product config: %q", err) } - var v2 productVariables + var v2 ProductVariables err = loadFromConfigFile(&v2, path) if err != nil { t.Errorf("Couldn't load default product config: %q", err) } } func TestDefaultProductVariableMarshaling(t *testing.T) { - v := productVariables{} + v := ProductVariables{} v.SetDefaultConfig() verifyProductVariableMarshaling(t, v) } func TestBootJarsMarshaling(t *testing.T) { - v := productVariables{} + v := ProductVariables{} v.SetDefaultConfig() v.BootJars = ConfiguredJarList{ apexes: []string{"apex"}, diff --git a/android/configured_jars.go b/android/configured_jars.go index 53fef052a6..c7b808f3d6 100644 --- a/android/configured_jars.go +++ b/android/configured_jars.go @@ -178,7 +178,7 @@ func (l *ConfiguredJarList) CopyOfApexJarPairs() []string { func (l *ConfiguredJarList) BuildPaths(ctx PathContext, dir OutputPath) WritablePaths { paths := make(WritablePaths, l.Len()) for i, jar := range l.jars { - paths[i] = dir.Join(ctx, ModuleStem(jar)+".jar") + paths[i] = dir.Join(ctx, ModuleStem(ctx.Config(), l.Apex(i), jar)+".jar") } return paths } @@ -187,8 +187,8 @@ func (l *ConfiguredJarList) BuildPaths(ctx PathContext, dir OutputPath) Writable // prefix. func (l *ConfiguredJarList) BuildPathsByModule(ctx PathContext, dir OutputPath) map[string]WritablePath { paths := map[string]WritablePath{} - for _, jar := range l.jars { - paths[jar] = dir.Join(ctx, ModuleStem(jar)+".jar") + for i, jar := range l.jars { + paths[jar] = dir.Join(ctx, ModuleStem(ctx.Config(), l.Apex(i), jar)+".jar") } return paths } @@ -228,24 +228,32 @@ func (l *ConfiguredJarList) MarshalJSON() ([]byte, error) { return json.Marshal(list) } -// ModuleStem hardcodes the stem of framework-minus-apex to return "framework". -// -// TODO(b/139391334): hard coded until we find a good way to query the stem of a -// module before any other mutators are run. -func ModuleStem(module string) string { - if module == "framework-minus-apex" { - return "framework" +func OverrideConfiguredJarLocationFor(cfg Config, apex string, jar string) (newApex string, newJar string) { + for _, entry := range cfg.productVariables.ConfiguredJarLocationOverrides { + tuple := strings.Split(entry, ":") + if len(tuple) != 4 { + panic("malformed configured jar location override '%s', expected format: :::") + } + if apex == tuple[0] && jar == tuple[1] { + return tuple[2], tuple[3] + } } - return module + return apex, jar +} + +// ModuleStem returns the overridden jar name. +func ModuleStem(cfg Config, apex string, jar string) string { + _, newJar := OverrideConfiguredJarLocationFor(cfg, apex, jar) + return newJar } // DevicePaths computes the on-device paths for the list of (apex, jar) pairs, // based on the operating system. func (l *ConfiguredJarList) DevicePaths(cfg Config, ostype OsType) []string { paths := make([]string, l.Len()) - for i, jar := range l.jars { - apex := l.apexes[i] - name := ModuleStem(jar) + ".jar" + for i := 0; i < l.Len(); i++ { + apex, jar := OverrideConfiguredJarLocationFor(cfg, l.Apex(i), l.Jar(i)) + name := jar + ".jar" var subdir string if apex == "platform" { @@ -311,4 +319,9 @@ func EmptyConfiguredJarList() ConfiguredJarList { return ConfiguredJarList{} } +// IsConfiguredJarForPlatform returns true if the given apex name is a special name for the platform. +func IsConfiguredJarForPlatform(apex string) bool { + return apex == "platform" || apex == "system_ext" +} + var earlyBootJarsKey = NewOnceKey("earlyBootJars") diff --git a/android/configured_jars_test.go b/android/configured_jars_test.go new file mode 100644 index 0000000000..4b586e4d7f --- /dev/null +++ b/android/configured_jars_test.go @@ -0,0 +1,46 @@ +// Copyright 2023 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 ( + "testing" +) + +func TestOverrideConfiguredJarLocationFor(t *testing.T) { + cfg := NullConfig("", "") + + cfg.productVariables = ProductVariables{ + ConfiguredJarLocationOverrides: []string{ + "platform:libfoo-old:com.android.foo:libfoo-new", + "com.android.bar:libbar-old:platform:libbar-new", + }, + } + + apex, jar := OverrideConfiguredJarLocationFor(cfg, "platform", "libfoo-old") + AssertStringEquals(t, "", "com.android.foo", apex) + AssertStringEquals(t, "", "libfoo-new", jar) + + apex, jar = OverrideConfiguredJarLocationFor(cfg, "platform", "libbar-old") + AssertStringEquals(t, "", "platform", apex) + AssertStringEquals(t, "", "libbar-old", jar) + + apex, jar = OverrideConfiguredJarLocationFor(cfg, "com.android.bar", "libbar-old") + AssertStringEquals(t, "", "platform", apex) + AssertStringEquals(t, "", "libbar-new", jar) + + apex, jar = OverrideConfiguredJarLocationFor(cfg, "platform", "libbar-old") + AssertStringEquals(t, "", "platform", apex) + AssertStringEquals(t, "", "libbar-old", jar) +} diff --git a/android/defaults.go b/android/defaults.go index 31d6014988..ff79002323 100644 --- a/android/defaults.go +++ b/android/defaults.go @@ -117,11 +117,6 @@ type DefaultsVisibilityProperties struct { type DefaultsModuleBase struct { DefaultableModuleBase - - // Included to support setting bazel_module.label for multiple Soong modules to the same Bazel - // target. This is primarily useful for modules that were architecture specific and instead are - // handled in Bazel as a select(). - BazelModuleBase } // The common pattern for defaults modules is to register separate instances of @@ -164,7 +159,6 @@ func (d *DefaultsModuleBase) isDefaults() bool { type DefaultsModule interface { Module Defaults - Bazelable } func (d *DefaultsModuleBase) properties() []interface{} { @@ -177,10 +171,6 @@ func (d *DefaultsModuleBase) productVariableProperties() interface{} { func (d *DefaultsModuleBase) GenerateAndroidBuildActions(ctx ModuleContext) {} -// ConvertWithBp2build to fulfill Bazelable interface; however, at this time defaults module are -// *NOT* converted with bp2build -func (defaultable *DefaultsModuleBase) ConvertWithBp2build(ctx TopDownMutatorContext) {} - func InitDefaultsModule(module DefaultsModule) { commonProperties := &commonProperties{} @@ -190,8 +180,6 @@ func InitDefaultsModule(module DefaultsModule) { &ApexProperties{}, &distProperties{}) - // Bazel module must be initialized _before_ Defaults to be included in cc_defaults module. - InitBazelModule(module) initAndroidModuleBase(module) initProductVariableModule(module) initArchModule(module) @@ -221,60 +209,10 @@ func InitDefaultsModule(module DefaultsModule) { var _ Defaults = (*DefaultsModuleBase)(nil) -// applyNamespacedVariableDefaults only runs in bp2build mode for -// defaultable/defaults modules. Its purpose is to merge namespaced product -// variable props from defaults deps, even if those defaults are custom module -// types created from soong_config_module_type, e.g. one that's wrapping a -// cc_defaults or java_defaults. -func applyNamespacedVariableDefaults(defaultDep Defaults, ctx TopDownMutatorContext) { - var dep, b Bazelable - - dep, ok := defaultDep.(Bazelable) - if !ok { - if depMod, ok := defaultDep.(Module); ok { - // Track that this dependency hasn't been converted to bp2build yet. - ctx.AddUnconvertedBp2buildDep(depMod.Name()) - return - } else { - panic("Expected default dep to be a Module.") - } - } - - b, ok = ctx.Module().(Bazelable) - if !ok { - return - } - - // namespacedVariableProps is a map from namespaces (e.g. acme, android, - // vendor_foo) to a slice of soong_config_variable struct pointers, - // containing properties for that particular module. - src := dep.namespacedVariableProps() - dst := b.namespacedVariableProps() - if dst == nil { - dst = make(namespacedVariableProperties) - } - - // Propagate all soong_config_variable structs from the dep. We'll merge the - // actual property values later in variable.go. - for namespace := range src { - if dst[namespace] == nil { - dst[namespace] = []interface{}{} - } - for _, i := range src[namespace] { - dst[namespace] = append(dst[namespace], i) - } - } - - b.setNamespacedVariableProps(dst) -} - func (defaultable *DefaultableModuleBase) applyDefaults(ctx TopDownMutatorContext, defaultsList []Defaults) { for _, defaults := range defaultsList { - if ctx.Config().BuildMode == Bp2build { - applyNamespacedVariableDefaults(defaults, ctx) - } for _, prop := range defaultable.defaultableProperties { if prop == defaultable.defaultableVariableProperties { defaultable.applyDefaultVariableProperties(ctx, defaults, prop) diff --git a/android/defs.go b/android/defs.go index 18eed2dae2..03968c10db 100644 --- a/android/defs.go +++ b/android/defs.go @@ -107,8 +107,8 @@ var ( Cat = pctx.AndroidStaticRule("Cat", blueprint.RuleParams{ - Command: "cat $in > $out", - Description: "concatenate licenses $out", + Command: "rm -f $out && cat $in > $out", + Description: "concatenate files to $out", }) // ubuntu 14.04 offcially use dash for /bin/sh, and its builtin echo command @@ -116,7 +116,7 @@ var ( // content to file. writeFile = pctx.AndroidStaticRule("writeFile", blueprint.RuleParams{ - Command: `/bin/bash -c 'echo -e -n "$$0" > $out' $content`, + Command: `rm -f $out && /bin/bash -c 'echo -e -n "$$0" > $out' $content`, Description: "writing file $out", }, "content") @@ -209,12 +209,14 @@ func WriteFileRuleVerbatim(ctx BuilderContext, outputFile WritablePath, content buildWriteFileRule(ctx, outputFile, content) } -func CatFileRule(ctx BuilderContext, paths Paths, outputFile WritablePath) { +// WriteExecutableFileRuleVerbatim is the same as WriteFileRuleVerbatim, but runs chmod +x on the result +func WriteExecutableFileRuleVerbatim(ctx BuilderContext, outputFile WritablePath, content string) { + intermediate := PathForIntermediates(ctx, "write_executable_file_intermediates").Join(ctx, outputFile.String()) + WriteFileRuleVerbatim(ctx, intermediate, content) ctx.Build(pctx, BuildParams{ - Rule: Cat, - Inputs: paths, - Output: outputFile, - Description: "combine files to " + outputFile.Base(), + Rule: CpExecutable, + Output: outputFile, + Input: intermediate, }) } @@ -230,7 +232,7 @@ func shellUnescape(s string) string { // ContentFromFileRuleForTests returns the content that was passed to a WriteFileRule for use // in tests. -func ContentFromFileRuleForTests(t *testing.T, params TestingBuildParams) string { +func ContentFromFileRuleForTests(t *testing.T, ctx *TestContext, params TestingBuildParams) string { t.Helper() if g, w := params.Rule, writeFile; g != w { t.Errorf("expected params.Rule to be %q, was %q", w, g) diff --git a/android/depset_generic.go b/android/depset_generic.go index f00e462c20..45c1937150 100644 --- a/android/depset_generic.go +++ b/android/depset_generic.go @@ -16,10 +16,9 @@ package android import ( "fmt" - "reflect" ) -// depSet is designed to be conceptually compatible with Bazel's depsets: +// DepSet is designed to be conceptually compatible with Bazel's depsets: // https://docs.bazel.build/versions/master/skylark/depsets.html type DepSetOrder int @@ -43,142 +42,114 @@ func (o DepSetOrder) String() string { } } -// A depSet efficiently stores a slice of an arbitrary type from transitive dependencies without -// copying. It is stored as a DAG of depSet nodes, each of which has some direct contents and a list -// of dependency depSet nodes. +type depSettableType comparable + +// A DepSet efficiently stores a slice of an arbitrary type from transitive dependencies without +// copying. It is stored as a DAG of DepSet nodes, each of which has some direct contents and a list +// of dependency DepSet nodes. // -// A depSet has an order that will be used to walk the DAG when ToList() is called. The order +// A DepSet has an order that will be used to walk the DAG when ToList() is called. The order // can be POSTORDER, PREORDER, or TOPOLOGICAL. POSTORDER and PREORDER orders return a postordered // or preordered left to right flattened list. TOPOLOGICAL returns a list that guarantees that // elements of children are listed after all of their parents (unless there are duplicate direct -// elements in the depSet or any of its transitive dependencies, in which case the ordering of the +// elements in the DepSet or any of its transitive dependencies, in which case the ordering of the // duplicated element is not guaranteed). // -// A depSet is created by newDepSet or newDepSetBuilder.Build from the slice for direct contents -// and the *depSets of dependencies. A depSet is immutable once created. -// -// This object uses reflection to remain agnostic to the type it contains. It should be replaced -// with generics once those exist in Go. Callers should generally use a thin wrapper around depSet -// that provides type-safe methods like DepSet for Paths. -type depSet struct { +// A DepSet is created by NewDepSet or NewDepSetBuilder.Build from the slice for direct contents +// and the *DepSets of dependencies. A DepSet is immutable once created. +type DepSet[T depSettableType] struct { preorder bool reverse bool order DepSetOrder - direct interface{} - transitive []*depSet -} - -type depSetInterface interface { - embeddedDepSet() *depSet -} - -func (d *depSet) embeddedDepSet() *depSet { - return d -} - -var _ depSetInterface = (*depSet)(nil) - -// newDepSet returns an immutable depSet with the given order, direct and transitive contents. -// direct must be a slice, but is not type-safe due to the lack of generics in Go. It can be a -// nil slice, but not a nil interface{}, i.e. []string(nil) but not nil. -func newDepSet(order DepSetOrder, direct interface{}, transitive interface{}) *depSet { - var directCopy interface{} - transitiveDepSet := sliceToDepSets(transitive, order) + direct []T + transitive []*DepSet[T] +} + +// NewDepSet returns an immutable DepSet with the given order, direct and transitive contents. +func NewDepSet[T depSettableType](order DepSetOrder, direct []T, transitive []*DepSet[T]) *DepSet[T] { + var directCopy []T + var transitiveCopy []*DepSet[T] + for _, t := range transitive { + if t.order != order { + panic(fmt.Errorf("incompatible order, new DepSet is %s but transitive DepSet is %s", + order, t.order)) + } + } if order == TOPOLOGICAL { - directCopy = reverseSlice(direct) - reverseSliceInPlace(transitiveDepSet) + // TOPOLOGICAL is implemented as a postorder traversal followed by reversing the output. + // Pre-reverse the inputs here so their order is maintained in the output. + directCopy = ReverseSlice(direct) + transitiveCopy = ReverseSlice(transitive) } else { - directCopy = copySlice(direct) + directCopy = append([]T(nil), direct...) + transitiveCopy = append([]*DepSet[T](nil), transitive...) } - return &depSet{ + return &DepSet[T]{ preorder: order == PREORDER, reverse: order == TOPOLOGICAL, order: order, direct: directCopy, - transitive: transitiveDepSet, + transitive: transitiveCopy, } } -// depSetBuilder is used to create an immutable depSet. -type depSetBuilder struct { +// DepSetBuilder is used to create an immutable DepSet. +type DepSetBuilder[T depSettableType] struct { order DepSetOrder - direct reflect.Value - transitive []*depSet + direct []T + transitive []*DepSet[T] } -// newDepSetBuilder returns a depSetBuilder to create an immutable depSet with the given order and -// type, represented by a slice of type that will be in the depSet. -func newDepSetBuilder(order DepSetOrder, typ interface{}) *depSetBuilder { - empty := reflect.Zero(reflect.TypeOf(typ)) - return &depSetBuilder{ - order: order, - direct: empty, +// NewDepSetBuilder returns a DepSetBuilder to create an immutable DepSet with the given order and +// type, represented by a slice of type that will be in the DepSet. +func NewDepSetBuilder[T depSettableType](order DepSetOrder) *DepSetBuilder[T] { + return &DepSetBuilder[T]{ + order: order, } } -// sliceToDepSets converts a slice of any type that implements depSetInterface (by having a depSet -// embedded in it) into a []*depSet. -func sliceToDepSets(in interface{}, order DepSetOrder) []*depSet { - slice := reflect.ValueOf(in) - length := slice.Len() - out := make([]*depSet, length) - for i := 0; i < length; i++ { - vi := slice.Index(i) - depSetIntf, ok := vi.Interface().(depSetInterface) - if !ok { - panic(fmt.Errorf("element %d is a %s, not a depSetInterface", i, vi.Type())) - } - depSet := depSetIntf.embeddedDepSet() - if depSet.order != order { - panic(fmt.Errorf("incompatible order, new depSet is %s but transitive depSet is %s", - order, depSet.order)) - } - out[i] = depSet - } - return out -} - -// DirectSlice adds direct contents to the depSet being built by a depSetBuilder. Newly added direct -// contents are to the right of any existing direct contents. The argument must be a slice, but -// is not type-safe due to the lack of generics in Go. -func (b *depSetBuilder) DirectSlice(direct interface{}) *depSetBuilder { - b.direct = reflect.AppendSlice(b.direct, reflect.ValueOf(direct)) +// DirectSlice adds direct contents to the DepSet being built by a DepSetBuilder. Newly added direct +// contents are to the right of any existing direct contents. +func (b *DepSetBuilder[T]) DirectSlice(direct []T) *DepSetBuilder[T] { + b.direct = append(b.direct, direct...) return b } -// Direct adds direct contents to the depSet being built by a depSetBuilder. Newly added direct -// contents are to the right of any existing direct contents. The argument must be the same type -// as the element of the slice passed to newDepSetBuilder, but is not type-safe due to the lack of -// generics in Go. -func (b *depSetBuilder) Direct(direct interface{}) *depSetBuilder { - b.direct = reflect.Append(b.direct, reflect.ValueOf(direct)) +// Direct adds direct contents to the DepSet being built by a DepSetBuilder. Newly added direct +// contents are to the right of any existing direct contents. +func (b *DepSetBuilder[T]) Direct(direct ...T) *DepSetBuilder[T] { + b.direct = append(b.direct, direct...) return b } // Transitive adds transitive contents to the DepSet being built by a DepSetBuilder. Newly added -// transitive contents are to the right of any existing transitive contents. The argument can -// be any slice of type that has depSet embedded in it. -func (b *depSetBuilder) Transitive(transitive interface{}) *depSetBuilder { - depSets := sliceToDepSets(transitive, b.order) - b.transitive = append(b.transitive, depSets...) +// transitive contents are to the right of any existing transitive contents. +func (b *DepSetBuilder[T]) Transitive(transitive ...*DepSet[T]) *DepSetBuilder[T] { + for _, t := range transitive { + if t.order != b.order { + panic(fmt.Errorf("incompatible order, new DepSet is %s but transitive DepSet is %s", + b.order, t.order)) + } + } + b.transitive = append(b.transitive, transitive...) return b } -// Returns the depSet being built by this depSetBuilder. The depSetBuilder retains its contents +// Returns the DepSet being built by this DepSetBuilder. The DepSetBuilder retains its contents // for creating more depSets. -func (b *depSetBuilder) Build() *depSet { - return newDepSet(b.order, b.direct.Interface(), b.transitive) +func (b *DepSetBuilder[T]) Build() *DepSet[T] { + return NewDepSet(b.order, b.direct, b.transitive) } // walk calls the visit method in depth-first order on a DepSet, preordered if d.preorder is set, // otherwise postordered. -func (d *depSet) walk(visit func(interface{})) { - visited := make(map[*depSet]bool) +func (d *DepSet[T]) walk(visit func([]T)) { + visited := make(map[*DepSet[T]]bool) - var dfs func(d *depSet) - dfs = func(d *depSet) { + var dfs func(d *DepSet[T]) + dfs = func(d *DepSet[T]) { visited[d] = true if d.preorder { visit(d.direct) @@ -197,155 +168,23 @@ func (d *depSet) walk(visit func(interface{})) { dfs(d) } -// ToList returns the depSet flattened to a list. The order in the list is based on the order -// of the depSet. POSTORDER and PREORDER orders return a postordered or preordered left to right +// ToList returns the DepSet flattened to a list. The order in the list is based on the order +// of the DepSet. POSTORDER and PREORDER orders return a postordered or preordered left to right // flattened list. TOPOLOGICAL returns a list that guarantees that elements of children are listed // after all of their parents (unless there are duplicate direct elements in the DepSet or any of // its transitive dependencies, in which case the ordering of the duplicated element is not // guaranteed). -// -// This method uses a reflection-based implementation to find the unique elements in slice, which -// is around 3x slower than a concrete implementation. Type-safe wrappers around depSet can -// provide their own implementation of ToList that calls depSet.toList with a method that -// uses a concrete implementation. -func (d *depSet) ToList() interface{} { - return d.toList(firstUnique) -} - -// toList returns the depSet flattened to a list. The order in the list is based on the order -// of the depSet. POSTORDER and PREORDER orders return a postordered or preordered left to right -// flattened list. TOPOLOGICAL returns a list that guarantees that elements of children are listed -// after all of their parents (unless there are duplicate direct elements in the DepSet or any of -// its transitive dependencies, in which case the ordering of the duplicated element is not -// guaranteed). The firstUniqueFunc is used to remove duplicates from the list. -func (d *depSet) toList(firstUniqueFunc func(interface{}) interface{}) interface{} { +func (d *DepSet[T]) ToList() []T { if d == nil { return nil } - slice := reflect.Zero(reflect.TypeOf(d.direct)) - d.walk(func(paths interface{}) { - slice = reflect.AppendSlice(slice, reflect.ValueOf(paths)) + var list []T + d.walk(func(paths []T) { + list = append(list, paths...) }) - list := slice.Interface() - list = firstUniqueFunc(list) + list = firstUniqueInPlace(list) if d.reverse { - reverseSliceInPlace(list) + ReverseSliceInPlace(list) } return list } - -// firstUnique returns all unique elements of a slice, keeping the first copy of each. It -// modifies the slice contents in place, and returns a subslice of the original slice. The -// argument must be a slice, but is not type-safe due to the lack of reflection in Go. -// -// Performance of the reflection-based firstUnique is up to 3x slower than a concrete type -// version such as FirstUniqueStrings. -func firstUnique(slice interface{}) interface{} { - // 4 was chosen based on Benchmark_firstUnique results. - if reflect.ValueOf(slice).Len() > 4 { - return firstUniqueMap(slice) - } - return firstUniqueList(slice) -} - -// firstUniqueList is an implementation of firstUnique using an O(N^2) list comparison to look for -// duplicates. -func firstUniqueList(in interface{}) interface{} { - writeIndex := 0 - slice := reflect.ValueOf(in) - length := slice.Len() -outer: - for readIndex := 0; readIndex < length; readIndex++ { - readValue := slice.Index(readIndex) - for compareIndex := 0; compareIndex < writeIndex; compareIndex++ { - compareValue := slice.Index(compareIndex) - // These two Interface() calls seem to cause an allocation and significantly - // slow down this list-based implementation. The map implementation below doesn't - // have this issue because reflect.Value.MapIndex takes a Value and appears to be - // able to do the map lookup without an allocation. - if readValue.Interface() == compareValue.Interface() { - // The value at readIndex already exists somewhere in the output region - // of the slice before writeIndex, skip it. - continue outer - } - } - if readIndex != writeIndex { - writeValue := slice.Index(writeIndex) - writeValue.Set(readValue) - } - writeIndex++ - } - return slice.Slice(0, writeIndex).Interface() -} - -var trueValue = reflect.ValueOf(true) - -// firstUniqueList is an implementation of firstUnique using an O(N) hash set lookup to look for -// duplicates. -func firstUniqueMap(in interface{}) interface{} { - writeIndex := 0 - slice := reflect.ValueOf(in) - length := slice.Len() - seen := reflect.MakeMapWithSize(reflect.MapOf(slice.Type().Elem(), trueValue.Type()), slice.Len()) - for readIndex := 0; readIndex < length; readIndex++ { - readValue := slice.Index(readIndex) - if seen.MapIndex(readValue).IsValid() { - continue - } - seen.SetMapIndex(readValue, trueValue) - if readIndex != writeIndex { - writeValue := slice.Index(writeIndex) - writeValue.Set(readValue) - } - writeIndex++ - } - return slice.Slice(0, writeIndex).Interface() -} - -// reverseSliceInPlace reverses the elements of a slice in place. The argument must be a slice, but -// is not type-safe due to the lack of reflection in Go. -func reverseSliceInPlace(in interface{}) { - swapper := reflect.Swapper(in) - slice := reflect.ValueOf(in) - length := slice.Len() - for i, j := 0, length-1; i < j; i, j = i+1, j-1 { - swapper(i, j) - } -} - -// reverseSlice returns a copy of a slice in reverse order. The argument must be a slice, but is -// not type-safe due to the lack of reflection in Go. -func reverseSlice(in interface{}) interface{} { - slice := reflect.ValueOf(in) - if !slice.IsValid() || slice.IsNil() { - return in - } - if slice.Kind() != reflect.Slice { - panic(fmt.Errorf("%t is not a slice", in)) - } - length := slice.Len() - if length == 0 { - return in - } - out := reflect.MakeSlice(slice.Type(), length, length) - for i := 0; i < length; i++ { - out.Index(i).Set(slice.Index(length - 1 - i)) - } - return out.Interface() -} - -// copySlice returns a copy of a slice. The argument must be a slice, but is not type-safe due to -// the lack of reflection in Go. -func copySlice(in interface{}) interface{} { - slice := reflect.ValueOf(in) - if !slice.IsValid() || slice.IsNil() { - return in - } - length := slice.Len() - if length == 0 { - return in - } - out := reflect.MakeSlice(slice.Type(), length, length) - reflect.Copy(out, slice) - return out.Interface() -} diff --git a/android/depset_paths.go b/android/depset_paths.go deleted file mode 100644 index ed561ba249..0000000000 --- a/android/depset_paths.go +++ /dev/null @@ -1,94 +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 android - -// This file implements DepSet, a thin type-safe wrapper around depSet that contains Paths. - -// A DepSet efficiently stores Paths from transitive dependencies without copying. It is stored -// as a DAG of DepSet nodes, each of which has some direct contents and a list of dependency -// DepSet nodes. -// -// A DepSet has an order that will be used to walk the DAG when ToList() is called. The order -// can be POSTORDER, PREORDER, or TOPOLOGICAL. POSTORDER and PREORDER orders return a postordered -// or preordered left to right flattened list. TOPOLOGICAL returns a list that guarantees that -// elements of children are listed after all of their parents (unless there are duplicate direct -// elements in the DepSet or any of its transitive dependencies, in which case the ordering of the -// duplicated element is not guaranteed). -// -// A DepSet is created by NewDepSet or NewDepSetBuilder.Build from the Paths for direct contents -// and the *DepSets of dependencies. A DepSet is immutable once created. -type DepSet struct { - depSet -} - -// DepSetBuilder is used to create an immutable DepSet. -type DepSetBuilder struct { - depSetBuilder -} - -// NewDepSet returns an immutable DepSet with the given order, direct and transitive contents. -func NewDepSet(order DepSetOrder, direct Paths, transitive []*DepSet) *DepSet { - return &DepSet{*newDepSet(order, direct, transitive)} -} - -// NewDepSetBuilder returns a DepSetBuilder to create an immutable DepSet with the given order. -func NewDepSetBuilder(order DepSetOrder) *DepSetBuilder { - return &DepSetBuilder{*newDepSetBuilder(order, Paths(nil))} -} - -// Direct adds direct contents to the DepSet being built by a DepSetBuilder. Newly added direct -// contents are to the right of any existing direct contents. -func (b *DepSetBuilder) Direct(direct ...Path) *DepSetBuilder { - b.depSetBuilder.DirectSlice(direct) - return b -} - -// Transitive adds transitive contents to the DepSet being built by a DepSetBuilder. Newly added -// transitive contents are to the right of any existing transitive contents. -func (b *DepSetBuilder) Transitive(transitive ...*DepSet) *DepSetBuilder { - b.depSetBuilder.Transitive(transitive) - return b -} - -// Returns the DepSet being built by this DepSetBuilder. The DepSetBuilder retains its contents -// for creating more DepSets. -func (b *DepSetBuilder) Build() *DepSet { - return &DepSet{*b.depSetBuilder.Build()} -} - -// ToList returns the DepSet flattened to a list. The order in the list is based on the order -// of the DepSet. POSTORDER and PREORDER orders return a postordered or preordered left to right -// flattened list. TOPOLOGICAL returns a list that guarantees that elements of children are listed -// after all of their parents (unless there are duplicate direct elements in the DepSet or any of -// its transitive dependencies, in which case the ordering of the duplicated element is not -// guaranteed). -func (d *DepSet) ToList() Paths { - if d == nil { - return nil - } - return d.toList(func(paths interface{}) interface{} { - return FirstUniquePaths(paths.(Paths)) - }).(Paths) -} - -// ToSortedList returns the direct and transitive contents of a DepSet in lexically sorted order -// with duplicates removed. -func (d *DepSet) ToSortedList() Paths { - if d == nil { - return nil - } - paths := d.ToList() - return SortedUniquePaths(paths) -} diff --git a/android/depset_test.go b/android/depset_test.go index 955ccb0017..376dffad1b 100644 --- a/android/depset_test.go +++ b/android/depset_test.go @@ -17,51 +17,40 @@ package android import ( "fmt" "reflect" - "strconv" "strings" "testing" ) func ExampleDepSet_ToList_postordered() { - a := NewDepSetBuilder(POSTORDER).Direct(PathForTesting("a")).Build() - b := NewDepSetBuilder(POSTORDER).Direct(PathForTesting("b")).Transitive(a).Build() - c := NewDepSetBuilder(POSTORDER).Direct(PathForTesting("c")).Transitive(a).Build() - d := NewDepSetBuilder(POSTORDER).Direct(PathForTesting("d")).Transitive(b, c).Build() + a := NewDepSetBuilder[Path](POSTORDER).Direct(PathForTesting("a")).Build() + b := NewDepSetBuilder[Path](POSTORDER).Direct(PathForTesting("b")).Transitive(a).Build() + c := NewDepSetBuilder[Path](POSTORDER).Direct(PathForTesting("c")).Transitive(a).Build() + d := NewDepSetBuilder[Path](POSTORDER).Direct(PathForTesting("d")).Transitive(b, c).Build() - fmt.Println(d.ToList().Strings()) + fmt.Println(Paths(d.ToList()).Strings()) // Output: [a b c d] } func ExampleDepSet_ToList_preordered() { - a := NewDepSetBuilder(PREORDER).Direct(PathForTesting("a")).Build() - b := NewDepSetBuilder(PREORDER).Direct(PathForTesting("b")).Transitive(a).Build() - c := NewDepSetBuilder(PREORDER).Direct(PathForTesting("c")).Transitive(a).Build() - d := NewDepSetBuilder(PREORDER).Direct(PathForTesting("d")).Transitive(b, c).Build() + a := NewDepSetBuilder[Path](PREORDER).Direct(PathForTesting("a")).Build() + b := NewDepSetBuilder[Path](PREORDER).Direct(PathForTesting("b")).Transitive(a).Build() + c := NewDepSetBuilder[Path](PREORDER).Direct(PathForTesting("c")).Transitive(a).Build() + d := NewDepSetBuilder[Path](PREORDER).Direct(PathForTesting("d")).Transitive(b, c).Build() - fmt.Println(d.ToList().Strings()) + fmt.Println(Paths(d.ToList()).Strings()) // Output: [d b a c] } func ExampleDepSet_ToList_topological() { - a := NewDepSetBuilder(TOPOLOGICAL).Direct(PathForTesting("a")).Build() - b := NewDepSetBuilder(TOPOLOGICAL).Direct(PathForTesting("b")).Transitive(a).Build() - c := NewDepSetBuilder(TOPOLOGICAL).Direct(PathForTesting("c")).Transitive(a).Build() - d := NewDepSetBuilder(TOPOLOGICAL).Direct(PathForTesting("d")).Transitive(b, c).Build() + a := NewDepSetBuilder[Path](TOPOLOGICAL).Direct(PathForTesting("a")).Build() + b := NewDepSetBuilder[Path](TOPOLOGICAL).Direct(PathForTesting("b")).Transitive(a).Build() + c := NewDepSetBuilder[Path](TOPOLOGICAL).Direct(PathForTesting("c")).Transitive(a).Build() + d := NewDepSetBuilder[Path](TOPOLOGICAL).Direct(PathForTesting("d")).Transitive(b, c).Build() - fmt.Println(d.ToList().Strings()) + fmt.Println(Paths(d.ToList()).Strings()) // Output: [d b c a] } -func ExampleDepSet_ToSortedList() { - a := NewDepSetBuilder(POSTORDER).Direct(PathForTesting("a")).Build() - b := NewDepSetBuilder(POSTORDER).Direct(PathForTesting("b")).Transitive(a).Build() - c := NewDepSetBuilder(POSTORDER).Direct(PathForTesting("c")).Transitive(a).Build() - d := NewDepSetBuilder(POSTORDER).Direct(PathForTesting("d")).Transitive(b, c).Build() - - fmt.Println(d.ToSortedList().Strings()) - // Output: [a b c d] -} - // Tests based on Bazel's ExpanderTestBase.java to ensure compatibility // https://github.com/bazelbuild/bazel/blob/master/src/test/java/com/google/devtools/build/lib/collect/nestedset/ExpanderTestBase.java func TestDepSet(t *testing.T) { @@ -74,13 +63,13 @@ func TestDepSet(t *testing.T) { tests := []struct { name string - depSet func(t *testing.T, order DepSetOrder) *DepSet + depSet func(t *testing.T, order DepSetOrder) *DepSet[Path] postorder, preorder, topological []string }{ { name: "simple", - depSet: func(t *testing.T, order DepSetOrder) *DepSet { - return NewDepSet(order, Paths{c, a, b}, nil) + depSet: func(t *testing.T, order DepSetOrder) *DepSet[Path] { + return NewDepSet[Path](order, Paths{c, a, b}, nil) }, postorder: []string{"c", "a", "b"}, preorder: []string{"c", "a", "b"}, @@ -88,8 +77,8 @@ func TestDepSet(t *testing.T) { }, { name: "simpleNoDuplicates", - depSet: func(t *testing.T, order DepSetOrder) *DepSet { - return NewDepSet(order, Paths{c, a, a, a, b}, nil) + depSet: func(t *testing.T, order DepSetOrder) *DepSet[Path] { + return NewDepSet[Path](order, Paths{c, a, a, a, b}, nil) }, postorder: []string{"c", "a", "b"}, preorder: []string{"c", "a", "b"}, @@ -97,9 +86,9 @@ func TestDepSet(t *testing.T) { }, { name: "nesting", - depSet: func(t *testing.T, order DepSetOrder) *DepSet { - subset := NewDepSet(order, Paths{c, a, e}, nil) - return NewDepSet(order, Paths{b, d}, []*DepSet{subset}) + depSet: func(t *testing.T, order DepSetOrder) *DepSet[Path] { + subset := NewDepSet[Path](order, Paths{c, a, e}, nil) + return NewDepSet[Path](order, Paths{b, d}, []*DepSet[Path]{subset}) }, postorder: []string{"c", "a", "e", "b", "d"}, preorder: []string{"b", "d", "c", "a", "e"}, @@ -107,14 +96,14 @@ func TestDepSet(t *testing.T) { }, { name: "builderReuse", - depSet: func(t *testing.T, order DepSetOrder) *DepSet { + depSet: func(t *testing.T, order DepSetOrder) *DepSet[Path] { assertEquals := func(t *testing.T, w, g Paths) { t.Helper() if !reflect.DeepEqual(w, g) { t.Errorf("want %q, got %q", w, g) } } - builder := NewDepSetBuilder(order) + builder := NewDepSetBuilder[Path](order) assertEquals(t, nil, builder.Build().ToList()) builder.Direct(b) @@ -123,7 +112,7 @@ func TestDepSet(t *testing.T) { builder.Direct(d) assertEquals(t, Paths{b, d}, builder.Build().ToList()) - child := NewDepSetBuilder(order).Direct(c, a, e).Build() + child := NewDepSetBuilder[Path](order).Direct(c, a, e).Build() builder.Transitive(child) return builder.Build() }, @@ -133,9 +122,9 @@ func TestDepSet(t *testing.T) { }, { name: "builderChaining", - depSet: func(t *testing.T, order DepSetOrder) *DepSet { - return NewDepSetBuilder(order).Direct(b).Direct(d). - Transitive(NewDepSetBuilder(order).Direct(c, a, e).Build()).Build() + depSet: func(t *testing.T, order DepSetOrder) *DepSet[Path] { + return NewDepSetBuilder[Path](order).Direct(b).Direct(d). + Transitive(NewDepSetBuilder[Path](order).Direct(c, a, e).Build()).Build() }, postorder: []string{"c", "a", "e", "b", "d"}, preorder: []string{"b", "d", "c", "a", "e"}, @@ -143,9 +132,9 @@ func TestDepSet(t *testing.T) { }, { name: "transitiveDepsHandledSeparately", - depSet: func(t *testing.T, order DepSetOrder) *DepSet { - subset := NewDepSetBuilder(order).Direct(c, a, e).Build() - builder := NewDepSetBuilder(order) + depSet: func(t *testing.T, order DepSetOrder) *DepSet[Path] { + subset := NewDepSetBuilder[Path](order).Direct(c, a, e).Build() + builder := NewDepSetBuilder[Path](order) // The fact that we add the transitive subset between the Direct(b) and Direct(d) // calls should not change the result. builder.Direct(b) @@ -159,9 +148,9 @@ func TestDepSet(t *testing.T) { }, { name: "nestingNoDuplicates", - depSet: func(t *testing.T, order DepSetOrder) *DepSet { - subset := NewDepSetBuilder(order).Direct(c, a, e).Build() - return NewDepSetBuilder(order).Direct(b, d, e).Transitive(subset).Build() + depSet: func(t *testing.T, order DepSetOrder) *DepSet[Path] { + subset := NewDepSetBuilder[Path](order).Direct(c, a, e).Build() + return NewDepSetBuilder[Path](order).Direct(b, d, e).Transitive(subset).Build() }, postorder: []string{"c", "a", "e", "b", "d"}, preorder: []string{"b", "d", "e", "c", "a"}, @@ -169,10 +158,10 @@ func TestDepSet(t *testing.T) { }, { name: "chain", - depSet: func(t *testing.T, order DepSetOrder) *DepSet { - c := NewDepSetBuilder(order).Direct(c).Build() - b := NewDepSetBuilder(order).Direct(b).Transitive(c).Build() - a := NewDepSetBuilder(order).Direct(a).Transitive(b).Build() + depSet: func(t *testing.T, order DepSetOrder) *DepSet[Path] { + c := NewDepSetBuilder[Path](order).Direct(c).Build() + b := NewDepSetBuilder[Path](order).Direct(b).Transitive(c).Build() + a := NewDepSetBuilder[Path](order).Direct(a).Transitive(b).Build() return a }, @@ -182,11 +171,11 @@ func TestDepSet(t *testing.T) { }, { name: "diamond", - depSet: func(t *testing.T, order DepSetOrder) *DepSet { - d := NewDepSetBuilder(order).Direct(d).Build() - c := NewDepSetBuilder(order).Direct(c).Transitive(d).Build() - b := NewDepSetBuilder(order).Direct(b).Transitive(d).Build() - a := NewDepSetBuilder(order).Direct(a).Transitive(b).Transitive(c).Build() + depSet: func(t *testing.T, order DepSetOrder) *DepSet[Path] { + d := NewDepSetBuilder[Path](order).Direct(d).Build() + c := NewDepSetBuilder[Path](order).Direct(c).Transitive(d).Build() + b := NewDepSetBuilder[Path](order).Direct(b).Transitive(d).Build() + a := NewDepSetBuilder[Path](order).Direct(a).Transitive(b).Transitive(c).Build() return a }, @@ -196,12 +185,12 @@ func TestDepSet(t *testing.T) { }, { name: "extendedDiamond", - depSet: func(t *testing.T, order DepSetOrder) *DepSet { - d := NewDepSetBuilder(order).Direct(d).Build() - e := NewDepSetBuilder(order).Direct(e).Build() - b := NewDepSetBuilder(order).Direct(b).Transitive(d).Transitive(e).Build() - c := NewDepSetBuilder(order).Direct(c).Transitive(e).Transitive(d).Build() - a := NewDepSetBuilder(order).Direct(a).Transitive(b).Transitive(c).Build() + depSet: func(t *testing.T, order DepSetOrder) *DepSet[Path] { + d := NewDepSetBuilder[Path](order).Direct(d).Build() + e := NewDepSetBuilder[Path](order).Direct(e).Build() + b := NewDepSetBuilder[Path](order).Direct(b).Transitive(d).Transitive(e).Build() + c := NewDepSetBuilder[Path](order).Direct(c).Transitive(e).Transitive(d).Build() + a := NewDepSetBuilder[Path](order).Direct(a).Transitive(b).Transitive(c).Build() return a }, postorder: []string{"d", "e", "b", "c", "a"}, @@ -210,13 +199,13 @@ func TestDepSet(t *testing.T) { }, { name: "extendedDiamondRightArm", - depSet: func(t *testing.T, order DepSetOrder) *DepSet { - d := NewDepSetBuilder(order).Direct(d).Build() - e := NewDepSetBuilder(order).Direct(e).Build() - b := NewDepSetBuilder(order).Direct(b).Transitive(d).Transitive(e).Build() - c2 := NewDepSetBuilder(order).Direct(c2).Transitive(e).Transitive(d).Build() - c := NewDepSetBuilder(order).Direct(c).Transitive(c2).Build() - a := NewDepSetBuilder(order).Direct(a).Transitive(b).Transitive(c).Build() + depSet: func(t *testing.T, order DepSetOrder) *DepSet[Path] { + d := NewDepSetBuilder[Path](order).Direct(d).Build() + e := NewDepSetBuilder[Path](order).Direct(e).Build() + b := NewDepSetBuilder[Path](order).Direct(b).Transitive(d).Transitive(e).Build() + c2 := NewDepSetBuilder[Path](order).Direct(c2).Transitive(e).Transitive(d).Build() + c := NewDepSetBuilder[Path](order).Direct(c).Transitive(c2).Build() + a := NewDepSetBuilder[Path](order).Direct(a).Transitive(b).Transitive(c).Build() return a }, postorder: []string{"d", "e", "b", "c2", "c", "a"}, @@ -225,10 +214,10 @@ func TestDepSet(t *testing.T) { }, { name: "orderConflict", - depSet: func(t *testing.T, order DepSetOrder) *DepSet { - child1 := NewDepSetBuilder(order).Direct(a, b).Build() - child2 := NewDepSetBuilder(order).Direct(b, a).Build() - parent := NewDepSetBuilder(order).Transitive(child1).Transitive(child2).Build() + depSet: func(t *testing.T, order DepSetOrder) *DepSet[Path] { + child1 := NewDepSetBuilder[Path](order).Direct(a, b).Build() + child2 := NewDepSetBuilder[Path](order).Direct(b, a).Build() + parent := NewDepSetBuilder[Path](order).Transitive(child1).Transitive(child2).Build() return parent }, postorder: []string{"a", "b"}, @@ -237,12 +226,12 @@ func TestDepSet(t *testing.T) { }, { name: "orderConflictNested", - depSet: func(t *testing.T, order DepSetOrder) *DepSet { - a := NewDepSetBuilder(order).Direct(a).Build() - b := NewDepSetBuilder(order).Direct(b).Build() - child1 := NewDepSetBuilder(order).Transitive(a).Transitive(b).Build() - child2 := NewDepSetBuilder(order).Transitive(b).Transitive(a).Build() - parent := NewDepSetBuilder(order).Transitive(child1).Transitive(child2).Build() + depSet: func(t *testing.T, order DepSetOrder) *DepSet[Path] { + a := NewDepSetBuilder[Path](order).Direct(a).Build() + b := NewDepSetBuilder[Path](order).Direct(b).Build() + child1 := NewDepSetBuilder[Path](order).Transitive(a).Transitive(b).Build() + child2 := NewDepSetBuilder[Path](order).Transitive(b).Transitive(a).Build() + parent := NewDepSetBuilder[Path](order).Transitive(child1).Transitive(child2).Build() return parent }, postorder: []string{"a", "b"}, @@ -255,19 +244,19 @@ func TestDepSet(t *testing.T) { t.Run(tt.name, func(t *testing.T) { t.Run("postorder", func(t *testing.T) { depSet := tt.depSet(t, POSTORDER) - if g, w := depSet.ToList().Strings(), tt.postorder; !reflect.DeepEqual(g, w) { + if g, w := Paths(depSet.ToList()).Strings(), tt.postorder; !reflect.DeepEqual(g, w) { t.Errorf("expected ToList() = %q, got %q", w, g) } }) t.Run("preorder", func(t *testing.T) { depSet := tt.depSet(t, PREORDER) - if g, w := depSet.ToList().Strings(), tt.preorder; !reflect.DeepEqual(g, w) { + if g, w := Paths(depSet.ToList()).Strings(), tt.preorder; !reflect.DeepEqual(g, w) { t.Errorf("expected ToList() = %q, got %q", w, g) } }) t.Run("topological", func(t *testing.T) { depSet := tt.depSet(t, TOPOLOGICAL) - if g, w := depSet.ToList().Strings(), tt.topological; !reflect.DeepEqual(g, w) { + if g, w := Paths(depSet.ToList()).Strings(), tt.topological; !reflect.DeepEqual(g, w) { t.Errorf("expected ToList() = %q, got %q", w, g) } }) @@ -288,7 +277,7 @@ func TestDepSetInvalidOrder(t *testing.T) { } } }() - NewDepSet(order1, nil, []*DepSet{NewDepSet(order2, nil, nil)}) + NewDepSet(order1, nil, []*DepSet[Path]{NewDepSet[Path](order2, nil, nil)}) t.Fatal("expected panic") } @@ -304,87 +293,3 @@ func TestDepSetInvalidOrder(t *testing.T) { }) } } - -func Test_firstUnique(t *testing.T) { - f := func(t *testing.T, imp func([]string) []string, in, want []string) { - t.Helper() - out := imp(in) - if !reflect.DeepEqual(out, want) { - t.Errorf("incorrect output:") - t.Errorf(" input: %#v", in) - t.Errorf(" expected: %#v", want) - t.Errorf(" got: %#v", out) - } - } - - for _, testCase := range firstUniqueStringsTestCases { - t.Run("list", func(t *testing.T) { - f(t, func(s []string) []string { - return firstUniqueList(s).([]string) - }, testCase.in, testCase.out) - }) - t.Run("map", func(t *testing.T) { - f(t, func(s []string) []string { - return firstUniqueMap(s).([]string) - }, testCase.in, testCase.out) - }) - } -} - -func Benchmark_firstUnique(b *testing.B) { - implementations := []struct { - name string - f func([]string) []string - }{ - { - name: "list", - f: func(slice []string) []string { - return firstUniqueList(slice).([]string) - }, - }, - { - name: "map", - f: func(slice []string) []string { - return firstUniqueMap(slice).([]string) - }, - }, - { - name: "optimal", - f: func(slice []string) []string { - return firstUnique(slice).([]string) - }, - }, - } - const maxSize = 1024 - uniqueStrings := make([]string, maxSize) - for i := range uniqueStrings { - uniqueStrings[i] = strconv.Itoa(i) - } - sameString := make([]string, maxSize) - for i := range sameString { - sameString[i] = uniqueStrings[0] - } - - f := func(b *testing.B, imp func([]string) []string, s []string) { - for i := 0; i < b.N; i++ { - b.ReportAllocs() - s = append([]string(nil), s...) - imp(s) - } - } - - for n := 1; n <= maxSize; n <<= 1 { - b.Run(strconv.Itoa(n), func(b *testing.B) { - for _, implementation := range implementations { - b.Run(implementation.name, func(b *testing.B) { - b.Run("same", func(b *testing.B) { - f(b, implementation.f, sameString[:n]) - }) - b.Run("unique", func(b *testing.B) { - f(b, implementation.f, uniqueStrings[:n]) - }) - }) - } - }) - } -} diff --git a/android/early_module_context.go b/android/early_module_context.go new file mode 100644 index 0000000000..8f75773583 --- /dev/null +++ b/android/early_module_context.go @@ -0,0 +1,169 @@ +// Copyright 2015 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" + "os" + "text/scanner" +) + +// EarlyModuleContext provides methods that can be called early, as soon as the properties have +// been parsed into the module and before any mutators have run. +type EarlyModuleContext interface { + // Module returns the current module as a Module. It should rarely be necessary, as the module already has a + // reference to itself. + Module() Module + + // ModuleName returns the name of the module. This is generally the value that was returned by Module.Name() when + // the module was created, but may have been modified by calls to BaseMutatorContext.Rename. + ModuleName() string + + // ModuleDir returns the path to the directory that contains the definition of the module. + ModuleDir() string + + // ModuleType returns the name of the module type that was used to create the module, as specified in + // RegisterModuleType. + ModuleType() string + + // BlueprintFile returns the name of the blueprint file that contains the definition of this + // module. + BlueprintsFile() string + + // ContainsProperty returns true if the specified property name was set in the module definition. + ContainsProperty(name string) bool + + // Errorf reports an error at the specified position of the module definition file. + Errorf(pos scanner.Position, fmt string, args ...interface{}) + + // ModuleErrorf reports an error at the line number of the module type in the module definition. + ModuleErrorf(fmt string, args ...interface{}) + + // PropertyErrorf reports an error at the line number of a property in the module definition. + PropertyErrorf(property, fmt string, args ...interface{}) + + // Failed returns true if any errors have been reported. In most cases the module can continue with generating + // build rules after an error, allowing it to report additional errors in a single run, but in cases where the error + // has prevented the module from creating necessary data it can return early when Failed returns true. + Failed() bool + + // AddNinjaFileDeps adds dependencies on the specified files to the rule that creates the ninja manifest. The + // primary builder will be rerun whenever the specified files are modified. + AddNinjaFileDeps(deps ...string) + + DeviceSpecific() bool + SocSpecific() bool + ProductSpecific() bool + SystemExtSpecific() bool + Platform() bool + + Config() Config + DeviceConfig() DeviceConfig + + // Deprecated: use Config() + AConfig() Config + + // GlobWithDeps returns a list of files that match the specified pattern but do not match any + // of the patterns in excludes. It also adds efficient dependencies to rerun the primary + // builder whenever a file matching the pattern as added or removed, without rerunning if a + // file that does not match the pattern is added to a searched directory. + GlobWithDeps(pattern string, excludes []string) ([]string, error) + + Glob(globPattern string, excludes []string) Paths + GlobFiles(globPattern string, excludes []string) Paths + IsSymlink(path Path) bool + Readlink(path Path) string + + // Namespace returns the Namespace object provided by the NameInterface set by Context.SetNameInterface, or the + // default SimpleNameInterface if Context.SetNameInterface was not called. + Namespace() *Namespace +} + +// Deprecated: use EarlyModuleContext instead +type BaseContext interface { + EarlyModuleContext +} + +type earlyModuleContext struct { + blueprint.EarlyModuleContext + + kind moduleKind + config Config +} + +func (e *earlyModuleContext) Glob(globPattern string, excludes []string) Paths { + return Glob(e, globPattern, excludes) +} + +func (e *earlyModuleContext) GlobFiles(globPattern string, excludes []string) Paths { + return GlobFiles(e, globPattern, excludes) +} + +func (e *earlyModuleContext) IsSymlink(path Path) bool { + fileInfo, err := e.config.fs.Lstat(path.String()) + if err != nil { + e.ModuleErrorf("os.Lstat(%q) failed: %s", path.String(), err) + } + return fileInfo.Mode()&os.ModeSymlink == os.ModeSymlink +} + +func (e *earlyModuleContext) Readlink(path Path) string { + dest, err := e.config.fs.Readlink(path.String()) + if err != nil { + e.ModuleErrorf("os.Readlink(%q) failed: %s", path.String(), err) + } + return dest +} + +func (e *earlyModuleContext) Module() Module { + module, _ := e.EarlyModuleContext.Module().(Module) + return module +} + +func (e *earlyModuleContext) Config() Config { + return e.EarlyModuleContext.Config().(Config) +} + +func (e *earlyModuleContext) AConfig() Config { + return e.config +} + +func (e *earlyModuleContext) DeviceConfig() DeviceConfig { + return DeviceConfig{e.config.deviceConfig} +} + +func (e *earlyModuleContext) Platform() bool { + return e.kind == platformModule +} + +func (e *earlyModuleContext) DeviceSpecific() bool { + return e.kind == deviceSpecificModule +} + +func (e *earlyModuleContext) SocSpecific() bool { + return e.kind == socSpecificModule +} + +func (e *earlyModuleContext) ProductSpecific() bool { + return e.kind == productSpecificModule +} + +func (e *earlyModuleContext) SystemExtSpecific() bool { + return e.kind == systemExtSpecificModule +} + +func (e *earlyModuleContext) Namespace() *Namespace { + return e.EarlyModuleContext.Namespace().(*Namespace) +} diff --git a/android/filegroup.go b/android/filegroup.go index f30ee51438..04bd8a8adb 100644 --- a/android/filegroup.go +++ b/android/filegroup.go @@ -15,13 +15,8 @@ package android import ( - "path/filepath" - "regexp" "strings" - "android/soong/bazel" - "android/soong/bazel/cquery" - "github.com/google/blueprint" ) @@ -38,145 +33,6 @@ func RegisterFilegroupBuildComponents(ctx RegistrationContext) { ctx.RegisterModuleType("filegroup_defaults", FileGroupDefaultsFactory) } -var convertedProtoLibrarySuffix = "_bp2build_converted" - -// IsFilegroup checks that a module is a filegroup type -func IsFilegroup(ctx bazel.OtherModuleContext, m blueprint.Module) bool { - return ctx.OtherModuleType(m) == "filegroup" -} - -var ( - // ignoring case, checks for proto or protos as an independent word in the name, whether at the - // beginning, end, or middle. e.g. "proto.foo", "bar-protos", "baz_proto_srcs" would all match - filegroupLikelyProtoPattern = regexp.MustCompile("(?i)(^|[^a-z])proto(s)?([^a-z]|$)") - filegroupLikelyAidlPattern = regexp.MustCompile("(?i)(^|[^a-z])aidl([^a-z]|$)") - - ProtoSrcLabelPartition = bazel.LabelPartition{ - Extensions: []string{".proto"}, - LabelMapper: isFilegroupWithPattern(filegroupLikelyProtoPattern), - } - AidlSrcLabelPartition = bazel.LabelPartition{ - Extensions: []string{".aidl"}, - LabelMapper: isFilegroupWithPattern(filegroupLikelyAidlPattern), - } -) - -func isFilegroupWithPattern(pattern *regexp.Regexp) bazel.LabelMapper { - return func(ctx bazel.OtherModuleContext, label bazel.Label) (string, bool) { - m, exists := ctx.ModuleFromName(label.OriginalModuleName) - labelStr := label.Label - if !exists || !IsFilegroup(ctx, m) { - return labelStr, false - } - likelyMatched := pattern.MatchString(label.OriginalModuleName) - return labelStr, likelyMatched - } -} - -// https://docs.bazel.build/versions/master/be/general.html#filegroup -type bazelFilegroupAttributes struct { - Srcs bazel.LabelListAttribute - Applicable_licenses bazel.LabelListAttribute -} - -type bazelAidlLibraryAttributes struct { - Srcs bazel.LabelListAttribute - Strip_import_prefix *string - Deps bazel.LabelListAttribute -} - -// api srcs can be contained in filegroups. -// this should be generated in api_bp2build workspace as well. -func (fg *fileGroup) ConvertWithApiBp2build(ctx TopDownMutatorContext) { - fg.ConvertWithBp2build(ctx) -} - -// ConvertWithBp2build performs bp2build conversion of filegroup -func (fg *fileGroup) ConvertWithBp2build(ctx TopDownMutatorContext) { - srcs := bazel.MakeLabelListAttribute( - BazelLabelForModuleSrcExcludes(ctx, fg.properties.Srcs, fg.properties.Exclude_srcs)) - - // For Bazel compatibility, don't generate the filegroup if there is only 1 - // source file, and that the source file is named the same as the module - // itself. In Bazel, eponymous filegroups like this would be an error. - // - // Instead, dependents on this single-file filegroup can just depend - // on the file target, instead of rule target, directly. - // - // You may ask: what if a filegroup has multiple files, and one of them - // shares the name? The answer: we haven't seen that in the wild, and - // should lock Soong itself down to prevent the behavior. For now, - // we raise an error if bp2build sees this problem. - for _, f := range srcs.Value.Includes { - if f.Label == fg.Name() { - if len(srcs.Value.Includes) > 1 { - ctx.ModuleErrorf("filegroup '%s' cannot contain a file with the same name", fg.Name()) - } - return - } - } - - // Convert module that has only AIDL files to aidl_library - // If the module has a mixed bag of AIDL and non-AIDL files, split the filegroup manually - // and then convert - if fg.ShouldConvertToAidlLibrary(ctx) { - tags := []string{"apex_available=//apex_available:anyapex"} - deps := bazel.MakeLabelListAttribute(BazelLabelForModuleDeps(ctx, fg.properties.Aidl.Deps)) - - attrs := &bazelAidlLibraryAttributes{ - Srcs: srcs, - Strip_import_prefix: fg.properties.Path, - Deps: deps, - } - - props := bazel.BazelTargetModuleProperties{ - Rule_class: "aidl_library", - Bzl_load_location: "//build/bazel/rules/aidl:aidl_library.bzl", - } - - ctx.CreateBazelTargetModule( - props, - CommonAttributes{ - Name: fg.Name(), - Tags: bazel.MakeStringListAttribute(tags), - }, - attrs) - } else { - if fg.ShouldConvertToProtoLibrary(ctx) { - attrs := &ProtoAttrs{ - Srcs: srcs, - Strip_import_prefix: fg.properties.Path, - } - - tags := []string{ - "apex_available=//apex_available:anyapex", - // TODO(b/246997908): we can remove this tag if we could figure out a solution for this bug. - "manual", - } - ctx.CreateBazelTargetModule( - bazel.BazelTargetModuleProperties{Rule_class: "proto_library"}, - CommonAttributes{ - Name: fg.Name() + convertedProtoLibrarySuffix, - Tags: bazel.MakeStringListAttribute(tags), - }, - attrs) - } - - // TODO(b/242847534): Still convert to a filegroup because other unconverted - // modules may depend on the filegroup - attrs := &bazelFilegroupAttributes{ - Srcs: srcs, - } - - props := bazel.BazelTargetModuleProperties{ - Rule_class: "filegroup", - Bzl_load_location: "//build/bazel/rules:filegroup.bzl", - } - - ctx.CreateBazelTargetModule(props, CommonAttributes{Name: fg.Name()}, attrs) - } -} - type fileGroupProperties struct { // srcs lists files that will be included in this filegroup Srcs []string `android:"path"` @@ -192,28 +48,16 @@ type fileGroupProperties struct { // Create a make variable with the specified name that contains the list of files in the // filegroup, relative to the root of the source tree. Export_to_make_var *string - - // aidl is explicitly provided for implicit aidl dependencies - // TODO(b/278298615): aidl prop is a no-op in Soong and is an escape hatch - // to include implicit aidl dependencies for bazel migration compatibility - Aidl struct { - // List of aidl files or filegroup depended on by srcs - Deps []string `android:"path"` - } } type fileGroup struct { ModuleBase - BazelModuleBase DefaultableModuleBase - FileGroupAsLibrary properties fileGroupProperties srcs Paths } -var _ MixedBuildBuildable = (*fileGroup)(nil) var _ SourceFileProducer = (*fileGroup)(nil) -var _ FileGroupAsLibrary = (*fileGroup)(nil) // filegroup contains a list of files that are referenced by other modules // properties (such as "srcs") using the syntax ":". filegroup are @@ -222,7 +66,6 @@ func FileGroupFactory() Module { module := &fileGroup{} module.AddProperties(&module.properties) InitAndroidModule(module) - InitBazelModule(module) InitDefaultableModule(module) return module } @@ -249,6 +92,7 @@ func (fg *fileGroup) GenerateAndroidBuildActions(ctx ModuleContext) { if fg.properties.Path != nil { fg.srcs = PathsWithModuleSrcSubDir(ctx, fg.srcs, String(fg.properties.Path)) } + ctx.SetProvider(blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: fg.srcs.Strings()}) } func (fg *fileGroup) Srcs() Paths { @@ -261,101 +105,6 @@ func (fg *fileGroup) MakeVars(ctx MakeVarsModuleContext) { } } -func (fg *fileGroup) QueueBazelCall(ctx BaseModuleContext) { - bazelCtx := ctx.Config().BazelContext - - bazelCtx.QueueBazelRequest( - fg.GetBazelLabel(ctx, fg), - cquery.GetOutputFiles, - configKey{arch: Common.String(), osType: CommonOS}) -} - -func (fg *fileGroup) IsMixedBuildSupported(ctx BaseModuleContext) bool { - // TODO(b/247782695), TODO(b/242847534) Fix mixed builds for filegroups - return false -} - -func (fg *fileGroup) ProcessBazelQueryResponse(ctx ModuleContext) { - bazelCtx := ctx.Config().BazelContext - // This is a short-term solution because we rely on info from Android.bp to handle - // a converted module. This will block when we want to remove Android.bp for all - // converted modules at some point. - // TODO(b/242847534): Implement a long-term solution in which we don't need to rely - // on info form Android.bp for modules that are already converted to Bazel - relativeRoot := ctx.ModuleDir() - if fg.properties.Path != nil { - relativeRoot = filepath.Join(relativeRoot, *fg.properties.Path) - } - - filePaths, err := bazelCtx.GetOutputFiles(fg.GetBazelLabel(ctx, fg), configKey{arch: Common.String(), osType: CommonOS}) - if err != nil { - ctx.ModuleErrorf(err.Error()) - return - } - - bazelOuts := make(Paths, 0, len(filePaths)) - for _, p := range filePaths { - bazelOuts = append(bazelOuts, PathForBazelOutRelative(ctx, relativeRoot, p)) - } - fg.srcs = bazelOuts -} - -func (fg *fileGroup) ShouldConvertToAidlLibrary(ctx BazelConversionPathContext) bool { - return fg.shouldConvertToLibrary(ctx, ".aidl") -} - -func (fg *fileGroup) ShouldConvertToProtoLibrary(ctx BazelConversionPathContext) bool { - return fg.shouldConvertToLibrary(ctx, ".proto") -} - -func (fg *fileGroup) shouldConvertToLibrary(ctx BazelConversionPathContext, suffix string) bool { - if len(fg.properties.Srcs) == 0 || !fg.ShouldConvertWithBp2build(ctx) { - return false - } - for _, src := range fg.properties.Srcs { - if !strings.HasSuffix(src, suffix) { - return false - } - } - return true -} - -func (fg *fileGroup) GetAidlLibraryLabel(ctx BazelConversionPathContext) string { - return fg.getFileGroupAsLibraryLabel(ctx) -} - -func (fg *fileGroup) GetProtoLibraryLabel(ctx BazelConversionPathContext) string { - return fg.getFileGroupAsLibraryLabel(ctx) + convertedProtoLibrarySuffix -} - -func (fg *fileGroup) getFileGroupAsLibraryLabel(ctx BazelConversionPathContext) string { - if ctx.OtherModuleDir(fg.module) == ctx.ModuleDir() { - return ":" + fg.Name() - } else { - return fg.GetBazelLabel(ctx, fg) - } -} - -// Given a name in srcs prop, check to see if the name references a filegroup -// and the filegroup is converted to aidl_library -func IsConvertedToAidlLibrary(ctx BazelConversionPathContext, name string) bool { - if fg, ok := ToFileGroupAsLibrary(ctx, name); ok { - return fg.ShouldConvertToAidlLibrary(ctx) - } - return false -} - -func ToFileGroupAsLibrary(ctx BazelConversionPathContext, name string) (FileGroupAsLibrary, bool) { - if module, ok := ctx.ModuleFromName(name); ok { - if IsFilegroup(ctx, module) { - if fg, ok := module.(FileGroupAsLibrary); ok { - return fg, true - } - } - } - return nil, false -} - // Defaults type FileGroupDefaults struct { ModuleBase diff --git a/android/filegroup_test.go b/android/filegroup_test.go index 893da57740..14e9368ca8 100644 --- a/android/filegroup_test.go +++ b/android/filegroup_test.go @@ -40,17 +40,8 @@ func TestFileGroupWithPathProp(t *testing.T) { } for _, testCase := range testCases { - outBaseDir := "outputbase" result := GroupFixturePreparers( PrepareForTestWithFilegroup, - FixtureModifyConfig(func(config Config) { - config.BazelContext = MockBazelContext{ - OutputBaseDir: outBaseDir, - LabelToOutputFiles: map[string][]string{ - "//:baz": []string{"a/b/c/d/test.aidl"}, - }, - } - }), ).RunTestWithBp(t, testCase.bp) fg := result.Module("baz", "").(*fileGroup) diff --git a/android/fixture.go b/android/fixture.go index dbc3bc5e01..5ad47e8c9f 100644 --- a/android/fixture.go +++ b/android/fixture.go @@ -275,6 +275,15 @@ func FixtureModifyContext(mutator func(ctx *TestContext)) FixturePreparer { }) } +// Sync the mock filesystem with the current config, then modify the context, +// This allows context modification that requires filesystem access. +func FixtureModifyContextWithMockFs(mutator func(ctx *TestContext)) FixturePreparer { + return newSimpleFixturePreparer(func(f *fixture) { + f.config.mockFileSystem("", f.mockFS) + mutator(f.ctx) + }) +} + func FixtureRegisterWithContext(registeringFunc func(ctx RegistrationContext)) FixturePreparer { return FixtureModifyContext(func(ctx *TestContext) { registeringFunc(ctx) }) } @@ -369,7 +378,7 @@ func FixtureModifyEnv(mutator func(env map[string]string)) FixturePreparer { // Allow access to the product variables when preparing the fixture. type FixtureProductVariables struct { - *productVariables + *ProductVariables } // Modify product variables. diff --git a/android/gen_notice.go b/android/gen_notice.go index 091345b9fa..1acc638e88 100644 --- a/android/gen_notice.go +++ b/android/gen_notice.go @@ -28,7 +28,7 @@ func init() { // Register the gen_notice module type. func RegisterGenNoticeBuildComponents(ctx RegistrationContext) { - ctx.RegisterSingletonType("gen_notice_build_rules", GenNoticeBuildRulesFactory) + ctx.RegisterParallelSingletonType("gen_notice_build_rules", GenNoticeBuildRulesFactory) ctx.RegisterModuleType("gen_notice", GenNoticeFactory) } diff --git a/android/license.go b/android/license.go index a09422b984..5bffc2519a 100644 --- a/android/license.go +++ b/android/license.go @@ -15,12 +15,7 @@ package android import ( - "fmt" - "os" - "github.com/google/blueprint" - - "android/soong/bazel" ) type licenseKindDependencyTag struct { @@ -53,54 +48,13 @@ type licenseProperties struct { Visibility []string } -var _ Bazelable = &licenseModule{} - type licenseModule struct { ModuleBase DefaultableModuleBase - BazelModuleBase properties licenseProperties } -type bazelLicenseAttributes struct { - License_kinds []string - Copyright_notice *string - License_text bazel.LabelAttribute - Package_name *string - Visibility []string -} - -func (m *licenseModule) ConvertWithBp2build(ctx TopDownMutatorContext) { - attrs := &bazelLicenseAttributes{ - License_kinds: m.properties.License_kinds, - Copyright_notice: m.properties.Copyright_notice, - Package_name: m.properties.Package_name, - Visibility: m.properties.Visibility, - } - - // TODO(asmundak): Soong supports multiple license texts while Bazel's license - // rule does not. Have android_license create a genrule to concatenate multiple - // license texts. - if len(m.properties.License_text) > 1 && ctx.Config().IsEnvTrue("BP2BUILD_VERBOSE") { - fmt.Fprintf(os.Stderr, "warning: using only the first license_text item from //%s:%s\n", - ctx.ModuleDir(), m.Name()) - } - if len(m.properties.License_text) >= 1 { - attrs.License_text.SetValue(BazelLabelForModuleSrcSingle(ctx, m.properties.License_text[0])) - } - - ctx.CreateBazelTargetModule( - bazel.BazelTargetModuleProperties{ - Rule_class: "android_license", - Bzl_load_location: "//build/bazel/rules/license:license.bzl", - }, - CommonAttributes{ - Name: m.Name(), - }, - attrs) -} - func (m *licenseModule) DepsMutator(ctx BottomUpMutatorContext) { for i, license := range m.properties.License_kinds { for j := i + 1; j < len(m.properties.License_kinds); j++ { @@ -131,14 +85,13 @@ func LicenseFactory() Module { module := &licenseModule{} base := module.base() - module.AddProperties(&base.nameProperties, &module.properties, &base.commonProperties.BazelConversionStatus) + module.AddProperties(&base.nameProperties, &module.properties) // The visibility property needs to be checked and parsed by the visibility module. setPrimaryVisibilityProperty(module, "visibility", &module.properties.Visibility) initAndroidModuleBase(module) InitDefaultableModule(module) - InitBazelModule(module) return module } diff --git a/android/license_kind.go b/android/license_kind.go index 24b91e4c2c..838deddd2a 100644 --- a/android/license_kind.go +++ b/android/license_kind.go @@ -14,8 +14,6 @@ package android -import "android/soong/bazel" - func init() { RegisterLicenseKindBuildComponents(InitRegistrationContext) } @@ -34,39 +32,13 @@ type licenseKindProperties struct { Visibility []string } -var _ Bazelable = &licenseKindModule{} - type licenseKindModule struct { ModuleBase DefaultableModuleBase - BazelModuleBase properties licenseKindProperties } -type bazelLicenseKindAttributes struct { - Conditions []string - Url string - Visibility []string -} - -func (m *licenseKindModule) ConvertWithBp2build(ctx TopDownMutatorContext) { - attrs := &bazelLicenseKindAttributes{ - Conditions: m.properties.Conditions, - Url: m.properties.Url, - Visibility: m.properties.Visibility, - } - ctx.CreateBazelTargetModule( - bazel.BazelTargetModuleProperties{ - Rule_class: "license_kind", - Bzl_load_location: "@rules_license//rules:license_kind.bzl", - }, - CommonAttributes{ - Name: m.Name(), - }, - attrs) -} - func (m *licenseKindModule) DepsMutator(ctx BottomUpMutatorContext) { // Nothing to do. } @@ -79,14 +51,13 @@ func LicenseKindFactory() Module { module := &licenseKindModule{} base := module.base() - module.AddProperties(&base.nameProperties, &module.properties, &base.commonProperties.BazelConversionStatus) + module.AddProperties(&base.nameProperties, &module.properties) // The visibility property needs to be checked and parsed by the visibility module. setPrimaryVisibilityProperty(module, "visibility", &module.properties.Visibility) initAndroidModuleBase(module) InitDefaultableModule(module) - InitBazelModule(module) return module } diff --git a/android/license_metadata.go b/android/license_metadata.go index 73000a9fa3..609ca79354 100644 --- a/android/license_metadata.go +++ b/android/license_metadata.go @@ -50,12 +50,19 @@ func buildLicenseMetadata(ctx ModuleContext, licenseMetadataFile WritablePath) { outputFiles = PathsIfNonNil(outputFiles...) } - isContainer := isContainerFromFileExtensions(base.installFiles, outputFiles) + // Only pass the last installed file to isContainerFromFileExtensions so a *.zip file in test data + // doesn't mark the whole module as a container. + var installFiles InstallPaths + if len(base.installFiles) > 0 { + installFiles = InstallPaths{base.installFiles[len(base.installFiles)-1]} + } + + isContainer := isContainerFromFileExtensions(installFiles, outputFiles) var allDepMetadataFiles Paths var allDepMetadataArgs []string var allDepOutputFiles Paths - var allDepMetadataDepSets []*PathsDepSet + var allDepMetadataDepSets []*DepSet[Path] ctx.VisitDirectDepsBlueprint(func(bpdep blueprint.Module) { dep, _ := bpdep.(Module) @@ -127,7 +134,7 @@ func buildLicenseMetadata(ctx ModuleContext, licenseMetadataFile WritablePath) { JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(base.commonProperties.Effective_license_text.Strings()), "-n ")) if isContainer { - transitiveDeps := newPathsDepSet(nil, allDepMetadataDepSets).ToList() + transitiveDeps := Paths(NewDepSet[Path](TOPOLOGICAL, nil, allDepMetadataDepSets).ToList()) args = append(args, JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(transitiveDeps.Strings()), "-d ")) orderOnlyDeps = append(orderOnlyDeps, transitiveDeps...) @@ -170,7 +177,7 @@ func buildLicenseMetadata(ctx ModuleContext, licenseMetadataFile WritablePath) { ctx.SetProvider(LicenseMetadataProvider, &LicenseMetadataInfo{ LicenseMetadataPath: licenseMetadataFile, - LicenseMetadataDepSet: newPathsDepSet(Paths{licenseMetadataFile}, allDepMetadataDepSets), + LicenseMetadataDepSet: NewDepSet(TOPOLOGICAL, Paths{licenseMetadataFile}, allDepMetadataDepSets), }) } @@ -198,7 +205,7 @@ var LicenseMetadataProvider = blueprint.NewProvider(&LicenseMetadataInfo{}) // LicenseMetadataInfo stores the license metadata path for a module. type LicenseMetadataInfo struct { LicenseMetadataPath Path - LicenseMetadataDepSet *PathsDepSet + LicenseMetadataDepSet *DepSet[Path] } // licenseAnnotationsFromTag returns the LicenseAnnotations for a tag (if any) converted into diff --git a/android/makefile_goal.go b/android/makefile_goal.go deleted file mode 100644 index 07354a6486..0000000000 --- a/android/makefile_goal.go +++ /dev/null @@ -1,98 +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 android - -import ( - "fmt" - "io" - "path/filepath" - - "github.com/google/blueprint/proptools" -) - -func init() { - RegisterModuleType("makefile_goal", MakefileGoalFactory) -} - -type makefileGoalProperties struct { - // Sources. - - // Makefile goal output file path, relative to PRODUCT_OUT. - Product_out_path *string -} - -type makefileGoal struct { - ModuleBase - - properties makefileGoalProperties - - // Destination. Output file path of this module. - outputFilePath OutputPath -} - -var _ AndroidMkEntriesProvider = (*makefileGoal)(nil) -var _ OutputFileProducer = (*makefileGoal)(nil) - -// Input file of this makefile_goal module. Nil if none specified. May use variable names in makefiles. -func (p *makefileGoal) inputPath() *string { - if p.properties.Product_out_path != nil { - return proptools.StringPtr(filepath.Join("$(PRODUCT_OUT)", proptools.String(p.properties.Product_out_path))) - } - return nil -} - -// OutputFileProducer -func (p *makefileGoal) OutputFiles(tag string) (Paths, error) { - if tag != "" { - return nil, fmt.Errorf("unsupported tag %q", tag) - } - return Paths{p.outputFilePath}, nil -} - -// AndroidMkEntriesProvider -func (p *makefileGoal) DepsMutator(ctx BottomUpMutatorContext) { - if p.inputPath() == nil { - ctx.PropertyErrorf("product_out_path", "Path relative to PRODUCT_OUT required") - } -} - -func (p *makefileGoal) GenerateAndroidBuildActions(ctx ModuleContext) { - filename := filepath.Base(proptools.String(p.inputPath())) - p.outputFilePath = PathForModuleOut(ctx, filename).OutputPath - - ctx.InstallFile(PathForModuleInstall(ctx, "etc"), ctx.ModuleName(), p.outputFilePath) -} - -func (p *makefileGoal) AndroidMkEntries() []AndroidMkEntries { - return []AndroidMkEntries{AndroidMkEntries{ - Class: "ETC", - OutputFile: OptionalPathForPath(p.outputFilePath), - ExtraFooters: []AndroidMkExtraFootersFunc{ - func(w io.Writer, name, prefix, moduleDir string) { - // Can't use Cp because inputPath() is not a valid Path. - fmt.Fprintf(w, "$(eval $(call copy-one-file,%s,%s))\n", proptools.String(p.inputPath()), p.outputFilePath) - }, - }, - }} -} - -// Import a Makefile goal to Soong by copying the file built by -// the goal to a path visible to Soong. This rule only works on boot images. -func MakefileGoalFactory() Module { - module := &makefileGoal{} - module.AddProperties(&module.properties) - InitAndroidModule(module) - return module -} diff --git a/android/metrics.go b/android/metrics.go index 3d41a1d6cb..6834b1bde1 100644 --- a/android/metrics.go +++ b/android/metrics.go @@ -15,9 +15,12 @@ package android import ( + "bytes" "io/ioutil" + "os" "runtime" - "sort" + "strconv" + "time" "github.com/google/blueprint/metrics" "google.golang.org/protobuf/proto" @@ -27,22 +30,25 @@ import ( var soongMetricsOnceKey = NewOnceKey("soong metrics") -type SoongMetrics struct { - Modules int - Variants int +type soongMetrics struct { + modules int + variants int + perfCollector perfCollector } -func readSoongMetrics(config Config) (SoongMetrics, bool) { - soongMetrics, ok := config.Peek(soongMetricsOnceKey) - if ok { - return soongMetrics.(SoongMetrics), true - } else { - return SoongMetrics{}, false - } +type perfCollector struct { + events []*soong_metrics_proto.PerfCounters + stop chan<- bool +} + +func getSoongMetrics(config Config) *soongMetrics { + return config.Once(soongMetricsOnceKey, func() interface{} { + return &soongMetrics{} + }).(*soongMetrics) } func init() { - RegisterSingletonType("soong_metrics", soongMetricsSingletonFactory) + RegisterParallelSingletonType("soong_metrics", soongMetricsSingletonFactory) } func soongMetricsSingletonFactory() Singleton { return soongMetricsSingleton{} } @@ -50,27 +56,27 @@ func soongMetricsSingletonFactory() Singleton { return soongMetricsSingleton{} } type soongMetricsSingleton struct{} func (soongMetricsSingleton) GenerateBuildActions(ctx SingletonContext) { - metrics := SoongMetrics{} + metrics := getSoongMetrics(ctx.Config()) ctx.VisitAllModules(func(m Module) { if ctx.PrimaryModule(m) == m { - metrics.Modules++ + metrics.modules++ } - metrics.Variants++ - }) - ctx.Config().Once(soongMetricsOnceKey, func() interface{} { - return metrics + metrics.variants++ }) } func collectMetrics(config Config, eventHandler *metrics.EventHandler) *soong_metrics_proto.SoongBuildMetrics { metrics := &soong_metrics_proto.SoongBuildMetrics{} - soongMetrics, ok := readSoongMetrics(config) - if ok { - metrics.Modules = proto.Uint32(uint32(soongMetrics.Modules)) - metrics.Variants = proto.Uint32(uint32(soongMetrics.Variants)) + soongMetrics := getSoongMetrics(config) + if soongMetrics.modules > 0 { + metrics.Modules = proto.Uint32(uint32(soongMetrics.modules)) + metrics.Variants = proto.Uint32(uint32(soongMetrics.variants)) } + soongMetrics.perfCollector.stop <- true + metrics.PerfCounters = soongMetrics.perfCollector.events + memStats := runtime.MemStats{} runtime.ReadMemStats(&memStats) metrics.MaxHeapSize = proto.Uint64(memStats.HeapSys) @@ -86,25 +92,115 @@ func collectMetrics(config Config, eventHandler *metrics.EventHandler) *soong_me } metrics.Events = append(metrics.Events, &perfInfo) } - mixedBuildsInfo := soong_metrics_proto.MixedBuildsInfo{} - mixedBuildEnabledModules := make([]string, 0, len(config.mixedBuildEnabledModules)) - for module, _ := range config.mixedBuildEnabledModules { - mixedBuildEnabledModules = append(mixedBuildEnabledModules, module) + + return metrics +} + +func StartBackgroundMetrics(config Config) { + perfCollector := &getSoongMetrics(config).perfCollector + stop := make(chan bool) + perfCollector.stop = stop + + previousTime := time.Now() + previousCpuTime := readCpuTime() + + ticker := time.NewTicker(time.Second) + + go func() { + for { + select { + case <-stop: + ticker.Stop() + return + case <-ticker.C: + // carry on + } + + currentTime := time.Now() + + var memStats runtime.MemStats + runtime.ReadMemStats(&memStats) + + currentCpuTime := readCpuTime() + + interval := currentTime.Sub(previousTime) + intervalCpuTime := currentCpuTime - previousCpuTime + intervalCpuPercent := intervalCpuTime * 100 / interval + + // heapAlloc is the memory that has been allocated on the heap but not yet GC'd. It may be referenced, + // or unrefenced but not yet GC'd. + heapAlloc := memStats.HeapAlloc + // heapUnused is the memory that was previously used by the heap, but is currently not used. It does not + // count memory that was used and then returned to the OS. + heapUnused := memStats.HeapIdle - memStats.HeapReleased + // heapOverhead is the memory used by the allocator and GC + heapOverhead := memStats.MSpanSys + memStats.MCacheSys + memStats.GCSys + // otherMem is the memory used outside of the heap. + otherMem := memStats.Sys - memStats.HeapSys - heapOverhead + + perfCollector.events = append(perfCollector.events, &soong_metrics_proto.PerfCounters{ + Time: proto.Uint64(uint64(currentTime.UnixNano())), + Groups: []*soong_metrics_proto.PerfCounterGroup{ + { + Name: proto.String("cpu"), + Counters: []*soong_metrics_proto.PerfCounter{ + {Name: proto.String("cpu_percent"), Value: proto.Int64(int64(intervalCpuPercent))}, + }, + }, { + Name: proto.String("memory"), + Counters: []*soong_metrics_proto.PerfCounter{ + {Name: proto.String("heap_alloc"), Value: proto.Int64(int64(heapAlloc))}, + {Name: proto.String("heap_unused"), Value: proto.Int64(int64(heapUnused))}, + {Name: proto.String("heap_overhead"), Value: proto.Int64(int64(heapOverhead))}, + {Name: proto.String("other"), Value: proto.Int64(int64(otherMem))}, + }, + }, + }, + }) + + previousTime = currentTime + previousCpuTime = currentCpuTime + } + }() +} + +func readCpuTime() time.Duration { + if runtime.GOOS != "linux" { + return 0 + } + + stat, err := os.ReadFile("/proc/self/stat") + if err != nil { + return 0 } - mixedBuildDisabledModules := make([]string, 0, len(config.mixedBuildDisabledModules)) - for module, _ := range config.mixedBuildDisabledModules { - mixedBuildDisabledModules = append(mixedBuildDisabledModules, module) + endOfComm := bytes.LastIndexByte(stat, ')') + if endOfComm < 0 || endOfComm > len(stat)-2 { + return 0 } - // Sorted for deterministic output. - sort.Strings(mixedBuildEnabledModules) - sort.Strings(mixedBuildDisabledModules) - mixedBuildsInfo.MixedBuildEnabledModules = mixedBuildEnabledModules - mixedBuildsInfo.MixedBuildDisabledModules = mixedBuildDisabledModules - metrics.MixedBuildsInfo = &mixedBuildsInfo + stat = stat[endOfComm+2:] - return metrics + statFields := bytes.Split(stat, []byte{' '}) + // This should come from sysconf(_SC_CLK_TCK), but there's no way to call that from Go. Assume it's 100, + // which is the value for all platforms we support. + const HZ = 100 + const MS_PER_HZ = 1e3 / HZ * time.Millisecond + + const STAT_UTIME_FIELD = 14 - 2 + const STAT_STIME_FIELD = 15 - 2 + if len(statFields) < STAT_STIME_FIELD { + return 0 + } + userCpuTicks, err := strconv.ParseUint(string(statFields[STAT_UTIME_FIELD]), 10, 64) + if err != nil { + return 0 + } + kernelCpuTicks, _ := strconv.ParseUint(string(statFields[STAT_STIME_FIELD]), 10, 64) + if err != nil { + return 0 + } + return time.Duration(userCpuTicks+kernelCpuTicks) * MS_PER_HZ } func WriteMetrics(config Config, eventHandler *metrics.EventHandler, metricsFile string) error { diff --git a/android/module.go b/android/module.go index 76b4e3d115..f571157998 100644 --- a/android/module.go +++ b/android/module.go @@ -15,18 +15,16 @@ package android import ( + "android/soong/bazel" + "crypto/md5" + "encoding/hex" + "encoding/json" "fmt" "net/url" - "os" - "path" "path/filepath" "reflect" - "regexp" "sort" "strings" - "text/scanner" - - "android/soong/bazel" "github.com/google/blueprint" "github.com/google/blueprint/proptools" @@ -37,465 +35,6 @@ var ( DeviceStaticLibrary = "static_library" ) -// BuildParameters describes the set of potential parameters to build a Ninja rule. -// In general, these correspond to a Ninja concept. -type BuildParams struct { - // A Ninja Rule that will be written to the Ninja file. This allows factoring out common code - // among multiple modules to reduce repetition in the Ninja file of action requirements. A rule - // can contain variables that should be provided in Args. - Rule blueprint.Rule - // Deps represents the depfile format. When using RuleBuilder, this defaults to GCC when depfiles - // are used. - Deps blueprint.Deps - // Depfile is a writeable path that allows correct incremental builds when the inputs have not - // been fully specified by the Ninja rule. Ninja supports a subset of the Makefile depfile syntax. - Depfile WritablePath - // A description of the build action. - Description string - // Output is an output file of the action. When using this field, references to $out in the Ninja - // command will refer to this file. - Output WritablePath - // Outputs is a slice of output file of the action. When using this field, references to $out in - // the Ninja command will refer to these files. - Outputs WritablePaths - // SymlinkOutput is an output file specifically that is a symlink. - SymlinkOutput WritablePath - // SymlinkOutputs is a slice of output files specifically that is a symlink. - SymlinkOutputs WritablePaths - // ImplicitOutput is an output file generated by the action. Note: references to `$out` in the - // Ninja command will NOT include references to this file. - ImplicitOutput WritablePath - // ImplicitOutputs is a slice of output files generated by the action. Note: references to `$out` - // in the Ninja command will NOT include references to these files. - ImplicitOutputs WritablePaths - // Input is an input file to the Ninja action. When using this field, references to $in in the - // Ninja command will refer to this file. - Input Path - // Inputs is a slice of input files to the Ninja action. When using this field, references to $in - // in the Ninja command will refer to these files. - Inputs Paths - // Implicit is an input file to the Ninja action. Note: references to `$in` in the Ninja command - // will NOT include references to this file. - Implicit Path - // Implicits is a slice of input files to the Ninja action. Note: references to `$in` in the Ninja - // command will NOT include references to these files. - Implicits Paths - // OrderOnly are Ninja order-only inputs to the action. When these are out of date, the output is - // not rebuilt until they are built, but changes in order-only dependencies alone do not cause the - // output to be rebuilt. - OrderOnly Paths - // Validation is an output path for a validation action. Validation outputs imply lower - // non-blocking priority to building non-validation outputs. - Validation Path - // Validations is a slice of output path for a validation action. Validation outputs imply lower - // non-blocking priority to building non-validation outputs. - Validations Paths - // Whether to skip outputting a default target statement which will be built by Ninja when no - // targets are specified on Ninja's command line. - Default bool - // Args is a key value mapping for replacements of variables within the Rule - Args map[string]string -} - -type ModuleBuildParams BuildParams - -// EarlyModuleContext provides methods that can be called early, as soon as the properties have -// been parsed into the module and before any mutators have run. -type EarlyModuleContext interface { - // Module returns the current module as a Module. It should rarely be necessary, as the module already has a - // reference to itself. - Module() Module - - // ModuleName returns the name of the module. This is generally the value that was returned by Module.Name() when - // the module was created, but may have been modified by calls to BaseMutatorContext.Rename. - ModuleName() string - - // ModuleDir returns the path to the directory that contains the definition of the module. - ModuleDir() string - - // ModuleType returns the name of the module type that was used to create the module, as specified in - // RegisterModuleType. - ModuleType() string - - // BlueprintFile returns the name of the blueprint file that contains the definition of this - // module. - BlueprintsFile() string - - // ContainsProperty returns true if the specified property name was set in the module definition. - ContainsProperty(name string) bool - - // Errorf reports an error at the specified position of the module definition file. - Errorf(pos scanner.Position, fmt string, args ...interface{}) - - // ModuleErrorf reports an error at the line number of the module type in the module definition. - ModuleErrorf(fmt string, args ...interface{}) - - // PropertyErrorf reports an error at the line number of a property in the module definition. - PropertyErrorf(property, fmt string, args ...interface{}) - - // Failed returns true if any errors have been reported. In most cases the module can continue with generating - // build rules after an error, allowing it to report additional errors in a single run, but in cases where the error - // has prevented the module from creating necessary data it can return early when Failed returns true. - Failed() bool - - // AddNinjaFileDeps adds dependencies on the specified files to the rule that creates the ninja manifest. The - // primary builder will be rerun whenever the specified files are modified. - AddNinjaFileDeps(deps ...string) - - DeviceSpecific() bool - SocSpecific() bool - ProductSpecific() bool - SystemExtSpecific() bool - Platform() bool - - Config() Config - DeviceConfig() DeviceConfig - - // Deprecated: use Config() - AConfig() Config - - // GlobWithDeps returns a list of files that match the specified pattern but do not match any - // of the patterns in excludes. It also adds efficient dependencies to rerun the primary - // builder whenever a file matching the pattern as added or removed, without rerunning if a - // file that does not match the pattern is added to a searched directory. - GlobWithDeps(pattern string, excludes []string) ([]string, error) - - Glob(globPattern string, excludes []string) Paths - GlobFiles(globPattern string, excludes []string) Paths - IsSymlink(path Path) bool - Readlink(path Path) string - - // Namespace returns the Namespace object provided by the NameInterface set by Context.SetNameInterface, or the - // default SimpleNameInterface if Context.SetNameInterface was not called. - Namespace() *Namespace -} - -// BaseModuleContext is the same as blueprint.BaseModuleContext except that Config() returns -// a Config instead of an interface{}, and some methods have been wrapped to use an android.Module -// instead of a blueprint.Module, plus some extra methods that return Android-specific information -// about the current module. -type BaseModuleContext interface { - EarlyModuleContext - - blueprintBaseModuleContext() blueprint.BaseModuleContext - - // OtherModuleName returns the name of another Module. See BaseModuleContext.ModuleName for more information. - // It is intended for use inside the visit functions of Visit* and WalkDeps. - OtherModuleName(m blueprint.Module) string - - // OtherModuleDir returns the directory of another Module. See BaseModuleContext.ModuleDir for more information. - // It is intended for use inside the visit functions of Visit* and WalkDeps. - OtherModuleDir(m blueprint.Module) string - - // OtherModuleErrorf reports an error on another Module. See BaseModuleContext.ModuleErrorf for more information. - // It is intended for use inside the visit functions of Visit* and WalkDeps. - OtherModuleErrorf(m blueprint.Module, fmt string, args ...interface{}) - - // OtherModuleDependencyTag returns the dependency tag used to depend on a module, or nil if there is no dependency - // on the module. When called inside a Visit* method with current module being visited, and there are multiple - // dependencies on the module being visited, it returns the dependency tag used for the current dependency. - OtherModuleDependencyTag(m blueprint.Module) blueprint.DependencyTag - - // OtherModuleExists returns true if a module with the specified name exists, as determined by the NameInterface - // passed to Context.SetNameInterface, or SimpleNameInterface if it was not called. - OtherModuleExists(name string) bool - - // OtherModuleDependencyVariantExists returns true if a module with the - // specified name and variant exists. The variant must match the given - // variations. It must also match all the non-local variations of the current - // module. In other words, it checks for the module that AddVariationDependencies - // would add a dependency on with the same arguments. - OtherModuleDependencyVariantExists(variations []blueprint.Variation, name string) bool - - // OtherModuleFarDependencyVariantExists returns true if a module with the - // specified name and variant exists. The variant must match the given - // variations, but not the non-local variations of the current module. In - // other words, it checks for the module that AddFarVariationDependencies - // would add a dependency on with the same arguments. - OtherModuleFarDependencyVariantExists(variations []blueprint.Variation, name string) bool - - // OtherModuleReverseDependencyVariantExists returns true if a module with the - // specified name exists with the same variations as the current module. In - // other words, it checks for the module that AddReverseDependency would add a - // dependency on with the same argument. - OtherModuleReverseDependencyVariantExists(name string) bool - - // OtherModuleType returns the type of another Module. See BaseModuleContext.ModuleType for more information. - // It is intended for use inside the visit functions of Visit* and WalkDeps. - OtherModuleType(m blueprint.Module) string - - // OtherModuleProvider returns the value for a provider for the given module. If the value is - // not set it returns the zero value of the type of the provider, so the return value can always - // be type asserted to the type of the provider. The value returned may be a deep copy of the - // value originally passed to SetProvider. - OtherModuleProvider(m blueprint.Module, provider blueprint.ProviderKey) interface{} - - // OtherModuleHasProvider returns true if the provider for the given module has been set. - OtherModuleHasProvider(m blueprint.Module, provider blueprint.ProviderKey) bool - - // Provider returns the value for a provider for the current module. If the value is - // not set it returns the zero value of the type of the provider, so the return value can always - // be type asserted to the type of the provider. It panics if called before the appropriate - // mutator or GenerateBuildActions pass for the provider. The value returned may be a deep - // copy of the value originally passed to SetProvider. - Provider(provider blueprint.ProviderKey) interface{} - - // HasProvider returns true if the provider for the current module has been set. - HasProvider(provider blueprint.ProviderKey) bool - - // SetProvider sets the value for a provider for the current module. It panics if not called - // during the appropriate mutator or GenerateBuildActions pass for the provider, if the value - // is not of the appropriate type, or if the value has already been set. The value should not - // be modified after being passed to SetProvider. - SetProvider(provider blueprint.ProviderKey, value interface{}) - - GetDirectDepsWithTag(tag blueprint.DependencyTag) []Module - - // GetDirectDepWithTag returns the Module the direct dependency with the specified name, or nil if - // none exists. It panics if the dependency does not have the specified tag. It skips any - // dependencies that are not an android.Module. - GetDirectDepWithTag(name string, tag blueprint.DependencyTag) blueprint.Module - - // GetDirectDep returns the Module and DependencyTag for the direct dependency with the specified - // name, or nil if none exists. If there are multiple dependencies on the same module it returns - // the first DependencyTag. - GetDirectDep(name string) (blueprint.Module, blueprint.DependencyTag) - - ModuleFromName(name string) (blueprint.Module, bool) - - // VisitDirectDepsBlueprint calls visit for each direct dependency. If there are multiple - // direct dependencies on the same module visit will be called multiple times on that module - // and OtherModuleDependencyTag will return a different tag for each. - // - // The Module passed to the visit function should not be retained outside of the visit - // function, it may be invalidated by future mutators. - VisitDirectDepsBlueprint(visit func(blueprint.Module)) - - // VisitDirectDeps calls visit for each direct dependency. If there are multiple - // direct dependencies on the same module visit will be called multiple times on that module - // and OtherModuleDependencyTag will return a different tag for each. It raises an error if any of the - // dependencies are not an android.Module. - // - // The Module passed to the visit function should not be retained outside of the visit - // function, it may be invalidated by future mutators. - VisitDirectDeps(visit func(Module)) - - VisitDirectDepsWithTag(tag blueprint.DependencyTag, visit func(Module)) - - // VisitDirectDepsIf calls pred for each direct dependency, and if pred returns true calls visit. If there are - // multiple direct dependencies on the same module pred and visit will be called multiple times on that module and - // OtherModuleDependencyTag will return a different tag for each. It skips any - // dependencies that are not an android.Module. - // - // The Module passed to the visit function should not be retained outside of the visit function, it may be - // invalidated by future mutators. - VisitDirectDepsIf(pred func(Module) bool, visit func(Module)) - // Deprecated: use WalkDeps instead to support multiple dependency tags on the same module - VisitDepsDepthFirst(visit func(Module)) - // Deprecated: use WalkDeps instead to support multiple dependency tags on the same module - VisitDepsDepthFirstIf(pred func(Module) bool, visit func(Module)) - - // WalkDeps calls visit for each transitive dependency, traversing the dependency tree in top down order. visit may - // be called multiple times for the same (child, parent) pair if there are multiple direct dependencies between the - // child and parent with different tags. OtherModuleDependencyTag will return the tag for the currently visited - // (child, parent) pair. If visit returns false WalkDeps will not continue recursing down to child. It skips - // any dependencies that are not an android.Module. - // - // The Modules passed to the visit function should not be retained outside of the visit function, they may be - // invalidated by future mutators. - WalkDeps(visit func(child, parent Module) bool) - - // WalkDepsBlueprint calls visit for each transitive dependency, traversing the dependency - // tree in top down order. visit may be called multiple times for the same (child, parent) - // pair if there are multiple direct dependencies between the child and parent with different - // tags. OtherModuleDependencyTag will return the tag for the currently visited - // (child, parent) pair. If visit returns false WalkDeps will not continue recursing down - // to child. - // - // The Modules passed to the visit function should not be retained outside of the visit function, they may be - // invalidated by future mutators. - WalkDepsBlueprint(visit func(blueprint.Module, blueprint.Module) bool) - - // GetWalkPath is supposed to be called in visit function passed in WalkDeps() - // and returns a top-down dependency path from a start module to current child module. - GetWalkPath() []Module - - // PrimaryModule returns the first variant of the current module. Variants of a module are always visited in - // order by mutators and GenerateBuildActions, so the data created by the current mutator can be read from the - // Module returned by PrimaryModule without data races. This can be used to perform singleton actions that are - // only done once for all variants of a module. - PrimaryModule() Module - - // FinalModule returns the last variant of the current module. Variants of a module are always visited in - // order by mutators and GenerateBuildActions, so the data created by the current mutator can be read from all - // variants using VisitAllModuleVariants if the current module == FinalModule(). This can be used to perform - // singleton actions that are only done once for all variants of a module. - FinalModule() Module - - // VisitAllModuleVariants calls visit for each variant of the current module. Variants of a module are always - // visited in order by mutators and GenerateBuildActions, so the data created by the current mutator can be read - // from all variants if the current module == FinalModule(). Otherwise, care must be taken to not access any - // data modified by the current mutator. - VisitAllModuleVariants(visit func(Module)) - - // GetTagPath is supposed to be called in visit function passed in WalkDeps() - // and returns a top-down dependency tags path from a start module to current child module. - // It has one less entry than GetWalkPath() as it contains the dependency tags that - // exist between each adjacent pair of modules in the GetWalkPath(). - // GetTagPath()[i] is the tag between GetWalkPath()[i] and GetWalkPath()[i+1] - GetTagPath() []blueprint.DependencyTag - - // GetPathString is supposed to be called in visit function passed in WalkDeps() - // and returns a multi-line string showing the modules and dependency tags - // among them along the top-down dependency path from a start module to current child module. - // skipFirst when set to true, the output doesn't include the start module, - // which is already printed when this function is used along with ModuleErrorf(). - GetPathString(skipFirst bool) string - - AddMissingDependencies(missingDeps []string) - - // AddUnconvertedBp2buildDep stores module name of a direct dependency that was not converted via bp2build - AddUnconvertedBp2buildDep(dep string) - - // AddMissingBp2buildDep stores the module name of a direct dependency that was not found. - AddMissingBp2buildDep(dep string) - - Target() Target - TargetPrimary() bool - - // The additional arch specific targets (e.g. 32/64 bit) that this module variant is - // responsible for creating. - MultiTargets() []Target - Arch() Arch - Os() OsType - Host() bool - Device() bool - Darwin() bool - Windows() bool - Debug() bool - PrimaryArch() bool -} - -// Deprecated: use EarlyModuleContext instead -type BaseContext interface { - EarlyModuleContext -} - -type ModuleContext interface { - BaseModuleContext - - blueprintModuleContext() blueprint.ModuleContext - - // Deprecated: use ModuleContext.Build instead. - ModuleBuild(pctx PackageContext, params ModuleBuildParams) - - // Returns a list of paths expanded from globs and modules referenced using ":module" syntax. The property must - // be tagged with `android:"path" to support automatic source module dependency resolution. - // - // Deprecated: use PathsForModuleSrc or PathsForModuleSrcExcludes instead. - ExpandSources(srcFiles, excludes []string) Paths - - // Returns a single path expanded from globs and modules referenced using ":module" syntax. The property must - // be tagged with `android:"path" to support automatic source module dependency resolution. - // - // Deprecated: use PathForModuleSrc instead. - ExpandSource(srcFile, prop string) Path - - ExpandOptionalSource(srcFile *string, prop string) OptionalPath - - // InstallExecutable creates a rule to copy srcPath to name in the installPath directory, - // with the given additional dependencies. The file is marked executable after copying. - // - // The installed file will be returned by FilesToInstall(), and the PackagingSpec for the - // installed file will be returned by PackagingSpecs() on this module or by - // TransitivePackagingSpecs() on modules that depend on this module through dependency tags - // for which IsInstallDepNeeded returns true. - InstallExecutable(installPath InstallPath, name string, srcPath Path, deps ...Path) InstallPath - - // InstallFile creates a rule to copy srcPath to name in the installPath directory, - // with the given additional dependencies. - // - // The installed file will be returned by FilesToInstall(), and the PackagingSpec for the - // installed file will be returned by PackagingSpecs() on this module or by - // TransitivePackagingSpecs() on modules that depend on this module through dependency tags - // for which IsInstallDepNeeded returns true. - InstallFile(installPath InstallPath, name string, srcPath Path, deps ...Path) InstallPath - - // InstallFileWithExtraFilesZip creates a rule to copy srcPath to name in the installPath - // directory, and also unzip a zip file containing extra files to install into the same - // directory. - // - // The installed file will be returned by FilesToInstall(), and the PackagingSpec for the - // installed file will be returned by PackagingSpecs() on this module or by - // TransitivePackagingSpecs() on modules that depend on this module through dependency tags - // for which IsInstallDepNeeded returns true. - InstallFileWithExtraFilesZip(installPath InstallPath, name string, srcPath Path, extraZip Path, deps ...Path) InstallPath - - // InstallSymlink creates a rule to create a symlink from src srcPath to name in the installPath - // directory. - // - // The installed symlink will be returned by FilesToInstall(), and the PackagingSpec for the - // installed file will be returned by PackagingSpecs() on this module or by - // TransitivePackagingSpecs() on modules that depend on this module through dependency tags - // for which IsInstallDepNeeded returns true. - InstallSymlink(installPath InstallPath, name string, srcPath InstallPath) InstallPath - - // InstallAbsoluteSymlink creates a rule to create an absolute symlink from src srcPath to name - // in the installPath directory. - // - // The installed symlink will be returned by FilesToInstall(), and the PackagingSpec for the - // installed file will be returned by PackagingSpecs() on this module or by - // TransitivePackagingSpecs() on modules that depend on this module through dependency tags - // for which IsInstallDepNeeded returns true. - InstallAbsoluteSymlink(installPath InstallPath, name string, absPath string) InstallPath - - // PackageFile creates a PackagingSpec as if InstallFile was called, but without creating - // the rule to copy the file. This is useful to define how a module would be packaged - // without installing it into the global installation directories. - // - // The created PackagingSpec for the will be returned by PackagingSpecs() on this module or by - // TransitivePackagingSpecs() on modules that depend on this module through dependency tags - // for which IsInstallDepNeeded returns true. - PackageFile(installPath InstallPath, name string, srcPath Path) PackagingSpec - - CheckbuildFile(srcPath Path) - - InstallInData() bool - InstallInTestcases() bool - InstallInSanitizerDir() bool - InstallInRamdisk() bool - InstallInVendorRamdisk() bool - InstallInDebugRamdisk() bool - InstallInRecovery() bool - InstallInRoot() bool - InstallInVendor() bool - InstallForceOS() (*OsType, *ArchType) - - RequiredModuleNames() []string - HostRequiredModuleNames() []string - TargetRequiredModuleNames() []string - - ModuleSubDir() string - - Variable(pctx PackageContext, name, value string) - Rule(pctx PackageContext, name string, params blueprint.RuleParams, argNames ...string) blueprint.Rule - // Similar to blueprint.ModuleContext.Build, but takes Paths instead of []string, - // and performs more verification. - Build(pctx PackageContext, params BuildParams) - // Phony creates a Make-style phony rule, a rule with no commands that can depend on other - // phony rules or real files. Phony can be called on the same name multiple times to add - // additional dependencies. - Phony(phony string, deps ...Path) - - // GetMissingDependencies returns the list of dependencies that were passed to AddDependencies or related methods, - // but do not exist. - GetMissingDependencies() []string - - // LicenseMetadataFile returns the path where the license metadata for this module will be - // generated. - LicenseMetadataFile() Path -} - type Module interface { blueprint.Module @@ -556,13 +95,6 @@ type Module interface { AddProperties(props ...interface{}) GetProperties() []interface{} - // IsConvertedByBp2build returns whether this module was converted via bp2build - IsConvertedByBp2build() bool - // Bp2buildTargets returns the target(s) generated for Bazel via bp2build for this module - Bp2buildTargets() []bp2buildInfo - GetUnconvertedBp2buildDeps() []string - GetMissingBp2buildDeps() []string - BuildParamsForTests() []BuildParams RuleParamsForTests() map[blueprint.Rule]blueprint.RuleParams VariablesForTests() map[string]string @@ -710,6 +242,31 @@ func SortedUniqueNamedPaths(l NamedPaths) NamedPaths { return l[:k+1] } +// soongConfigTrace holds all references to VendorVars. Uses []string for blueprint:"mutated" +type soongConfigTrace struct { + Bools []string `json:",omitempty"` + Strings []string `json:",omitempty"` + IsSets []string `json:",omitempty"` +} + +func (c *soongConfigTrace) isEmpty() bool { + return len(c.Bools) == 0 && len(c.Strings) == 0 && len(c.IsSets) == 0 +} + +// Returns hash of serialized trace records (empty string if there's no trace recorded) +func (c *soongConfigTrace) hash() string { + // Use MD5 for speed. We don't care collision or preimage attack + if c.isEmpty() { + return "" + } + j, err := json.Marshal(c) + if err != nil { + panic(fmt.Errorf("json marshal of %#v failed: %#v", *c, err)) + } + hash := md5.Sum(j) + return hex.EncodeToString(hash[:]) +} + type nameProperties struct { // The name of the module. Must be unique across all modules. Name *string @@ -939,7 +496,8 @@ type commonProperties struct { NamespaceExportedToMake bool `blueprint:"mutated"` - MissingDeps []string `blueprint:"mutated"` + MissingDeps []string `blueprint:"mutated"` + CheckedMissingDeps bool `blueprint:"mutated"` // Name and variant strings stored by mutators to enable Module.String() DebugName string `blueprint:"mutated"` @@ -951,38 +509,13 @@ type commonProperties struct { // constants in image.go, but can also be set to a custom value by individual module types. ImageVariation string `blueprint:"mutated"` - // Bazel conversion status - BazelConversionStatus BazelConversionStatus `blueprint:"mutated"` -} - -// CommonAttributes represents the common Bazel attributes from which properties -// in `commonProperties` are translated/mapped; such properties are annotated in -// a list their corresponding attribute. It is embedded within `bp2buildInfo`. -type CommonAttributes struct { - // Soong nameProperties -> Bazel name - Name string - - // Data mapped from: Required - Data bazel.LabelListAttribute - - // SkipData is neither a Soong nor Bazel target attribute - // If true, this will not fill the data attribute automatically - // This is useful for Soong modules that have 1:many Bazel targets - // Some of the generated Bazel targets might not have a data attribute - SkipData *bool - - Tags bazel.StringListAttribute - - Applicable_licenses bazel.LabelListAttribute - - Testonly *bool -} - -// constraintAttributes represents Bazel attributes pertaining to build constraints, -// which make restrict building a Bazel target for some set of platforms. -type constraintAttributes struct { - // Constraint values this target can be built for. - Target_compatible_with bazel.LabelListAttribute + // SoongConfigTrace records accesses to VendorVars (soong_config). The trace will be hashed + // and used as a subdir of PathForModuleOut. Note that we mainly focus on incremental + // builds among similar products (e.g. aosp_cf_x86_64_phone and aosp_cf_x86_64_foldable), + // and there are variables other than soong_config, which isn't captured by soong config + // trace, but influence modules among products. + SoongConfigTrace soongConfigTrace `blueprint:"mutated"` + SoongConfigTraceHash string `blueprint:"mutated"` } type distProperties struct { @@ -1222,189 +755,6 @@ func InitCommonOSAndroidMultiTargetsArchModule(m Module, hod HostOrDeviceSupport m.base().commonProperties.CreateCommonOSVariant = true } -func (attrs *CommonAttributes) fillCommonBp2BuildModuleAttrs(ctx *topDownMutatorContext, - enabledPropertyOverrides bazel.BoolAttribute) constraintAttributes { - - mod := ctx.Module().base() - // Assert passed-in attributes include Name - if len(attrs.Name) == 0 { - if ctx.ModuleType() != "package" { - ctx.ModuleErrorf("CommonAttributes in fillCommonBp2BuildModuleAttrs expects a `.Name`!") - } - } - - depsToLabelList := func(deps []string) bazel.LabelListAttribute { - return bazel.MakeLabelListAttribute(BazelLabelForModuleDeps(ctx, deps)) - } - - var enabledProperty bazel.BoolAttribute - - onlyAndroid := false - neitherHostNorDevice := false - - osSupport := map[string]bool{} - - // if the target is enabled and supports arch variance, determine the defaults based on the module - // type's host or device property and host_supported/device_supported properties - if mod.commonProperties.ArchSpecific { - moduleSupportsDevice := mod.DeviceSupported() - moduleSupportsHost := mod.HostSupported() - if moduleSupportsHost && !moduleSupportsDevice { - // for host only, we specify as unsupported on android rather than listing all host osSupport - // TODO(b/220874839): consider replacing this with a constraint that covers all host osSupport - // instead - enabledProperty.SetSelectValue(bazel.OsConfigurationAxis, Android.Name, proptools.BoolPtr(false)) - } else if moduleSupportsDevice && !moduleSupportsHost { - enabledProperty.SetSelectValue(bazel.OsConfigurationAxis, Android.Name, proptools.BoolPtr(true)) - // specify as a positive to ensure any target-specific enabled can be resolved - // also save that a target is only android, as if there is only the positive restriction on - // android, it'll be dropped, so we may need to add it back later - onlyAndroid = true - } else if !moduleSupportsHost && !moduleSupportsDevice { - neitherHostNorDevice = true - } - - for _, osType := range OsTypeList() { - if osType.Class == Host { - osSupport[osType.Name] = moduleSupportsHost - } else if osType.Class == Device { - osSupport[osType.Name] = moduleSupportsDevice - } - } - } - - if neitherHostNorDevice { - // we can't build this, disable - enabledProperty.Value = proptools.BoolPtr(false) - } else if mod.commonProperties.Enabled != nil { - enabledProperty.SetValue(mod.commonProperties.Enabled) - if !*mod.commonProperties.Enabled { - for oss, enabled := range osSupport { - if val := enabledProperty.SelectValue(bazel.OsConfigurationAxis, oss); enabled && val != nil && *val { - // if this should be disabled by default, clear out any enabling we've done - enabledProperty.SetSelectValue(bazel.OsConfigurationAxis, oss, nil) - } - } - } - } - - attrs.Applicable_licenses = bazel.MakeLabelListAttribute(BazelLabelForModuleDeps(ctx, mod.commonProperties.Licenses)) - - // The required property can contain the module itself. This causes a cycle - // when generated as the 'data' label list attribute in Bazel. Remove it if - // it exists. See b/247985196. - _, requiredWithoutCycles := RemoveFromList(ctx.ModuleName(), mod.commonProperties.Required) - requiredWithoutCycles = FirstUniqueStrings(requiredWithoutCycles) - required := depsToLabelList(requiredWithoutCycles) - archVariantProps := mod.GetArchVariantProperties(ctx, &commonProperties{}) - for axis, configToProps := range archVariantProps { - for config, _props := range configToProps { - if archProps, ok := _props.(*commonProperties); ok { - _, requiredWithoutCycles := RemoveFromList(ctx.ModuleName(), archProps.Required) - requiredWithoutCycles = FirstUniqueStrings(requiredWithoutCycles) - required.SetSelectValue(axis, config, depsToLabelList(requiredWithoutCycles).Value) - if !neitherHostNorDevice { - if archProps.Enabled != nil { - if axis != bazel.OsConfigurationAxis || osSupport[config] { - enabledProperty.SetSelectValue(axis, config, archProps.Enabled) - } - } - } - } - } - } - - if !neitherHostNorDevice { - if enabledPropertyOverrides.Value != nil { - enabledProperty.Value = enabledPropertyOverrides.Value - } - for _, axis := range enabledPropertyOverrides.SortedConfigurationAxes() { - configToBools := enabledPropertyOverrides.ConfigurableValues[axis] - for cfg, val := range configToBools { - if axis != bazel.OsConfigurationAxis || osSupport[cfg] { - enabledProperty.SetSelectValue(axis, cfg, &val) - } - } - } - } - - productConfigEnabledLabels := []bazel.Label{} - // TODO(b/234497586): Soong config variables and product variables have different overriding behavior, we - // should handle it correctly - if !proptools.BoolDefault(enabledProperty.Value, true) && !neitherHostNorDevice { - // If the module is not enabled by default, then we can check if a - // product variable enables it - productConfigEnabledLabels = productVariableConfigEnableLabels(ctx) - - if len(productConfigEnabledLabels) > 0 { - // In this case, an existing product variable configuration overrides any - // module-level `enable: false` definition - newValue := true - enabledProperty.Value = &newValue - } - } - - productConfigEnabledAttribute := bazel.MakeLabelListAttribute(bazel.LabelList{ - productConfigEnabledLabels, nil, - }) - - platformEnabledAttribute, err := enabledProperty.ToLabelListAttribute( - bazel.LabelList{[]bazel.Label{{Label: "@platforms//:incompatible"}}, nil}, - bazel.LabelList{[]bazel.Label{}, nil}) - if err != nil { - ctx.ModuleErrorf("Error processing platform enabled attribute: %s", err) - } - - // if android is the only arch/os enabled, then add a restriction to only be compatible with android - if platformEnabledAttribute.IsNil() && onlyAndroid { - l := bazel.LabelAttribute{} - l.SetValue(bazel.Label{Label: bazel.OsConfigurationAxis.SelectKey(Android.Name)}) - platformEnabledAttribute.Add(&l) - } - - if !proptools.Bool(attrs.SkipData) { - attrs.Data.Append(required) - } - // SkipData is not an attribute of any Bazel target - // Set this to nil so that it does not appear in the generated build file - attrs.SkipData = nil - - moduleEnableConstraints := bazel.LabelListAttribute{} - moduleEnableConstraints.Append(platformEnabledAttribute) - moduleEnableConstraints.Append(productConfigEnabledAttribute) - - return constraintAttributes{Target_compatible_with: moduleEnableConstraints} -} - -// Check product variables for `enabled: true` flag override. -// Returns a list of the constraint_value targets who enable this override. -func productVariableConfigEnableLabels(ctx *topDownMutatorContext) []bazel.Label { - productVariableProps := ProductVariableProperties(ctx, ctx.Module()) - productConfigEnablingTargets := []bazel.Label{} - const propName = "Enabled" - if productConfigProps, exists := productVariableProps[propName]; exists { - for productConfigProp, prop := range productConfigProps { - flag, ok := prop.(*bool) - if !ok { - ctx.ModuleErrorf("Could not convert product variable %s property", proptools.PropertyNameForField(propName)) - } - - if *flag { - axis := productConfigProp.ConfigurationAxis() - targetLabel := axis.SelectKey(productConfigProp.SelectKey()) - productConfigEnablingTargets = append(productConfigEnablingTargets, bazel.Label{ - Label: targetLabel, - }) - } else { - // TODO(b/210546943): handle negative case where `enabled: false` - ctx.ModuleErrorf("`enabled: false` is not currently supported for configuration variables. See b/210546943", proptools.PropertyNameForField(propName)) - } - } - } - - return productConfigEnablingTargets -} - // A ModuleBase object contains the properties that are common to all Android // modules. It should be included as an anonymous field in every module // struct definition. InitAndroidModule should then be called from the module's @@ -1480,14 +830,15 @@ type ModuleBase struct { noAddressSanitizer bool installFiles InstallPaths - installFilesDepSet *installPathsDepSet + installFilesDepSet *DepSet[InstallPath] checkbuildFiles Paths packagingSpecs []PackagingSpec - packagingSpecsDepSet *packagingSpecsDepSet + packagingSpecsDepSet *DepSet[PackagingSpec] // katiInstalls tracks the install rules that were created by Soong but are being exported // to Make to convert to ninja rules so that Make can add additional dependencies. katiInstalls katiInstalls katiSymlinks katiInstalls + testData []DataPath // The files to copy to the dist as explicitly specified in the .bp file. distFiles TaggedDistFiles @@ -1518,78 +869,6 @@ type ModuleBase struct { licenseMetadataFile WritablePath } -// A struct containing all relevant information about a Bazel target converted via bp2build. -type bp2buildInfo struct { - Dir string - BazelProps bazel.BazelTargetModuleProperties - CommonAttrs CommonAttributes - ConstraintAttrs constraintAttributes - Attrs interface{} -} - -// TargetName returns the Bazel target name of a bp2build converted target. -func (b bp2buildInfo) TargetName() string { - return b.CommonAttrs.Name -} - -// TargetPackage returns the Bazel package of a bp2build converted target. -func (b bp2buildInfo) TargetPackage() string { - return b.Dir -} - -// BazelRuleClass returns the Bazel rule class of a bp2build converted target. -func (b bp2buildInfo) BazelRuleClass() string { - return b.BazelProps.Rule_class -} - -// BazelRuleLoadLocation returns the location of the Bazel rule of a bp2build converted target. -// This may be empty as native Bazel rules do not need to be loaded. -func (b bp2buildInfo) BazelRuleLoadLocation() string { - return b.BazelProps.Bzl_load_location -} - -// BazelAttributes returns the Bazel attributes of a bp2build converted target. -func (b bp2buildInfo) BazelAttributes() []interface{} { - return []interface{}{&b.CommonAttrs, &b.ConstraintAttrs, b.Attrs} -} - -func (m *ModuleBase) addBp2buildInfo(info bp2buildInfo) { - m.commonProperties.BazelConversionStatus.Bp2buildInfo = append(m.commonProperties.BazelConversionStatus.Bp2buildInfo, info) -} - -// IsConvertedByBp2build returns whether this module was converted via bp2build. -func (m *ModuleBase) IsConvertedByBp2build() bool { - return len(m.commonProperties.BazelConversionStatus.Bp2buildInfo) > 0 -} - -// Bp2buildTargets returns the Bazel targets bp2build generated for this module. -func (m *ModuleBase) Bp2buildTargets() []bp2buildInfo { - return m.commonProperties.BazelConversionStatus.Bp2buildInfo -} - -// AddUnconvertedBp2buildDep stores module name of a dependency that was not converted to Bazel. -func (b *baseModuleContext) AddUnconvertedBp2buildDep(dep string) { - unconvertedDeps := &b.Module().base().commonProperties.BazelConversionStatus.UnconvertedDeps - *unconvertedDeps = append(*unconvertedDeps, dep) -} - -// AddMissingBp2buildDep stores module name of a dependency that was not found in a Android.bp file. -func (b *baseModuleContext) AddMissingBp2buildDep(dep string) { - missingDeps := &b.Module().base().commonProperties.BazelConversionStatus.MissingDeps - *missingDeps = append(*missingDeps, dep) -} - -// GetUnconvertedBp2buildDeps returns the list of module names of this module's direct dependencies that -// were not converted to Bazel. -func (m *ModuleBase) GetUnconvertedBp2buildDeps() []string { - return FirstUniqueStrings(m.commonProperties.BazelConversionStatus.UnconvertedDeps) -} - -// GetMissingBp2buildDeps returns the list of module names that were not found in Android.bp files. -func (m *ModuleBase) GetMissingBp2buildDeps() []string { - return FirstUniqueStrings(m.commonProperties.BazelConversionStatus.MissingDeps) -} - func (m *ModuleBase) AddJSONData(d *map[string]interface{}) { (*d)["Android"] = map[string]interface{}{ // Properties set in Blueprint or in blueprint of a defaults modules @@ -2046,9 +1325,9 @@ func (m *ModuleBase) EffectiveLicenseFiles() Paths { // computeInstallDeps finds the installed paths of all dependencies that have a dependency // tag that is annotated as needing installation via the isInstallDepNeeded method. -func (m *ModuleBase) computeInstallDeps(ctx ModuleContext) ([]*installPathsDepSet, []*packagingSpecsDepSet) { - var installDeps []*installPathsDepSet - var packagingSpecs []*packagingSpecsDepSet +func (m *ModuleBase) computeInstallDeps(ctx ModuleContext) ([]*DepSet[InstallPath], []*DepSet[PackagingSpec]) { + var installDeps []*DepSet[InstallPath] + var packagingSpecs []*DepSet[PackagingSpec] ctx.VisitDirectDeps(func(dep Module) { if isInstallDepNeeded(dep, ctx.OtherModuleDependencyTag(dep)) { // Installation is still handled by Make, so anything hidden from Make is not @@ -2343,7 +1622,7 @@ func (m *ModuleBase) GenerateBuildActions(blueprintCtx blueprint.ModuleContext) // set m.installFilesDepSet to only the transitive dependencies to be used as the dependencies // of installed files of this module. It will be replaced by a depset including the installed // files of this module at the end for use by modules that depend on this one. - m.installFilesDepSet = newInstallPathsDepSet(nil, dependencyInstallFiles) + m.installFilesDepSet = NewDepSet[InstallPath](TOPOLOGICAL, nil, dependencyInstallFiles) // Temporarily continue to call blueprintCtx.GetMissingDependencies() to maintain the previous behavior of never // reporting missing dependency errors in Blueprint when AllowMissingDependencies == true. @@ -2400,11 +1679,7 @@ func (m *ModuleBase) GenerateBuildActions(blueprintCtx blueprint.ModuleContext) return } - if mixedBuildMod, handled := m.isHandledByBazel(ctx); handled { - mixedBuildMod.ProcessBazelQueryResponse(ctx) - } else { - m.module.GenerateAndroidBuildActions(ctx) - } + m.module.GenerateAndroidBuildActions(ctx) if ctx.Failed() { return } @@ -2435,6 +1710,7 @@ func (m *ModuleBase) GenerateBuildActions(blueprintCtx blueprint.ModuleContext) m.packagingSpecs = append(m.packagingSpecs, ctx.packagingSpecs...) m.katiInstalls = append(m.katiInstalls, ctx.katiInstalls...) m.katiSymlinks = append(m.katiSymlinks, ctx.katiSymlinks...) + m.testData = append(m.testData, ctx.testData...) } else if ctx.Config().AllowMissingDependencies() { // If the module is not enabled it will not create any build rules, nothing will call // ctx.GetMissingDependencies(), and blueprint will consider the missing dependencies to be unhandled @@ -2450,8 +1726,8 @@ func (m *ModuleBase) GenerateBuildActions(blueprintCtx blueprint.ModuleContext) } } - m.installFilesDepSet = newInstallPathsDepSet(m.installFiles, dependencyInstallFiles) - m.packagingSpecsDepSet = newPackagingSpecsDepSet(m.packagingSpecs, dependencyPackagingSpecs) + m.installFilesDepSet = NewDepSet[InstallPath](TOPOLOGICAL, m.installFiles, dependencyInstallFiles) + m.packagingSpecsDepSet = NewDepSet[PackagingSpec](TOPOLOGICAL, m.packagingSpecs, dependencyPackagingSpecs) buildLicenseMetadata(ctx, m.licenseMetadataFile) @@ -2460,15 +1736,6 @@ func (m *ModuleBase) GenerateBuildActions(blueprintCtx blueprint.ModuleContext) m.variables = ctx.variables } -func (m *ModuleBase) isHandledByBazel(ctx ModuleContext) (MixedBuildBuildable, bool) { - if mixedBuildMod, ok := m.module.(MixedBuildBuildable); ok { - if mixedBuildMod.IsMixedBuildSupported(ctx) && MixedBuildsEnabled(ctx) { - return mixedBuildMod, true - } - } - return nil, false -} - // Check the supplied dist structure to make sure that it is valid. // // property - the base property, e.g. dist or dists[1], which is combined with the @@ -2495,163 +1762,6 @@ func checkDistProperties(ctx *moduleContext, property string, dist *Dist) { } -type earlyModuleContext struct { - blueprint.EarlyModuleContext - - kind moduleKind - config Config -} - -func (e *earlyModuleContext) Glob(globPattern string, excludes []string) Paths { - return Glob(e, globPattern, excludes) -} - -func (e *earlyModuleContext) GlobFiles(globPattern string, excludes []string) Paths { - return GlobFiles(e, globPattern, excludes) -} - -func (e *earlyModuleContext) IsSymlink(path Path) bool { - fileInfo, err := e.config.fs.Lstat(path.String()) - if err != nil { - e.ModuleErrorf("os.Lstat(%q) failed: %s", path.String(), err) - } - return fileInfo.Mode()&os.ModeSymlink == os.ModeSymlink -} - -func (e *earlyModuleContext) Readlink(path Path) string { - dest, err := e.config.fs.Readlink(path.String()) - if err != nil { - e.ModuleErrorf("os.Readlink(%q) failed: %s", path.String(), err) - } - return dest -} - -func (e *earlyModuleContext) Module() Module { - module, _ := e.EarlyModuleContext.Module().(Module) - return module -} - -func (e *earlyModuleContext) Config() Config { - return e.EarlyModuleContext.Config().(Config) -} - -func (e *earlyModuleContext) AConfig() Config { - return e.config -} - -func (e *earlyModuleContext) DeviceConfig() DeviceConfig { - return DeviceConfig{e.config.deviceConfig} -} - -func (e *earlyModuleContext) Platform() bool { - return e.kind == platformModule -} - -func (e *earlyModuleContext) DeviceSpecific() bool { - return e.kind == deviceSpecificModule -} - -func (e *earlyModuleContext) SocSpecific() bool { - return e.kind == socSpecificModule -} - -func (e *earlyModuleContext) ProductSpecific() bool { - return e.kind == productSpecificModule -} - -func (e *earlyModuleContext) SystemExtSpecific() bool { - return e.kind == systemExtSpecificModule -} - -func (e *earlyModuleContext) Namespace() *Namespace { - return e.EarlyModuleContext.Namespace().(*Namespace) -} - -type baseModuleContext struct { - bp blueprint.BaseModuleContext - earlyModuleContext - os OsType - target Target - multiTargets []Target - targetPrimary bool - debug bool - - walkPath []Module - tagPath []blueprint.DependencyTag - - strictVisitDeps bool // If true, enforce that all dependencies are enabled - - bazelConversionMode bool -} - -func (b *baseModuleContext) isBazelConversionMode() bool { - return b.bazelConversionMode -} -func (b *baseModuleContext) OtherModuleName(m blueprint.Module) string { - return b.bp.OtherModuleName(m) -} -func (b *baseModuleContext) OtherModuleDir(m blueprint.Module) string { return b.bp.OtherModuleDir(m) } -func (b *baseModuleContext) OtherModuleErrorf(m blueprint.Module, fmt string, args ...interface{}) { - b.bp.OtherModuleErrorf(m, fmt, args...) -} -func (b *baseModuleContext) OtherModuleDependencyTag(m blueprint.Module) blueprint.DependencyTag { - return b.bp.OtherModuleDependencyTag(m) -} -func (b *baseModuleContext) OtherModuleExists(name string) bool { return b.bp.OtherModuleExists(name) } -func (b *baseModuleContext) OtherModuleDependencyVariantExists(variations []blueprint.Variation, name string) bool { - return b.bp.OtherModuleDependencyVariantExists(variations, name) -} -func (b *baseModuleContext) OtherModuleFarDependencyVariantExists(variations []blueprint.Variation, name string) bool { - return b.bp.OtherModuleFarDependencyVariantExists(variations, name) -} -func (b *baseModuleContext) OtherModuleReverseDependencyVariantExists(name string) bool { - return b.bp.OtherModuleReverseDependencyVariantExists(name) -} -func (b *baseModuleContext) OtherModuleType(m blueprint.Module) string { - return b.bp.OtherModuleType(m) -} -func (b *baseModuleContext) OtherModuleProvider(m blueprint.Module, provider blueprint.ProviderKey) interface{} { - return b.bp.OtherModuleProvider(m, provider) -} -func (b *baseModuleContext) OtherModuleHasProvider(m blueprint.Module, provider blueprint.ProviderKey) bool { - return b.bp.OtherModuleHasProvider(m, provider) -} -func (b *baseModuleContext) Provider(provider blueprint.ProviderKey) interface{} { - return b.bp.Provider(provider) -} -func (b *baseModuleContext) HasProvider(provider blueprint.ProviderKey) bool { - return b.bp.HasProvider(provider) -} -func (b *baseModuleContext) SetProvider(provider blueprint.ProviderKey, value interface{}) { - b.bp.SetProvider(provider, value) -} - -func (b *baseModuleContext) GetDirectDepWithTag(name string, tag blueprint.DependencyTag) blueprint.Module { - return b.bp.GetDirectDepWithTag(name, tag) -} - -func (b *baseModuleContext) blueprintBaseModuleContext() blueprint.BaseModuleContext { - return b.bp -} - -type moduleContext struct { - bp blueprint.ModuleContext - baseModuleContext - packagingSpecs []PackagingSpec - installFiles InstallPaths - checkbuildFiles Paths - module Module - phonies map[string]Paths - - katiInstalls []katiInstall - katiSymlinks []katiInstall - - // For tests - buildParams []BuildParams - ruleParams map[blueprint.Rule]blueprint.RuleParams - variables map[string]string -} - // katiInstall stores a request from Soong to Make to create an install rule. type katiInstall struct { from Path @@ -2695,789 +1805,40 @@ func (installs katiInstalls) InstallPaths() InstallPaths { return paths } -func (m *moduleContext) ninjaError(params BuildParams, err error) (PackageContext, BuildParams) { - return pctx, BuildParams{ - Rule: ErrorRule, - Description: params.Description, - Output: params.Output, - Outputs: params.Outputs, - ImplicitOutput: params.ImplicitOutput, - ImplicitOutputs: params.ImplicitOutputs, - Args: map[string]string{ - "error": err.Error(), - }, - } +// Makes this module a platform module, i.e. not specific to soc, device, +// product, or system_ext. +func (m *ModuleBase) MakeAsPlatform() { + m.commonProperties.Vendor = boolPtr(false) + m.commonProperties.Proprietary = boolPtr(false) + m.commonProperties.Soc_specific = boolPtr(false) + m.commonProperties.Product_specific = boolPtr(false) + m.commonProperties.System_ext_specific = boolPtr(false) } -func (m *moduleContext) ModuleBuild(pctx PackageContext, params ModuleBuildParams) { - m.Build(pctx, BuildParams(params)) +func (m *ModuleBase) MakeAsSystemExt() { + m.commonProperties.Vendor = boolPtr(false) + m.commonProperties.Proprietary = boolPtr(false) + m.commonProperties.Soc_specific = boolPtr(false) + m.commonProperties.Product_specific = boolPtr(false) + m.commonProperties.System_ext_specific = boolPtr(true) } -func validateBuildParams(params blueprint.BuildParams) error { - // Validate that the symlink outputs are declared outputs or implicit outputs - allOutputs := map[string]bool{} - for _, output := range params.Outputs { - allOutputs[output] = true - } - for _, output := range params.ImplicitOutputs { - allOutputs[output] = true - } - for _, symlinkOutput := range params.SymlinkOutputs { - if !allOutputs[symlinkOutput] { - return fmt.Errorf( - "Symlink output %s is not a declared output or implicit output", - symlinkOutput) - } - } - return nil +// IsNativeBridgeSupported returns true if "native_bridge_supported" is explicitly set as "true" +func (m *ModuleBase) IsNativeBridgeSupported() bool { + return proptools.Bool(m.commonProperties.Native_bridge_supported) } -// Convert build parameters from their concrete Android types into their string representations, -// and combine the singular and plural fields of the same type (e.g. Output and Outputs). -func convertBuildParams(params BuildParams) blueprint.BuildParams { - bparams := blueprint.BuildParams{ - Rule: params.Rule, - Description: params.Description, - Deps: params.Deps, - Outputs: params.Outputs.Strings(), - ImplicitOutputs: params.ImplicitOutputs.Strings(), - SymlinkOutputs: params.SymlinkOutputs.Strings(), - Inputs: params.Inputs.Strings(), - Implicits: params.Implicits.Strings(), - OrderOnly: params.OrderOnly.Strings(), - Validations: params.Validations.Strings(), - Args: params.Args, - Optional: !params.Default, +// ModuleNameWithPossibleOverride returns the name of the OverrideModule that overrides the current +// variant of this OverridableModule, or ctx.ModuleName() if this module is not an OverridableModule +// or if this variant is not overridden. +func ModuleNameWithPossibleOverride(ctx BaseModuleContext) string { + if overridable, ok := ctx.Module().(OverridableModule); ok { + if o := overridable.GetOverriddenBy(); o != "" { + return o + } } - - if params.Depfile != nil { - bparams.Depfile = params.Depfile.String() - } - if params.Output != nil { - bparams.Outputs = append(bparams.Outputs, params.Output.String()) - } - if params.SymlinkOutput != nil { - bparams.SymlinkOutputs = append(bparams.SymlinkOutputs, params.SymlinkOutput.String()) - } - if params.ImplicitOutput != nil { - bparams.ImplicitOutputs = append(bparams.ImplicitOutputs, params.ImplicitOutput.String()) - } - if params.Input != nil { - bparams.Inputs = append(bparams.Inputs, params.Input.String()) - } - if params.Implicit != nil { - bparams.Implicits = append(bparams.Implicits, params.Implicit.String()) - } - if params.Validation != nil { - bparams.Validations = append(bparams.Validations, params.Validation.String()) - } - - bparams.Outputs = proptools.NinjaEscapeList(bparams.Outputs) - bparams.ImplicitOutputs = proptools.NinjaEscapeList(bparams.ImplicitOutputs) - bparams.SymlinkOutputs = proptools.NinjaEscapeList(bparams.SymlinkOutputs) - bparams.Inputs = proptools.NinjaEscapeList(bparams.Inputs) - bparams.Implicits = proptools.NinjaEscapeList(bparams.Implicits) - bparams.OrderOnly = proptools.NinjaEscapeList(bparams.OrderOnly) - bparams.Validations = proptools.NinjaEscapeList(bparams.Validations) - bparams.Depfile = proptools.NinjaEscape(bparams.Depfile) - - return bparams -} - -func (m *moduleContext) Variable(pctx PackageContext, name, value string) { - if m.config.captureBuild { - m.variables[name] = value - } - - m.bp.Variable(pctx.PackageContext, name, value) -} - -func (m *moduleContext) Rule(pctx PackageContext, name string, params blueprint.RuleParams, - argNames ...string) blueprint.Rule { - - if m.config.UseRemoteBuild() { - if params.Pool == nil { - // When USE_GOMA=true or USE_RBE=true are set and the rule is not supported by goma/RBE, restrict - // jobs to the local parallelism value - params.Pool = localPool - } else if params.Pool == remotePool { - // remotePool is a fake pool used to identify rule that are supported for remoting. If the rule's - // pool is the remotePool, replace with nil so that ninja runs it at NINJA_REMOTE_NUM_JOBS - // parallelism. - params.Pool = nil - } - } - - rule := m.bp.Rule(pctx.PackageContext, name, params, argNames...) - - if m.config.captureBuild { - m.ruleParams[rule] = params - } - - return rule -} - -func (m *moduleContext) Build(pctx PackageContext, params BuildParams) { - if params.Description != "" { - params.Description = "${moduleDesc}" + params.Description + "${moduleDescSuffix}" - } - - if missingDeps := m.GetMissingDependencies(); len(missingDeps) > 0 { - pctx, params = m.ninjaError(params, fmt.Errorf("module %s missing dependencies: %s\n", - m.ModuleName(), strings.Join(missingDeps, ", "))) - } - - if m.config.captureBuild { - m.buildParams = append(m.buildParams, params) - } - - bparams := convertBuildParams(params) - err := validateBuildParams(bparams) - if err != nil { - m.ModuleErrorf( - "%s: build parameter validation failed: %s", - m.ModuleName(), - err.Error()) - } - m.bp.Build(pctx.PackageContext, bparams) -} - -func (m *moduleContext) Phony(name string, deps ...Path) { - addPhony(m.config, name, deps...) -} - -func (m *moduleContext) GetMissingDependencies() []string { - var missingDeps []string - missingDeps = append(missingDeps, m.Module().base().commonProperties.MissingDeps...) - missingDeps = append(missingDeps, m.bp.GetMissingDependencies()...) - missingDeps = FirstUniqueStrings(missingDeps) - return missingDeps -} - -func (b *baseModuleContext) AddMissingDependencies(deps []string) { - if deps != nil { - missingDeps := &b.Module().base().commonProperties.MissingDeps - *missingDeps = append(*missingDeps, deps...) - *missingDeps = FirstUniqueStrings(*missingDeps) - } -} - -type AllowDisabledModuleDependency interface { - blueprint.DependencyTag - AllowDisabledModuleDependency(target Module) bool -} - -func (b *baseModuleContext) validateAndroidModule(module blueprint.Module, tag blueprint.DependencyTag, strict bool) Module { - aModule, _ := module.(Module) - - if !strict { - return aModule - } - - if aModule == nil { - b.ModuleErrorf("module %q (%#v) not an android module", b.OtherModuleName(module), tag) - return nil - } - - if !aModule.Enabled() { - if t, ok := tag.(AllowDisabledModuleDependency); !ok || !t.AllowDisabledModuleDependency(aModule) { - if b.Config().AllowMissingDependencies() { - b.AddMissingDependencies([]string{b.OtherModuleName(aModule)}) - } else { - b.ModuleErrorf("depends on disabled module %q", b.OtherModuleName(aModule)) - } - } - return nil - } - return aModule -} - -type dep struct { - mod blueprint.Module - tag blueprint.DependencyTag -} - -func (b *baseModuleContext) getDirectDepsInternal(name string, tag blueprint.DependencyTag) []dep { - var deps []dep - b.VisitDirectDepsBlueprint(func(module blueprint.Module) { - if aModule, _ := module.(Module); aModule != nil { - if aModule.base().BaseModuleName() == name { - returnedTag := b.bp.OtherModuleDependencyTag(aModule) - if tag == nil || returnedTag == tag { - deps = append(deps, dep{aModule, returnedTag}) - } - } - } else if b.bp.OtherModuleName(module) == name { - returnedTag := b.bp.OtherModuleDependencyTag(module) - if tag == nil || returnedTag == tag { - deps = append(deps, dep{module, returnedTag}) - } - } - }) - return deps -} - -func (b *baseModuleContext) getDirectDepInternal(name string, tag blueprint.DependencyTag) (blueprint.Module, blueprint.DependencyTag) { - deps := b.getDirectDepsInternal(name, tag) - if len(deps) == 1 { - return deps[0].mod, deps[0].tag - } else if len(deps) >= 2 { - panic(fmt.Errorf("Multiple dependencies having same BaseModuleName() %q found from %q", - name, b.ModuleName())) - } else { - return nil, nil - } -} - -func (b *baseModuleContext) getDirectDepFirstTag(name string) (blueprint.Module, blueprint.DependencyTag) { - foundDeps := b.getDirectDepsInternal(name, nil) - deps := map[blueprint.Module]bool{} - for _, dep := range foundDeps { - deps[dep.mod] = true - } - if len(deps) == 1 { - return foundDeps[0].mod, foundDeps[0].tag - } else if len(deps) >= 2 { - // this could happen if two dependencies have the same name in different namespaces - // TODO(b/186554727): this should not occur if namespaces are handled within - // getDirectDepsInternal. - panic(fmt.Errorf("Multiple dependencies having same BaseModuleName() %q found from %q", - name, b.ModuleName())) - } else { - return nil, nil - } -} - -func (b *baseModuleContext) GetDirectDepsWithTag(tag blueprint.DependencyTag) []Module { - var deps []Module - b.VisitDirectDepsBlueprint(func(module blueprint.Module) { - if aModule, _ := module.(Module); aModule != nil { - if b.bp.OtherModuleDependencyTag(aModule) == tag { - deps = append(deps, aModule) - } - } - }) - return deps -} - -func (m *moduleContext) GetDirectDepWithTag(name string, tag blueprint.DependencyTag) blueprint.Module { - module, _ := m.getDirectDepInternal(name, tag) - return module -} - -// GetDirectDep returns the Module and DependencyTag for the direct dependency with the specified -// name, or nil if none exists. If there are multiple dependencies on the same module it returns the -// first DependencyTag. -func (b *baseModuleContext) GetDirectDep(name string) (blueprint.Module, blueprint.DependencyTag) { - return b.getDirectDepFirstTag(name) -} - -func (b *baseModuleContext) ModuleFromName(name string) (blueprint.Module, bool) { - if !b.isBazelConversionMode() { - panic("cannot call ModuleFromName if not in bazel conversion mode") - } - if moduleName, _ := SrcIsModuleWithTag(name); moduleName != "" { - return b.bp.ModuleFromName(moduleName) - } else { - return b.bp.ModuleFromName(name) - } -} - -func (b *baseModuleContext) VisitDirectDepsBlueprint(visit func(blueprint.Module)) { - b.bp.VisitDirectDeps(visit) -} - -func (b *baseModuleContext) VisitDirectDeps(visit func(Module)) { - b.bp.VisitDirectDeps(func(module blueprint.Module) { - if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil { - visit(aModule) - } - }) -} - -func (b *baseModuleContext) VisitDirectDepsWithTag(tag blueprint.DependencyTag, visit func(Module)) { - b.bp.VisitDirectDeps(func(module blueprint.Module) { - if b.bp.OtherModuleDependencyTag(module) == tag { - if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil { - visit(aModule) - } - } - }) -} - -func (b *baseModuleContext) VisitDirectDepsIf(pred func(Module) bool, visit func(Module)) { - b.bp.VisitDirectDepsIf( - // pred - func(module blueprint.Module) bool { - if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil { - return pred(aModule) - } else { - return false - } - }, - // visit - func(module blueprint.Module) { - visit(module.(Module)) - }) -} - -func (b *baseModuleContext) VisitDepsDepthFirst(visit func(Module)) { - b.bp.VisitDepsDepthFirst(func(module blueprint.Module) { - if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil { - visit(aModule) - } - }) -} - -func (b *baseModuleContext) VisitDepsDepthFirstIf(pred func(Module) bool, visit func(Module)) { - b.bp.VisitDepsDepthFirstIf( - // pred - func(module blueprint.Module) bool { - if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil { - return pred(aModule) - } else { - return false - } - }, - // visit - func(module blueprint.Module) { - visit(module.(Module)) - }) -} - -func (b *baseModuleContext) WalkDepsBlueprint(visit func(blueprint.Module, blueprint.Module) bool) { - b.bp.WalkDeps(visit) -} - -func (b *baseModuleContext) WalkDeps(visit func(Module, Module) bool) { - b.walkPath = []Module{b.Module()} - b.tagPath = []blueprint.DependencyTag{} - b.bp.WalkDeps(func(child, parent blueprint.Module) bool { - childAndroidModule, _ := child.(Module) - parentAndroidModule, _ := parent.(Module) - if childAndroidModule != nil && parentAndroidModule != nil { - // record walkPath before visit - for b.walkPath[len(b.walkPath)-1] != parentAndroidModule { - b.walkPath = b.walkPath[0 : len(b.walkPath)-1] - b.tagPath = b.tagPath[0 : len(b.tagPath)-1] - } - b.walkPath = append(b.walkPath, childAndroidModule) - b.tagPath = append(b.tagPath, b.OtherModuleDependencyTag(childAndroidModule)) - return visit(childAndroidModule, parentAndroidModule) - } else { - return false - } - }) -} - -func (b *baseModuleContext) GetWalkPath() []Module { - return b.walkPath -} - -func (b *baseModuleContext) GetTagPath() []blueprint.DependencyTag { - return b.tagPath -} - -func (b *baseModuleContext) VisitAllModuleVariants(visit func(Module)) { - b.bp.VisitAllModuleVariants(func(module blueprint.Module) { - visit(module.(Module)) - }) -} - -func (b *baseModuleContext) PrimaryModule() Module { - return b.bp.PrimaryModule().(Module) -} - -func (b *baseModuleContext) FinalModule() Module { - return b.bp.FinalModule().(Module) -} - -// IsMetaDependencyTag returns true for cross-cutting metadata dependencies. -func IsMetaDependencyTag(tag blueprint.DependencyTag) bool { - if tag == licenseKindTag { - return true - } else if tag == licensesTag { - return true - } - return false -} - -// A regexp for removing boilerplate from BaseDependencyTag from the string representation of -// a dependency tag. -var tagCleaner = regexp.MustCompile(`\QBaseDependencyTag:{}\E(, )?`) - -// PrettyPrintTag returns string representation of the tag, but prefers -// custom String() method if available. -func PrettyPrintTag(tag blueprint.DependencyTag) string { - // Use tag's custom String() method if available. - if stringer, ok := tag.(fmt.Stringer); ok { - return stringer.String() - } - - // Otherwise, get a default string representation of the tag's struct. - tagString := fmt.Sprintf("%T: %+v", tag, tag) - - // Remove the boilerplate from BaseDependencyTag as it adds no value. - tagString = tagCleaner.ReplaceAllString(tagString, "") - return tagString -} - -func (b *baseModuleContext) GetPathString(skipFirst bool) string { - sb := strings.Builder{} - tagPath := b.GetTagPath() - walkPath := b.GetWalkPath() - if !skipFirst { - sb.WriteString(walkPath[0].String()) - } - for i, m := range walkPath[1:] { - sb.WriteString("\n") - sb.WriteString(fmt.Sprintf(" via tag %s\n", PrettyPrintTag(tagPath[i]))) - sb.WriteString(fmt.Sprintf(" -> %s", m.String())) - } - return sb.String() -} - -func (m *moduleContext) ModuleSubDir() string { - return m.bp.ModuleSubDir() -} - -func (b *baseModuleContext) Target() Target { - return b.target -} - -func (b *baseModuleContext) TargetPrimary() bool { - return b.targetPrimary -} - -func (b *baseModuleContext) MultiTargets() []Target { - return b.multiTargets -} - -func (b *baseModuleContext) Arch() Arch { - return b.target.Arch -} - -func (b *baseModuleContext) Os() OsType { - return b.os -} - -func (b *baseModuleContext) Host() bool { - return b.os.Class == Host -} - -func (b *baseModuleContext) Device() bool { - return b.os.Class == Device -} - -func (b *baseModuleContext) Darwin() bool { - return b.os == Darwin -} - -func (b *baseModuleContext) Windows() bool { - return b.os == Windows -} - -func (b *baseModuleContext) Debug() bool { - return b.debug -} - -func (b *baseModuleContext) PrimaryArch() bool { - if len(b.config.Targets[b.target.Os]) <= 1 { - return true - } - return b.target.Arch.ArchType == b.config.Targets[b.target.Os][0].Arch.ArchType -} - -// Makes this module a platform module, i.e. not specific to soc, device, -// product, or system_ext. -func (m *ModuleBase) MakeAsPlatform() { - m.commonProperties.Vendor = boolPtr(false) - m.commonProperties.Proprietary = boolPtr(false) - m.commonProperties.Soc_specific = boolPtr(false) - m.commonProperties.Product_specific = boolPtr(false) - m.commonProperties.System_ext_specific = boolPtr(false) -} - -func (m *ModuleBase) MakeAsSystemExt() { - m.commonProperties.Vendor = boolPtr(false) - m.commonProperties.Proprietary = boolPtr(false) - m.commonProperties.Soc_specific = boolPtr(false) - m.commonProperties.Product_specific = boolPtr(false) - m.commonProperties.System_ext_specific = boolPtr(true) -} - -// IsNativeBridgeSupported returns true if "native_bridge_supported" is explicitly set as "true" -func (m *ModuleBase) IsNativeBridgeSupported() bool { - return proptools.Bool(m.commonProperties.Native_bridge_supported) -} - -func (m *moduleContext) InstallInData() bool { - return m.module.InstallInData() -} - -func (m *moduleContext) InstallInTestcases() bool { - return m.module.InstallInTestcases() -} - -func (m *moduleContext) InstallInSanitizerDir() bool { - return m.module.InstallInSanitizerDir() -} - -func (m *moduleContext) InstallInRamdisk() bool { - return m.module.InstallInRamdisk() -} - -func (m *moduleContext) InstallInVendorRamdisk() bool { - return m.module.InstallInVendorRamdisk() -} - -func (m *moduleContext) InstallInDebugRamdisk() bool { - return m.module.InstallInDebugRamdisk() -} - -func (m *moduleContext) InstallInRecovery() bool { - return m.module.InstallInRecovery() -} - -func (m *moduleContext) InstallInRoot() bool { - return m.module.InstallInRoot() -} - -func (m *moduleContext) InstallForceOS() (*OsType, *ArchType) { - return m.module.InstallForceOS() -} - -func (m *moduleContext) InstallInVendor() bool { - return m.module.InstallInVendor() -} - -func (m *moduleContext) skipInstall() bool { - if m.module.base().commonProperties.SkipInstall { - return true - } - - if m.module.base().commonProperties.HideFromMake { - return true - } - - // We'll need a solution for choosing which of modules with the same name in different - // namespaces to install. For now, reuse the list of namespaces exported to Make as the - // list of namespaces to install in a Soong-only build. - if !m.module.base().commonProperties.NamespaceExportedToMake { - return true - } - - return false -} - -func (m *moduleContext) InstallFile(installPath InstallPath, name string, srcPath Path, - deps ...Path) InstallPath { - return m.installFile(installPath, name, srcPath, deps, false, nil) -} - -func (m *moduleContext) InstallExecutable(installPath InstallPath, name string, srcPath Path, - deps ...Path) InstallPath { - return m.installFile(installPath, name, srcPath, deps, true, nil) -} - -func (m *moduleContext) InstallFileWithExtraFilesZip(installPath InstallPath, name string, srcPath Path, - extraZip Path, deps ...Path) InstallPath { - return m.installFile(installPath, name, srcPath, deps, false, &extraFilesZip{ - zip: extraZip, - dir: installPath, - }) -} - -func (m *moduleContext) PackageFile(installPath InstallPath, name string, srcPath Path) PackagingSpec { - fullInstallPath := installPath.Join(m, name) - return m.packageFile(fullInstallPath, srcPath, false) -} - -func (m *moduleContext) packageFile(fullInstallPath InstallPath, srcPath Path, executable bool) PackagingSpec { - licenseFiles := m.Module().EffectiveLicenseFiles() - spec := PackagingSpec{ - relPathInPackage: Rel(m, fullInstallPath.PartitionDir(), fullInstallPath.String()), - srcPath: srcPath, - symlinkTarget: "", - executable: executable, - effectiveLicenseFiles: &licenseFiles, - partition: fullInstallPath.partition, - } - m.packagingSpecs = append(m.packagingSpecs, spec) - return spec -} - -func (m *moduleContext) installFile(installPath InstallPath, name string, srcPath Path, deps []Path, - executable bool, extraZip *extraFilesZip) InstallPath { - - fullInstallPath := installPath.Join(m, name) - m.module.base().hooks.runInstallHooks(m, srcPath, fullInstallPath, false) - - if !m.skipInstall() { - deps = append(deps, m.module.base().installFilesDepSet.ToList().Paths()...) - - var implicitDeps, orderOnlyDeps Paths - - if m.Host() { - // Installed host modules might be used during the build, depend directly on their - // dependencies so their timestamp is updated whenever their dependency is updated - implicitDeps = deps - } else { - orderOnlyDeps = deps - } - - if m.Config().KatiEnabled() { - // When creating the install rule in Soong but embedding in Make, write the rule to a - // makefile instead of directly to the ninja file so that main.mk can add the - // dependencies from the `required` property that are hard to resolve in Soong. - m.katiInstalls = append(m.katiInstalls, katiInstall{ - from: srcPath, - to: fullInstallPath, - implicitDeps: implicitDeps, - orderOnlyDeps: orderOnlyDeps, - executable: executable, - extraFiles: extraZip, - }) - } else { - rule := Cp - if executable { - rule = CpExecutable - } - - extraCmds := "" - if extraZip != nil { - extraCmds += fmt.Sprintf(" && ( unzip -qDD -d '%s' '%s' 2>&1 | grep -v \"zipfile is empty\"; exit $${PIPESTATUS[0]} )", - extraZip.dir.String(), extraZip.zip.String()) - extraCmds += " || ( code=$$?; if [ $$code -ne 0 -a $$code -ne 1 ]; then exit $$code; fi )" - implicitDeps = append(implicitDeps, extraZip.zip) - } - - m.Build(pctx, BuildParams{ - Rule: rule, - Description: "install " + fullInstallPath.Base(), - Output: fullInstallPath, - Input: srcPath, - Implicits: implicitDeps, - OrderOnly: orderOnlyDeps, - Default: !m.Config().KatiEnabled(), - Args: map[string]string{ - "extraCmds": extraCmds, - }, - }) - } - - m.installFiles = append(m.installFiles, fullInstallPath) - } - - m.packageFile(fullInstallPath, srcPath, executable) - - m.checkbuildFiles = append(m.checkbuildFiles, srcPath) - - return fullInstallPath -} - -func (m *moduleContext) InstallSymlink(installPath InstallPath, name string, srcPath InstallPath) InstallPath { - fullInstallPath := installPath.Join(m, name) - m.module.base().hooks.runInstallHooks(m, srcPath, fullInstallPath, true) - - relPath, err := filepath.Rel(path.Dir(fullInstallPath.String()), srcPath.String()) - if err != nil { - panic(fmt.Sprintf("Unable to generate symlink between %q and %q: %s", fullInstallPath.Base(), srcPath.Base(), err)) - } - if !m.skipInstall() { - - if m.Config().KatiEnabled() { - // When creating the symlink rule in Soong but embedding in Make, write the rule to a - // makefile instead of directly to the ninja file so that main.mk can add the - // dependencies from the `required` property that are hard to resolve in Soong. - m.katiSymlinks = append(m.katiSymlinks, katiInstall{ - from: srcPath, - to: fullInstallPath, - }) - } else { - // The symlink doesn't need updating when the target is modified, but we sometimes - // have a dependency on a symlink to a binary instead of to the binary directly, and - // the mtime of the symlink must be updated when the binary is modified, so use a - // normal dependency here instead of an order-only dependency. - m.Build(pctx, BuildParams{ - Rule: Symlink, - Description: "install symlink " + fullInstallPath.Base(), - Output: fullInstallPath, - Input: srcPath, - Default: !m.Config().KatiEnabled(), - Args: map[string]string{ - "fromPath": relPath, - }, - }) - } - - m.installFiles = append(m.installFiles, fullInstallPath) - m.checkbuildFiles = append(m.checkbuildFiles, srcPath) - } - - m.packagingSpecs = append(m.packagingSpecs, PackagingSpec{ - relPathInPackage: Rel(m, fullInstallPath.PartitionDir(), fullInstallPath.String()), - srcPath: nil, - symlinkTarget: relPath, - executable: false, - partition: fullInstallPath.partition, - }) - - return fullInstallPath -} - -// installPath/name -> absPath where absPath might be a path that is available only at runtime -// (e.g. /apex/...) -func (m *moduleContext) InstallAbsoluteSymlink(installPath InstallPath, name string, absPath string) InstallPath { - fullInstallPath := installPath.Join(m, name) - m.module.base().hooks.runInstallHooks(m, nil, fullInstallPath, true) - - if !m.skipInstall() { - if m.Config().KatiEnabled() { - // When creating the symlink rule in Soong but embedding in Make, write the rule to a - // makefile instead of directly to the ninja file so that main.mk can add the - // dependencies from the `required` property that are hard to resolve in Soong. - m.katiSymlinks = append(m.katiSymlinks, katiInstall{ - absFrom: absPath, - to: fullInstallPath, - }) - } else { - m.Build(pctx, BuildParams{ - Rule: Symlink, - Description: "install symlink " + fullInstallPath.Base() + " -> " + absPath, - Output: fullInstallPath, - Default: !m.Config().KatiEnabled(), - Args: map[string]string{ - "fromPath": absPath, - }, - }) - } - - m.installFiles = append(m.installFiles, fullInstallPath) - } - - m.packagingSpecs = append(m.packagingSpecs, PackagingSpec{ - relPathInPackage: Rel(m, fullInstallPath.PartitionDir(), fullInstallPath.String()), - srcPath: nil, - symlinkTarget: absPath, - executable: false, - partition: fullInstallPath.partition, - }) - - return fullInstallPath -} - -func (m *moduleContext) CheckbuildFile(srcPath Path) { - m.checkbuildFiles = append(m.checkbuildFiles, srcPath) -} - -func (m *moduleContext) blueprintModuleContext() blueprint.ModuleContext { - return m.bp -} - -func (m *moduleContext) LicenseMetadataFile() Path { - return m.module.base().licenseMetadataFile -} + return ctx.ModuleName() +} // SrcIsModule decodes module references in the format ":unqualified-name" or "//namespace:name" // into the module name, or empty string if the input was not a module reference. @@ -3685,46 +2046,10 @@ type HostToolProvider interface { HostToolPath() OptionalPath } -// Returns a list of paths expanded from globs and modules referenced using ":module" syntax. The property must -// be tagged with `android:"path" to support automatic source module dependency resolution. -// -// Deprecated: use PathsForModuleSrc or PathsForModuleSrcExcludes instead. -func (m *moduleContext) ExpandSources(srcFiles, excludes []string) Paths { - return PathsForModuleSrcExcludes(m, srcFiles, excludes) -} - -// Returns a single path expanded from globs and modules referenced using ":module" syntax. The property must -// be tagged with `android:"path" to support automatic source module dependency resolution. -// -// Deprecated: use PathForModuleSrc instead. -func (m *moduleContext) ExpandSource(srcFile, _ string) Path { - return PathForModuleSrc(m, srcFile) -} - -// Returns an optional single path expanded from globs and modules referenced using ":module" syntax if -// the srcFile is non-nil. The property must be tagged with `android:"path" to support automatic source module -// dependency resolution. -func (m *moduleContext) ExpandOptionalSource(srcFile *string, _ string) OptionalPath { - if srcFile != nil { - return OptionalPathForPath(PathForModuleSrc(m, *srcFile)) - } - return OptionalPath{} -} - -func (m *moduleContext) RequiredModuleNames() []string { - return m.module.RequiredModuleNames() -} - -func (m *moduleContext) HostRequiredModuleNames() []string { - return m.module.HostRequiredModuleNames() -} - -func (m *moduleContext) TargetRequiredModuleNames() []string { - return m.module.TargetRequiredModuleNames() -} - func init() { - RegisterSingletonType("buildtarget", BuildTargetSingleton) + RegisterParallelSingletonType("buildtarget", BuildTargetSingleton) + RegisterParallelSingletonType("soongconfigtrace", soongConfigTraceSingletonFunc) + FinalDepsMutators(registerSoongConfigTraceMutator) } func BuildTargetSingleton() Singleton { @@ -3887,22 +2212,53 @@ func CheckBlueprintSyntax(ctx BaseModuleContext, filename string, contents strin return blueprint.CheckBlueprintSyntax(bpctx.ModuleFactories(), filename, contents) } -// installPathsDepSet is a thin type-safe wrapper around the generic depSet. It always uses -// topological order. -type installPathsDepSet struct { - depSet +func registerSoongConfigTraceMutator(ctx RegisterMutatorsContext) { + ctx.BottomUp("soongconfigtrace", soongConfigTraceMutator).Parallel() +} + +// soongConfigTraceMutator accumulates recorded soong_config trace from children. Also it normalizes +// SoongConfigTrace to make it consistent. +func soongConfigTraceMutator(ctx BottomUpMutatorContext) { + trace := &ctx.Module().base().commonProperties.SoongConfigTrace + ctx.VisitDirectDeps(func(m Module) { + childTrace := &m.base().commonProperties.SoongConfigTrace + trace.Bools = append(trace.Bools, childTrace.Bools...) + trace.Strings = append(trace.Strings, childTrace.Strings...) + trace.IsSets = append(trace.IsSets, childTrace.IsSets...) + }) + trace.Bools = SortedUniqueStrings(trace.Bools) + trace.Strings = SortedUniqueStrings(trace.Strings) + trace.IsSets = SortedUniqueStrings(trace.IsSets) + + ctx.Module().base().commonProperties.SoongConfigTraceHash = trace.hash() } -// newInstallPathsDepSet returns an immutable packagingSpecsDepSet with the given direct and -// transitive contents. -func newInstallPathsDepSet(direct InstallPaths, transitive []*installPathsDepSet) *installPathsDepSet { - return &installPathsDepSet{*newDepSet(TOPOLOGICAL, direct, transitive)} +// soongConfigTraceSingleton writes a map from each module's config hash value to trace data. +func soongConfigTraceSingletonFunc() Singleton { + return &soongConfigTraceSingleton{} } -// ToList returns the installPathsDepSet flattened to a list in topological order. -func (d *installPathsDepSet) ToList() InstallPaths { - if d == nil { - return nil +type soongConfigTraceSingleton struct { +} + +func (s *soongConfigTraceSingleton) GenerateBuildActions(ctx SingletonContext) { + outFile := PathForOutput(ctx, "soong_config_trace.json") + + traces := make(map[string]*soongConfigTrace) + ctx.VisitAllModules(func(module Module) { + trace := &module.base().commonProperties.SoongConfigTrace + if !trace.isEmpty() { + hash := module.base().commonProperties.SoongConfigTraceHash + traces[hash] = trace + } + }) + + j, err := json.Marshal(traces) + if err != nil { + ctx.Errorf("json marshal to %q failed: %#v", outFile, err) + return } - return d.depSet.ToList().(InstallPaths) + + WriteFileRule(ctx, outFile, string(j)) + ctx.Phony("soong_config_trace", outFile) } diff --git a/android/module_context.go b/android/module_context.go new file mode 100644 index 0000000000..39986df879 --- /dev/null +++ b/android/module_context.go @@ -0,0 +1,724 @@ +// Copyright 2015 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" + "github.com/google/blueprint" + "github.com/google/blueprint/proptools" + "path" + "path/filepath" + "strings" +) + +// BuildParameters describes the set of potential parameters to build a Ninja rule. +// In general, these correspond to a Ninja concept. +type BuildParams struct { + // A Ninja Rule that will be written to the Ninja file. This allows factoring out common code + // among multiple modules to reduce repetition in the Ninja file of action requirements. A rule + // can contain variables that should be provided in Args. + Rule blueprint.Rule + // Deps represents the depfile format. When using RuleBuilder, this defaults to GCC when depfiles + // are used. + Deps blueprint.Deps + // Depfile is a writeable path that allows correct incremental builds when the inputs have not + // been fully specified by the Ninja rule. Ninja supports a subset of the Makefile depfile syntax. + Depfile WritablePath + // A description of the build action. + Description string + // Output is an output file of the action. When using this field, references to $out in the Ninja + // command will refer to this file. + Output WritablePath + // Outputs is a slice of output file of the action. When using this field, references to $out in + // the Ninja command will refer to these files. + Outputs WritablePaths + // SymlinkOutput is an output file specifically that is a symlink. + SymlinkOutput WritablePath + // SymlinkOutputs is a slice of output files specifically that is a symlink. + SymlinkOutputs WritablePaths + // ImplicitOutput is an output file generated by the action. Note: references to `$out` in the + // Ninja command will NOT include references to this file. + ImplicitOutput WritablePath + // ImplicitOutputs is a slice of output files generated by the action. Note: references to `$out` + // in the Ninja command will NOT include references to these files. + ImplicitOutputs WritablePaths + // Input is an input file to the Ninja action. When using this field, references to $in in the + // Ninja command will refer to this file. + Input Path + // Inputs is a slice of input files to the Ninja action. When using this field, references to $in + // in the Ninja command will refer to these files. + Inputs Paths + // Implicit is an input file to the Ninja action. Note: references to `$in` in the Ninja command + // will NOT include references to this file. + Implicit Path + // Implicits is a slice of input files to the Ninja action. Note: references to `$in` in the Ninja + // command will NOT include references to these files. + Implicits Paths + // OrderOnly are Ninja order-only inputs to the action. When these are out of date, the output is + // not rebuilt until they are built, but changes in order-only dependencies alone do not cause the + // output to be rebuilt. + OrderOnly Paths + // Validation is an output path for a validation action. Validation outputs imply lower + // non-blocking priority to building non-validation outputs. + Validation Path + // Validations is a slice of output path for a validation action. Validation outputs imply lower + // non-blocking priority to building non-validation outputs. + Validations Paths + // Whether to skip outputting a default target statement which will be built by Ninja when no + // targets are specified on Ninja's command line. + Default bool + // Args is a key value mapping for replacements of variables within the Rule + Args map[string]string +} + +type ModuleBuildParams BuildParams + +type ModuleContext interface { + BaseModuleContext + + blueprintModuleContext() blueprint.ModuleContext + + // Deprecated: use ModuleContext.Build instead. + ModuleBuild(pctx PackageContext, params ModuleBuildParams) + + // Returns a list of paths expanded from globs and modules referenced using ":module" syntax. The property must + // be tagged with `android:"path" to support automatic source module dependency resolution. + // + // Deprecated: use PathsForModuleSrc or PathsForModuleSrcExcludes instead. + ExpandSources(srcFiles, excludes []string) Paths + + // Returns a single path expanded from globs and modules referenced using ":module" syntax. The property must + // be tagged with `android:"path" to support automatic source module dependency resolution. + // + // Deprecated: use PathForModuleSrc instead. + ExpandSource(srcFile, prop string) Path + + ExpandOptionalSource(srcFile *string, prop string) OptionalPath + + // InstallExecutable creates a rule to copy srcPath to name in the installPath directory, + // with the given additional dependencies. The file is marked executable after copying. + // + // The installed file will be returned by FilesToInstall(), and the PackagingSpec for the + // installed file will be returned by PackagingSpecs() on this module or by + // TransitivePackagingSpecs() on modules that depend on this module through dependency tags + // for which IsInstallDepNeeded returns true. + InstallExecutable(installPath InstallPath, name string, srcPath Path, deps ...InstallPath) InstallPath + + // InstallFile creates a rule to copy srcPath to name in the installPath directory, + // with the given additional dependencies. + // + // The installed file will be returned by FilesToInstall(), and the PackagingSpec for the + // installed file will be returned by PackagingSpecs() on this module or by + // TransitivePackagingSpecs() on modules that depend on this module through dependency tags + // for which IsInstallDepNeeded returns true. + InstallFile(installPath InstallPath, name string, srcPath Path, deps ...InstallPath) InstallPath + + // InstallFileWithExtraFilesZip creates a rule to copy srcPath to name in the installPath + // directory, and also unzip a zip file containing extra files to install into the same + // directory. + // + // The installed file will be returned by FilesToInstall(), and the PackagingSpec for the + // installed file will be returned by PackagingSpecs() on this module or by + // TransitivePackagingSpecs() on modules that depend on this module through dependency tags + // for which IsInstallDepNeeded returns true. + InstallFileWithExtraFilesZip(installPath InstallPath, name string, srcPath Path, extraZip Path, deps ...InstallPath) InstallPath + + // InstallSymlink creates a rule to create a symlink from src srcPath to name in the installPath + // directory. + // + // The installed symlink will be returned by FilesToInstall(), and the PackagingSpec for the + // installed file will be returned by PackagingSpecs() on this module or by + // TransitivePackagingSpecs() on modules that depend on this module through dependency tags + // for which IsInstallDepNeeded returns true. + InstallSymlink(installPath InstallPath, name string, srcPath InstallPath) InstallPath + + // InstallAbsoluteSymlink creates a rule to create an absolute symlink from src srcPath to name + // in the installPath directory. + // + // The installed symlink will be returned by FilesToInstall(), and the PackagingSpec for the + // installed file will be returned by PackagingSpecs() on this module or by + // TransitivePackagingSpecs() on modules that depend on this module through dependency tags + // for which IsInstallDepNeeded returns true. + InstallAbsoluteSymlink(installPath InstallPath, name string, absPath string) InstallPath + + // InstallTestData creates rules to install test data (e.g. data files used during a test) into + // the installPath directory. + // + // The installed files will be returned by FilesToInstall(), and the PackagingSpec for the + // installed files will be returned by PackagingSpecs() on this module or by + // TransitivePackagingSpecs() on modules that depend on this module through dependency tags + // for which IsInstallDepNeeded returns true. + InstallTestData(installPath InstallPath, data []DataPath) InstallPaths + + // PackageFile creates a PackagingSpec as if InstallFile was called, but without creating + // the rule to copy the file. This is useful to define how a module would be packaged + // without installing it into the global installation directories. + // + // The created PackagingSpec for the will be returned by PackagingSpecs() on this module or by + // TransitivePackagingSpecs() on modules that depend on this module through dependency tags + // for which IsInstallDepNeeded returns true. + PackageFile(installPath InstallPath, name string, srcPath Path) PackagingSpec + + CheckbuildFile(srcPath Path) + + InstallInData() bool + InstallInTestcases() bool + InstallInSanitizerDir() bool + InstallInRamdisk() bool + InstallInVendorRamdisk() bool + InstallInDebugRamdisk() bool + InstallInRecovery() bool + InstallInRoot() bool + InstallInVendor() bool + InstallForceOS() (*OsType, *ArchType) + + RequiredModuleNames() []string + HostRequiredModuleNames() []string + TargetRequiredModuleNames() []string + + ModuleSubDir() string + SoongConfigTraceHash() string + + Variable(pctx PackageContext, name, value string) + Rule(pctx PackageContext, name string, params blueprint.RuleParams, argNames ...string) blueprint.Rule + // Similar to blueprint.ModuleContext.Build, but takes Paths instead of []string, + // and performs more verification. + Build(pctx PackageContext, params BuildParams) + // Phony creates a Make-style phony rule, a rule with no commands that can depend on other + // phony rules or real files. Phony can be called on the same name multiple times to add + // additional dependencies. + Phony(phony string, deps ...Path) + + // GetMissingDependencies returns the list of dependencies that were passed to AddDependencies or related methods, + // but do not exist. + GetMissingDependencies() []string + + // LicenseMetadataFile returns the path where the license metadata for this module will be + // generated. + LicenseMetadataFile() Path +} + +type moduleContext struct { + bp blueprint.ModuleContext + baseModuleContext + packagingSpecs []PackagingSpec + installFiles InstallPaths + checkbuildFiles Paths + module Module + phonies map[string]Paths + + katiInstalls []katiInstall + katiSymlinks []katiInstall + + testData []DataPath + + // For tests + buildParams []BuildParams + ruleParams map[blueprint.Rule]blueprint.RuleParams + variables map[string]string +} + +func (m *moduleContext) ninjaError(params BuildParams, err error) (PackageContext, BuildParams) { + return pctx, BuildParams{ + Rule: ErrorRule, + Description: params.Description, + Output: params.Output, + Outputs: params.Outputs, + ImplicitOutput: params.ImplicitOutput, + ImplicitOutputs: params.ImplicitOutputs, + Args: map[string]string{ + "error": err.Error(), + }, + } +} + +func (m *moduleContext) ModuleBuild(pctx PackageContext, params ModuleBuildParams) { + m.Build(pctx, BuildParams(params)) +} + +func validateBuildParams(params blueprint.BuildParams) error { + // Validate that the symlink outputs are declared outputs or implicit outputs + allOutputs := map[string]bool{} + for _, output := range params.Outputs { + allOutputs[output] = true + } + for _, output := range params.ImplicitOutputs { + allOutputs[output] = true + } + for _, symlinkOutput := range params.SymlinkOutputs { + if !allOutputs[symlinkOutput] { + return fmt.Errorf( + "Symlink output %s is not a declared output or implicit output", + symlinkOutput) + } + } + return nil +} + +// Convert build parameters from their concrete Android types into their string representations, +// and combine the singular and plural fields of the same type (e.g. Output and Outputs). +func convertBuildParams(params BuildParams) blueprint.BuildParams { + bparams := blueprint.BuildParams{ + Rule: params.Rule, + Description: params.Description, + Deps: params.Deps, + Outputs: params.Outputs.Strings(), + ImplicitOutputs: params.ImplicitOutputs.Strings(), + SymlinkOutputs: params.SymlinkOutputs.Strings(), + Inputs: params.Inputs.Strings(), + Implicits: params.Implicits.Strings(), + OrderOnly: params.OrderOnly.Strings(), + Validations: params.Validations.Strings(), + Args: params.Args, + Optional: !params.Default, + } + + if params.Depfile != nil { + bparams.Depfile = params.Depfile.String() + } + if params.Output != nil { + bparams.Outputs = append(bparams.Outputs, params.Output.String()) + } + if params.SymlinkOutput != nil { + bparams.SymlinkOutputs = append(bparams.SymlinkOutputs, params.SymlinkOutput.String()) + } + if params.ImplicitOutput != nil { + bparams.ImplicitOutputs = append(bparams.ImplicitOutputs, params.ImplicitOutput.String()) + } + if params.Input != nil { + bparams.Inputs = append(bparams.Inputs, params.Input.String()) + } + if params.Implicit != nil { + bparams.Implicits = append(bparams.Implicits, params.Implicit.String()) + } + if params.Validation != nil { + bparams.Validations = append(bparams.Validations, params.Validation.String()) + } + + bparams.Outputs = proptools.NinjaEscapeList(bparams.Outputs) + bparams.ImplicitOutputs = proptools.NinjaEscapeList(bparams.ImplicitOutputs) + bparams.SymlinkOutputs = proptools.NinjaEscapeList(bparams.SymlinkOutputs) + bparams.Inputs = proptools.NinjaEscapeList(bparams.Inputs) + bparams.Implicits = proptools.NinjaEscapeList(bparams.Implicits) + bparams.OrderOnly = proptools.NinjaEscapeList(bparams.OrderOnly) + bparams.Validations = proptools.NinjaEscapeList(bparams.Validations) + bparams.Depfile = proptools.NinjaEscape(bparams.Depfile) + + return bparams +} + +func (m *moduleContext) Variable(pctx PackageContext, name, value string) { + if m.config.captureBuild { + m.variables[name] = value + } + + m.bp.Variable(pctx.PackageContext, name, value) +} + +func (m *moduleContext) Rule(pctx PackageContext, name string, params blueprint.RuleParams, + argNames ...string) blueprint.Rule { + + if m.config.UseRemoteBuild() { + if params.Pool == nil { + // When USE_GOMA=true or USE_RBE=true are set and the rule is not supported by goma/RBE, restrict + // jobs to the local parallelism value + params.Pool = localPool + } else if params.Pool == remotePool { + // remotePool is a fake pool used to identify rule that are supported for remoting. If the rule's + // pool is the remotePool, replace with nil so that ninja runs it at NINJA_REMOTE_NUM_JOBS + // parallelism. + params.Pool = nil + } + } + + rule := m.bp.Rule(pctx.PackageContext, name, params, argNames...) + + if m.config.captureBuild { + m.ruleParams[rule] = params + } + + return rule +} + +func (m *moduleContext) Build(pctx PackageContext, params BuildParams) { + if params.Description != "" { + params.Description = "${moduleDesc}" + params.Description + "${moduleDescSuffix}" + } + + if missingDeps := m.GetMissingDependencies(); len(missingDeps) > 0 { + pctx, params = m.ninjaError(params, fmt.Errorf("module %s missing dependencies: %s\n", + m.ModuleName(), strings.Join(missingDeps, ", "))) + } + + if m.config.captureBuild { + m.buildParams = append(m.buildParams, params) + } + + bparams := convertBuildParams(params) + err := validateBuildParams(bparams) + if err != nil { + m.ModuleErrorf( + "%s: build parameter validation failed: %s", + m.ModuleName(), + err.Error()) + } + m.bp.Build(pctx.PackageContext, bparams) +} + +func (m *moduleContext) Phony(name string, deps ...Path) { + addPhony(m.config, name, deps...) +} + +func (m *moduleContext) GetMissingDependencies() []string { + var missingDeps []string + missingDeps = append(missingDeps, m.Module().base().commonProperties.MissingDeps...) + missingDeps = append(missingDeps, m.bp.GetMissingDependencies()...) + missingDeps = FirstUniqueStrings(missingDeps) + return missingDeps +} + +func (m *moduleContext) GetDirectDepWithTag(name string, tag blueprint.DependencyTag) blueprint.Module { + module, _ := m.getDirectDepInternal(name, tag) + return module +} + +func (m *moduleContext) ModuleSubDir() string { + return m.bp.ModuleSubDir() +} + +func (m *moduleContext) SoongConfigTraceHash() string { + return m.module.base().commonProperties.SoongConfigTraceHash +} + +func (m *moduleContext) InstallInData() bool { + return m.module.InstallInData() +} + +func (m *moduleContext) InstallInTestcases() bool { + return m.module.InstallInTestcases() +} + +func (m *moduleContext) InstallInSanitizerDir() bool { + return m.module.InstallInSanitizerDir() +} + +func (m *moduleContext) InstallInRamdisk() bool { + return m.module.InstallInRamdisk() +} + +func (m *moduleContext) InstallInVendorRamdisk() bool { + return m.module.InstallInVendorRamdisk() +} + +func (m *moduleContext) InstallInDebugRamdisk() bool { + return m.module.InstallInDebugRamdisk() +} + +func (m *moduleContext) InstallInRecovery() bool { + return m.module.InstallInRecovery() +} + +func (m *moduleContext) InstallInRoot() bool { + return m.module.InstallInRoot() +} + +func (m *moduleContext) InstallForceOS() (*OsType, *ArchType) { + return m.module.InstallForceOS() +} + +func (m *moduleContext) InstallInVendor() bool { + return m.module.InstallInVendor() +} + +func (m *moduleContext) skipInstall() bool { + if m.module.base().commonProperties.SkipInstall { + return true + } + + if m.module.base().commonProperties.HideFromMake { + return true + } + + // We'll need a solution for choosing which of modules with the same name in different + // namespaces to install. For now, reuse the list of namespaces exported to Make as the + // list of namespaces to install in a Soong-only build. + if !m.module.base().commonProperties.NamespaceExportedToMake { + return true + } + + return false +} + +func (m *moduleContext) InstallFile(installPath InstallPath, name string, srcPath Path, + deps ...InstallPath) InstallPath { + return m.installFile(installPath, name, srcPath, deps, false, true, nil) +} + +func (m *moduleContext) InstallExecutable(installPath InstallPath, name string, srcPath Path, + deps ...InstallPath) InstallPath { + return m.installFile(installPath, name, srcPath, deps, true, true, nil) +} + +func (m *moduleContext) InstallFileWithExtraFilesZip(installPath InstallPath, name string, srcPath Path, + extraZip Path, deps ...InstallPath) InstallPath { + return m.installFile(installPath, name, srcPath, deps, false, true, &extraFilesZip{ + zip: extraZip, + dir: installPath, + }) +} + +func (m *moduleContext) PackageFile(installPath InstallPath, name string, srcPath Path) PackagingSpec { + fullInstallPath := installPath.Join(m, name) + return m.packageFile(fullInstallPath, srcPath, false) +} + +func (m *moduleContext) packageFile(fullInstallPath InstallPath, srcPath Path, executable bool) PackagingSpec { + licenseFiles := m.Module().EffectiveLicenseFiles() + spec := PackagingSpec{ + relPathInPackage: Rel(m, fullInstallPath.PartitionDir(), fullInstallPath.String()), + srcPath: srcPath, + symlinkTarget: "", + executable: executable, + effectiveLicenseFiles: &licenseFiles, + partition: fullInstallPath.partition, + } + m.packagingSpecs = append(m.packagingSpecs, spec) + return spec +} + +func (m *moduleContext) installFile(installPath InstallPath, name string, srcPath Path, deps []InstallPath, + executable bool, hooks bool, extraZip *extraFilesZip) InstallPath { + + fullInstallPath := installPath.Join(m, name) + if hooks { + m.module.base().hooks.runInstallHooks(m, srcPath, fullInstallPath, false) + } + + if !m.skipInstall() { + deps = append(deps, InstallPaths(m.module.base().installFilesDepSet.ToList())...) + + var implicitDeps, orderOnlyDeps Paths + + if m.Host() { + // Installed host modules might be used during the build, depend directly on their + // dependencies so their timestamp is updated whenever their dependency is updated + implicitDeps = InstallPaths(deps).Paths() + } else { + orderOnlyDeps = InstallPaths(deps).Paths() + } + + if m.Config().KatiEnabled() { + // When creating the install rule in Soong but embedding in Make, write the rule to a + // makefile instead of directly to the ninja file so that main.mk can add the + // dependencies from the `required` property that are hard to resolve in Soong. + m.katiInstalls = append(m.katiInstalls, katiInstall{ + from: srcPath, + to: fullInstallPath, + implicitDeps: implicitDeps, + orderOnlyDeps: orderOnlyDeps, + executable: executable, + extraFiles: extraZip, + }) + } else { + rule := Cp + if executable { + rule = CpExecutable + } + + extraCmds := "" + if extraZip != nil { + extraCmds += fmt.Sprintf(" && ( unzip -qDD -d '%s' '%s' 2>&1 | grep -v \"zipfile is empty\"; exit $${PIPESTATUS[0]} )", + extraZip.dir.String(), extraZip.zip.String()) + extraCmds += " || ( code=$$?; if [ $$code -ne 0 -a $$code -ne 1 ]; then exit $$code; fi )" + implicitDeps = append(implicitDeps, extraZip.zip) + } + + m.Build(pctx, BuildParams{ + Rule: rule, + Description: "install " + fullInstallPath.Base(), + Output: fullInstallPath, + Input: srcPath, + Implicits: implicitDeps, + OrderOnly: orderOnlyDeps, + Default: !m.Config().KatiEnabled(), + Args: map[string]string{ + "extraCmds": extraCmds, + }, + }) + } + + m.installFiles = append(m.installFiles, fullInstallPath) + } + + m.packageFile(fullInstallPath, srcPath, executable) + + m.checkbuildFiles = append(m.checkbuildFiles, srcPath) + + return fullInstallPath +} + +func (m *moduleContext) InstallSymlink(installPath InstallPath, name string, srcPath InstallPath) InstallPath { + fullInstallPath := installPath.Join(m, name) + m.module.base().hooks.runInstallHooks(m, srcPath, fullInstallPath, true) + + relPath, err := filepath.Rel(path.Dir(fullInstallPath.String()), srcPath.String()) + if err != nil { + panic(fmt.Sprintf("Unable to generate symlink between %q and %q: %s", fullInstallPath.Base(), srcPath.Base(), err)) + } + if !m.skipInstall() { + + if m.Config().KatiEnabled() { + // When creating the symlink rule in Soong but embedding in Make, write the rule to a + // makefile instead of directly to the ninja file so that main.mk can add the + // dependencies from the `required` property that are hard to resolve in Soong. + m.katiSymlinks = append(m.katiSymlinks, katiInstall{ + from: srcPath, + to: fullInstallPath, + }) + } else { + // The symlink doesn't need updating when the target is modified, but we sometimes + // have a dependency on a symlink to a binary instead of to the binary directly, and + // the mtime of the symlink must be updated when the binary is modified, so use a + // normal dependency here instead of an order-only dependency. + m.Build(pctx, BuildParams{ + Rule: Symlink, + Description: "install symlink " + fullInstallPath.Base(), + Output: fullInstallPath, + Input: srcPath, + Default: !m.Config().KatiEnabled(), + Args: map[string]string{ + "fromPath": relPath, + }, + }) + } + + m.installFiles = append(m.installFiles, fullInstallPath) + m.checkbuildFiles = append(m.checkbuildFiles, srcPath) + } + + m.packagingSpecs = append(m.packagingSpecs, PackagingSpec{ + relPathInPackage: Rel(m, fullInstallPath.PartitionDir(), fullInstallPath.String()), + srcPath: nil, + symlinkTarget: relPath, + executable: false, + partition: fullInstallPath.partition, + }) + + return fullInstallPath +} + +// installPath/name -> absPath where absPath might be a path that is available only at runtime +// (e.g. /apex/...) +func (m *moduleContext) InstallAbsoluteSymlink(installPath InstallPath, name string, absPath string) InstallPath { + fullInstallPath := installPath.Join(m, name) + m.module.base().hooks.runInstallHooks(m, nil, fullInstallPath, true) + + if !m.skipInstall() { + if m.Config().KatiEnabled() { + // When creating the symlink rule in Soong but embedding in Make, write the rule to a + // makefile instead of directly to the ninja file so that main.mk can add the + // dependencies from the `required` property that are hard to resolve in Soong. + m.katiSymlinks = append(m.katiSymlinks, katiInstall{ + absFrom: absPath, + to: fullInstallPath, + }) + } else { + m.Build(pctx, BuildParams{ + Rule: Symlink, + Description: "install symlink " + fullInstallPath.Base() + " -> " + absPath, + Output: fullInstallPath, + Default: !m.Config().KatiEnabled(), + Args: map[string]string{ + "fromPath": absPath, + }, + }) + } + + m.installFiles = append(m.installFiles, fullInstallPath) + } + + m.packagingSpecs = append(m.packagingSpecs, PackagingSpec{ + relPathInPackage: Rel(m, fullInstallPath.PartitionDir(), fullInstallPath.String()), + srcPath: nil, + symlinkTarget: absPath, + executable: false, + partition: fullInstallPath.partition, + }) + + return fullInstallPath +} + +func (m *moduleContext) InstallTestData(installPath InstallPath, data []DataPath) InstallPaths { + m.testData = append(m.testData, data...) + + ret := make(InstallPaths, 0, len(data)) + for _, d := range data { + relPath := d.ToRelativeInstallPath() + installed := m.installFile(installPath, relPath, d.SrcPath, nil, false, false, nil) + ret = append(ret, installed) + } + + return ret +} + +func (m *moduleContext) CheckbuildFile(srcPath Path) { + m.checkbuildFiles = append(m.checkbuildFiles, srcPath) +} + +func (m *moduleContext) blueprintModuleContext() blueprint.ModuleContext { + return m.bp +} + +func (m *moduleContext) LicenseMetadataFile() Path { + return m.module.base().licenseMetadataFile +} + +// Returns a list of paths expanded from globs and modules referenced using ":module" syntax. The property must +// be tagged with `android:"path" to support automatic source module dependency resolution. +// +// Deprecated: use PathsForModuleSrc or PathsForModuleSrcExcludes instead. +func (m *moduleContext) ExpandSources(srcFiles, excludes []string) Paths { + return PathsForModuleSrcExcludes(m, srcFiles, excludes) +} + +// Returns a single path expanded from globs and modules referenced using ":module" syntax. The property must +// be tagged with `android:"path" to support automatic source module dependency resolution. +// +// Deprecated: use PathForModuleSrc instead. +func (m *moduleContext) ExpandSource(srcFile, _ string) Path { + return PathForModuleSrc(m, srcFile) +} + +// Returns an optional single path expanded from globs and modules referenced using ":module" syntax if +// the srcFile is non-nil. The property must be tagged with `android:"path" to support automatic source module +// dependency resolution. +func (m *moduleContext) ExpandOptionalSource(srcFile *string, _ string) OptionalPath { + if srcFile != nil { + return OptionalPathForPath(PathForModuleSrc(m, *srcFile)) + } + return OptionalPath{} +} + +func (m *moduleContext) RequiredModuleNames() []string { + return m.module.RequiredModuleNames() +} + +func (m *moduleContext) HostRequiredModuleNames() []string { + return m.module.HostRequiredModuleNames() +} + +func (m *moduleContext) TargetRequiredModuleNames() []string { + return m.module.TargetRequiredModuleNames() +} diff --git a/android/mutator.go b/android/mutator.go index 4ec960472d..0d391a47bf 100644 --- a/android/mutator.go +++ b/android/mutator.go @@ -15,8 +15,6 @@ package android import ( - "android/soong/bazel" - "github.com/google/blueprint" ) @@ -29,41 +27,6 @@ import ( // run FinalDeps mutators (CreateVariations disallowed in this phase) // continue on to GenerateAndroidBuildActions -// RegisterMutatorsForBazelConversion is a alternate registration pipeline for bp2build. Exported for testing. -func RegisterMutatorsForBazelConversion(ctx *Context, preArchMutators []RegisterMutatorFunc) { - bp2buildMutators := append(preArchMutators, registerBp2buildConversionMutator) - registerMutatorsForBazelConversion(ctx, bp2buildMutators) -} - -// RegisterMutatorsForApiBazelConversion is an alternate registration pipeline for api_bp2build -// This pipeline restricts generation of Bazel targets to Soong modules that contribute APIs -func RegisterMutatorsForApiBazelConversion(ctx *Context, preArchMutators []RegisterMutatorFunc) { - bp2buildMutators := append(preArchMutators, registerApiBp2buildConversionMutator) - registerMutatorsForBazelConversion(ctx, bp2buildMutators) -} - -func registerMutatorsForBazelConversion(ctx *Context, bp2buildMutators []RegisterMutatorFunc) { - mctx := ®isterMutatorsContext{ - bazelConversionMode: true, - } - - allMutators := append([]RegisterMutatorFunc{ - RegisterNamespaceMutator, - RegisterDefaultsPreArchMutators, - // TODO(b/165114590): this is required to resolve deps that are only prebuilts, but we should - // evaluate the impact on conversion. - RegisterPrebuiltsPreArchMutators, - }, - bp2buildMutators...) - - // Register bp2build mutators - for _, f := range allMutators { - f(mctx) - } - - mctx.mutators.registerAll(ctx) -} - // collateGloballyRegisteredMutators constructs the list of mutators that have been registered // with the InitRegistrationContext and will be used at runtime. func collateGloballyRegisteredMutators() sortableComponents { @@ -95,9 +58,8 @@ func collateRegisteredMutators(preArch, preDeps, postDeps, finalDeps []RegisterM } type registerMutatorsContext struct { - mutators sortableComponents - finalPhase bool - bazelConversionMode bool + mutators sortableComponents + finalPhase bool } type RegisterMutatorsContext interface { @@ -220,21 +182,6 @@ func FinalDepsMutators(f RegisterMutatorFunc) { finalDeps = append(finalDeps, f) } -var bp2buildPreArchMutators = []RegisterMutatorFunc{} - -// A minimal context for Bp2build conversion -type Bp2buildMutatorContext interface { - BazelConversionPathContext - - CreateBazelTargetModule(bazel.BazelTargetModuleProperties, CommonAttributes, interface{}) -} - -// PreArchBp2BuildMutators adds mutators to be register for converting Android Blueprint modules -// into Bazel BUILD targets that should run prior to deps and conversion. -func PreArchBp2BuildMutators(f RegisterMutatorFunc) { - bp2buildPreArchMutators = append(bp2buildPreArchMutators, f) -} - type BaseMutatorContext interface { BaseModuleContext @@ -254,25 +201,6 @@ type TopDownMutatorContext interface { // CreateModule creates a new module by calling the factory method for the specified moduleType, and applies // the specified property structs to it as if the properties were set in a blueprint file. CreateModule(ModuleFactory, ...interface{}) Module - - // CreateBazelTargetModule creates a BazelTargetModule by calling the - // factory method, just like in CreateModule, but also requires - // BazelTargetModuleProperties containing additional metadata for the - // bp2build codegenerator. - CreateBazelTargetModule(bazel.BazelTargetModuleProperties, CommonAttributes, interface{}) - - // CreateBazelTargetModuleWithRestrictions creates a BazelTargetModule by calling the - // factory method, just like in CreateModule, but also requires - // BazelTargetModuleProperties containing additional metadata for the - // bp2build codegenerator. The generated target is restricted to only be buildable for certain - // platforms, as dictated by a given bool attribute: the target will not be buildable in - // any platform for which this bool attribute is false. - CreateBazelTargetModuleWithRestrictions(bazel.BazelTargetModuleProperties, CommonAttributes, interface{}, bazel.BoolAttribute) - - // CreateBazelTargetAliasInDir creates an alias definition in `dir` directory. - // This function can be used to create alias definitions in a directory that is different - // from the directory of the visited Soong module. - CreateBazelTargetAliasInDir(dir string, name string, actual bazel.Label) } type topDownMutatorContext struct { @@ -407,10 +335,9 @@ type bottomUpMutatorContext struct { } func bottomUpMutatorContextFactory(ctx blueprint.BottomUpMutatorContext, a Module, - finalPhase, bazelConversionMode bool) BottomUpMutatorContext { + finalPhase bool) BottomUpMutatorContext { moduleContext := a.base().baseModuleContextFactory(ctx) - moduleContext.bazelConversionMode = bazelConversionMode return &bottomUpMutatorContext{ bp: ctx, @@ -421,10 +348,9 @@ func bottomUpMutatorContextFactory(ctx blueprint.BottomUpMutatorContext, a Modul func (x *registerMutatorsContext) BottomUp(name string, m BottomUpMutator) MutatorHandle { finalPhase := x.finalPhase - bazelConversionMode := x.bazelConversionMode f := func(ctx blueprint.BottomUpMutatorContext) { if a, ok := ctx.Module().(Module); ok { - m(bottomUpMutatorContextFactory(ctx, a, finalPhase, bazelConversionMode)) + m(bottomUpMutatorContextFactory(ctx, a, finalPhase)) } } mutator := &mutator{name: x.mutatorName(name), bottomUpMutator: f} @@ -541,15 +467,13 @@ type TransitionMutator interface { } type androidTransitionMutator struct { - finalPhase bool - bazelConversionMode bool - mutator TransitionMutator + finalPhase bool + mutator TransitionMutator } func (a *androidTransitionMutator) Split(ctx blueprint.BaseModuleContext) []string { if m, ok := ctx.Module().(Module); ok { moduleContext := m.base().baseModuleContextFactory(ctx) - moduleContext.bazelConversionMode = a.bazelConversionMode return a.mutator.Split(&moduleContext) } else { return []string{""} @@ -598,15 +522,14 @@ func (a *androidTransitionMutator) IncomingTransition(ctx blueprint.IncomingTran func (a *androidTransitionMutator) Mutate(ctx blueprint.BottomUpMutatorContext, variation string) { if am, ok := ctx.Module().(Module); ok { - a.mutator.Mutate(bottomUpMutatorContextFactory(ctx, am, a.finalPhase, a.bazelConversionMode), variation) + a.mutator.Mutate(bottomUpMutatorContextFactory(ctx, am, a.finalPhase), variation) } } func (x *registerMutatorsContext) Transition(name string, m TransitionMutator) { atm := &androidTransitionMutator{ - finalPhase: x.finalPhase, - bazelConversionMode: x.bazelConversionMode, - mutator: m, + finalPhase: x.finalPhase, + mutator: m, } mutator := &mutator{ name: name, @@ -615,9 +538,6 @@ func (x *registerMutatorsContext) Transition(name string, m TransitionMutator) { } func (x *registerMutatorsContext) mutatorName(name string) string { - if x.bazelConversionMode { - return name + "_bp2build" - } return name } @@ -625,7 +545,6 @@ func (x *registerMutatorsContext) TopDown(name string, m TopDownMutator) Mutator f := func(ctx blueprint.TopDownMutatorContext) { if a, ok := ctx.Module().(Module); ok { moduleContext := a.base().baseModuleContextFactory(ctx) - moduleContext.bazelConversionMode = x.bazelConversionMode actx := &topDownMutatorContext{ bp: ctx, baseModuleContext: moduleContext, @@ -689,105 +608,6 @@ func registerDepsMutator(ctx RegisterMutatorsContext) { ctx.BottomUp("deps", depsMutator).Parallel() } -func registerDepsMutatorBp2Build(ctx RegisterMutatorsContext) { - // TODO(b/179313531): Consider a separate mutator that only runs depsMutator for modules that are - // being converted to build targets. - ctx.BottomUp("deps", depsMutator).Parallel() -} - -func (t *topDownMutatorContext) CreateBazelTargetModule( - bazelProps bazel.BazelTargetModuleProperties, - commonAttrs CommonAttributes, - attrs interface{}) { - t.createBazelTargetModule(bazelProps, commonAttrs, attrs, bazel.BoolAttribute{}) -} - -func (t *topDownMutatorContext) CreateBazelTargetModuleWithRestrictions( - bazelProps bazel.BazelTargetModuleProperties, - commonAttrs CommonAttributes, - attrs interface{}, - enabledProperty bazel.BoolAttribute) { - t.createBazelTargetModule(bazelProps, commonAttrs, attrs, enabledProperty) -} - -var ( - bazelAliasModuleProperties = bazel.BazelTargetModuleProperties{ - Rule_class: "alias", - } -) - -type bazelAliasAttributes struct { - Actual *bazel.LabelAttribute -} - -func (t *topDownMutatorContext) CreateBazelTargetAliasInDir( - dir string, - name string, - actual bazel.Label) { - mod := t.Module() - attrs := &bazelAliasAttributes{ - Actual: bazel.MakeLabelAttribute(actual.Label), - } - info := bp2buildInfo{ - Dir: dir, - BazelProps: bazelAliasModuleProperties, - CommonAttrs: CommonAttributes{Name: name}, - ConstraintAttrs: constraintAttributes{}, - Attrs: attrs, - } - mod.base().addBp2buildInfo(info) -} - -// ApexAvailableTags converts the apex_available property value of an ApexModule -// module and returns it as a list of keyed tags. -func ApexAvailableTags(mod Module) bazel.StringListAttribute { - attr := bazel.StringListAttribute{} - // Transform specific attributes into tags. - if am, ok := mod.(ApexModule); ok { - // TODO(b/218841706): hidl_interface has the apex_available prop, but it's - // defined directly as a prop and not via ApexModule, so this doesn't - // pick those props up. - apexAvailable := am.apexModuleBase().ApexAvailable() - // If a user does not specify apex_available in Android.bp, then soong provides a default. - // To avoid verbosity of BUILD files, remove this default from user-facing BUILD files. - if len(am.apexModuleBase().ApexProperties.Apex_available) == 0 { - apexAvailable = []string{} - } - attr.Value = ConvertApexAvailableToTags(apexAvailable) - } - return attr -} - -func ConvertApexAvailableToTags(apexAvailable []string) []string { - if len(apexAvailable) == 0 { - // We need nil specifically to make bp2build not add the tags property at all, - // instead of adding it with an empty list - return nil - } - result := make([]string, 0, len(apexAvailable)) - for _, a := range apexAvailable { - result = append(result, "apex_available="+a) - } - return result -} - -func (t *topDownMutatorContext) createBazelTargetModule( - bazelProps bazel.BazelTargetModuleProperties, - commonAttrs CommonAttributes, - attrs interface{}, - enabledProperty bazel.BoolAttribute) { - constraintAttributes := commonAttrs.fillCommonBp2BuildModuleAttrs(t, enabledProperty) - mod := t.Module() - info := bp2buildInfo{ - Dir: t.OtherModuleDir(mod), - BazelProps: bazelProps, - CommonAttrs: commonAttrs, - ConstraintAttrs: constraintAttributes, - Attrs: attrs, - } - mod.base().addBp2buildInfo(info) -} - // android.topDownMutatorContext either has to embed blueprint.TopDownMutatorContext, in which case every method that // has an overridden version in android.BaseModuleContext has to be manually forwarded to BaseModuleContext to avoid // ambiguous method errors, or it has to store a blueprint.TopDownMutatorContext non-embedded, in which case every @@ -826,10 +646,16 @@ func (b *bottomUpMutatorContext) Rename(name string) { } func (b *bottomUpMutatorContext) AddDependency(module blueprint.Module, tag blueprint.DependencyTag, name ...string) []blueprint.Module { + if b.baseModuleContext.checkedMissingDeps() { + panic("Adding deps not allowed after checking for missing deps") + } return b.bp.AddDependency(module, tag, name...) } func (b *bottomUpMutatorContext) AddReverseDependency(module blueprint.Module, tag blueprint.DependencyTag, name string) { + if b.baseModuleContext.checkedMissingDeps() { + panic("Adding deps not allowed after checking for missing deps") + } b.bp.AddReverseDependency(module, tag, name) } @@ -879,11 +705,17 @@ func (b *bottomUpMutatorContext) SetDefaultDependencyVariation(variation *string func (b *bottomUpMutatorContext) AddVariationDependencies(variations []blueprint.Variation, tag blueprint.DependencyTag, names ...string) []blueprint.Module { + if b.baseModuleContext.checkedMissingDeps() { + panic("Adding deps not allowed after checking for missing deps") + } return b.bp.AddVariationDependencies(variations, tag, names...) } func (b *bottomUpMutatorContext) AddFarVariationDependencies(variations []blueprint.Variation, tag blueprint.DependencyTag, names ...string) []blueprint.Module { + if b.baseModuleContext.checkedMissingDeps() { + panic("Adding deps not allowed after checking for missing deps") + } return b.bp.AddFarVariationDependencies(variations, tag, names...) } @@ -893,10 +725,16 @@ func (b *bottomUpMutatorContext) AddInterVariantDependency(tag blueprint.Depende } func (b *bottomUpMutatorContext) ReplaceDependencies(name string) { + if b.baseModuleContext.checkedMissingDeps() { + panic("Adding deps not allowed after checking for missing deps") + } b.bp.ReplaceDependencies(name) } func (b *bottomUpMutatorContext) ReplaceDependenciesIf(name string, predicate blueprint.ReplaceDependencyPredicate) { + if b.baseModuleContext.checkedMissingDeps() { + panic("Adding deps not allowed after checking for missing deps") + } b.bp.ReplaceDependenciesIf(name, predicate) } diff --git a/android/mutator_test.go b/android/mutator_test.go index dbdfa33628..21eebd2c8c 100644 --- a/android/mutator_test.go +++ b/android/mutator_test.go @@ -16,7 +16,6 @@ package android import ( "fmt" - "reflect" "strings" "testing" @@ -268,22 +267,3 @@ func TestNoCreateVariationsInFinalDeps(t *testing.T) { FixtureWithRootAndroidBp(`test {name: "foo"}`), ).RunTest(t) } - -func TestConvertApexAvailableToTags(t *testing.T) { - input := []string{ - "com.android.adbd", - "//apex_available:platform", - } - actual := ConvertApexAvailableToTags(input) - expected := []string{ - "apex_available=com.android.adbd", - "apex_available=//apex_available:platform", - } - if !reflect.DeepEqual(actual, expected) { - t.Errorf("Expected: %v, actual: %v", expected, actual) - } - - if ConvertApexAvailableToTags(nil) != nil { - t.Errorf("Expected providing nil to return nil") - } -} diff --git a/android/neverallow.go b/android/neverallow.go index 2139c3c3eb..f721b945c1 100644 --- a/android/neverallow.go +++ b/android/neverallow.go @@ -55,11 +55,12 @@ func init() { AddNeverAllowRules(createJavaDeviceForHostRules()...) AddNeverAllowRules(createCcSdkVariantRules()...) AddNeverAllowRules(createUncompressDexRules()...) - AddNeverAllowRules(createMakefileGoalRules()...) AddNeverAllowRules(createInitFirstStageRules()...) AddNeverAllowRules(createProhibitFrameworkAccessRules()...) AddNeverAllowRules(createBp2BuildRule()) AddNeverAllowRules(createCcStubsRule()) + AddNeverAllowRules(createJavaExcludeStaticLibsRule()) + AddNeverAllowRules(createProhibitHeaderOnlyRule()) } // Add a NeverAllow rule to the set of rules to apply. @@ -168,6 +169,8 @@ func createJavaDeviceForHostRules() []Rule { "external/kotlinx.coroutines", "external/robolectric-shadows", "external/robolectric", + "frameworks/base/ravenwood", + "frameworks/base/tools/hoststubgen", "frameworks/layoutlib", } @@ -236,24 +239,12 @@ func createUncompressDexRules() []Rule { } } -func createMakefileGoalRules() []Rule { - allowlist := []string{ - // libwifi_hal uses makefile_goal for its dependencies - "frameworks/opt/net/wifi/libwifi_hal", - } - return []Rule{ - NeverAllow(). - ModuleType("makefile_goal"). - WithoutMatcher("product_out_path", Regexp("^boot[0-9a-zA-Z.-]*[.]img$")). - NotIn(allowlist...). - Because("Only boot images may be imported as a makefile goal if not in allowed projects"), - } -} - func createInitFirstStageRules() []Rule { return []Rule{ NeverAllow(). + Without("name", "init_first_stage_defaults"). Without("name", "init_first_stage"). + Without("name", "init_first_stage.microdroid"). With("install_in_root", "true"). Because("install_in_root is only for init_first_stage."), } @@ -268,6 +259,21 @@ func createProhibitFrameworkAccessRules() []Rule { } } +func createJavaExcludeStaticLibsRule() Rule { + return NeverAllow(). + NotIn("build/soong", "libcore", "frameworks/base/api"). + ModuleType("java_library"). + WithMatcher("exclude_static_libs", isSetMatcherInstance). + Because("exclude_static_libs property is only allowed for java modules defined in build/soong, libcore, and frameworks/base/api") +} + +func createProhibitHeaderOnlyRule() Rule { + return NeverAllow(). + Without("name", "framework-minus-apex-headers"). + With("headers_only", "true"). + Because("headers_only can only be used for generating framework-minus-apex headers for non-updatable modules") +} + func neverallowMutator(ctx BottomUpMutatorContext) { m, ok := ctx.Module().(Module) if !ok { diff --git a/android/neverallow_test.go b/android/neverallow_test.go index 5f5f9a1931..b2620ef926 100644 --- a/android/neverallow_test.go +++ b/android/neverallow_test.go @@ -313,45 +313,6 @@ var neverallowTests = []struct { "module \"outside_art_libraries\": violates neverallow", }, }, - { - name: "disallowed makefile_goal", - fs: map[string][]byte{ - "Android.bp": []byte(` - makefile_goal { - name: "foo", - product_out_path: "boot/trap.img" - } - `), - }, - expectedErrors: []string{ - "Only boot images.* may be imported as a makefile goal", - }, - }, - { - name: "disallowed makefile_goal outside external", - fs: map[string][]byte{ - "project/Android.bp": []byte(` - makefile_goal { - name: "foo", - product_out_path: "obj/EXE/foo", - } - `), - }, - expectedErrors: []string{ - "not in allowed projects", - }, - }, - { - name: "allow makefile_goal within external", - fs: map[string][]byte{ - "frameworks/opt/net/wifi/libwifi_hal/Android.bp": []byte(` - makefile_goal { - name: "foo", - product_out_path: "obj/EXE/foo", - } - `), - }, - }, // Tests for the rule prohibiting the use of framework { name: "prohibit framework", @@ -383,6 +344,38 @@ var neverallowTests = []struct { `module "outside_allowed_list": violates neverallow`, }, }, + // Test for the rule restricting use of exclude_static_libs + { + name: `"exclude_static_libs" outside allowed directory`, + fs: map[string][]byte{ + "a/b/Android.bp": []byte(` + java_library { + name: "baz", + exclude_static_libs: [ + "bar", + ], + } + `), + }, + expectedErrors: []string{ + `exclude_static_libs property is only allowed for java modules defined in build/soong, libcore, and frameworks/base/api`, + }, + }, + // Test for only allowing headers_only for framework-minus-apex-headers + { + name: `"headers_only" outside framework-minus-apex-headers modules`, + fs: map[string][]byte{ + "a/b/Android.bp": []byte(` + java_library { + name: "baz", + headers_only: true, + } + `), + }, + expectedErrors: []string{ + `headers_only can only be used for generating framework-minus-apex headers for non-updatable modules`, + }, + }, } var prepareForNeverAllowTest = GroupFixturePreparers( @@ -391,7 +384,6 @@ var prepareForNeverAllowTest = GroupFixturePreparers( ctx.RegisterModuleType("java_library", newMockJavaLibraryModule) ctx.RegisterModuleType("java_library_host", newMockJavaLibraryModule) ctx.RegisterModuleType("java_device_for_host", newMockJavaLibraryModule) - ctx.RegisterModuleType("makefile_goal", newMockMakefileGoalModule) }), ) @@ -470,9 +462,11 @@ func (p *mockCcLibraryModule) GenerateAndroidBuildActions(ModuleContext) { } type mockJavaLibraryProperties struct { - Libs []string - Sdk_version *string - Uncompress_dex *bool + Libs []string + Sdk_version *string + Uncompress_dex *bool + Exclude_static_libs []string + Headers_only *bool } type mockJavaLibraryModule struct { @@ -489,22 +483,3 @@ func newMockJavaLibraryModule() Module { func (p *mockJavaLibraryModule) GenerateAndroidBuildActions(ModuleContext) { } - -type mockMakefileGoalProperties struct { - Product_out_path *string -} - -type mockMakefileGoalModule struct { - ModuleBase - properties mockMakefileGoalProperties -} - -func newMockMakefileGoalModule() Module { - m := &mockMakefileGoalModule{} - m.AddProperties(&m.properties) - InitAndroidModule(m) - return m -} - -func (p *mockMakefileGoalModule) GenerateAndroidBuildActions(ModuleContext) { -} diff --git a/android/ninja_deps.go b/android/ninja_deps.go index 2f442d5f0e..1d50a47ec6 100644 --- a/android/ninja_deps.go +++ b/android/ninja_deps.go @@ -14,7 +14,10 @@ package android -import "sort" +import ( + "android/soong/starlark_import" + "sort" +) func (c *config) addNinjaFileDeps(deps ...string) { for _, dep := range deps { @@ -40,4 +43,11 @@ type ninjaDepsSingleton struct{} func (ninjaDepsSingleton) GenerateBuildActions(ctx SingletonContext) { ctx.AddNinjaFileDeps(ctx.Config().ninjaFileDeps()...) + + deps, err := starlark_import.GetNinjaDeps() + if err != nil { + ctx.Errorf("Error running starlark code: %s", err) + } else { + ctx.AddNinjaFileDeps(deps...) + } } diff --git a/android/override_module.go b/android/override_module.go index 2d30a852d0..1341f537cc 100644 --- a/android/override_module.go +++ b/android/override_module.go @@ -28,7 +28,6 @@ package android // module based on it. import ( - "fmt" "sort" "sync" @@ -47,8 +46,8 @@ type OverrideModule interface { // Internal funcs to handle interoperability between override modules and prebuilts. // i.e. cases where an overriding module, too, is overridden by a prebuilt module. - setOverriddenByPrebuilt(overridden bool) - getOverriddenByPrebuilt() bool + setOverriddenByPrebuilt(prebuilt Module) + getOverriddenByPrebuilt() Module // Directory containing the Blueprint definition of the overriding module setModuleDir(string) @@ -61,7 +60,7 @@ type OverrideModuleBase struct { overridingProperties []interface{} - overriddenByPrebuilt bool + overriddenByPrebuilt Module moduleDir string } @@ -97,11 +96,11 @@ func (o *OverrideModuleBase) GetOverriddenModuleName() string { return proptools.String(o.moduleProperties.Base) } -func (o *OverrideModuleBase) setOverriddenByPrebuilt(overridden bool) { - o.overriddenByPrebuilt = overridden +func (o *OverrideModuleBase) setOverriddenByPrebuilt(prebuilt Module) { + o.overriddenByPrebuilt = prebuilt } -func (o *OverrideModuleBase) getOverriddenByPrebuilt() bool { +func (o *OverrideModuleBase) getOverriddenByPrebuilt() Module { return o.overriddenByPrebuilt } @@ -121,7 +120,7 @@ type OverridableModule interface { addOverride(o OverrideModule) getOverrides() []OverrideModule - override(ctx BaseModuleContext, o OverrideModule) + override(ctx BaseModuleContext, m Module, o OverrideModule) GetOverriddenBy() string GetOverriddenByModuleDir() string @@ -192,7 +191,8 @@ func (b *OverridableModuleBase) setOverridesProperty(overridesProperty *[]string } // Overrides a base module with the given OverrideModule. -func (b *OverridableModuleBase) override(ctx BaseModuleContext, o OverrideModule) { +func (b *OverridableModuleBase) override(ctx BaseModuleContext, m Module, o OverrideModule) { + for _, p := range b.overridableProperties { for _, op := range o.getOverridingProperties() { if proptools.TypeEqual(p, op) { @@ -270,7 +270,7 @@ func overrideModuleDepsMutator(ctx BottomUpMutatorContext) { panic("PrebuiltDepTag leads to a non-prebuilt module " + dep.Name()) } if prebuilt.UsePrebuilt() { - module.setOverriddenByPrebuilt(true) + module.setOverriddenByPrebuilt(dep) return } }) @@ -302,10 +302,13 @@ func performOverrideMutator(ctx BottomUpMutatorContext) { // is specified. ctx.AliasVariation(variants[0]) for i, o := range overrides { - mods[i+1].(OverridableModule).override(ctx, o) - if o.getOverriddenByPrebuilt() { - // The overriding module itself, too, is overridden by a prebuilt. Skip its installation. - mods[i+1].HideFromMake() + mods[i+1].(OverridableModule).override(ctx, mods[i+1], o) + if prebuilt := o.getOverriddenByPrebuilt(); prebuilt != nil { + // The overriding module itself, too, is overridden by a prebuilt. + // Perform the same check for replacement + checkInvariantsForSourceAndPrebuilt(ctx, mods[i+1], prebuilt) + // Copy the flag and hide it in make + mods[i+1].ReplacedByPrebuilt() } } } else if o, ok := ctx.Module().(OverrideModule); ok { @@ -334,39 +337,3 @@ func replaceDepsOnOverridingModuleMutator(ctx BottomUpMutatorContext) { } } } - -// ModuleNameWithPossibleOverride returns the name of the OverrideModule that overrides the current -// variant of this OverridableModule, or ctx.ModuleName() if this module is not an OverridableModule -// or if this variant is not overridden. -func ModuleNameWithPossibleOverride(ctx BazelConversionContext) string { - if overridable, ok := ctx.Module().(OverridableModule); ok { - if o := overridable.GetOverriddenBy(); o != "" { - return o - } - } - return ctx.OtherModuleName(ctx.Module()) -} - -// ModuleDirWithPossibleOverride returns the dir of the OverrideModule that overrides the current -// variant of this OverridableModule, or ctx.ModuleName() if this module is not an OverridableModule -// or if this variant is not overridden. -func moduleDirWithPossibleOverride(ctx BazelConversionContext) string { - if overridable, ok := ctx.Module().(OverridableModule); ok { - if o := overridable.GetOverriddenByModuleDir(); o != "" { - return o - } - } - return ctx.OtherModuleDir(ctx.Module()) -} - -// MaybeBp2buildLabelOfOverridingModule returns the bazel label of the -// overriding module of an OverridableModule (e.g. override_apex label of a base -// apex), or the module's label itself if not overridden. -func MaybeBp2buildLabelOfOverridingModule(ctx BazelConversionContext) string { - moduleName := ModuleNameWithPossibleOverride(ctx) - moduleDir := moduleDirWithPossibleOverride(ctx) - if moduleDir == Bp2BuildTopLevel { - moduleDir = "" - } - return fmt.Sprintf("//%s:%s", moduleDir, moduleName) -} diff --git a/android/package.go b/android/package.go index 7fbc700417..878e4c4edf 100644 --- a/android/package.go +++ b/android/package.go @@ -15,9 +15,6 @@ package android import ( - "path/filepath" - - "android/soong/bazel" "github.com/google/blueprint" "github.com/google/blueprint/proptools" ) @@ -40,52 +37,12 @@ type packageProperties struct { Default_applicable_licenses []string } -type bazelPackageAttributes struct { - Default_visibility []string - Default_package_metadata bazel.LabelListAttribute -} - type packageModule struct { ModuleBase - BazelModuleBase properties packageProperties } -var _ Bazelable = &packageModule{} - -func (p *packageModule) ConvertWithBp2build(ctx TopDownMutatorContext) { - defaultPackageMetadata := bazel.MakeLabelListAttribute(BazelLabelForModuleDeps(ctx, p.properties.Default_applicable_licenses)) - // If METADATA file exists in the package, add it to package(default_package_metadata=) using a - // filegroup(name="default_metadata_file") which can be accessed later on each module in Bazel - // using attribute "applicable_licenses". - // Attribute applicable_licenses of filegroup "default_metadata_file" has to be set to [], - // otherwise Bazel reports cyclic reference error. - if existed, _, _ := ctx.Config().fs.Exists(filepath.Join(ctx.ModuleDir(), "METADATA")); existed { - ctx.CreateBazelTargetModule( - bazel.BazelTargetModuleProperties{ - Rule_class: "filegroup", - }, - CommonAttributes{Name: "default_metadata_file"}, - &bazelFilegroupAttributes{ - Srcs: bazel.MakeLabelListAttribute(BazelLabelForModuleSrc(ctx, []string{"METADATA"})), - Applicable_licenses: bazel.LabelListAttribute{Value: bazel.LabelList{Includes: []bazel.Label{}}, EmitEmptyList: true}, - }) - defaultPackageMetadata.Value.Add(&bazel.Label{Label: ":default_metadata_file"}) - } - - ctx.CreateBazelTargetModule( - bazel.BazelTargetModuleProperties{ - Rule_class: "package", - }, - CommonAttributes{}, - &bazelPackageAttributes{ - Default_package_metadata: defaultPackageMetadata, - // FIXME(asmundak): once b/221436821 is resolved - Default_visibility: []string{"//visibility:public"}, - }) -} - func (p *packageModule) GenerateAndroidBuildActions(ModuleContext) { // Nothing to do. } @@ -102,7 +59,7 @@ func (p *packageModule) qualifiedModuleId(ctx BaseModuleContext) qualifiedModule func PackageFactory() Module { module := &packageModule{} - module.AddProperties(&module.properties, &module.commonProperties.BazelConversionStatus) + module.AddProperties(&module.properties) // The name is the relative path from build root to the directory containing this // module. Set that name at the earliest possible moment that information is available @@ -119,7 +76,5 @@ func PackageFactory() Module { // its checking and parsing phases so make it the primary licenses property. setPrimaryLicensesProperty(module, "default_applicable_licenses", &module.properties.Default_applicable_licenses) - InitBazelModule(module) - return module } diff --git a/android/packaging.go b/android/packaging.go index c764a6df5b..503bb97e0c 100644 --- a/android/packaging.go +++ b/android/packaging.go @@ -282,23 +282,3 @@ func (p *PackagingBase) CopyDepsToZip(ctx ModuleContext, specs map[string]Packag builder.Build("zip_deps", fmt.Sprintf("Zipping deps for %s", ctx.ModuleName())) return entries } - -// packagingSpecsDepSet is a thin type-safe wrapper around the generic depSet. It always uses -// topological order. -type packagingSpecsDepSet struct { - depSet -} - -// newPackagingSpecsDepSet returns an immutable packagingSpecsDepSet with the given direct and -// transitive contents. -func newPackagingSpecsDepSet(direct []PackagingSpec, transitive []*packagingSpecsDepSet) *packagingSpecsDepSet { - return &packagingSpecsDepSet{*newDepSet(TOPOLOGICAL, direct, transitive)} -} - -// ToList returns the packagingSpecsDepSet flattened to a list in topological order. -func (d *packagingSpecsDepSet) ToList() []PackagingSpec { - if d == nil { - return nil - } - return d.depSet.ToList().([]PackagingSpec) -} diff --git a/android/paths.go b/android/paths.go index bf7bcae39c..1bd718d362 100644 --- a/android/paths.go +++ b/android/paths.go @@ -116,6 +116,48 @@ type ModuleInstallPathContext interface { var _ ModuleInstallPathContext = ModuleContext(nil) +type baseModuleContextToModuleInstallPathContext struct { + BaseModuleContext +} + +func (ctx *baseModuleContextToModuleInstallPathContext) InstallInData() bool { + return ctx.Module().InstallInData() +} + +func (ctx *baseModuleContextToModuleInstallPathContext) InstallInTestcases() bool { + return ctx.Module().InstallInTestcases() +} + +func (ctx *baseModuleContextToModuleInstallPathContext) InstallInSanitizerDir() bool { + return ctx.Module().InstallInSanitizerDir() +} + +func (ctx *baseModuleContextToModuleInstallPathContext) InstallInRamdisk() bool { + return ctx.Module().InstallInRamdisk() +} + +func (ctx *baseModuleContextToModuleInstallPathContext) InstallInVendorRamdisk() bool { + return ctx.Module().InstallInVendorRamdisk() +} + +func (ctx *baseModuleContextToModuleInstallPathContext) InstallInDebugRamdisk() bool { + return ctx.Module().InstallInDebugRamdisk() +} + +func (ctx *baseModuleContextToModuleInstallPathContext) InstallInRecovery() bool { + return ctx.Module().InstallInRecovery() +} + +func (ctx *baseModuleContextToModuleInstallPathContext) InstallInRoot() bool { + return ctx.Module().InstallInRoot() +} + +func (ctx *baseModuleContextToModuleInstallPathContext) InstallForceOS() (*OsType, *ArchType) { + return ctx.Module().InstallForceOS() +} + +var _ ModuleInstallPathContext = (*baseModuleContextToModuleInstallPathContext)(nil) + // errorfContext is the interface containing the Errorf method matching the // Errorf method in blueprint.SingletonContext. type errorfContext interface { @@ -396,7 +438,7 @@ func ExistentPathsForSources(ctx PathGlobContext, paths []string) Paths { // // Properties passed as the paths argument must have been annotated with struct tag // `android:"path"` so that dependencies on SourceFileProducer modules will have already been handled by the -// path_deps mutator. +// pathdeps mutator. // If a requested module is not found as a dependency: // - if ctx.Config().AllowMissingDependencies() is true, this module to be marked as having // missing dependencies @@ -425,7 +467,7 @@ type SourceInput struct { // excluding the items (similarly resolved // Properties passed as the paths argument must have been annotated with struct tag // `android:"path"` so that dependencies on SourceFileProducer modules will have already been handled by the -// path_deps mutator. +// pathdeps mutator. // If a requested module is not found as a dependency: // - if ctx.Config().AllowMissingDependencies() is true, this module to be marked as having // missing dependencies @@ -480,7 +522,7 @@ func (p OutputPaths) Strings() []string { // PathForGoBinary returns the path to the installed location of a bootstrap_go_binary module. func PathForGoBinary(ctx PathContext, goBinary bootstrap.GoBinaryTool) Path { - goBinaryInstallDir := pathForInstall(ctx, ctx.Config().BuildOS, ctx.Config().BuildArch, "bin", false) + goBinaryInstallDir := pathForInstall(ctx, ctx.Config().BuildOS, ctx.Config().BuildArch, "bin") rel := Rel(ctx, goBinaryInstallDir.String(), goBinary.InstallPath()) return goBinaryInstallDir.Join(ctx, rel) } @@ -560,7 +602,7 @@ func GetModuleFromPathDep(ctx ModuleWithDepsPathContext, moduleName, tag string) // and a list of the module names of missing module dependencies are returned as the second return. // Properties passed as the paths argument must have been annotated with struct tag // `android:"path"` so that dependencies on SourceFileProducer modules will have already been handled by the -// path_deps mutator. +// pathdeps mutator. func PathsAndMissingDepsForModuleSrcExcludes(ctx ModuleMissingDepsPathContext, paths, excludes []string) (Paths, []string) { return PathsAndMissingDepsRelativeToModuleSourceDir(SourceInput{ Context: ctx, @@ -1029,16 +1071,16 @@ func (p basePath) withRel(rel string) basePath { return p } +func (p basePath) RelativeToTop() Path { + ensureTestOnly() + return p +} + // SourcePath is a Path representing a file path rooted from SrcDir type SourcePath struct { basePath } -func (p SourcePath) RelativeToTop() Path { - ensureTestOnly() - return p -} - var _ Path = SourcePath{} func (p SourcePath) withRel(rel string) SourcePath { @@ -1176,6 +1218,16 @@ func PathForSourceRelaxed(ctx PathContext, pathComponents ...string) SourcePath return path } +// PathForArbitraryOutput creates a path for the given components. Unlike PathForOutput, +// the path is relative to the root of the output folder, not the out/soong folder. +func PathForArbitraryOutput(ctx PathContext, pathComponents ...string) Path { + p, err := validatePath(pathComponents...) + if err != nil { + reportPathError(ctx, err) + } + return basePath{path: filepath.Join(ctx.Config().OutDir(), p)} +} + // MaybeExistentPathForSource joins the provided path components and validates that the result // neither escapes the source dir nor is in the out dir. // It does not validate whether the path exists. @@ -1522,10 +1574,11 @@ type ModuleOutPathContext interface { ModuleName() string ModuleDir() string ModuleSubDir() string + SoongConfigTraceHash() string } func pathForModuleOut(ctx ModuleOutPathContext) OutputPath { - return PathForOutput(ctx, ".intermediates", ctx.ModuleDir(), ctx.ModuleName(), ctx.ModuleSubDir()) + return PathForOutput(ctx, ".intermediates", ctx.ModuleDir(), ctx.ModuleName(), ctx.ModuleSubDir(), ctx.SoongConfigTraceHash()) } // PathForModuleOut returns a Path representing the paths... under the module's @@ -1722,20 +1775,20 @@ func (p InstallPath) ToMakePath() InstallPath { // module appended with paths... func PathForModuleInstall(ctx ModuleInstallPathContext, pathComponents ...string) InstallPath { os, arch := osAndArch(ctx) - partition := modulePartition(ctx, os) - return makePathForInstall(ctx, os, arch, partition, ctx.Debug(), pathComponents...) + partition := modulePartition(ctx, os.Class == Device) + return pathForInstall(ctx, os, arch, partition, pathComponents...) } // PathForHostDexInstall returns an InstallPath representing the install path for the // module appended with paths... func PathForHostDexInstall(ctx ModuleInstallPathContext, pathComponents ...string) InstallPath { - return makePathForInstall(ctx, ctx.Config().BuildOS, ctx.Config().BuildArch, "", ctx.Debug(), pathComponents...) + return pathForInstall(ctx, ctx.Config().BuildOS, ctx.Config().BuildArch, "", pathComponents...) } // PathForModuleInPartitionInstall is similar to PathForModuleInstall but partition is provided by the caller func PathForModuleInPartitionInstall(ctx ModuleInstallPathContext, partition string, pathComponents ...string) InstallPath { os, arch := osAndArch(ctx) - return makePathForInstall(ctx, os, arch, partition, ctx.Debug(), pathComponents...) + return pathForInstall(ctx, os, arch, partition, pathComponents...) } func osAndArch(ctx ModuleInstallPathContext) (OsType, ArchType) { @@ -1751,12 +1804,7 @@ func osAndArch(ctx ModuleInstallPathContext) (OsType, ArchType) { return os, arch } -func makePathForInstall(ctx ModuleInstallPathContext, os OsType, arch ArchType, partition string, debug bool, pathComponents ...string) InstallPath { - ret := pathForInstall(ctx, os, arch, partition, debug, pathComponents...) - return ret -} - -func pathForInstall(ctx PathContext, os OsType, arch ArchType, partition string, debug bool, +func pathForInstall(ctx PathContext, os OsType, arch ArchType, partition string, pathComponents ...string) InstallPath { var partitionPaths []string @@ -1786,9 +1834,6 @@ func pathForInstall(ctx PathContext, os OsType, arch ArchType, partition string, } partitionPaths = []string{"host", osName + "-" + archName, partition} } - if debug { - partitionPaths = append([]string{"debug"}, partitionPaths...) - } partitionPath, err := validatePath(partitionPaths...) if err != nil { @@ -1832,12 +1877,12 @@ func InstallPathToOnDevicePath(ctx PathContext, path InstallPath) string { return "/" + rel } -func modulePartition(ctx ModuleInstallPathContext, os OsType) string { +func modulePartition(ctx ModuleInstallPathContext, device bool) string { var partition string if ctx.InstallInTestcases() { // "testcases" install directory can be used for host or device modules. partition = "testcases" - } else if os.Class == Device { + } else if device { if ctx.InstallInData() { partition = "data" } else if ctx.InstallInRamdisk() { @@ -1920,7 +1965,9 @@ func (p InstallPaths) Strings() []string { // validatePathInternal ensures that a path does not leave its component, and // optionally doesn't contain Ninja variables. func validatePathInternal(allowNinjaVariables bool, pathComponents ...string) (string, error) { - for _, path := range pathComponents { + initialEmpty := 0 + finalEmpty := 0 + for i, path := range pathComponents { if !allowNinjaVariables && strings.Contains(path, "$") { return "", fmt.Errorf("Path contains invalid character($): %s", path) } @@ -1929,11 +1976,25 @@ func validatePathInternal(allowNinjaVariables bool, pathComponents ...string) (s if path == ".." || strings.HasPrefix(path, "../") || strings.HasPrefix(path, "/") { return "", fmt.Errorf("Path is outside directory: %s", path) } + + if i == initialEmpty && pathComponents[i] == "" { + initialEmpty++ + } + if i == finalEmpty && pathComponents[len(pathComponents)-1-i] == "" { + finalEmpty++ + } + } + // Optimization: filepath.Join("foo", "") returns a newly allocated copy + // of "foo", while filepath.Join("foo") does not. Strip out any empty + // path components. + if initialEmpty == len(pathComponents) { + return "", nil } + nonEmptyPathComponents := pathComponents[initialEmpty : len(pathComponents)-finalEmpty] // TODO: filepath.Join isn't necessarily correct with embedded ninja // variables. '..' may remove the entire ninja variable, even if it // will be expanded to multiple nested directories. - return filepath.Join(pathComponents...), nil + return filepath.Join(nonEmptyPathComponents...), nil } // validateSafePath validates a path that we trust (may contain ninja @@ -2197,6 +2258,19 @@ type DataPath struct { SrcPath Path // The install path of the data file, relative to the install root. RelativeInstallPath string + // If WithoutRel is true, use SrcPath.Base() instead of SrcPath.Rel() as the filename. + WithoutRel bool +} + +func (d *DataPath) ToRelativeInstallPath() string { + relPath := d.SrcPath.Rel() + if d.WithoutRel { + relPath = d.SrcPath.Base() + } + if d.RelativeInstallPath != "" { + relPath = filepath.Join(d.RelativeInstallPath, relPath) + } + return relPath } // PathsIfNonNil returns a Paths containing only the non-nil input arguments. @@ -2245,23 +2319,3 @@ func IsThirdPartyPath(path string) bool { } return false } - -// PathsDepSet is a thin type-safe wrapper around the generic depSet. It always uses -// topological order. -type PathsDepSet struct { - depSet -} - -// newPathsDepSet returns an immutable PathsDepSet with the given direct and -// transitive contents. -func newPathsDepSet(direct Paths, transitive []*PathsDepSet) *PathsDepSet { - return &PathsDepSet{*newDepSet(TOPOLOGICAL, direct, transitive)} -} - -// ToList returns the PathsDepSet flattened to a list in topological order. -func (d *PathsDepSet) ToList() Paths { - if d == nil { - return nil - } - return d.depSet.ToList().(Paths) -} diff --git a/android/paths_test.go b/android/paths_test.go index 2f87977a9c..bf46c34f3c 100644 --- a/android/paths_test.go +++ b/android/paths_test.go @@ -36,6 +36,22 @@ var commonValidatePathTestCases = []strsTestCase{ in: []string{""}, out: "", }, + { + in: []string{"", ""}, + out: "", + }, + { + in: []string{"a", ""}, + out: "a", + }, + { + in: []string{"", "a"}, + out: "a", + }, + { + in: []string{"", "a", ""}, + out: "a", + }, { in: []string{"a/b"}, out: "a/b", diff --git a/android/plugin.go b/android/plugin.go new file mode 100644 index 0000000000..d62fc9453d --- /dev/null +++ b/android/plugin.go @@ -0,0 +1,140 @@ +// Copyright 2022 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package android + +import ( + "encoding/json" + "os" + "strings" + + "github.com/google/blueprint" +) + +func init() { + RegisterPluginSingletonBuildComponents(InitRegistrationContext) +} + +func RegisterPluginSingletonBuildComponents(ctx RegistrationContext) { + ctx.RegisterParallelSingletonType("plugins", pluginSingletonFactory) +} + +// pluginSingleton is a singleton to handle allowlisting of the final Android-.mk file +// output. +func pluginSingletonFactory() Singleton { + return &pluginSingleton{} +} + +type pluginSingleton struct{} + +var allowedPluginsByName = map[string]bool{ + "aidl-soong-rules": true, + "arm_compute_library_nn_driver": true, + "cuttlefish-soong-rules": true, + "gki-soong-rules": true, + "hidl-soong-rules": true, + "kernel-config-soong-rules": true, + "soong-angle-codegen": true, + "soong-api": true, + "soong-art": true, + "soong-ca-certificates": true, + "soong-ca-certificates-apex": true, + "soong-clang": true, + "soong-clang-prebuilts": true, + "soong-csuite": true, + "soong-fluoride": true, + "soong-fs_config": true, + "soong-icu": true, + "soong-java-config-error_prone": true, + "soong-libchrome": true, + "soong-llvm": true, + "soong-robolectric": true, + "soong-rust-prebuilts": true, + "soong-selinux": true, + "soong-wayland-protocol-codegen": true, + "treble_report_app": true, + "treble_report_local": true, + "treble_report_module": true, + "vintf-compatibility-matrix-soong-rules": true, + "xsdc-soong-rules": true, +} + +var internalPluginsPaths = []string{ + "vendor/google/build/soong/internal_plugins.json", +} + +type pluginProvider interface { + IsPluginFor(string) bool +} + +func maybeAddInternalPluginsToAllowlist(ctx SingletonContext) { + for _, internalPluginsPath := range internalPluginsPaths { + if path := ExistentPathForSource(ctx, internalPluginsPath); path.Valid() { + ctx.AddNinjaFileDeps(path.String()) + absPath := absolutePath(path.String()) + var moreAllowed map[string]bool + data, err := os.ReadFile(absPath) + if err != nil { + ctx.Errorf("Failed to open internal plugins path %q %q", internalPluginsPath, err) + } + if err := json.Unmarshal(data, &moreAllowed); err != nil { + ctx.Errorf("Internal plugins file %q did not parse correctly: %q", data, err) + } + for k, v := range moreAllowed { + allowedPluginsByName[k] = v + } + } + } +} + +func (p *pluginSingleton) GenerateBuildActions(ctx SingletonContext) { + for _, p := range ctx.DeviceConfig().BuildBrokenPluginValidation() { + allowedPluginsByName[p] = true + } + maybeAddInternalPluginsToAllowlist(ctx) + + disallowedPlugins := map[string]bool{} + ctx.VisitAllModulesBlueprint(func(module blueprint.Module) { + if ctx.ModuleType(module) != "bootstrap_go_package" { + return + } + + p, ok := module.(pluginProvider) + if !ok || !p.IsPluginFor("soong_build") { + return + } + + name := ctx.ModuleName(module) + if _, ok := allowedPluginsByName[name]; ok { + return + } + + dir := ctx.ModuleDir(module) + + // allow use of plugins within Soong to not allowlist everything + if strings.HasPrefix(dir, "build/soong") { + return + } + + // allow third party users outside of external to create new plugins, i.e. non-google paths + // under vendor or hardware + if !strings.HasPrefix(dir, "external/") && IsThirdPartyPath(dir) { + return + } + disallowedPlugins[name] = true + }) + if len(disallowedPlugins) > 0 { + ctx.Errorf("New plugins are not supported; however %q were found. Please reach out to the build team or use BUILD_BROKEN_PLUGIN_VALIDATION (see Changes.md for more info).", SortedStringKeys(disallowedPlugins)) + } +} diff --git a/android/prebuilt.go b/android/prebuilt.go index 9b5c0e919a..a32a37d3a3 100644 --- a/android/prebuilt.go +++ b/android/prebuilt.go @@ -387,7 +387,7 @@ func RegisterPrebuiltsPreArchMutators(ctx RegisterMutatorsContext) { func RegisterPrebuiltsPostDepsMutators(ctx RegisterMutatorsContext) { ctx.BottomUp("prebuilt_source", PrebuiltSourceDepsMutator).Parallel() - ctx.TopDown("prebuilt_select", PrebuiltSelectModuleMutator).Parallel() + ctx.BottomUp("prebuilt_select", PrebuiltSelectModuleMutator).Parallel() ctx.BottomUp("prebuilt_postdeps", PrebuiltPostDepsMutator).Parallel() } @@ -406,6 +406,8 @@ func PrebuiltRenameMutator(ctx BottomUpMutatorContext) { // PrebuiltSourceDepsMutator adds dependencies to the prebuilt module from the // corresponding source module, if one exists for the same variant. +// Add a dependency from the prebuilt to `all_apex_contributions` +// The metadata will be used for source vs prebuilts selection func PrebuiltSourceDepsMutator(ctx BottomUpMutatorContext) { m := ctx.Module() // If this module is a prebuilt, is enabled and has not been renamed to source then add a @@ -416,12 +418,38 @@ func PrebuiltSourceDepsMutator(ctx BottomUpMutatorContext) { ctx.AddReverseDependency(ctx.Module(), PrebuiltDepTag, name) p.properties.SourceExists = true } + // Add a dependency from the prebuilt to the `all_apex_contributions` + // metadata module + // TODO: When all branches contain this singleton module, make this strict + // TODO: Add this dependency only for mainline prebuilts and not every prebuilt module + if ctx.OtherModuleExists("all_apex_contributions") { + ctx.AddDependency(m, acDepTag, "all_apex_contributions") + } + + } +} + +// checkInvariantsForSourceAndPrebuilt checks if invariants are kept when replacing +// source with prebuilt. Note that the current module for the context is the source module. +func checkInvariantsForSourceAndPrebuilt(ctx BaseModuleContext, s, p Module) { + if _, ok := s.(OverrideModule); ok { + // skip the check when the source module is `override_X` because it's only a placeholder + // for the actual source module. The check will be invoked for the actual module. + return + } + if sourcePartition, prebuiltPartition := s.PartitionTag(ctx.DeviceConfig()), p.PartitionTag(ctx.DeviceConfig()); sourcePartition != prebuiltPartition { + ctx.OtherModuleErrorf(p, "partition is different: %s(%s) != %s(%s)", + sourcePartition, ctx.ModuleName(), prebuiltPartition, ctx.OtherModuleName(p)) } } // PrebuiltSelectModuleMutator marks prebuilts that are used, either overriding source modules or // because the source module doesn't exist. It also disables installing overridden source modules. -func PrebuiltSelectModuleMutator(ctx TopDownMutatorContext) { +// +// If the visited module is the metadata module `all_apex_contributions`, it sets a +// provider containing metadata about whether source or prebuilt of mainline modules should be used. +// This logic was added here to prevent the overhead of creating a new mutator. +func PrebuiltSelectModuleMutator(ctx BottomUpMutatorContext) { m := ctx.Module() if p := GetEmbeddedPrebuilt(m); p != nil { if p.srcsSupplier == nil && p.srcsPropertyName == "" { @@ -430,15 +458,29 @@ func PrebuiltSelectModuleMutator(ctx TopDownMutatorContext) { if !p.properties.SourceExists { p.properties.UsePrebuilt = p.usePrebuilt(ctx, nil, m) } + // Propagate the provider received from `all_apex_contributions` + // to the source module + ctx.VisitDirectDepsWithTag(acDepTag, func(am Module) { + psi := ctx.OtherModuleProvider(am, PrebuiltSelectionInfoProvider).(PrebuiltSelectionInfoMap) + ctx.SetProvider(PrebuiltSelectionInfoProvider, psi) + }) + } else if s, ok := ctx.Module().(Module); ok { ctx.VisitDirectDepsWithTag(PrebuiltDepTag, func(prebuiltModule Module) { p := GetEmbeddedPrebuilt(prebuiltModule) if p.usePrebuilt(ctx, s, prebuiltModule) { + checkInvariantsForSourceAndPrebuilt(ctx, s, prebuiltModule) + p.properties.UsePrebuilt = true s.ReplacedByPrebuilt() } }) } + // If this is `all_apex_contributions`, set a provider containing + // metadata about source vs prebuilts selection + if am, ok := m.(*allApexContributions); ok { + am.SetPrebuiltSelectionInfoProvider(ctx) + } } // PrebuiltPostDepsMutator replaces dependencies on the source module with dependencies on the @@ -464,9 +506,66 @@ func PrebuiltPostDepsMutator(ctx BottomUpMutatorContext) { } } +// A wrapper around PrebuiltSelectionInfoMap.IsSelected with special handling for java_sdk_library +// java_sdk_library is a macro that creates +// 1. top-level impl library +// 2. stub libraries (suffixed with .stubs...) +// +// java_sdk_library_import is a macro that creates +// 1. top-level "impl" library +// 2. stub libraries (suffixed with .stubs...) +// +// the impl of java_sdk_library_import is a "hook" for hiddenapi and dexpreopt processing. It does not have an impl jar, but acts as a shim +// to provide the jar deapxed from the prebuilt apex +// +// isSelected uses `all_apex_contributions` to supersede source vs prebuilts selection of the stub libraries. It does not supersede the +// selection of the top-level "impl" library so that this hook can work +// +// TODO (b/308174306) - Fix this when we need to support multiple prebuilts in main +func isSelected(psi PrebuiltSelectionInfoMap, m Module) bool { + if sdkLibrary, ok := m.(interface{ SdkLibraryName() *string }); ok && sdkLibrary.SdkLibraryName() != nil { + sln := proptools.String(sdkLibrary.SdkLibraryName()) + // This is the top-level library + // Do not supersede the existing prebuilts vs source selection mechanisms + if sln == m.base().BaseModuleName() { + return false + } + + // Stub library created by java_sdk_library_import + if p := GetEmbeddedPrebuilt(m); p != nil { + return psi.IsSelected(sln, PrebuiltNameFromSource(sln)) + } + + // Stub library created by java_sdk_library + return psi.IsSelected(sln, sln) + } + return psi.IsSelected(m.base().BaseModuleName(), m.Name()) +} + // usePrebuilt returns true if a prebuilt should be used instead of the source module. The prebuilt // will be used if it is marked "prefer" or if the source module is disabled. -func (p *Prebuilt) usePrebuilt(ctx TopDownMutatorContext, source Module, prebuilt Module) bool { +func (p *Prebuilt) usePrebuilt(ctx BaseMutatorContext, source Module, prebuilt Module) bool { + // Use `all_apex_contributions` for source vs prebuilt selection. + psi := PrebuiltSelectionInfoMap{} + ctx.VisitDirectDepsWithTag(PrebuiltDepTag, func(am Module) { + if ctx.OtherModuleHasProvider(am, PrebuiltSelectionInfoProvider) { + psi = ctx.OtherModuleProvider(am, PrebuiltSelectionInfoProvider).(PrebuiltSelectionInfoMap) + } + }) + + // If the source module is explicitly listed in the metadata module, use that + if source != nil && isSelected(psi, source) { + return false + } + // If the prebuilt module is explicitly listed in the metadata module, use that + if isSelected(psi, prebuilt) { + return true + } + + // If the baseModuleName could not be found in the metadata module, + // fall back to the existing source vs prebuilt selection. + // TODO: Drop the fallback mechanisms + if p.srcsSupplier != nil && len(p.srcsSupplier(ctx, prebuilt)) == 0 { return false } diff --git a/android/prebuilt_build_tool.go b/android/prebuilt_build_tool.go index e5edf9129a..17b323067f 100644 --- a/android/prebuilt_build_tool.go +++ b/android/prebuilt_build_tool.go @@ -17,7 +17,7 @@ package android import "path/filepath" func init() { - RegisterModuleType("prebuilt_build_tool", prebuiltBuildToolFactory) + RegisterModuleType("prebuilt_build_tool", NewPrebuiltBuildTool) } type prebuiltBuildToolProperties struct { @@ -101,7 +101,7 @@ var _ HostToolProvider = &prebuiltBuildTool{} // prebuilt_build_tool is to declare prebuilts to be used during the build, particularly for use // in genrules with the "tools" property. -func prebuiltBuildToolFactory() Module { +func NewPrebuiltBuildTool() Module { module := &prebuiltBuildTool{} module.AddProperties(&module.properties) InitSingleSourcePrebuiltModule(module, &module.properties, "Src") diff --git a/android/prebuilt_test.go b/android/prebuilt_test.go index fa40d1fb35..953258eddb 100644 --- a/android/prebuilt_test.go +++ b/android/prebuilt_test.go @@ -334,6 +334,78 @@ func TestPrebuilts(t *testing.T) { // be used. prebuilt: []OsType{Android, buildOS}, }, + { + name: "apex_contributions supersedes any source preferred via use_source_config_var", + modules: ` + source { + name: "bar", + } + + prebuilt { + name: "bar", + use_source_config_var: {config_namespace: "acme", var_name: "use_source"}, + srcs: ["prebuilt_file"], + } + apex_contributions { + name: "my_mainline_module_contribution", + api_domain: "apexfoo", + // this metadata module contains prebuilt + contents: ["prebuilt_bar"], + } + all_apex_contributions { + name: "all_apex_contributions", + } + `, + preparer: FixtureModifyProductVariables(func(variables FixtureProductVariables) { + variables.VendorVars = map[string]map[string]string{ + "acme": { + "use_source": "true", + }, + } + variables.BuildFlags = map[string]string{ + "RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "my_mainline_module_contribution", + } + }), + // use_source_config_var indicates that source should be used + // but this is superseded by `my_mainline_module_contribution` + prebuilt: []OsType{Android, buildOS}, + }, + { + name: "apex_contributions supersedes any prebuilt preferred via use_source_config_var", + modules: ` + source { + name: "bar", + } + + prebuilt { + name: "bar", + use_source_config_var: {config_namespace: "acme", var_name: "use_source"}, + srcs: ["prebuilt_file"], + } + apex_contributions { + name: "my_mainline_module_contribution", + api_domain: "apexfoo", + // this metadata module contains source + contents: ["bar"], + } + all_apex_contributions { + name: "all_apex_contributions", + } + `, + preparer: FixtureModifyProductVariables(func(variables FixtureProductVariables) { + variables.VendorVars = map[string]map[string]string{ + "acme": { + "use_source": "false", + }, + } + variables.BuildFlags = map[string]string{ + "RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "my_mainline_module_contribution", + } + }), + // use_source_config_var indicates that prebuilt should be used + // but this is superseded by `my_mainline_module_contribution` + prebuilt: nil, + }, { name: "prebuilt use_source_config_var={acme, use_source} - acme_use_source=true", modules: ` @@ -497,6 +569,58 @@ func TestPrebuilts(t *testing.T) { } } +func testPrebuiltErrorWithFixture(t *testing.T, expectedError, bp string, fixture FixturePreparer) { + t.Helper() + fs := MockFS{ + "prebuilt_file": nil, + } + GroupFixturePreparers( + PrepareForTestWithArchMutator, + PrepareForTestWithPrebuilts, + PrepareForTestWithOverrides, + fs.AddToFixture(), + FixtureRegisterWithContext(registerTestPrebuiltModules), + OptionalFixturePreparer(fixture), + ). + ExtendWithErrorHandler(FixtureExpectsAtLeastOneErrorMatchingPattern(expectedError)). + RunTestWithBp(t, bp) + +} + +func testPrebuiltError(t *testing.T, expectedError, bp string) { + testPrebuiltErrorWithFixture(t, expectedError, bp, nil) +} + +func TestPrebuiltShouldNotChangePartition(t *testing.T) { + testPrebuiltError(t, `partition is different`, ` + source { + name: "foo", + vendor: true, + } + prebuilt { + name: "foo", + prefer: true, + srcs: ["prebuilt_file"], + }`) +} + +func TestPrebuiltShouldNotChangePartition_WithOverride(t *testing.T) { + testPrebuiltError(t, `partition is different`, ` + source { + name: "foo", + vendor: true, + } + override_source { + name: "bar", + base: "foo", + } + prebuilt { + name: "bar", + prefer: true, + srcs: ["prebuilt_file"], + }`) +} + func registerTestPrebuiltBuildComponents(ctx RegistrationContext) { registerTestPrebuiltModules(ctx) @@ -513,6 +637,7 @@ func registerTestPrebuiltModules(ctx RegistrationContext) { ctx.RegisterModuleType("soong_config_module_type", SoongConfigModuleTypeFactory) ctx.RegisterModuleType("soong_config_string_variable", SoongConfigStringVariableDummyFactory) ctx.RegisterModuleType("soong_config_bool_variable", SoongConfigBoolVariableDummyFactory) + RegisterApexContributionsBuildComponents(ctx) } type prebuiltModule struct { @@ -607,3 +732,33 @@ func newOverrideSourceModule() Module { InitOverrideModule(m) return m } + +func TestPrebuiltErrorCannotListBothSourceAndPrebuiltInContributions(t *testing.T) { + selectMainlineModuleContritbutions := GroupFixturePreparers( + FixtureModifyProductVariables(func(variables FixtureProductVariables) { + variables.BuildFlags = map[string]string{ + "RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "my_apex_contributions", + } + }), + ) + testPrebuiltErrorWithFixture(t, `Cannot use Soong module: prebuilt_foo from apex_contributions: my_apex_contributions because it has been added previously as: foo from apex_contributions: my_apex_contributions`, ` + source { + name: "foo", + } + prebuilt { + name: "foo", + srcs: ["prebuilt_file"], + } + apex_contributions { + name: "my_apex_contributions", + api_domain: "my_mainline_module", + contents: [ + "foo", + "prebuilt_foo", + ], + } + all_apex_contributions { + name: "all_apex_contributions", + } + `, selectMainlineModuleContritbutions) +} diff --git a/android/proto.go b/android/proto.go index 09e50c8c39..0d8e0972a1 100644 --- a/android/proto.go +++ b/android/proto.go @@ -17,8 +17,6 @@ package android import ( "strings" - "android/soong/bazel" - "github.com/google/blueprint" "github.com/google/blueprint/proptools" ) @@ -152,102 +150,3 @@ func ProtoRule(rule *RuleBuilder, protoFile Path, flags ProtoFlags, deps Paths, rule.Command(). BuiltTool("dep_fixer").Flag(depFile.String()) } - -// Bp2buildProtoInfo contains information necessary to pass on to language specific conversion. -type Bp2buildProtoInfo struct { - Type *string - Name string - Proto_libs bazel.LabelList -} - -type ProtoAttrs struct { - Srcs bazel.LabelListAttribute - Strip_import_prefix *string - Deps bazel.LabelListAttribute -} - -// For each package in the include_dirs property a proto_library target should -// be added to the BUILD file in that package and a mapping should be added here -var includeDirsToProtoDeps = map[string]string{ - "external/protobuf/src": "//external/protobuf:libprotobuf-proto", -} - -// Bp2buildProtoProperties converts proto properties, creating a proto_library and returning the -// information necessary for language-specific handling. -func Bp2buildProtoProperties(ctx Bp2buildMutatorContext, m *ModuleBase, srcs bazel.LabelListAttribute) (Bp2buildProtoInfo, bool) { - var info Bp2buildProtoInfo - if srcs.IsEmpty() { - return info, false - } - - var protoLibraries bazel.LabelList - var directProtoSrcs bazel.LabelList - - // For filegroups that should be converted to proto_library just collect the - // labels of converted proto_library targets. - for _, protoSrc := range srcs.Value.Includes { - src := protoSrc.OriginalModuleName - if fg, ok := ToFileGroupAsLibrary(ctx, src); ok && - fg.ShouldConvertToProtoLibrary(ctx) { - protoLibraries.Add(&bazel.Label{ - Label: fg.GetProtoLibraryLabel(ctx), - }) - } else { - directProtoSrcs.Add(&protoSrc) - } - } - - info.Name = m.Name() + "_proto" - - if len(directProtoSrcs.Includes) > 0 { - attrs := ProtoAttrs{ - Srcs: bazel.MakeLabelListAttribute(directProtoSrcs), - } - attrs.Deps.Append(bazel.MakeLabelListAttribute(protoLibraries)) - - for axis, configToProps := range m.GetArchVariantProperties(ctx, &ProtoProperties{}) { - for _, rawProps := range configToProps { - var props *ProtoProperties - var ok bool - if props, ok = rawProps.(*ProtoProperties); !ok { - ctx.ModuleErrorf("Could not cast ProtoProperties to expected type") - } - if axis == bazel.NoConfigAxis { - info.Type = props.Proto.Type - - if !proptools.BoolDefault(props.Proto.Canonical_path_from_root, canonicalPathFromRootDefault) { - // an empty string indicates to strips the package path - path := "" - attrs.Strip_import_prefix = &path - } - - for _, dir := range props.Proto.Include_dirs { - if dep, ok := includeDirsToProtoDeps[dir]; ok { - attrs.Deps.Add(bazel.MakeLabelAttribute(dep)) - } else { - ctx.PropertyErrorf("Could not find the proto_library target for include dir", dir) - } - } - } else if props.Proto.Type != info.Type && props.Proto.Type != nil { - ctx.ModuleErrorf("Cannot handle arch-variant types for protos at this time.") - } - } - } - - tags := ApexAvailableTags(ctx.Module()) - - ctx.CreateBazelTargetModule( - bazel.BazelTargetModuleProperties{Rule_class: "proto_library"}, - CommonAttributes{Name: info.Name, Tags: tags}, - &attrs, - ) - - protoLibraries.Add(&bazel.Label{ - Label: ":" + info.Name, - }) - } - - info.Proto_libs = protoLibraries - - return info, true -} diff --git a/android/register.go b/android/register.go index 1a3db9d903..cd968cd02f 100644 --- a/android/register.go +++ b/android/register.go @@ -16,9 +16,8 @@ package android import ( "fmt" - "reflect" - "github.com/google/blueprint" + "reflect" ) // A sortable component is one whose registration order affects the order in which it is executed @@ -62,19 +61,15 @@ var moduleTypesForDocs = map[string]reflect.Value{} var moduleTypeByFactory = map[reflect.Value]string{} type singleton struct { - // True if this should be registered as a pre-singleton, false otherwise. - pre bool + // True if this should be registered as a parallel singleton. + parallel bool name string factory SingletonFactory } -func newSingleton(name string, factory SingletonFactory) singleton { - return singleton{false, name, factory} -} - -func newPreSingleton(name string, factory SingletonFactory) singleton { - return singleton{true, name, factory} +func newSingleton(name string, factory SingletonFactory, parallel bool) singleton { + return singleton{parallel: parallel, name: name, factory: factory} } func (s singleton) componentName() string { @@ -83,17 +78,12 @@ func (s singleton) componentName() string { func (s singleton) register(ctx *Context) { adaptor := SingletonFactoryAdaptor(ctx, s.factory) - if s.pre { - ctx.RegisterPreSingletonType(s.name, adaptor) - } else { - ctx.RegisterSingletonType(s.name, adaptor) - } + ctx.RegisterSingletonType(s.name, adaptor, s.parallel) } var _ sortableComponent = singleton{} var singletons sortableComponents -var preSingletons sortableComponents type mutator struct { name string @@ -145,12 +135,16 @@ func RegisterModuleTypeForDocs(name string, factory reflect.Value) { moduleTypeByFactory[factory] = name } +func registerSingletonType(name string, factory SingletonFactory, parallel bool) { + singletons = append(singletons, newSingleton(name, factory, parallel)) +} + func RegisterSingletonType(name string, factory SingletonFactory) { - singletons = append(singletons, newSingleton(name, factory)) + registerSingletonType(name, factory, false) } -func RegisterPreSingletonType(name string, factory SingletonFactory) { - preSingletons = append(preSingletons, newPreSingleton(name, factory)) +func RegisterParallelSingletonType(name string, factory SingletonFactory) { + registerSingletonType(name, factory, true) } type Context struct { @@ -166,38 +160,9 @@ func NewContext(config Config) *Context { return ctx } -// Helper function to register the module types used in bp2build and -// api_bp2build. -func registerModuleTypes(ctx *Context) { - for _, t := range moduleTypes { - t.register(ctx) - } - // Required for SingletonModule types, even though we are not using them. - for _, t := range singletons { - t.register(ctx) - } -} - -// RegisterForBazelConversion registers an alternate shadow pipeline of -// singletons, module types and mutators to register for converting Blueprint -// files to semantically equivalent BUILD files. -func (ctx *Context) RegisterForBazelConversion() { - registerModuleTypes(ctx) - RegisterMutatorsForBazelConversion(ctx, bp2buildPreArchMutators) -} - -// RegisterForApiBazelConversion is similar to RegisterForBazelConversion except that -// it only generates API targets in the generated workspace -func (ctx *Context) RegisterForApiBazelConversion() { - registerModuleTypes(ctx) - RegisterMutatorsForApiBazelConversion(ctx, bp2buildPreArchMutators) -} - // Register the pipeline of singletons, module types, and mutators for // generating build.ninja and other files for Kati, from Android.bp files. func (ctx *Context) Register() { - preSingletons.registerAll(ctx) - for _, t := range moduleTypes { t.register(ctx) } @@ -220,17 +185,15 @@ func (ctx *Context) registerSingletonMakeVarsProvider(makevars SingletonMakeVars func collateGloballyRegisteredSingletons() sortableComponents { allSingletons := append(sortableComponents(nil), singletons...) allSingletons = append(allSingletons, - singleton{false, "bazeldeps", BazelSingleton}, - // Register phony just before makevars so it can write out its phony rules as Make rules - singleton{false, "phony", phonySingletonFactory}, + singleton{parallel: false, name: "phony", factory: phonySingletonFactory}, // Register makevars after other singletons so they can export values through makevars - singleton{false, "makevars", makeVarsSingletonFunc}, + singleton{parallel: false, name: "makevars", factory: makeVarsSingletonFunc}, // Register env and ninjadeps last so that they can track all used environment variables and // Ninja file dependencies stored in the config. - singleton{false, "ninjadeps", ninjaDepsSingletonFactory}, + singleton{parallel: false, name: "ninjadeps", factory: ninjaDepsSingletonFactory}, ) return allSingletons @@ -259,7 +222,8 @@ func ModuleTypeByFactory() map[reflect.Value]string { type RegistrationContext interface { RegisterModuleType(name string, factory ModuleFactory) RegisterSingletonModuleType(name string, factory SingletonModuleFactory) - RegisterPreSingletonType(name string, factory SingletonFactory) + RegisterParallelSingletonModuleType(name string, factory SingletonModuleFactory) + RegisterParallelSingletonType(name string, factory SingletonFactory) RegisterSingletonType(name string, factory SingletonFactory) PreArchMutators(f RegisterMutatorFunc) @@ -290,9 +254,8 @@ type RegistrationContext interface { // ctx := android.NewTestContext(config) // RegisterBuildComponents(ctx) var InitRegistrationContext RegistrationContext = &initRegistrationContext{ - moduleTypes: make(map[string]ModuleFactory), - singletonTypes: make(map[string]SingletonFactory), - preSingletonTypes: make(map[string]SingletonFactory), + moduleTypes: make(map[string]ModuleFactory), + singletonTypes: make(map[string]SingletonFactory), } // Make sure the TestContext implements RegistrationContext. @@ -301,7 +264,6 @@ var _ RegistrationContext = (*TestContext)(nil) type initRegistrationContext struct { moduleTypes map[string]ModuleFactory singletonTypes map[string]SingletonFactory - preSingletonTypes map[string]SingletonFactory moduleTypesForDocs map[string]reflect.Value } @@ -315,8 +277,15 @@ func (ctx *initRegistrationContext) RegisterModuleType(name string, factory Modu } func (ctx *initRegistrationContext) RegisterSingletonModuleType(name string, factory SingletonModuleFactory) { + ctx.registerSingletonModuleType(name, factory, false) +} +func (ctx *initRegistrationContext) RegisterParallelSingletonModuleType(name string, factory SingletonModuleFactory) { + ctx.registerSingletonModuleType(name, factory, true) +} + +func (ctx *initRegistrationContext) registerSingletonModuleType(name string, factory SingletonModuleFactory, parallel bool) { s, m := SingletonModuleFactoryAdaptor(name, factory) - ctx.RegisterSingletonType(name, s) + ctx.registerSingletonType(name, s, parallel) 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 @@ -324,20 +293,20 @@ func (ctx *initRegistrationContext) RegisterSingletonModuleType(name string, fac RegisterModuleTypeForDocs(name, reflect.ValueOf(factory)) } -func (ctx *initRegistrationContext) RegisterSingletonType(name string, factory SingletonFactory) { +func (ctx *initRegistrationContext) registerSingletonType(name string, factory SingletonFactory, parallel bool) { if _, present := ctx.singletonTypes[name]; present { panic(fmt.Sprintf("singleton type %q is already registered", name)) } ctx.singletonTypes[name] = factory - RegisterSingletonType(name, factory) + registerSingletonType(name, factory, parallel) } -func (ctx *initRegistrationContext) RegisterPreSingletonType(name string, factory SingletonFactory) { - if _, present := ctx.preSingletonTypes[name]; present { - panic(fmt.Sprintf("pre singleton type %q is already registered", name)) - } - ctx.preSingletonTypes[name] = factory - RegisterPreSingletonType(name, factory) +func (ctx *initRegistrationContext) RegisterSingletonType(name string, factory SingletonFactory) { + ctx.registerSingletonType(name, factory, false) +} + +func (ctx *initRegistrationContext) RegisterParallelSingletonType(name string, factory SingletonFactory) { + ctx.registerSingletonType(name, factory, true) } func (ctx *initRegistrationContext) PreArchMutators(f RegisterMutatorFunc) { diff --git a/android/rule_builder.go b/android/rule_builder.go index 155fbdf714..1a491f7082 100644 --- a/android/rule_builder.go +++ b/android/rule_builder.go @@ -53,6 +53,7 @@ type RuleBuilder struct { remoteable RemoteRuleSupports rbeParams *remoteexec.REParams outDir WritablePath + sboxOutSubDir string sboxTools bool sboxInputs bool sboxManifestPath WritablePath @@ -65,9 +66,18 @@ func NewRuleBuilder(pctx PackageContext, ctx BuilderContext) *RuleBuilder { pctx: pctx, ctx: ctx, temporariesSet: make(map[WritablePath]bool), + sboxOutSubDir: sboxOutSubDir, } } +// SetSboxOutDirDirAsEmpty sets the out subdirectory to an empty string +// This is useful for sandboxing actions that change the execution root to a path in out/ (e.g mixed builds) +// For such actions, SetSboxOutDirDirAsEmpty ensures that the path does not become $SBOX_SANDBOX_DIR/out/out/bazel/output/execroot/__main__/... +func (rb *RuleBuilder) SetSboxOutDirDirAsEmpty() *RuleBuilder { + rb.sboxOutSubDir = "" + return rb +} + // RuleBuilderInstall is a tuple of install from and to locations. type RuleBuilderInstall struct { From Path @@ -464,13 +474,23 @@ func (r *RuleBuilder) depFileMergerCmd(depFiles WritablePaths) *RuleBuilderComma Inputs(depFiles.Paths()) } +// BuildWithNinjaVars adds the built command line to the build graph, with dependencies on Inputs and Tools, and output files for +// Outputs. This function will not escape Ninja variables, so it may be used to write sandbox manifests using Ninja variables. +func (r *RuleBuilder) BuildWithUnescapedNinjaVars(name string, desc string) { + r.build(name, desc, false) +} + // Build adds the built command line to the build graph, with dependencies on Inputs and Tools, and output files for // Outputs. func (r *RuleBuilder) Build(name string, desc string) { + r.build(name, desc, true) +} + +func (r *RuleBuilder) build(name string, desc string, ninjaEscapeCommandString bool) { name = ninjaNameEscape(name) if len(r.missingDeps) > 0 { - r.ctx.Build(pctx, BuildParams{ + r.ctx.Build(r.pctx, BuildParams{ Rule: ErrorRule, Outputs: r.Outputs(), Description: desc, @@ -582,12 +602,10 @@ func (r *RuleBuilder) Build(name string, desc string) { // Add copy rules to the manifest to copy each output file from the sbox directory. // to the output directory after running the commands. - sboxOutputs := make([]string, len(outputs)) - for i, output := range outputs { + for _, output := range outputs { rel := Rel(r.ctx, r.outDir.String(), output.String()) - sboxOutputs[i] = filepath.Join(sboxOutDir, rel) command.CopyAfter = append(command.CopyAfter, &sbox_proto.Copy{ - From: proto.String(filepath.Join(sboxOutSubDir, rel)), + From: proto.String(filepath.Join(r.sboxOutSubDir, rel)), To: proto.String(output.String()), }) } @@ -611,12 +629,35 @@ func (r *RuleBuilder) Build(name string, desc string) { name, r.sboxManifestPath.String(), r.outDir.String()) } - // Create a rule to write the manifest as a the textproto. + // Create a rule to write the manifest as textproto. pbText, err := prototext.Marshal(&manifest) if err != nil { ReportPathErrorf(r.ctx, "sbox manifest failed to marshal: %q", err) } - WriteFileRule(r.ctx, r.sboxManifestPath, string(pbText)) + if ninjaEscapeCommandString { + WriteFileRule(r.ctx, r.sboxManifestPath, string(pbText)) + } else { + // We need to have a rule to write files that is + // defined on the RuleBuilder's pctx in order to + // write Ninja variables in the string. + // The WriteFileRule function above rule can only write + // raw strings because it is defined on the android + // package's pctx, and it can't access variables defined + // in another context. + r.ctx.Build(r.pctx, BuildParams{ + Rule: r.ctx.Rule(r.pctx, "unescapedWriteFile", blueprint.RuleParams{ + Command: `rm -rf ${out} && cat ${out}.rsp > ${out}`, + Rspfile: "${out}.rsp", + RspfileContent: "${content}", + Description: "write file", + }, "content"), + Output: r.sboxManifestPath, + Description: "write sbox manifest " + r.sboxManifestPath.Base(), + Args: map[string]string{ + "content": string(pbText), + }, + }) + } // Generate a new string to use as the command line of the sbox rule. This uses // a RuleBuilderCommand as a convenience method of building the command line, then @@ -715,9 +756,13 @@ func (r *RuleBuilder) Build(name string, desc string) { pool = localPool } + if ninjaEscapeCommandString { + commandString = proptools.NinjaEscape(commandString) + } + r.ctx.Build(r.pctx, BuildParams{ - Rule: r.ctx.Rule(pctx, name, blueprint.RuleParams{ - Command: proptools.NinjaEscape(commandString), + Rule: r.ctx.Rule(r.pctx, name, blueprint.RuleParams{ + Command: commandString, CommandDeps: proptools.NinjaEscapeList(tools.Strings()), Restat: r.restat, Rspfile: proptools.NinjaEscape(rspFile), @@ -826,7 +871,7 @@ func (c *RuleBuilderCommand) PathForOutput(path WritablePath) string { func sboxPathForToolRel(ctx BuilderContext, path Path) string { // Errors will be handled in RuleBuilder.Build where we have a context to report them - toolDir := pathForInstall(ctx, ctx.Config().BuildOS, ctx.Config().BuildArch, "", false) + toolDir := pathForInstall(ctx, ctx.Config().BuildOS, ctx.Config().BuildArch, "") relOutSoong, isRelOutSoong, _ := maybeRelErr(toolDir.String(), path.String()) if isRelOutSoong { // The tool is in the Soong output directory, it will be copied to __SBOX_OUT_DIR__/tools/out @@ -1296,9 +1341,9 @@ func (c *RuleBuilderCommand) String() string { // RuleBuilderSboxProtoForTests takes the BuildParams for the manifest passed to RuleBuilder.Sbox() // and returns sbox testproto generated by the RuleBuilder. -func RuleBuilderSboxProtoForTests(t *testing.T, params TestingBuildParams) *sbox_proto.Manifest { +func RuleBuilderSboxProtoForTests(t *testing.T, ctx *TestContext, params TestingBuildParams) *sbox_proto.Manifest { t.Helper() - content := ContentFromFileRuleForTests(t, params) + content := ContentFromFileRuleForTests(t, ctx, params) manifest := sbox_proto.Manifest{} err := prototext.Unmarshal([]byte(content), &manifest) if err != nil { diff --git a/android/rule_builder_test.go b/android/rule_builder_test.go index 86647eb22e..63c35272af 100644 --- a/android/rule_builder_test.go +++ b/android/rule_builder_test.go @@ -28,6 +28,17 @@ import ( "android/soong/shared" ) +var ( + pctx_ruleBuilderTest = NewPackageContext("android/soong/rule_builder") + pctx_ruleBuilderTestSubContext = NewPackageContext("android/soong/rule_builder/config") +) + +func init() { + pctx_ruleBuilderTest.Import("android/soong/rule_builder/config") + pctx_ruleBuilderTest.StaticVariable("cmdFlags", "${config.ConfigFlags}") + pctx_ruleBuilderTestSubContext.StaticVariable("ConfigFlags", "--some-clang-flag") +} + func builderContext() BuilderContext { return BuilderContextForTesting(TestConfig("out", nil, "", map[string][]byte{ "ld": nil, @@ -496,11 +507,13 @@ func testRuleBuilderFactory() Module { type testRuleBuilderModule struct { ModuleBase properties struct { - Srcs []string + Srcs []string + Flags []string - Restat bool - Sbox bool - Sbox_inputs bool + Restat bool + Sbox bool + Sbox_inputs bool + Unescape_ninja_vars bool } } @@ -518,8 +531,9 @@ func (t *testRuleBuilderModule) GenerateAndroidBuildActions(ctx ModuleContext) { rspFileContents2 := PathsForSource(ctx, []string{"rsp_in2"}) manifestPath := PathForModuleOut(ctx, "sbox.textproto") - testRuleBuilder_Build(ctx, in, implicit, orderOnly, validation, out, outDep, outDir, - manifestPath, t.properties.Restat, t.properties.Sbox, t.properties.Sbox_inputs, + testRuleBuilder_Build(ctx, in, implicit, orderOnly, validation, t.properties.Flags, + out, outDep, outDir, + manifestPath, t.properties.Restat, t.properties.Sbox, t.properties.Sbox_inputs, t.properties.Unescape_ninja_vars, rspFile, rspFileContents, rspFile2, rspFileContents2) } @@ -543,17 +557,18 @@ func (t *testRuleBuilderSingleton) GenerateBuildActions(ctx SingletonContext) { rspFileContents2 := PathsForSource(ctx, []string{"rsp_in2"}) manifestPath := PathForOutput(ctx, "singleton/sbox.textproto") - testRuleBuilder_Build(ctx, in, implicit, orderOnly, validation, out, outDep, outDir, - manifestPath, true, false, false, + testRuleBuilder_Build(ctx, in, implicit, orderOnly, validation, nil, out, outDep, outDir, + manifestPath, true, false, false, false, rspFile, rspFileContents, rspFile2, rspFileContents2) } func testRuleBuilder_Build(ctx BuilderContext, in Paths, implicit, orderOnly, validation Path, + flags []string, out, outDep, outDir, manifestPath WritablePath, - restat, sbox, sboxInputs bool, + restat, sbox, sboxInputs, unescapeNinjaVars bool, rspFile WritablePath, rspFileContents Paths, rspFile2 WritablePath, rspFileContents2 Paths) { - rule := NewRuleBuilder(pctx, ctx) + rule := NewRuleBuilder(pctx_ruleBuilderTest, ctx) if sbox { rule.Sbox(outDir, manifestPath) @@ -564,6 +579,7 @@ func testRuleBuilder_Build(ctx BuilderContext, in Paths, implicit, orderOnly, va rule.Command(). Tool(PathForSource(ctx, "cp")). + Flags(flags). Inputs(in). Implicit(implicit). OrderOnly(orderOnly). @@ -577,7 +593,11 @@ func testRuleBuilder_Build(ctx BuilderContext, in Paths, implicit, orderOnly, va rule.Restat() } - rule.Build("rule", "desc") + if unescapeNinjaVars { + rule.BuildWithUnescapedNinjaVars("rule", "desc") + } else { + rule.Build("rule", "desc") + } } var prepareForRuleBuilderTest = FixtureRegisterWithContext(func(ctx RegistrationContext) { @@ -663,7 +683,7 @@ func TestRuleBuilder_Build(t *testing.T) { t.Errorf("want Deps = %q, got %q", blueprint.DepsGCC, params.Deps) } - rspFile2Content := ContentFromFileRuleForTests(t, rspFile2Params) + rspFile2Content := ContentFromFileRuleForTests(t, result.TestContext, rspFile2Params) AssertStringEquals(t, "rspFile2 content", "rsp_in2\n", rspFile2Content) } @@ -777,7 +797,7 @@ func TestRuleBuilderHashInputs(t *testing.T) { t.Run(test.name, func(t *testing.T) { t.Run("sbox", func(t *testing.T) { gen := result.ModuleForTests(test.name+"_sbox", "") - manifest := RuleBuilderSboxProtoForTests(t, gen.Output("sbox.textproto")) + manifest := RuleBuilderSboxProtoForTests(t, result.TestContext, gen.Output("sbox.textproto")) hash := manifest.Commands[0].GetInputHash() AssertStringEquals(t, "hash", test.expectedHash, hash) @@ -792,3 +812,47 @@ func TestRuleBuilderHashInputs(t *testing.T) { }) } } + +func TestRuleBuilderWithNinjaVarEscaping(t *testing.T) { + bp := ` + rule_builder_test { + name: "foo_sbox_escaped_ninja", + flags: ["${cmdFlags}"], + sbox: true, + sbox_inputs: true, + } + rule_builder_test { + name: "foo_sbox", + flags: ["${cmdFlags}"], + sbox: true, + sbox_inputs: true, + unescape_ninja_vars: true, + } + ` + result := GroupFixturePreparers( + prepareForRuleBuilderTest, + FixtureWithRootAndroidBp(bp), + ).RunTest(t) + + escapedNinjaMod := result.ModuleForTests("foo_sbox_escaped_ninja", "").Rule("writeFile") + AssertStringDoesContain( + t, + "", + escapedNinjaMod.BuildParams.Args["content"], + "$${cmdFlags}", + ) + + unescapedNinjaMod := result.ModuleForTests("foo_sbox", "").Rule("unescapedWriteFile") + AssertStringDoesContain( + t, + "", + unescapedNinjaMod.BuildParams.Args["content"], + "${cmdFlags}", + ) + AssertStringDoesNotContain( + t, + "", + unescapedNinjaMod.BuildParams.Args["content"], + "$${cmdFlags}", + ) +} diff --git a/android/sdk.go b/android/sdk.go index 63e0bbeec2..6b598ab9ef 100644 --- a/android/sdk.go +++ b/android/sdk.go @@ -830,6 +830,9 @@ type SdkMemberContext interface { // IsTargetBuildBeforeTiramisu return true if the target build release for which this snapshot is // being generated is before Tiramisu, i.e. S. IsTargetBuildBeforeTiramisu() bool + + // ModuleErrorf reports an error at the line number of the module type in the module definition. + ModuleErrorf(fmt string, args ...interface{}) } // ExportedComponentsInfo contains information about the components that this module exports to an diff --git a/android/sdk_version.go b/android/sdk_version.go index 0ae8073522..aafcee79a3 100644 --- a/android/sdk_version.go +++ b/android/sdk_version.go @@ -48,6 +48,7 @@ const ( SdkPublic SdkSystem SdkTest + SdkTestFrameworksCore SdkModule SdkSystemServer SdkPrivate @@ -67,6 +68,8 @@ func (k SdkKind) String() string { return "system" case SdkTest: return "test" + case SdkTestFrameworksCore: + return "test_frameworks_core" case SdkCore: return "core" case SdkCorePlatform: @@ -84,22 +87,6 @@ func (k SdkKind) String() string { } } -// JavaLibraryName returns the soong module containing the Java APIs of that API surface. -func (k SdkKind) JavaLibraryName(c Config) string { - name := k.DefaultJavaLibraryName() - return JavaApiLibraryName(c, name) -} - -// JavaApiLibraryName returns the name of .txt equivalent of a java_library, but does -// not check if either module exists. -// TODO: Return .txt (single-tree or multi-tree equivalents) based on config -func JavaApiLibraryName(c Config, name string) string { - if c.BuildFromTextStub() { - return name + ".from-text" - } - return name -} - func (k SdkKind) DefaultJavaLibraryName() string { switch k { case SdkPublic: @@ -108,6 +95,8 @@ func (k SdkKind) DefaultJavaLibraryName() string { return "android_system_stubs_current" case SdkTest: return "android_test_stubs_current" + case SdkTestFrameworksCore: + return "android_test_frameworks_core_stubs_current" case SdkCore: return "core.current.stubs" case SdkModule: @@ -153,7 +142,7 @@ func (s SdkSpec) Stable() bool { return true case SdkCore, SdkPublic, SdkSystem, SdkModule, SdkSystemServer: return true - case SdkCorePlatform, SdkTest, SdkPrivate: + case SdkCorePlatform, SdkTest, SdkTestFrameworksCore, SdkPrivate: return false default: panic(fmt.Errorf("unknown SdkKind=%v", s.Kind)) @@ -201,7 +190,8 @@ func (s SdkSpec) UsePrebuilt(ctx EarlyModuleContext) bool { return ctx.Config().AlwaysUsePrebuiltSdks() } else if !s.ApiLevel.IsPreview() { // validation check - if s.Kind != SdkPublic && s.Kind != SdkSystem && s.Kind != SdkTest && s.Kind != SdkModule && s.Kind != SdkSystemServer { + if s.Kind != SdkPublic && s.Kind != SdkSystem && s.Kind != SdkTest && + s.Kind != SdkTestFrameworksCore && s.Kind != SdkModule && s.Kind != SdkSystemServer { panic(fmt.Errorf("prebuilt SDK is not not available for SdkKind=%q", s.Kind)) return false } @@ -282,6 +272,8 @@ func SdkSpecFromWithConfig(config Config, str string) SdkSpec { kind = SdkSystem case "test": kind = SdkTest + case "test_frameworks_core": + kind = SdkTestFrameworksCore case "module": kind = SdkModule case "system_server": @@ -326,11 +318,10 @@ func init() { // Export the name of the soong modules representing the various Java API surfaces. func javaSdkMakeVars(ctx MakeVarsContext) { - ctx.Strict("ANDROID_PUBLIC_STUBS", SdkPublic.JavaLibraryName(ctx.Config())) - ctx.Strict("ANDROID_SYSTEM_STUBS", SdkSystem.JavaLibraryName(ctx.Config())) - ctx.Strict("ANDROID_TEST_STUBS", SdkTest.JavaLibraryName(ctx.Config())) - ctx.Strict("ANDROID_MODULE_LIB_STUBS", SdkModule.JavaLibraryName(ctx.Config())) - ctx.Strict("ANDROID_SYSTEM_SERVER_STUBS", SdkSystemServer.JavaLibraryName(ctx.Config())) - // TODO (jihoonkang): Create a .txt equivalent for core.current.stubs - ctx.Strict("ANDROID_CORE_STUBS", SdkCore.JavaLibraryName(ctx.Config())) + ctx.Strict("ANDROID_PUBLIC_STUBS", SdkPublic.DefaultJavaLibraryName()) + ctx.Strict("ANDROID_SYSTEM_STUBS", SdkSystem.DefaultJavaLibraryName()) + ctx.Strict("ANDROID_TEST_STUBS", SdkTest.DefaultJavaLibraryName()) + ctx.Strict("ANDROID_MODULE_LIB_STUBS", SdkModule.DefaultJavaLibraryName()) + ctx.Strict("ANDROID_SYSTEM_SERVER_STUBS", SdkSystemServer.DefaultJavaLibraryName()) + ctx.Strict("ANDROID_CORE_STUBS", SdkCore.DefaultJavaLibraryName()) } diff --git a/android/sdk_version_test.go b/android/sdk_version_test.go index ea99c4d625..30bd002c23 100644 --- a/android/sdk_version_test.go +++ b/android/sdk_version_test.go @@ -75,7 +75,7 @@ func TestSdkSpecFrom(t *testing.T) { config := NullConfig("", "") - config.productVariables = productVariables{ + config.productVariables = ProductVariables{ Platform_sdk_version: intPtr(31), Platform_sdk_codename: stringPtr("Tiramisu"), Platform_version_active_codenames: []string{"Tiramisu"}, diff --git a/android/soong_config_modules.go b/android/soong_config_modules.go index 5fa60124ea..ec0edfd73a 100644 --- a/android/soong_config_modules.go +++ b/android/soong_config_modules.go @@ -187,7 +187,6 @@ func (*soongConfigModuleTypeImport) GenerateAndroidBuildActions(ModuleContext) { type soongConfigModuleTypeModule struct { ModuleBase - BazelModuleBase properties soongconfig.ModuleTypeProperties } @@ -395,10 +394,6 @@ func loadSoongConfigModuleTypeDefinition(ctx LoadHookContext, from string) map[s return (map[string]blueprint.ModuleFactory)(nil) } - if ctx.Config().BuildMode == Bp2build { - ctx.Config().Bp2buildSoongConfigDefinitions.AddVars(mtDef) - } - globalModuleTypes := ctx.moduleFactories() factories := make(map[string]blueprint.ModuleFactory) @@ -406,7 +401,7 @@ func loadSoongConfigModuleTypeDefinition(ctx LoadHookContext, from string) map[s for name, moduleType := range mtDef.ModuleTypes { factory := globalModuleTypes[moduleType.BaseModuleType] if factory != nil { - factories[name] = configModuleFactory(factory, moduleType, ctx.Config().BuildMode == Bp2build) + factories[name] = configModuleFactory(factory, moduleType) } else { reportErrors(ctx, from, fmt.Errorf("missing global module type factory for %q", moduleType.BaseModuleType)) @@ -421,9 +416,60 @@ func loadSoongConfigModuleTypeDefinition(ctx LoadHookContext, from string) map[s }).(map[string]blueprint.ModuleFactory) } +// tracingConfig is a wrapper to soongconfig.SoongConfig which records all accesses to SoongConfig. +type tracingConfig struct { + config soongconfig.SoongConfig + boolSet map[string]bool + stringSet map[string]string + isSetSet map[string]bool +} + +func (c *tracingConfig) Bool(name string) bool { + c.boolSet[name] = c.config.Bool(name) + return c.boolSet[name] +} + +func (c *tracingConfig) String(name string) string { + c.stringSet[name] = c.config.String(name) + return c.stringSet[name] +} + +func (c *tracingConfig) IsSet(name string) bool { + c.isSetSet[name] = c.config.IsSet(name) + return c.isSetSet[name] +} + +func (c *tracingConfig) getTrace() soongConfigTrace { + ret := soongConfigTrace{} + + for k, v := range c.boolSet { + ret.Bools = append(ret.Bools, fmt.Sprintf("%q:%t", k, v)) + } + for k, v := range c.stringSet { + ret.Strings = append(ret.Strings, fmt.Sprintf("%q:%q", k, v)) + } + for k, v := range c.isSetSet { + ret.IsSets = append(ret.IsSets, fmt.Sprintf("%q:%t", k, v)) + } + + return ret +} + +func newTracingConfig(config soongconfig.SoongConfig) *tracingConfig { + c := tracingConfig{ + config: config, + boolSet: make(map[string]bool), + stringSet: make(map[string]string), + isSetSet: make(map[string]bool), + } + return &c +} + +var _ soongconfig.SoongConfig = (*tracingConfig)(nil) + // configModuleFactory takes an existing soongConfigModuleFactory and a // ModuleType to create a new ModuleFactory that uses a custom loadhook. -func configModuleFactory(factory blueprint.ModuleFactory, moduleType *soongconfig.ModuleType, bp2build bool) blueprint.ModuleFactory { +func configModuleFactory(factory blueprint.ModuleFactory, moduleType *soongconfig.ModuleType) blueprint.ModuleFactory { // Defer creation of conditional properties struct until the first call from the factory // method. That avoids having to make a special call to the factory to create the properties // structs from which the conditional properties struct is created. This is needed in order to @@ -464,38 +510,22 @@ func configModuleFactory(factory blueprint.ModuleFactory, moduleType *soongconfi conditionalProps := proptools.CloneEmptyProperties(conditionalFactoryProps) props = append(props, conditionalProps.Interface()) - if bp2build { - // The loadhook is different for bp2build, since we don't want to set a specific - // set of property values based on a vendor var -- we want __all of them__ to - // generate select statements, so we put the entire soong_config_variables - // struct, together with the namespace representing those variables, while - // creating the custom module with the factory. - AddLoadHook(module, func(ctx LoadHookContext) { - if m, ok := module.(Bazelable); ok { - m.SetBaseModuleType(moduleType.BaseModuleType) - // Instead of applying all properties, keep the entire conditionalProps struct as - // part of the custom module so dependent modules can create the selects accordingly - m.setNamespacedVariableProps(namespacedVariableProperties{ - moduleType.ConfigNamespace: []interface{}{conditionalProps.Interface()}, - }) - } - }) - } else { - // Regular Soong operation wraps the existing module factory with a - // conditional on Soong config variables by reading the product - // config variables from Make. - AddLoadHook(module, func(ctx LoadHookContext) { - config := ctx.Config().VendorConfig(moduleType.ConfigNamespace) - newProps, err := soongconfig.PropertiesToApply(moduleType, conditionalProps, config) - if err != nil { - ctx.ModuleErrorf("%s", err) - return - } - for _, ps := range newProps { - ctx.AppendProperties(ps) - } - }) - } + // Regular Soong operation wraps the existing module factory with a + // conditional on Soong config variables by reading the product + // config variables from Make. + AddLoadHook(module, func(ctx LoadHookContext) { + tracingConfig := newTracingConfig(ctx.Config().VendorConfig(moduleType.ConfigNamespace)) + newProps, err := soongconfig.PropertiesToApply(moduleType, conditionalProps, tracingConfig) + if err != nil { + ctx.ModuleErrorf("%s", err) + return + } + for _, ps := range newProps { + ctx.AppendProperties(ps) + } + + module.(Module).base().commonProperties.SoongConfigTrace = tracingConfig.getTrace() + }) return module, props } } diff --git a/android/soong_config_modules_test.go b/android/soong_config_modules_test.go index cab3e2d6ba..79bdeb8297 100644 --- a/android/soong_config_modules_test.go +++ b/android/soong_config_modules_test.go @@ -16,6 +16,7 @@ package android import ( "fmt" + "path/filepath" "testing" ) @@ -34,7 +35,8 @@ func soongConfigTestDefaultsModuleFactory() Module { type soongConfigTestModule struct { ModuleBase DefaultableModuleBase - props soongConfigTestModuleProperties + props soongConfigTestModuleProperties + outputPath ModuleOutPath } type soongConfigTestModuleProperties struct { @@ -49,7 +51,9 @@ func soongConfigTestModuleFactory() Module { return m } -func (t soongConfigTestModule) GenerateAndroidBuildActions(ModuleContext) {} +func (t *soongConfigTestModule) GenerateAndroidBuildActions(ctx ModuleContext) { + t.outputPath = PathForModuleOut(ctx, "test") +} var prepareForSoongConfigTestModule = FixtureRegisterWithContext(func(ctx RegistrationContext) { ctx.RegisterModuleType("test_defaults", soongConfigTestDefaultsModuleFactory) @@ -503,3 +507,197 @@ func TestSoongConfigModuleSingletonModule(t *testing.T) { }) } } + +func TestSoongConfigModuleTrace(t *testing.T) { + bp := ` + soong_config_module_type { + name: "acme_test", + module_type: "test", + config_namespace: "acme", + variables: ["board", "feature1", "FEATURE3", "unused_string_var"], + bool_variables: ["feature2", "unused_feature", "always_true"], + value_variables: ["size", "unused_size"], + properties: ["cflags", "srcs", "defaults"], + } + + soong_config_module_type { + name: "acme_test_defaults", + module_type: "test_defaults", + config_namespace: "acme", + variables: ["board", "feature1", "FEATURE3", "unused_string_var"], + bool_variables: ["feature2", "unused_feature", "always_true"], + value_variables: ["size", "unused_size"], + properties: ["cflags", "srcs", "defaults"], + } + + soong_config_string_variable { + name: "board", + values: ["soc_a", "soc_b", "soc_c"], + } + + soong_config_string_variable { + name: "unused_string_var", + values: ["a", "b"], + } + + soong_config_bool_variable { + name: "feature1", + } + + soong_config_bool_variable { + name: "FEATURE3", + } + + test_defaults { + name: "test_defaults", + cflags: ["DEFAULT"], + } + + test { + name: "normal", + defaults: ["test_defaults"], + } + + acme_test { + name: "board_1", + defaults: ["test_defaults"], + soong_config_variables: { + board: { + soc_a: { + cflags: ["-DSOC_A"], + }, + }, + }, + } + + acme_test { + name: "board_2", + defaults: ["test_defaults"], + soong_config_variables: { + board: { + soc_a: { + cflags: ["-DSOC_A"], + }, + }, + }, + } + + acme_test { + name: "size", + defaults: ["test_defaults"], + soong_config_variables: { + size: { + cflags: ["-DSIZE=%s"], + }, + }, + } + + acme_test { + name: "board_and_size", + defaults: ["test_defaults"], + soong_config_variables: { + board: { + soc_a: { + cflags: ["-DSOC_A"], + }, + }, + size: { + cflags: ["-DSIZE=%s"], + }, + }, + } + + acme_test_defaults { + name: "board_defaults", + soong_config_variables: { + board: { + soc_a: { + cflags: ["-DSOC_A"], + }, + }, + }, + } + + acme_test_defaults { + name: "size_defaults", + soong_config_variables: { + size: { + cflags: ["-DSIZE=%s"], + }, + }, + } + + test { + name: "board_and_size_with_defaults", + defaults: ["board_defaults", "size_defaults"], + } + ` + + fixtureForVendorVars := func(vars map[string]map[string]string) FixturePreparer { + return FixtureModifyProductVariables(func(variables FixtureProductVariables) { + variables.VendorVars = vars + }) + } + + preparer := fixtureForVendorVars(map[string]map[string]string{ + "acme": { + "board": "soc_a", + "size": "42", + "feature1": "true", + "feature2": "false", + // FEATURE3 unset + "unused_feature": "true", // unused + "unused_size": "1", // unused + "unused_string_var": "a", // unused + "always_true": "true", + }, + }) + + t.Run("soong config trace hash", func(t *testing.T) { + result := GroupFixturePreparers( + preparer, + PrepareForTestWithDefaults, + PrepareForTestWithSoongConfigModuleBuildComponents, + prepareForSoongConfigTestModule, + FixtureRegisterWithContext(func(ctx RegistrationContext) { + ctx.FinalDepsMutators(registerSoongConfigTraceMutator) + }), + FixtureWithRootAndroidBp(bp), + ).RunTest(t) + + // Hashes of modules not using soong config should be empty + normal := result.ModuleForTests("normal", "").Module().(*soongConfigTestModule) + AssertDeepEquals(t, "normal hash", normal.base().commonProperties.SoongConfigTraceHash, "") + AssertDeepEquals(t, "normal hash out", normal.outputPath.RelativeToTop().String(), "out/soong/.intermediates/normal/test") + + board1 := result.ModuleForTests("board_1", "").Module().(*soongConfigTestModule) + board2 := result.ModuleForTests("board_2", "").Module().(*soongConfigTestModule) + size := result.ModuleForTests("size", "").Module().(*soongConfigTestModule) + + // Trace mutator sets soong config trace hash correctly + board1Hash := board1.base().commonProperties.SoongConfigTrace.hash() + board1Output := board1.outputPath.RelativeToTop().String() + AssertDeepEquals(t, "board hash calc", board1Hash, board1.base().commonProperties.SoongConfigTraceHash) + AssertDeepEquals(t, "board hash path", board1Output, filepath.Join("out/soong/.intermediates/board_1", board1Hash, "test")) + + sizeHash := size.base().commonProperties.SoongConfigTrace.hash() + sizeOutput := size.outputPath.RelativeToTop().String() + AssertDeepEquals(t, "size hash calc", sizeHash, size.base().commonProperties.SoongConfigTraceHash) + AssertDeepEquals(t, "size hash path", sizeOutput, filepath.Join("out/soong/.intermediates/size", sizeHash, "test")) + + // Trace should be identical for modules using the same set of variables + AssertDeepEquals(t, "board trace", board1.base().commonProperties.SoongConfigTrace, board2.base().commonProperties.SoongConfigTrace) + AssertDeepEquals(t, "board hash", board1.base().commonProperties.SoongConfigTraceHash, board2.base().commonProperties.SoongConfigTraceHash) + + // Trace hash should be different for different sets of soong variables + AssertBoolEquals(t, "board hash not equal to size hash", board1.base().commonProperties.SoongConfigTraceHash == size.commonProperties.SoongConfigTraceHash, false) + + boardSize := result.ModuleForTests("board_and_size", "").Module().(*soongConfigTestModule) + boardSizeDefaults := result.ModuleForTests("board_and_size_with_defaults", "").Module() + + // Trace should propagate + AssertDeepEquals(t, "board_size hash calc", boardSize.base().commonProperties.SoongConfigTrace.hash(), boardSize.base().commonProperties.SoongConfigTraceHash) + AssertDeepEquals(t, "board_size trace", boardSize.base().commonProperties.SoongConfigTrace, boardSizeDefaults.base().commonProperties.SoongConfigTrace) + AssertDeepEquals(t, "board_size hash", boardSize.base().commonProperties.SoongConfigTraceHash, boardSizeDefaults.base().commonProperties.SoongConfigTraceHash) + }) +} diff --git a/android/test_asserts.go b/android/test_asserts.go index 4143f150dd..c33ade5a2b 100644 --- a/android/test_asserts.go +++ b/android/test_asserts.go @@ -17,6 +17,7 @@ package android import ( "fmt" "reflect" + "regexp" "strings" "testing" ) @@ -137,6 +138,20 @@ func AssertStringContainsEquals(t *testing.T, message string, s string, substrin } } +// AssertStringMatches checks if the string matches the given regular expression. If it does not match, +// then an error is reported with the supplied message including a reason for why it failed. +func AssertStringMatches(t *testing.T, message, s, expectedRex string) { + t.Helper() + ok, err := regexp.MatchString(expectedRex, s) + if err != nil { + t.Fatalf("regexp failure trying to match %s against `%s` expression: %s", s, expectedRex, err) + return + } + if !ok { + t.Errorf("%s: %s does not match regular expression %s", message, s, expectedRex) + } +} + // AssertStringListContains checks if the list of strings contains the expected string. If it does // not then it reports an error prefixed with the supplied message and including a reason for why it // failed. @@ -185,6 +200,22 @@ func AssertArrayString(t *testing.T, message string, expected, actual []string) } } +// Asserts that each of the Paths in actual end with the corresponding string +// from expected. Useful to test that output paths contain expected items without +// hard-coding where intermediate files might be located. +func AssertPathsEndWith(t *testing.T, message string, expected []string, actual []Path) { + t.Helper() + if len(expected) != len(actual) { + t.Errorf("%s (length): expected %d, actual %d", message, len(expected), len(actual)) + return + } + for i := range expected { + if !strings.HasSuffix(actual[i].String(), expected[i]) { + t.Errorf("%s (item %d): expected '%s', actual '%s'", message, i, expected[i], actual[i].String()) + } + } +} + // AssertDeepEquals checks if the expected and actual values are equal using reflect.DeepEqual and // if they are not then it reports an error prefixed with the supplied message and including a // reason for why it failed. diff --git a/android/test_config.go b/android/test_config.go index 28d9ec4038..9e1ac70dfc 100644 --- a/android/test_config.go +++ b/android/test_config.go @@ -35,7 +35,7 @@ func TestConfig(buildDir string, env map[string]string, bp string, fs map[string envCopy["PATH"] = os.Getenv("PATH") config := &config{ - productVariables: productVariables{ + productVariables: ProductVariables{ DeviceName: stringPtr("test_device"), DeviceProduct: stringPtr("test_product"), Platform_sdk_version: intPtr(30), @@ -61,11 +61,7 @@ func TestConfig(buildDir string, env map[string]string, bp string, fs map[string // passed to PathForSource or PathForModuleSrc. TestAllowNonExistentPaths: true, - BazelContext: noopBazelContext{}, - BuildMode: BazelProdMode, - mixedBuildDisabledModules: make(map[string]struct{}), - mixedBuildEnabledModules: make(map[string]struct{}), - bazelForceEnabledModules: make(map[string]struct{}), + BuildMode: AnalysisNoBazel, } config.deviceConfig = &deviceConfig{ config: config, diff --git a/android/test_suites.go b/android/test_suites.go index b570b2383f..adcc15a6e9 100644 --- a/android/test_suites.go +++ b/android/test_suites.go @@ -15,7 +15,7 @@ package android func init() { - RegisterSingletonType("testsuites", testSuiteFilesFactory) + RegisterParallelSingletonType("testsuites", testSuiteFilesFactory) } func testSuiteFilesFactory() Singleton { @@ -24,6 +24,7 @@ func testSuiteFilesFactory() Singleton { type testSuiteFiles struct { robolectric WritablePath + ravenwood WritablePath } type TestSuiteModule interface { @@ -47,12 +48,15 @@ func (t *testSuiteFiles) GenerateBuildActions(ctx SingletonContext) { }) t.robolectric = robolectricTestSuite(ctx, files["robolectric-tests"]) - ctx.Phony("robolectric-tests", t.robolectric) + + t.ravenwood = ravenwoodTestSuite(ctx, files["ravenwood-tests"]) + ctx.Phony("ravenwood-tests", t.ravenwood) } func (t *testSuiteFiles) MakeVars(ctx MakeVarsContext) { ctx.DistForGoal("robolectric-tests", t.robolectric) + ctx.DistForGoal("ravenwood-tests", t.ravenwood) } func robolectricTestSuite(ctx SingletonContext, files map[string]InstallPaths) WritablePath { @@ -60,7 +64,7 @@ func robolectricTestSuite(ctx SingletonContext, files map[string]InstallPaths) W for _, module := range SortedKeys(files) { installedPaths = append(installedPaths, files[module]...) } - testCasesDir := pathForInstall(ctx, ctx.Config().BuildOS, X86, "testcases", false) + testCasesDir := pathForInstall(ctx, ctx.Config().BuildOS, X86, "testcases") outputFile := PathForOutput(ctx, "packaging", "robolectric-tests.zip") rule := NewRuleBuilder(pctx, ctx) @@ -68,8 +72,29 @@ func robolectricTestSuite(ctx SingletonContext, files map[string]InstallPaths) W FlagWithOutput("-o ", outputFile). FlagWithArg("-P ", "host/testcases"). FlagWithArg("-C ", testCasesDir.String()). - FlagWithRspFileInputList("-r ", outputFile.ReplaceExtension(ctx, "rsp"), installedPaths.Paths()) + FlagWithRspFileInputList("-r ", outputFile.ReplaceExtension(ctx, "rsp"), installedPaths.Paths()). + Flag("-sha256") rule.Build("robolectric_tests_zip", "robolectric-tests.zip") return outputFile } + +func ravenwoodTestSuite(ctx SingletonContext, files map[string]InstallPaths) WritablePath { + var installedPaths InstallPaths + for _, module := range SortedKeys(files) { + installedPaths = append(installedPaths, files[module]...) + } + testCasesDir := pathForInstall(ctx, ctx.Config().BuildOS, X86, "testcases") + + outputFile := PathForOutput(ctx, "packaging", "ravenwood-tests.zip") + rule := NewRuleBuilder(pctx, ctx) + rule.Command().BuiltTool("soong_zip"). + FlagWithOutput("-o ", outputFile). + FlagWithArg("-P ", "host/testcases"). + FlagWithArg("-C ", testCasesDir.String()). + FlagWithRspFileInputList("-r ", outputFile.ReplaceExtension(ctx, "rsp"), installedPaths.Paths()). + Flag("-sha256") + rule.Build("ravenwood_tests_zip", "ravenwood-tests.zip") + + return outputFile +} diff --git a/android/testing.go b/android/testing.go index 2a9c6584e0..fa4dffd9b5 100644 --- a/android/testing.go +++ b/android/testing.go @@ -186,12 +186,12 @@ type TestContext struct { bp2buildPreArch, bp2buildMutators []RegisterMutatorFunc NameResolver *NameResolver - // The list of pre-singletons and singletons registered for the test. - preSingletons, singletons sortableComponents + // The list of singletons registered for the test. + singletons sortableComponents - // The order in which the pre-singletons, mutators and singletons will be run in this test + // The order in which the mutators and singletons will be run in this test // context; for debugging. - preSingletonOrder, mutatorOrder, singletonOrder []string + mutatorOrder, singletonOrder []string } func (ctx *TestContext) PreArchMutators(f RegisterMutatorFunc) { @@ -219,10 +219,6 @@ func (ctx *TestContext) FinalDepsMutators(f RegisterMutatorFunc) { ctx.finalDeps = append(ctx.finalDeps, f) } -func (ctx *TestContext) RegisterBp2BuildConfig(config Bp2BuildConversionAllowlist) { - ctx.config.Bp2buildPackageConfig = config -} - // PreArchBp2BuildMutators adds mutators to be register for converting Android Blueprint modules // into Bazel BUILD targets that should run prior to deps and conversion. func (ctx *TestContext) PreArchBp2BuildMutators(f RegisterMutatorFunc) { @@ -397,9 +393,6 @@ type registrationSorter struct { // Used to ensure that this is only created once. once sync.Once - // The order of pre-singletons - preSingletonOrder registeredComponentOrder - // The order of mutators mutatorOrder registeredComponentOrder @@ -412,9 +405,6 @@ type registrationSorter struct { // Only the first call has any effect. func (s *registrationSorter) populate() { s.once.Do(func() { - // Create an ordering from the globally registered pre-singletons. - s.preSingletonOrder = registeredComponentOrderFromExistingOrder("pre-singleton", preSingletons) - // Created an ordering from the globally registered mutators. globallyRegisteredMutators := collateGloballyRegisteredMutators() s.mutatorOrder = registeredComponentOrderFromExistingOrder("mutator", globallyRegisteredMutators) @@ -441,11 +431,6 @@ func globallyRegisteredComponentsOrder() *registrationSorter { func (ctx *TestContext) Register() { globalOrder := globallyRegisteredComponentsOrder() - // Ensure that the pre-singletons used in the test are in the same order as they are used at - // runtime. - globalOrder.preSingletonOrder.enforceOrdering(ctx.preSingletons) - ctx.preSingletons.registerAll(ctx.Context) - mutators := collateRegisteredMutators(ctx.preArch, ctx.preDeps, ctx.postDeps, ctx.finalDeps) // Ensure that the mutators used in the test are in the same order as they are used at runtime. globalOrder.mutatorOrder.enforceOrdering(mutators) @@ -456,23 +441,10 @@ func (ctx *TestContext) Register() { ctx.singletons.registerAll(ctx.Context) // Save the sorted components order away to make them easy to access while debugging. - ctx.preSingletonOrder = componentsToNames(preSingletons) ctx.mutatorOrder = componentsToNames(mutators) ctx.singletonOrder = componentsToNames(singletons) } -// RegisterForBazelConversion prepares a test context for bp2build conversion. -func (ctx *TestContext) RegisterForBazelConversion() { - ctx.config.BuildMode = Bp2build - RegisterMutatorsForBazelConversion(ctx.Context, ctx.bp2buildPreArch) -} - -// RegisterForApiBazelConversion prepares a test context for API bp2build conversion. -func (ctx *TestContext) RegisterForApiBazelConversion() { - ctx.config.BuildMode = ApiBp2build - RegisterMutatorsForApiBazelConversion(ctx.Context, ctx.bp2buildPreArch) -} - func (ctx *TestContext) ParseFileList(rootDir string, filePaths []string) (deps []string, errs []error) { // This function adapts the old style ParseFileList calls that are spread throughout the tests // to the new style that takes a config. @@ -495,12 +467,18 @@ func (ctx *TestContext) RegisterSingletonModuleType(name string, factory Singlet ctx.RegisterModuleType(name, m) } +func (ctx *TestContext) RegisterParallelSingletonModuleType(name string, factory SingletonModuleFactory) { + s, m := SingletonModuleFactoryAdaptor(name, factory) + ctx.RegisterParallelSingletonType(name, s) + ctx.RegisterModuleType(name, m) +} + func (ctx *TestContext) RegisterSingletonType(name string, factory SingletonFactory) { - ctx.singletons = append(ctx.singletons, newSingleton(name, factory)) + ctx.singletons = append(ctx.singletons, newSingleton(name, factory, false)) } -func (ctx *TestContext) RegisterPreSingletonType(name string, factory SingletonFactory) { - ctx.preSingletons = append(ctx.preSingletons, newPreSingleton(name, factory)) +func (ctx *TestContext) RegisterParallelSingletonType(name string, factory SingletonFactory) { + ctx.singletons = append(ctx.singletons, newSingleton(name, factory, true)) } // ModuleVariantForTests selects a specific variant of the module with the given @@ -1305,3 +1283,10 @@ func StringRelativeToTop(config Config, command string) string { func StringsRelativeToTop(config Config, command []string) []string { return normalizeStringArrayRelativeToTop(config, command) } + +func EnsureListContainsSuffix(t *testing.T, result []string, expected string) { + t.Helper() + if !SuffixInList(result, expected) { + t.Errorf("%q is not found in %v", expected, result) + } +} diff --git a/android/updatable_modules.go b/android/updatable_modules.go index 6d0eeb716f..1548170f91 100644 --- a/android/updatable_modules.go +++ b/android/updatable_modules.go @@ -33,4 +33,4 @@ package android // * AOSP - xx9990000 // * x-mainline-prod - xx9990000 // * master - 990090000 -const DefaultUpdatableModuleVersion = "340090000" +const DefaultUpdatableModuleVersion = "990090000" diff --git a/android/util.go b/android/util.go index 08a3521a52..ae1c65756c 100644 --- a/android/util.go +++ b/android/util.go @@ -25,12 +25,12 @@ import ( ) // CopyOf returns a new slice that has the same contents as s. -func CopyOf(s []string) []string { +func CopyOf[T any](s []T) []T { // If the input is nil, return nil and not an empty list if s == nil { return s } - return append([]string{}, s...) + return append([]T{}, s...) } // Concat returns a new slice concatenated from the two input slices. It does not change the input @@ -42,6 +42,16 @@ func Concat[T any](s1, s2 []T) []T { return res } +// JoinPathsWithPrefix converts the paths to strings, prefixes them +// with prefix and then joins them separated by " ". +func JoinPathsWithPrefix(paths []Path, prefix string) string { + strs := make([]string, len(paths)) + for i := range paths { + strs[i] = paths[i].String() + } + return JoinWithPrefixAndSeparator(strs, prefix, " ") +} + // JoinWithPrefix prepends the prefix to each string in the list and // returns them joined together with " " as separator. func JoinWithPrefix(strs []string, prefix string) string { @@ -51,17 +61,39 @@ func JoinWithPrefix(strs []string, prefix string) string { // JoinWithPrefixAndSeparator prepends the prefix to each string in the list and // returns them joined together with the given separator. func JoinWithPrefixAndSeparator(strs []string, prefix string, sep string) string { + return JoinWithPrefixSuffixAndSeparator(strs, prefix, "", sep) +} + +// JoinWithSuffixAndSeparator appends the suffix to each string in the list and +// returns them joined together with the given separator. +func JoinWithSuffixAndSeparator(strs []string, suffix string, sep string) string { + return JoinWithPrefixSuffixAndSeparator(strs, "", suffix, sep) +} + +// JoinWithPrefixSuffixAndSeparator appends the prefix/suffix to each string in the list and +// returns them joined together with the given separator. +func JoinWithPrefixSuffixAndSeparator(strs []string, prefix, suffix, sep string) string { if len(strs) == 0 { return "" } + // Pre-calculate the length of the result + length := 0 + for _, s := range strs { + length += len(s) + } + length += (len(prefix)+len(suffix))*len(strs) + len(sep)*(len(strs)-1) + var buf strings.Builder + buf.Grow(length) buf.WriteString(prefix) buf.WriteString(strs[0]) + buf.WriteString(suffix) for i := 1; i < len(strs); i++ { buf.WriteString(sep) buf.WriteString(prefix) buf.WriteString(strs[i]) + buf.WriteString(suffix) } return buf.String() } @@ -127,19 +159,17 @@ func SortedUniqueStringValues(m interface{}) []string { } // IndexList returns the index of the first occurrence of the given string in the list or -1 -func IndexList(s string, list []string) int { +func IndexList[T comparable](t T, list []T) int { for i, l := range list { - if l == s { + if l == t { return i } } - return -1 } -// InList checks if the string belongs to the list -func InList(s string, list []string) bool { - return IndexList(s, list) != -1 +func InList[T comparable](t T, list []T) bool { + return IndexList(t, list) != -1 } func setFromList[T comparable](l []T) map[T]bool { @@ -278,44 +308,87 @@ func RemoveFromList(s string, list []string) (bool, []string) { } // FirstUniqueStrings returns all unique elements of a slice of strings, keeping the first copy of -// each. It modifies the slice contents in place, and returns a subslice of the original slice. +// each. It does not modify the input slice. func FirstUniqueStrings(list []string) []string { - // Do not moodify the input in-place, operate on a copy instead. - list = CopyOf(list) + return firstUnique(list) +} + +// firstUnique returns all unique elements of a slice, keeping the first copy of each. It +// does not modify the input slice. +func firstUnique[T comparable](slice []T) []T { + // Do not modify the input in-place, operate on a copy instead. + slice = CopyOf(slice) + return firstUniqueInPlace(slice) +} + +// firstUniqueInPlace returns all unique elements of a slice, keeping the first copy of +// each. It modifies the slice contents in place, and returns a subslice of the original +// slice. +func firstUniqueInPlace[T comparable](slice []T) []T { // 128 was chosen based on BenchmarkFirstUniqueStrings results. - if len(list) > 128 { - return firstUniqueStringsMap(list) + if len(slice) > 128 { + return firstUniqueMap(slice) } - return firstUniqueStringsList(list) + return firstUniqueList(slice) } -func firstUniqueStringsList(list []string) []string { - k := 0 +// firstUniqueList is an implementation of firstUnique using an O(N^2) list comparison to look for +// duplicates. +func firstUniqueList[T any](in []T) []T { + writeIndex := 0 outer: - for i := 0; i < len(list); i++ { - for j := 0; j < k; j++ { - if list[i] == list[j] { + for readIndex := 0; readIndex < len(in); readIndex++ { + for compareIndex := 0; compareIndex < writeIndex; compareIndex++ { + if interface{}(in[readIndex]) == interface{}(in[compareIndex]) { + // The value at readIndex already exists somewhere in the output region + // of the slice before writeIndex, skip it. continue outer } } - list[k] = list[i] - k++ + if readIndex != writeIndex { + in[writeIndex] = in[readIndex] + } + writeIndex++ } - return list[:k] + return in[0:writeIndex] } -func firstUniqueStringsMap(list []string) []string { - k := 0 - seen := make(map[string]bool, len(list)) - for i := 0; i < len(list); i++ { - if seen[list[i]] { +// firstUniqueMap is an implementation of firstUnique using an O(N) hash set lookup to look for +// duplicates. +func firstUniqueMap[T comparable](in []T) []T { + writeIndex := 0 + seen := make(map[T]bool, len(in)) + for readIndex := 0; readIndex < len(in); readIndex++ { + if _, exists := seen[in[readIndex]]; exists { continue } - seen[list[i]] = true - list[k] = list[i] - k++ + seen[in[readIndex]] = true + if readIndex != writeIndex { + in[writeIndex] = in[readIndex] + } + writeIndex++ + } + return in[0:writeIndex] +} + +// ReverseSliceInPlace reverses the elements of a slice in place and returns it. +func ReverseSliceInPlace[T any](in []T) []T { + for i, j := 0, len(in)-1; i < j; i, j = i+1, j-1 { + in[i], in[j] = in[j], in[i] } - return list[:k] + return in +} + +// ReverseSlice returns a copy of a slice in reverse order. +func ReverseSlice[T any](in []T) []T { + if in == nil { + return in + } + out := make([]T, len(in)) + for i := 0; i < len(in); i++ { + out[i] = in[len(in)-1-i] + } + return out } // LastUniqueStrings returns all unique elements of a slice of strings, keeping the last copy of @@ -518,3 +591,9 @@ func CheckDuplicate(values []string) (duplicate string, found bool) { } return "", false } + +func AddToStringSet(set map[string]bool, items []string) { + for _, item := range items { + set[item] = true + } +} diff --git a/android/util_test.go b/android/util_test.go index a2ef589588..699135bfce 100644 --- a/android/util_test.go +++ b/android/util_test.go @@ -20,6 +20,7 @@ import ( "strconv" "strings" "testing" + "unsafe" ) var firstUniqueStringsTestCases = []struct { @@ -74,10 +75,10 @@ func TestFirstUniqueStrings(t *testing.T) { for _, testCase := range firstUniqueStringsTestCases { t.Run("list", func(t *testing.T) { - f(t, firstUniqueStringsList, testCase.in, testCase.out) + f(t, firstUniqueList[string], testCase.in, testCase.out) }) t.Run("map", func(t *testing.T) { - f(t, firstUniqueStringsMap, testCase.in, testCase.out) + f(t, firstUniqueMap[string], testCase.in, testCase.out) }) } } @@ -385,7 +386,7 @@ func TestCopyOfEmptyAndNil(t *testing.T) { emptyList := []string{} copyOfEmptyList := CopyOf(emptyList) AssertBoolEquals(t, "Copy of an empty list should be an empty list and not nil", true, copyOfEmptyList != nil) - copyOfNilList := CopyOf(nil) + copyOfNilList := CopyOf([]string(nil)) AssertBoolEquals(t, "Copy of a nil list should be a nil list and not an empty list", true, copyOfNilList == nil) } @@ -604,11 +605,11 @@ func BenchmarkFirstUniqueStrings(b *testing.B) { }{ { name: "list", - f: firstUniqueStringsList, + f: firstUniqueList[string], }, { name: "map", - f: firstUniqueStringsMap, + f: firstUniqueMap[string], }, { name: "optimal", @@ -754,3 +755,65 @@ func TestSortedUniqueStringValues(t *testing.T) { }) } } + +var reverseTestCases = []struct { + name string + in []string + expected []string +}{ + { + name: "nil", + in: nil, + expected: nil, + }, + { + name: "empty", + in: []string{}, + expected: []string{}, + }, + { + name: "one", + in: []string{"one"}, + expected: []string{"one"}, + }, + { + name: "even", + in: []string{"one", "two"}, + expected: []string{"two", "one"}, + }, + { + name: "odd", + in: []string{"one", "two", "three"}, + expected: []string{"three", "two", "one"}, + }, +} + +func TestReverseSliceInPlace(t *testing.T) { + for _, testCase := range reverseTestCases { + t.Run(testCase.name, func(t *testing.T) { + slice := CopyOf(testCase.in) + slice2 := slice + ReverseSliceInPlace(slice) + if !reflect.DeepEqual(slice, testCase.expected) { + t.Errorf("expected %#v, got %#v", testCase.expected, slice) + } + if unsafe.SliceData(slice) != unsafe.SliceData(slice2) { + t.Errorf("expected slices to share backing array") + } + }) + } +} + +func TestReverseSlice(t *testing.T) { + for _, testCase := range reverseTestCases { + t.Run(testCase.name, func(t *testing.T) { + slice := ReverseSlice(testCase.in) + if !reflect.DeepEqual(slice, testCase.expected) { + t.Errorf("expected %#v, got %#v", testCase.expected, slice) + } + if cap(slice) > 0 && unsafe.SliceData(testCase.in) == unsafe.SliceData(slice) { + t.Errorf("expected slices to have different backing arrays") + } + }) + } +} diff --git a/android/variable.go b/android/variable.go index 8a64b83fd4..042c48bb85 100644 --- a/android/variable.go +++ b/android/variable.go @@ -20,7 +20,6 @@ import ( "runtime" "strings" - "android/soong/android/soongconfig" "android/soong/bazel" "github.com/google/blueprint/proptools" @@ -116,6 +115,11 @@ type variableProperties struct { Cflags []string } + Build_from_text_stub struct { + Static_libs []string + Exclude_static_libs []string + } + // debuggable is true for eng and userdebug builds, and can be used to turn on additional // debugging features that don't significantly impact runtime behavior. userdebug builds // are used for dogfooding and performance testing, and should be as similar to user builds @@ -140,6 +144,7 @@ type variableProperties struct { Srcs []string Exclude_srcs []string + Cmd *string } // eng is true for -eng builds, and can be used to turn on additional heavyweight debugging @@ -159,10 +164,6 @@ type variableProperties struct { } } - Pdk struct { - Enabled *bool `android:"arch_variant"` - } `android:"arch_variant"` - Uml struct { Cppflags []string } @@ -178,21 +179,24 @@ type variableProperties struct { Whole_static_libs []string `android:"arch_variant"` } `android:"arch_variant"` - Flatten_apex struct { - Enabled *bool - } - Native_coverage struct { Src *string `android:"arch_variant"` Srcs []string `android:"arch_variant"` Exclude_srcs []string `android:"arch_variant"` } `android:"arch_variant"` + + // release_aidl_use_unfrozen is "true" when a device can + // use the unfrozen versions of AIDL interfaces. + Release_aidl_use_unfrozen struct { + Cflags []string + Cmd *string + } } `android:"arch_variant"` } var defaultProductVariables interface{} = variableProperties{} -type productVariables struct { +type ProductVariables struct { // Suffix to add to generated Makefiles Make_suffix *string `json:",omitempty"` @@ -227,6 +231,9 @@ type productVariables struct { DeviceCurrentApiLevelForVendorModules *string `json:",omitempty"` DeviceSystemSdkVersions []string `json:",omitempty"` DeviceMaxPageSizeSupported *string `json:",omitempty"` + DeviceNoBionicPageSizeMacro *bool `json:",omitempty"` + + VendorApiLevel *string `json:",omitempty"` RecoverySnapshotVersion *string `json:",omitempty"` @@ -295,6 +302,7 @@ type productVariables struct { Uml *bool `json:",omitempty"` Arc *bool `json:",omitempty"` MinimizeJavaDebugInfo *bool `json:",omitempty"` + Build_from_text_stub *bool `json:",omitempty"` Check_elf_files *bool `json:",omitempty"` @@ -317,6 +325,7 @@ type productVariables struct { MemtagHeapSyncIncludePaths []string `json:",omitempty"` HWASanIncludePaths []string `json:",omitempty"` + HWASanExcludePaths []string `json:",omitempty"` VendorPath *string `json:",omitempty"` OdmPath *string `json:",omitempty"` @@ -376,17 +385,11 @@ type productVariables struct { MultitreeUpdateMeta bool `json:",omitempty"` - BoardVendorSepolicyDirs []string `json:",omitempty"` - BoardOdmSepolicyDirs []string `json:",omitempty"` - BoardReqdMaskPolicy []string `json:",omitempty"` - BoardPlatVendorPolicy []string `json:",omitempty"` - BoardSystemExtPublicPrebuiltDirs []string `json:",omitempty"` - BoardSystemExtPrivatePrebuiltDirs []string `json:",omitempty"` - BoardProductPublicPrebuiltDirs []string `json:",omitempty"` - BoardProductPrivatePrebuiltDirs []string `json:",omitempty"` - SystemExtPublicSepolicyDirs []string `json:",omitempty"` - SystemExtPrivateSepolicyDirs []string `json:",omitempty"` - BoardSepolicyM4Defs []string `json:",omitempty"` + BoardVendorSepolicyDirs []string `json:",omitempty"` + BoardOdmSepolicyDirs []string `json:",omitempty"` + SystemExtPublicSepolicyDirs []string `json:",omitempty"` + SystemExtPrivateSepolicyDirs []string `json:",omitempty"` + BoardSepolicyM4Defs []string `json:",omitempty"` BoardSepolicyVers *string `json:",omitempty"` PlatformSepolicyVersion *string `json:",omitempty"` @@ -402,7 +405,6 @@ type productVariables struct { Ndk_abis *bool `json:",omitempty"` TrimmedApex *bool `json:",omitempty"` - Flatten_apex *bool `json:",omitempty"` ForceApexSymlinkOptimization *bool `json:",omitempty"` CompressedApex *bool `json:",omitempty"` Aml_abis *bool `json:",omitempty"` @@ -411,9 +413,10 @@ type productVariables struct { WithDexpreopt bool `json:",omitempty"` - ManifestPackageNameOverrides []string `json:",omitempty"` - CertificateOverrides []string `json:",omitempty"` - PackageNameOverrides []string `json:",omitempty"` + ManifestPackageNameOverrides []string `json:",omitempty"` + CertificateOverrides []string `json:",omitempty"` + PackageNameOverrides []string `json:",omitempty"` + ConfiguredJarLocationOverrides []string `json:",omitempty"` ApexGlobalMinSdkVersionOverride *string `json:",omitempty"` @@ -427,19 +430,13 @@ type productVariables struct { ProductPublicSepolicyDirs []string `json:",omitempty"` ProductPrivateSepolicyDirs []string `json:",omitempty"` - ProductVndkVersion *string `json:",omitempty"` - TargetFSConfigGen []string `json:",omitempty"` - MissingUsesLibraries []string `json:",omitempty"` - EnforceProductPartitionInterface *bool `json:",omitempty"` EnforceInterPartitionJavaSdkLibrary *bool `json:",omitempty"` InterPartitionJavaLibraryAllowList []string `json:",omitempty"` - InstallExtraFlattenedApexes *bool `json:",omitempty"` - BoardUsesRecoveryAsBoot *bool `json:",omitempty"` BoardKernelBinaries []string `json:",omitempty"` @@ -451,15 +448,19 @@ type productVariables struct { ShippingApiLevel *string `json:",omitempty"` - BuildBrokenClangAsFlags bool `json:",omitempty"` - BuildBrokenClangCFlags bool `json:",omitempty"` - BuildBrokenClangProperty bool `json:",omitempty"` - BuildBrokenDepfile *bool `json:",omitempty"` - BuildBrokenEnforceSyspropOwner bool `json:",omitempty"` - BuildBrokenTrebleSyspropNeverallow bool `json:",omitempty"` - BuildBrokenUsesSoongPython2Modules bool `json:",omitempty"` - BuildBrokenVendorPropertyNamespace bool `json:",omitempty"` - BuildBrokenInputDirModules []string `json:",omitempty"` + BuildBrokenPluginValidation []string `json:",omitempty"` + BuildBrokenClangAsFlags bool `json:",omitempty"` + BuildBrokenClangCFlags bool `json:",omitempty"` + BuildBrokenClangProperty bool `json:",omitempty"` + GenruleSandboxing *bool `json:",omitempty"` + BuildBrokenEnforceSyspropOwner bool `json:",omitempty"` + BuildBrokenTrebleSyspropNeverallow bool `json:",omitempty"` + BuildBrokenUsesSoongPython2Modules bool `json:",omitempty"` + BuildBrokenVendorPropertyNamespace bool `json:",omitempty"` + BuildBrokenIncorrectPartitionImages bool `json:",omitempty"` + BuildBrokenInputDirModules []string `json:",omitempty"` + + BuildWarningBadOptionalUsesLibsAllowlist []string `json:",omitempty"` BuildDebugfsRestrictionsEnabled bool `json:",omitempty"` @@ -467,7 +468,7 @@ type productVariables struct { SelinuxIgnoreNeverallows bool `json:",omitempty"` - SepolicySplit bool `json:",omitempty"` + Release_aidl_use_unfrozen *bool `json:",omitempty"` SepolicyFreezeTestExtraDirs []string `json:",omitempty"` SepolicyFreezeTestExtraPrebuiltDirs []string `json:",omitempty"` @@ -484,6 +485,86 @@ type productVariables struct { ProductManufacturer string `json:",omitempty"` ProductBrand string `json:",omitempty"` BuildVersionTags []string `json:",omitempty"` + + ReleaseVersion string `json:",omitempty"` + ReleaseAconfigValueSets []string `json:",omitempty"` + + ReleaseAconfigFlagDefaultPermission string `json:",omitempty"` + + ReleaseDefaultModuleBuildFromSource *bool `json:",omitempty"` + + KeepVndk *bool `json:",omitempty"` + + CheckVendorSeappViolations *bool `json:",omitempty"` + + // PartitionVarsForBazelMigrationOnlyDoNotUse are extra variables that are used to define the + // partition images. They should not be read from soong modules. + PartitionVarsForBazelMigrationOnlyDoNotUse PartitionVariables `json:",omitempty"` + + NextReleaseHideFlaggedApi *bool `json:",omitempty"` + + Release_expose_flagged_api *bool `json:",omitempty"` + + BuildFlags map[string]string `json:",omitempty"` + + BuildFromSourceStub *bool `json:",omitempty"` +} + +type PartitionQualifiedVariablesType struct { + BuildingImage bool `json:",omitempty"` + BoardErofsCompressor string `json:",omitempty"` + BoardErofsCompressHints string `json:",omitempty"` + BoardErofsPclusterSize string `json:",omitempty"` + BoardExtfsInodeCount string `json:",omitempty"` + BoardExtfsRsvPct string `json:",omitempty"` + BoardF2fsSloadCompressFlags string `json:",omitempty"` + BoardFileSystemCompress string `json:",omitempty"` + BoardFileSystemType string `json:",omitempty"` + BoardJournalSize string `json:",omitempty"` + BoardPartitionReservedSize string `json:",omitempty"` + BoardPartitionSize string `json:",omitempty"` + BoardSquashfsBlockSize string `json:",omitempty"` + BoardSquashfsCompressor string `json:",omitempty"` + BoardSquashfsCompressorOpt string `json:",omitempty"` + BoardSquashfsDisable4kAlign string `json:",omitempty"` + ProductBaseFsPath string `json:",omitempty"` + ProductHeadroom string `json:",omitempty"` + ProductVerityPartition string `json:",omitempty"` + + BoardAvbAddHashtreeFooterArgs string `json:",omitempty"` + BoardAvbKeyPath string `json:",omitempty"` + BoardAvbAlgorithm string `json:",omitempty"` + BoardAvbRollbackIndex string `json:",omitempty"` + BoardAvbRollbackIndexLocation string `json:",omitempty"` +} + +type PartitionVariables struct { + ProductDirectory string `json:",omitempty"` + PartitionQualifiedVariables map[string]PartitionQualifiedVariablesType + TargetUserimagesUseExt2 bool `json:",omitempty"` + TargetUserimagesUseExt3 bool `json:",omitempty"` + TargetUserimagesUseExt4 bool `json:",omitempty"` + + TargetUserimagesSparseExtDisabled bool `json:",omitempty"` + TargetUserimagesSparseErofsDisabled bool `json:",omitempty"` + TargetUserimagesSparseSquashfsDisabled bool `json:",omitempty"` + TargetUserimagesSparseF2fsDisabled bool `json:",omitempty"` + + BoardErofsCompressor string `json:",omitempty"` + BoardErofsCompressorHints string `json:",omitempty"` + BoardErofsPclusterSize string `json:",omitempty"` + BoardErofsShareDupBlocks string `json:",omitempty"` + BoardErofsUseLegacyCompression string `json:",omitempty"` + BoardExt4ShareDupBlocks string `json:",omitempty"` + BoardFlashLogicalBlockSize string `json:",omitempty"` + BoardFlashEraseBlockSize string `json:",omitempty"` + BoardUsesRecoveryAsBoot bool `json:",omitempty"` + ProductUseDynamicPartitionSize bool `json:",omitempty"` + CopyImagesForTargetFilesZip bool `json:",omitempty"` + + BoardAvbEnable bool `json:",omitempty"` + + ProductPackages []string `json:",omitempty"` } func boolPtr(v bool) *bool { @@ -498,8 +579,8 @@ func stringPtr(v string) *string { return &v } -func (v *productVariables) SetDefaultConfig() { - *v = productVariables{ +func (v *ProductVariables) SetDefaultConfig() { + *v = ProductVariables{ BuildNumberFile: stringPtr("build_number.txt"), Platform_version_name: stringPtr("S"), @@ -511,19 +592,20 @@ func (v *productVariables) SetDefaultConfig() { Platform_version_all_preview_codenames: []string{"S"}, Platform_vndk_version: stringPtr("S"), - HostArch: stringPtr("x86_64"), - HostSecondaryArch: stringPtr("x86"), - DeviceName: stringPtr("generic_arm64"), - DeviceProduct: stringPtr("aosp_arm-eng"), - DeviceArch: stringPtr("arm64"), - DeviceArchVariant: stringPtr("armv8-a"), - DeviceCpuVariant: stringPtr("generic"), - DeviceAbi: []string{"arm64-v8a"}, - DeviceSecondaryArch: stringPtr("arm"), - DeviceSecondaryArchVariant: stringPtr("armv8-a"), - DeviceSecondaryCpuVariant: stringPtr("generic"), - DeviceSecondaryAbi: []string{"armeabi-v7a", "armeabi"}, - DeviceMaxPageSizeSupported: stringPtr("4096"), + HostArch: stringPtr("x86_64"), + HostSecondaryArch: stringPtr("x86"), + DeviceName: stringPtr("generic_arm64"), + DeviceProduct: stringPtr("aosp_arm-eng"), + DeviceArch: stringPtr("arm64"), + DeviceArchVariant: stringPtr("armv8-a"), + DeviceCpuVariant: stringPtr("generic"), + DeviceAbi: []string{"arm64-v8a"}, + DeviceSecondaryArch: stringPtr("arm"), + DeviceSecondaryArchVariant: stringPtr("armv8-a"), + DeviceSecondaryCpuVariant: stringPtr("generic"), + DeviceSecondaryAbi: []string{"armeabi-v7a", "armeabi"}, + DeviceMaxPageSizeSupported: stringPtr("4096"), + DeviceNoBionicPageSizeMacro: boolPtr(false), AAPTConfig: []string{"normal", "large", "xlarge", "hdpi", "xhdpi", "xxhdpi"}, AAPTPreferredConfig: stringPtr("xhdpi"), @@ -536,6 +618,7 @@ func (v *productVariables) SetDefaultConfig() { Malloc_pattern_fill_contents: boolPtr(false), Safestack: boolPtr(false), TrimmedApex: boolPtr(false), + Build_from_text_stub: boolPtr(false), BootJars: ConfiguredJarList{apexes: []string{}, jars: []string{}}, ApexBootJars: ConfiguredJarList{apexes: []string{}, jars: []string{}}, @@ -548,129 +631,115 @@ func (v *productVariables) SetDefaultConfig() { } } +func (this *ProductVariables) GetBuildFlagBool(flag string) bool { + val, ok := this.BuildFlags[flag] + if !ok { + return false + } + return val == "true" +} + // ProductConfigContext requires the access to the Module to get product config properties. type ProductConfigContext interface { Module() Module } -// ProductConfigProperty contains the information for a single property (may be a struct) paired -// with the appropriate ProductConfigVariable. +// ProductConfigOrSoongConfigProperty represents either a soong config variable + its value +// or a product config variable. You can get both a ConfigurationAxis and a SelectKey from it +// for use in bazel attributes. ProductVariableProperties() will return a map from properties -> +// this interface -> property structs for use in bp2build converters +type ProductConfigOrSoongConfigProperty interface { + // Name of the product variable or soong config variable + Name() string + // AlwaysEmit returns true for soong config variables but false for product variables. This + // is intended to indicate if we need to always emit empty lists in the select statements. + AlwaysEmit() bool + // ConfigurationAxis returns the bazel.ConfigurationAxis that represents this variable. The + // configuration axis will change depending on the variable and whether it's arch/os variant + // as well. + ConfigurationAxis() bazel.ConfigurationAxis + // SelectKey returns a string that represents the key of a select branch, however, it is not + // actually the real label written out to the build file. + // this.ConfigurationAxis().SelectKey(this.SelectKey()) will give the actual label. + SelectKey() string +} + +// ProductConfigProperty represents a product config variable, and if it is arch-variant or not. type ProductConfigProperty struct { // The name of the product variable, e.g. "safestack", "malloc_not_svelte", // "board" - Name string + name string - // Namespace of the variable, if this is a soong_config_module_type variable - // e.g. "acme", "ANDROID", "vendor_name" - Namespace string + arch string +} - // Unique configuration to identify this product config property (i.e. a - // primary key), as just using the product variable name is not sufficient. - // - // For product variables, this is the product variable name + optional - // archvariant information. e.g. - // - // product_variables: { - // foo: { - // cflags: ["-Dfoo"], - // }, - // }, - // - // FullConfig would be "foo". - // - // target: { - // android: { - // product_variables: { - // foo: { - // cflags: ["-Dfoo-android"], - // }, - // }, - // }, - // }, - // - // FullConfig would be "foo-android". - // - // For soong config variables, this is the namespace + product variable name - // + value of the variable, if applicable. The value can also be - // conditions_default. - // - // e.g. - // - // soong_config_variables: { - // feature1: { - // conditions_default: { - // cflags: ["-DDEFAULT1"], - // }, - // cflags: ["-DFEATURE1"], - // }, - // } - // - // where feature1 is created in the "acme" namespace, so FullConfig would be - // "acme__feature1" and "acme__feature1__conditions_default". - // - // e.g. - // - // soong_config_variables: { - // board: { - // soc_a: { - // cflags: ["-DSOC_A"], - // }, - // soc_b: { - // cflags: ["-DSOC_B"], - // }, - // soc_c: {}, - // conditions_default: { - // cflags: ["-DSOC_DEFAULT"] - // }, - // }, - // } - // - // where board is created in the "acme" namespace, so FullConfig would be - // "acme__board__soc_a", "acme__board__soc_b", and - // "acme__board__conditions_default" - FullConfig string +func (p ProductConfigProperty) Name() string { + return p.name +} - // keeps track of whether this product variable is nested under an arch variant - OuterAxis bazel.ConfigurationAxis +func (p ProductConfigProperty) AlwaysEmit() bool { + return false } -func (p *ProductConfigProperty) AlwaysEmit() bool { - return p.Namespace != "" +func (p ProductConfigProperty) ConfigurationAxis() bazel.ConfigurationAxis { + return bazel.ProductVariableConfigurationAxis(p.arch != "", p.name+"__"+p.arch) } -func (p *ProductConfigProperty) ConfigurationAxis() bazel.ConfigurationAxis { - if p.Namespace == "" { - return bazel.ProductVariableConfigurationAxis(p.FullConfig, p.OuterAxis) +func (p ProductConfigProperty) SelectKey() string { + if p.arch == "" { + return strings.ToLower(p.name) } else { - // Soong config variables can be uniquely identified by the namespace - // (e.g. acme, android) and the product variable name (e.g. board, size) - return bazel.ProductVariableConfigurationAxis(p.Namespace+"__"+p.Name, bazel.NoConfigAxis) + return strings.ToLower(p.name + "-" + p.arch) } } +// SoongConfigProperty represents a soong config variable, its value if it's a string variable, +// and if it's dependent on the OS or not +type SoongConfigProperty struct { + name string + namespace string + // Can be an empty string for bool/value soong config variables + value string + // If there is a target: field inside a soong config property struct, the os that it selects + // on will be represented here. + os string +} + +func (p SoongConfigProperty) Name() string { + return p.name +} + +func (p SoongConfigProperty) AlwaysEmit() bool { + return true +} + +func (p SoongConfigProperty) ConfigurationAxis() bazel.ConfigurationAxis { + return bazel.ProductVariableConfigurationAxis(false, p.namespace+"__"+p.name+"__"+p.os) +} + // SelectKey returns the literal string that represents this variable in a BUILD // select statement. -func (p *ProductConfigProperty) SelectKey() string { - if p.Namespace == "" { - return strings.ToLower(p.FullConfig) - } - - if p.FullConfig == bazel.ConditionsDefaultConfigKey { +func (p SoongConfigProperty) SelectKey() string { + // p.value being conditions_default can happen with or without a desired os. When not using + // an os, we want to emit literally just //conditions:default in the select statement, but + // when using an os, we want to emit namespace__name__conditions_default__os, so that + // the branch is only taken if the variable is not set, and we're on the desired os. + // ConfigurationAxis#SelectKey will map the conditions_default result of this function to + // //conditions:default. + if p.value == bazel.ConditionsDefaultConfigKey && p.os == "" { return bazel.ConditionsDefaultConfigKey } - value := p.FullConfig - if value == p.Name { - value = "" + parts := []string{p.namespace, p.name} + if p.value != "" && p.value != bazel.ConditionsDefaultSelectKey { + parts = append(parts, p.value) } - - // e.g. acme__feature1, android__board__soc_a - selectKey := strings.ToLower(strings.Join([]string{p.Namespace, p.Name}, "__")) - if value != "" { - selectKey = strings.ToLower(strings.Join([]string{selectKey, value}, "__")) + if p.os != "" { + parts = append(parts, p.os) } - return selectKey + // e.g. acme__feature1, android__board__soc_a, my_namespace__my_variables__my_value__my_os + return strings.ToLower(strings.Join(parts, "__")) } // ProductConfigProperties is a map of maps to group property values according @@ -686,90 +755,57 @@ func (p *ProductConfigProperty) SelectKey() string { // // The value of the map is the interface{} representing the value of the // property, like ["-DDEFINES"] for cflags. -type ProductConfigProperties map[string]map[ProductConfigProperty]interface{} - -// ProductVariableProperties returns a ProductConfigProperties containing only the properties which -// have been set for the given module. -func ProductVariableProperties(ctx ArchVariantContext, module Module) ProductConfigProperties { - moduleBase := module.base() - - productConfigProperties := ProductConfigProperties{} - - if moduleBase.variableProperties != nil { - productVariablesProperty := proptools.FieldNameForProperty("product_variables") - productVariableValues( - productVariablesProperty, - moduleBase.variableProperties, - "", - "", - &productConfigProperties, - bazel.ConfigurationAxis{}, - ) - - for axis, configToProps := range moduleBase.GetArchVariantProperties(ctx, moduleBase.variableProperties) { - for config, props := range configToProps { - // GetArchVariantProperties is creating an instance of the requested type - // and productVariablesValues expects an interface, so no need to cast - productVariableValues( - productVariablesProperty, - props, - "", - config, - &productConfigProperties, - axis) - } - } - } +type ProductConfigProperties map[string]map[ProductConfigOrSoongConfigProperty]interface{} - if m, ok := module.(Bazelable); ok && m.namespacedVariableProps() != nil { - for namespace, namespacedVariableProps := range m.namespacedVariableProps() { - for _, namespacedVariableProp := range namespacedVariableProps { - productVariableValues( - soongconfig.SoongConfigProperty, - namespacedVariableProp, - namespace, - "", - &productConfigProperties, - bazel.NoConfigAxis) - } - } +func (p *ProductConfigProperties) AddProductConfigProperty( + propertyName, productVariableName, arch string, propertyValue interface{}) { + + productConfigProp := ProductConfigProperty{ + name: productVariableName, // e.g. size, feature1, feature2, FEATURE3, board + arch: arch, // e.g. "", x86, arm64 } - return productConfigProperties + p.AddEitherProperty(propertyName, productConfigProp, propertyValue) } -func (p *ProductConfigProperties) AddProductConfigProperty( - propertyName, namespace, productVariableName, config string, property interface{}, outerAxis bazel.ConfigurationAxis) { - if (*p)[propertyName] == nil { - (*p)[propertyName] = make(map[ProductConfigProperty]interface{}) +func (p *ProductConfigProperties) AddSoongConfigProperty( + propertyName, namespace, variableName, value, os string, propertyValue interface{}) { + + soongConfigProp := SoongConfigProperty{ + namespace: namespace, + name: variableName, // e.g. size, feature1, feature2, FEATURE3, board + value: value, + os: os, // e.g. android, linux_x86 } - productConfigProp := ProductConfigProperty{ - Namespace: namespace, // e.g. acme, android - Name: productVariableName, // e.g. size, feature1, feature2, FEATURE3, board - FullConfig: config, // e.g. size, feature1-x86, size__conditions_default - OuterAxis: outerAxis, + p.AddEitherProperty(propertyName, soongConfigProp, propertyValue) +} + +func (p *ProductConfigProperties) AddEitherProperty( + propertyName string, key ProductConfigOrSoongConfigProperty, propertyValue interface{}) { + if (*p)[propertyName] == nil { + (*p)[propertyName] = make(map[ProductConfigOrSoongConfigProperty]interface{}) } - if existing, ok := (*p)[propertyName][productConfigProp]; ok && namespace != "" { + if existing, ok := (*p)[propertyName][key]; ok { switch dst := existing.(type) { case []string: - if src, ok := property.([]string); ok { - dst = append(dst, src...) - (*p)[propertyName][productConfigProp] = dst + src, ok := propertyValue.([]string) + if !ok { + panic("Conflicting types") } + dst = append(dst, src...) + (*p)[propertyName][key] = dst default: - panic(fmt.Errorf("TODO: handle merging value %s", existing)) + if existing != propertyValue { + panic(fmt.Errorf("TODO: handle merging value %#v", existing)) + } } } else { - (*p)[propertyName][productConfigProp] = property + (*p)[propertyName][key] = propertyValue } } -var ( - conditionsDefaultField string = proptools.FieldNameForProperty(bazel.ConditionsDefaultConfigKey) -) - // maybeExtractConfigVarProp attempts to read this value as a config var struct // wrapped by interfaces and ptrs. If it's not the right type, the second return // value is false. @@ -799,10 +835,7 @@ func maybeExtractConfigVarProp(v reflect.Value) (reflect.Value, bool) { return v, true } -func (productConfigProperties *ProductConfigProperties) AddProductConfigProperties(namespace, suffix string, variableValues reflect.Value, outerAxis bazel.ConfigurationAxis) { - // variableValues can either be a product_variables or - // soong_config_variables struct. - // +func (productConfigProperties *ProductConfigProperties) AddProductConfigProperties(variableValues reflect.Value, arch string) { // Example of product_variables: // // product_variables: { @@ -815,19 +848,44 @@ func (productConfigProperties *ProductConfigProperties) AddProductConfigProperti // ], // }, // }, + + for i := 0; i < variableValues.NumField(); i++ { + // e.g. Platform_sdk_version, Unbundled_build, Malloc_not_svelte, etc. + productVariableName := variableValues.Type().Field(i).Name + + variableValue := variableValues.Field(i) + // Check if any properties were set for the module + if variableValue.IsZero() { + // e.g. feature1: {}, malloc_not_svelte: {} + continue + } + + for j := 0; j < variableValue.NumField(); j++ { + property := variableValue.Field(j) + // e.g. Asflags, Cflags, Enabled, etc. + propertyName := variableValue.Type().Field(j).Name + if property.Kind() != reflect.Interface { + productConfigProperties.AddProductConfigProperty(propertyName, productVariableName, arch, property.Interface()) + } + } + } + +} + +func (productConfigProperties *ProductConfigProperties) AddSoongConfigProperties(namespace string, soongConfigVariablesStruct reflect.Value) error { // // Example of soong_config_variables: // // soong_config_variables: { // feature1: { - // conditions_default: { + // conditions_default: { // ... // }, // cflags: ... // }, // feature2: { // cflags: ... - // conditions_default: { + // conditions_default: { // ... // }, // }, @@ -835,7 +893,7 @@ func (productConfigProperties *ProductConfigProperties) AddProductConfigProperti // soc_a: { // ... // }, - // soc_a: { + // soc_b: { // ... // }, // soc_c: {}, @@ -844,36 +902,39 @@ func (productConfigProperties *ProductConfigProperties) AddProductConfigProperti // }, // }, // } - for i := 0; i < variableValues.NumField(); i++ { - // e.g. Platform_sdk_version, Unbundled_build, Malloc_not_svelte, etc. - productVariableName := variableValues.Type().Field(i).Name - - variableValue := variableValues.Field(i) + for i := 0; i < soongConfigVariablesStruct.NumField(); i++ { + // e.g. feature1, feature2, board + variableName := soongConfigVariablesStruct.Type().Field(i).Name + variableStruct := soongConfigVariablesStruct.Field(i) // Check if any properties were set for the module - if variableValue.IsZero() { - // e.g. feature1: {}, malloc_not_svelte: {} + if variableStruct.IsZero() { + // e.g. feature1: {} continue } // Unlike product variables, config variables require a few more // indirections to extract the struct from the reflect.Value. - if v, ok := maybeExtractConfigVarProp(variableValue); ok { - variableValue = v + if v, ok := maybeExtractConfigVarProp(variableStruct); ok { + variableStruct = v + } else if !v.IsValid() { + // Skip invalid variables which may not used, else leads to panic + continue } - for j := 0; j < variableValue.NumField(); j++ { - property := variableValue.Field(j) - // e.g. Asflags, Cflags, Enabled, etc. - propertyName := variableValue.Type().Field(j).Name - // config can also be "conditions_default". - config := proptools.PropertyNameForField(propertyName) + for j := 0; j < variableStruct.NumField(); j++ { + propertyOrStruct := variableStruct.Field(j) + // propertyOrValueName can either be: + // - A property, like: Asflags, Cflags, Enabled, etc. + // - A soong config string variable's value, like soc_a, soc_b, soc_c in the example above + // - "conditions_default" + propertyOrValueName := variableStruct.Type().Field(j).Name // If the property wasn't set, no need to pass it along - if property.IsZero() { + if propertyOrStruct.IsZero() { continue } - if v, ok := maybeExtractConfigVarProp(property); ok { + if v, ok := maybeExtractConfigVarProp(propertyOrStruct); ok { // The field is a struct, which is used by: // 1) soong_config_string_variables // @@ -891,6 +952,9 @@ func (productConfigProperties *ProductConfigProperties) AddProductConfigProperti // cflags: ..., // static_libs: ... // } + // + // This means that propertyOrValueName is either conditions_default, or a soong + // config string variable's value. field := v // Iterate over fields of this struct prop. for k := 0; k < field.NumField(); k++ { @@ -900,47 +964,60 @@ func (productConfigProperties *ProductConfigProperties) AddProductConfigProperti if field.Field(k).IsZero() && namespace == "" { continue } - actualPropertyName := field.Type().Field(k).Name - - productConfigProperties.AddProductConfigProperty( - actualPropertyName, // e.g. cflags, static_libs - namespace, // e.g. acme, android - productVariableName, // e.g. size, feature1, FEATURE2, board - config, - field.Field(k).Interface(), // e.g. ["-DDEFAULT"], ["foo", "bar"], - outerAxis, - ) + + propertyName := field.Type().Field(k).Name + if propertyName == "Target" { + productConfigProperties.AddSoongConfigPropertiesFromTargetStruct(namespace, variableName, proptools.PropertyNameForField(propertyOrValueName), field.Field(k)) + } else if propertyName == "Arch" || propertyName == "Multilib" { + return fmt.Errorf("Arch/Multilib are not currently supported in soong config variable structs") + } else { + productConfigProperties.AddSoongConfigProperty(propertyName, namespace, variableName, proptools.PropertyNameForField(propertyOrValueName), "", field.Field(k).Interface()) + } } - } else if property.Kind() != reflect.Interface { + } else if propertyOrStruct.Kind() != reflect.Interface { // If not an interface, then this is not a conditions_default or - // a struct prop. That is, this is a regular product variable, - // or a bool/value config variable. - config := productVariableName + suffix - productConfigProperties.AddProductConfigProperty( - propertyName, - namespace, - productVariableName, - config, - property.Interface(), - outerAxis, - ) + // a struct prop. That is, this is a bool/value config variable. + if propertyOrValueName == "Target" { + productConfigProperties.AddSoongConfigPropertiesFromTargetStruct(namespace, variableName, "", propertyOrStruct) + } else if propertyOrValueName == "Arch" || propertyOrValueName == "Multilib" { + return fmt.Errorf("Arch/Multilib are not currently supported in soong config variable structs") + } else { + productConfigProperties.AddSoongConfigProperty(propertyOrValueName, namespace, variableName, "", "", propertyOrStruct.Interface()) + } } } } + return nil } -// productVariableValues uses reflection to convert a property struct for -// product_variables and soong_config_variables to structs that can be generated -// as select statements. -func productVariableValues( - fieldName string, variableProps interface{}, namespace, suffix string, productConfigProperties *ProductConfigProperties, outerAxis bazel.ConfigurationAxis) { - if suffix != "" { - suffix = "-" + suffix +func (productConfigProperties *ProductConfigProperties) AddSoongConfigPropertiesFromTargetStruct(namespace, soongConfigVariableName string, soongConfigVariableValue string, targetStruct reflect.Value) { + // targetStruct will be a struct with fields like "android", "host", "arm", "x86", + // "android_arm", etc. The values of each of those fields will be a regular property struct. + for i := 0; i < targetStruct.NumField(); i++ { + targetFieldName := targetStruct.Type().Field(i).Name + archOrOsSpecificStruct := targetStruct.Field(i) + for j := 0; j < archOrOsSpecificStruct.NumField(); j++ { + property := archOrOsSpecificStruct.Field(j) + // e.g. Asflags, Cflags, Enabled, etc. + propertyName := archOrOsSpecificStruct.Type().Field(j).Name + + if targetFieldName == "Android" { + productConfigProperties.AddSoongConfigProperty(propertyName, namespace, soongConfigVariableName, soongConfigVariableValue, "android", property.Interface()) + } else if targetFieldName == "Host" { + for _, os := range osTypeList { + if os.Class == Host { + productConfigProperties.AddSoongConfigProperty(propertyName, namespace, soongConfigVariableName, soongConfigVariableValue, os.Name, property.Interface()) + } + } + } else if !archOrOsSpecificStruct.IsZero() { + // One problem with supporting additional fields is that if multiple branches of + // "target" overlap, we don't want them to be in the same select statement (aka + // configuration axis). "android" and "host" are disjoint, so it's ok that we only + // have 2 axes right now. (soongConfigVariables and soongConfigVariablesPlusOs) + panic("TODO: support other target types in soong config variable structs: " + targetFieldName) + } + } } - - // variableValues represent the product_variables or soong_config_variables struct. - variableValues := reflect.ValueOf(variableProps).Elem().FieldByName(fieldName) - productConfigProperties.AddProductConfigProperties(namespace, suffix, variableValues, outerAxis) } func VariableMutator(mctx BottomUpMutatorContext) { diff --git a/android_sdk/sdk_repo_host.go b/android_sdk/sdk_repo_host.go index 61058df098..373e88306b 100644 --- a/android_sdk/sdk_repo_host.go +++ b/android_sdk/sdk_repo_host.go @@ -165,10 +165,11 @@ func (s *sdkRepoHost) GenerateAndroidBuildActions(ctx android.ModuleContext) { Flag(dir.Join(ctx, strip).String()) } } else { + llvmObjCopy := config.ClangPath(ctx, "bin/llvm-objcopy") llvmStrip := config.ClangPath(ctx, "bin/llvm-strip") - llvmLib := config.ClangPath(ctx, "lib/x86_64-unknown-linux-gnu/libc++.so.1") + llvmLib := config.ClangPath(ctx, "lib/x86_64-unknown-linux-gnu/libc++.so") for _, strip := range s.properties.Strip_files { - cmd := builder.Command().Tool(llvmStrip).ImplicitTool(llvmLib) + cmd := builder.Command().Tool(llvmStrip).ImplicitTool(llvmLib).ImplicitTool(llvmObjCopy) if !ctx.Windows() { cmd.Flag("-x") } @@ -242,7 +243,7 @@ func (s *sdkRepoHost) AndroidMk() android.AndroidMkData { fmt.Fprintln(w, ".PHONY:", name, "sdk_repo", "sdk-repo-"+name) fmt.Fprintln(w, "sdk_repo", "sdk-repo-"+name+":", strings.Join(s.FilesToInstall().Strings(), " ")) - fmt.Fprintf(w, "$(call dist-for-goals,sdk_repo sdk-repo-%s,%s:%s-$(FILE_NAME_TAG).zip)\n\n", s.BaseModuleName(), s.outputFile.String(), s.outputBaseName) + fmt.Fprintf(w, "$(call dist-for-goals,sdk_repo sdk-repo-%s,%s:%s-FILE_NAME_TAG_PLACEHOLDER.zip)\n\n", s.BaseModuleName(), s.outputFile.String(), s.outputBaseName) }, } } diff --git a/androidmk/androidmk/android.go b/androidmk/androidmk/android.go index ef347d8058..a8f7947a13 100644 --- a/androidmk/androidmk/android.go +++ b/androidmk/androidmk/android.go @@ -106,6 +106,7 @@ func init() { "LOCAL_ARM_MODE_HACK": "instruction_set", "LOCAL_SDK_VERSION": "sdk_version", "LOCAL_MIN_SDK_VERSION": "min_sdk_version", + "LOCAL_TARGET_SDK_VERSION": "target_sdk_version", "LOCAL_NDK_STL_VARIANT": "stl", "LOCAL_JAR_MANIFEST": "manifest", "LOCAL_CERTIFICATE": "certificate", diff --git a/androidmk/androidmk/androidmk_test.go b/androidmk/androidmk/androidmk_test.go index afde68b49a..0580ae5b66 100644 --- a/androidmk/androidmk/androidmk_test.go +++ b/androidmk/androidmk/androidmk_test.go @@ -1450,6 +1450,7 @@ LOCAL_PACKAGE_NAME := foo LOCAL_PRODUCT_MODULE := true LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res LOCAL_SDK_VERSION := current +LOCAL_TARGET_SDK_VERSION := target_version LOCAL_RRO_THEME := FooTheme include $(BUILD_RRO_PACKAGE) @@ -1460,6 +1461,7 @@ runtime_resource_overlay { product_specific: true, sdk_version: "current", + target_sdk_version: "target_version", theme: "FooTheme", } diff --git a/apex/Android.bp b/apex/Android.bp index 61d7fb2e2e..27017ae028 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -8,8 +8,9 @@ bootstrap_go_package { deps: [ "blueprint", "soong", + "soong-aconfig", + "soong-aconfig-codegen", "soong-android", - "soong-bazel", "soong-bpf", "soong-cc", "soong-filesystem", @@ -26,10 +27,8 @@ bootstrap_go_package { "apex_sdk_member.go", "apex_singleton.go", "builder.go", - "bp2build.go", "deapexer.go", "key.go", - "metadata.go", "prebuilt.go", "testing.go", "vndk.go", @@ -39,7 +38,6 @@ bootstrap_go_package { "bootclasspath_fragment_test.go", "classpath_element_test.go", "dexpreopt_bootjars_test.go", - "metadata_test.go", "platform_bootclasspath_test.go", "systemserver_classpath_fragment_test.go", "vndk_test.go", diff --git a/apex/androidmk.go b/apex/androidmk.go index 684833de4b..bc68ad36bd 100644 --- a/apex/androidmk.go +++ b/apex/androidmk.go @@ -42,7 +42,7 @@ func (class apexFileClass) nameInMake() string { return "ETC" case nativeSharedLib: return "SHARED_LIBRARIES" - case nativeExecutable, shBinary, pyBinary, goBinary: + case nativeExecutable, shBinary: return "EXECUTABLES" case javaSharedLib: return "JAVA_LIBRARIES" @@ -67,7 +67,7 @@ func (a *apexBundle) fullModuleName(apexBundleName string, linkToSystemLib bool, if linkToSystemLib { return fi.androidMkModuleName } - return fi.androidMkModuleName + "." + apexBundleName + a.suffix + return fi.androidMkModuleName + "." + apexBundleName } // androidMkForFiles generates Make definitions for the contents of an @@ -85,15 +85,6 @@ func (a *apexBundle) androidMkForFiles(w io.Writer, apexBundleName, moduleDir st // conflicts between two apexes with the same apexName. moduleNames := []string{} - apexType := a.properties.ApexType - // To avoid creating duplicate build rules, run this function only when primaryApexType is true - // to install symbol files in $(PRODUCT_OUT}/apex. - // And if apexType is flattened, run this function to install files in $(PRODUCT_OUT}/system/apex. - if !a.primaryApexType && apexType != flattenedApex { - return moduleNames - } - - seenDataOutPaths := make(map[string]bool) for _, fi := range a.filesInfo { linkToSystemLib := a.linkToSystemLib && fi.transitiveDep && fi.availableToPlatform() @@ -131,69 +122,23 @@ func (a *apexBundle) androidMkForFiles(w io.Writer, apexBundleName, moduleDir st } // /apex//{lib|framework|...} pathForSymbol := filepath.Join("$(PRODUCT_OUT)", "apex", apexBundleName, fi.installDir) - var modulePath string - if apexType == flattenedApex { - // /system/apex//{lib|framework|...} - modulePath = filepath.Join(a.installDir.String(), apexBundleName, fi.installDir) - fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", modulePath) - if a.primaryApexType { - fmt.Fprintln(w, "LOCAL_SOONG_SYMBOL_PATH :=", pathForSymbol) - } - android.AndroidMkEmitAssignList(w, "LOCAL_MODULE_SYMLINKS", fi.symlinks) - newDataPaths := []android.DataPath{} - for _, path := range fi.dataPaths { - dataOutPath := modulePath + ":" + path.SrcPath.Rel() - if ok := seenDataOutPaths[dataOutPath]; !ok { - newDataPaths = append(newDataPaths, path) - seenDataOutPaths[dataOutPath] = true - } - } - android.AndroidMkEmitAssignList(w, "LOCAL_TEST_DATA", android.AndroidMkDataPaths(newDataPaths)) - } else { - modulePath = pathForSymbol - fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", modulePath) + modulePath := pathForSymbol + fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", modulePath) - // For non-flattend APEXes, the merged notice file is attached to the APEX itself. - // We don't need to have notice file for the individual modules in it. Otherwise, - // we will have duplicated notice entries. - fmt.Fprintln(w, "LOCAL_NO_NOTICE_FILE := true") - } + // For non-flattend APEXes, the merged notice file is attached to the APEX itself. + // We don't need to have notice file for the individual modules in it. Otherwise, + // we will have duplicated notice entries. + fmt.Fprintln(w, "LOCAL_NO_NOTICE_FILE := true") fmt.Fprintln(w, "LOCAL_SOONG_INSTALLED_MODULE :=", filepath.Join(modulePath, fi.stem())) fmt.Fprintln(w, "LOCAL_SOONG_INSTALL_PAIRS :=", fi.builtFile.String()+":"+filepath.Join(modulePath, fi.stem())) fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", fi.builtFile.String()) fmt.Fprintln(w, "LOCAL_MODULE_CLASS :=", fi.class.nameInMake()) if fi.module != nil { // This apexFile's module comes from Soong - archStr := fi.module.Target().Arch.ArchType.String() - host := false - switch fi.module.Target().Os.Class { - case android.Host: - if fi.module.Target().HostCross { - if fi.module.Target().Arch.ArchType != android.Common { - fmt.Fprintln(w, "LOCAL_MODULE_HOST_CROSS_ARCH :=", archStr) - } - } else { - if fi.module.Target().Arch.ArchType != android.Common { - fmt.Fprintln(w, "LOCAL_MODULE_HOST_ARCH :=", archStr) - } - } - host = true - case android.Device: - if fi.module.Target().Arch.ArchType != android.Common { - fmt.Fprintln(w, "LOCAL_MODULE_TARGET_ARCH :=", archStr) - } + if fi.module.Target().Arch.ArchType != android.Common { + archStr := fi.module.Target().Arch.ArchType.String() + fmt.Fprintln(w, "LOCAL_MODULE_TARGET_ARCH :=", archStr) } - if host { - makeOs := fi.module.Target().Os.String() - if fi.module.Target().Os == android.Linux || fi.module.Target().Os == android.LinuxBionic || fi.module.Target().Os == android.LinuxMusl { - makeOs = "linux" - } - fmt.Fprintln(w, "LOCAL_MODULE_HOST_OS :=", makeOs) - fmt.Fprintln(w, "LOCAL_IS_HOST_MODULE := true") - } - } else if fi.isBazelPrebuilt && fi.arch != "" { - // This apexFile comes from Bazel - fmt.Fprintln(w, "LOCAL_MODULE_TARGET_ARCH :=", fi.arch) } if fi.jacocoReportClassesFile != nil { fmt.Fprintln(w, "LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR :=", fi.jacocoReportClassesFile.String()) @@ -237,56 +182,27 @@ func (a *apexBundle) androidMkForFiles(w io.Writer, apexBundleName, moduleDir st fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_android_app_set.mk") case nativeSharedLib, nativeExecutable, nativeTest: fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", fi.stem()) - if fi.isBazelPrebuilt { - fmt.Fprintln(w, "LOCAL_SOONG_UNSTRIPPED_BINARY :=", fi.unstrippedBuiltFile) - } else { - if ccMod, ok := fi.module.(*cc.Module); ok { - if ccMod.UnstrippedOutputFile() != nil { - fmt.Fprintln(w, "LOCAL_SOONG_UNSTRIPPED_BINARY :=", ccMod.UnstrippedOutputFile().String()) - } - ccMod.AndroidMkWriteAdditionalDependenciesForSourceAbiDiff(w) - if ccMod.CoverageOutputFile().Valid() { - fmt.Fprintln(w, "LOCAL_PREBUILT_COVERAGE_ARCHIVE :=", ccMod.CoverageOutputFile().String()) - } - } else if rustMod, ok := fi.module.(*rust.Module); ok { - if rustMod.UnstrippedOutputFile() != nil { - fmt.Fprintln(w, "LOCAL_SOONG_UNSTRIPPED_BINARY :=", rustMod.UnstrippedOutputFile().String()) - } + if ccMod, ok := fi.module.(*cc.Module); ok { + if ccMod.UnstrippedOutputFile() != nil { + fmt.Fprintln(w, "LOCAL_SOONG_UNSTRIPPED_BINARY :=", ccMod.UnstrippedOutputFile().String()) + } + ccMod.AndroidMkWriteAdditionalDependenciesForSourceAbiDiff(w) + if ccMod.CoverageOutputFile().Valid() { + fmt.Fprintln(w, "LOCAL_PREBUILT_COVERAGE_ARCHIVE :=", ccMod.CoverageOutputFile().String()) + } + } else if rustMod, ok := fi.module.(*rust.Module); ok { + if rustMod.UnstrippedOutputFile() != nil { + fmt.Fprintln(w, "LOCAL_SOONG_UNSTRIPPED_BINARY :=", rustMod.UnstrippedOutputFile().String()) } } fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_cc_rust_prebuilt.mk") default: fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", fi.stem()) - if fi.builtFile == a.manifestPbOut && apexType == flattenedApex { - if a.primaryApexType { - // To install companion files (init_rc, vintf_fragments) - // Copy some common properties of apexBundle to apex_manifest - commonProperties := []string{ - "LOCAL_FULL_INIT_RC", "LOCAL_FULL_VINTF_FRAGMENTS", - } - for _, name := range commonProperties { - if value, ok := apexAndroidMkData.Entries.EntryMap[name]; ok { - android.AndroidMkEmitAssignList(w, name, value) - } - } - - // Make apex_manifest.pb module for this APEX to override all other - // modules in the APEXes being overridden by this APEX - var patterns []string - for _, o := range a.overridableProperties.Overrides { - patterns = append(patterns, "%."+o+a.suffix) - } - android.AndroidMkEmitAssignList(w, "LOCAL_OVERRIDES_MODULES", patterns) - } - - // File_contexts of flattened APEXes should be merged into file_contexts.bin - fmt.Fprintln(w, "LOCAL_FILE_CONTEXTS :=", a.fileContexts) - } fmt.Fprintln(w, "include $(BUILD_PREBUILT)") } // m will build . as well. - if fi.androidMkModuleName != moduleName && a.primaryApexType { + if fi.androidMkModuleName != moduleName { fmt.Fprintf(w, ".PHONY: %s\n", fi.androidMkModuleName) fmt.Fprintf(w, "%s: %s\n", fi.androidMkModuleName, moduleName) } @@ -315,78 +231,64 @@ func (a *apexBundle) androidMkForType() android.AndroidMkData { return android.AndroidMkData{ Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) { moduleNames := []string{} - apexType := a.properties.ApexType if a.installable() { moduleNames = a.androidMkForFiles(w, name, moduleDir, data) } - if apexType == flattenedApex { - // Only image APEXes can be flattened. - fmt.Fprintln(w, "\ninclude $(CLEAR_VARS) # apex.apexBundle.flat") - fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir) - fmt.Fprintln(w, "LOCAL_MODULE :=", name+a.suffix) - data.Entries.WriteLicenseVariables(w) - a.writeRequiredModules(w, moduleNames) - fmt.Fprintln(w, "include $(BUILD_PHONY_PACKAGE)") - - } else { - fmt.Fprintln(w, "\ninclude $(CLEAR_VARS) # apex.apexBundle") - 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.String()) - stemSuffix := apexType.suffix() - if a.isCompressed { - stemSuffix = imageCapexSuffix - } - fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", name+stemSuffix) - fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE :=", !a.installable()) - if a.installable() { - fmt.Fprintln(w, "LOCAL_SOONG_INSTALLED_MODULE :=", a.installedFile.String()) - fmt.Fprintln(w, "LOCAL_SOONG_INSTALL_PAIRS :=", a.outputFile.String()+":"+a.installedFile.String()) - } + fmt.Fprintln(w, "\ninclude $(CLEAR_VARS) # apex.apexBundle") + fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir) + fmt.Fprintln(w, "LOCAL_MODULE :=", name) + 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.String()) + stemSuffix := imageApexSuffix + if a.isCompressed { + stemSuffix = imageCapexSuffix + } + fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", name+stemSuffix) + fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE :=", !a.installable()) + if a.installable() { + fmt.Fprintln(w, "LOCAL_SOONG_INSTALLED_MODULE :=", a.installedFile.String()) + fmt.Fprintln(w, "LOCAL_SOONG_INSTALL_PAIRS :=", a.outputFile.String()+":"+a.installedFile.String()) + fmt.Fprintln(w, "LOCAL_SOONG_INSTALL_SYMLINKS := ", strings.Join(a.compatSymlinks.Strings(), " ")) + } + fmt.Fprintln(w, "LOCAL_APEX_KEY_PATH := ", a.apexKeysPath.String()) - // Because apex writes .mk with Custom(), we need to write manually some common properties - // which are available via data.Entries - commonProperties := []string{ - "LOCAL_FULL_INIT_RC", "LOCAL_FULL_VINTF_FRAGMENTS", - "LOCAL_PROPRIETARY_MODULE", "LOCAL_VENDOR_MODULE", "LOCAL_ODM_MODULE", "LOCAL_PRODUCT_MODULE", "LOCAL_SYSTEM_EXT_MODULE", - "LOCAL_MODULE_OWNER", - } - for _, name := range commonProperties { - if value, ok := data.Entries.EntryMap[name]; ok { - android.AndroidMkEmitAssignList(w, name, value) - } + // Because apex writes .mk with Custom(), we need to write manually some common properties + // which are available via data.Entries + commonProperties := []string{ + "LOCAL_FULL_INIT_RC", "LOCAL_FULL_VINTF_FRAGMENTS", + "LOCAL_PROPRIETARY_MODULE", "LOCAL_VENDOR_MODULE", "LOCAL_ODM_MODULE", "LOCAL_PRODUCT_MODULE", "LOCAL_SYSTEM_EXT_MODULE", + "LOCAL_MODULE_OWNER", + } + for _, name := range commonProperties { + if value, ok := data.Entries.EntryMap[name]; ok { + android.AndroidMkEmitAssignList(w, name, value) } + } - android.AndroidMkEmitAssignList(w, "LOCAL_OVERRIDES_MODULES", a.overridableProperties.Overrides) - a.writeRequiredModules(w, moduleNames) - - fmt.Fprintln(w, "include $(BUILD_PREBUILT)") + android.AndroidMkEmitAssignList(w, "LOCAL_OVERRIDES_MODULES", a.overridableProperties.Overrides) + a.writeRequiredModules(w, moduleNames) - if apexType == imageApex { - fmt.Fprintln(w, "ALL_MODULES.$(my_register_name).BUNDLE :=", a.bundleModuleFile.String()) - } - android.AndroidMkEmitAssignList(w, "ALL_MODULES.$(my_register_name).LINT_REPORTS", a.lintReports.Strings()) - - if a.installedFilesFile != nil { - goal := "checkbuild" - distFile := name + "-installed-files.txt" - fmt.Fprintln(w, ".PHONY:", goal) - fmt.Fprintf(w, "$(call dist-for-goals,%s,%s:%s)\n", - goal, a.installedFilesFile.String(), distFile) - fmt.Fprintf(w, "$(call declare-0p-target,%s)\n", a.installedFilesFile.String()) - } - for _, dist := range data.Entries.GetDistForGoals(a) { - fmt.Fprintf(w, dist) - } + fmt.Fprintln(w, "include $(BUILD_PREBUILT)") + fmt.Fprintln(w, "ALL_MODULES.$(my_register_name).BUNDLE :=", a.bundleModuleFile.String()) + android.AndroidMkEmitAssignList(w, "ALL_MODULES.$(my_register_name).LINT_REPORTS", a.lintReports.Strings()) - distCoverageFiles(w, "ndk_apis_usedby_apex", a.nativeApisUsedByModuleFile.String()) - distCoverageFiles(w, "ndk_apis_backedby_apex", a.nativeApisBackedByModuleFile.String()) - distCoverageFiles(w, "java_apis_used_by_apex", a.javaApisUsedByModuleFile.String()) + if a.installedFilesFile != nil { + goal := "checkbuild" + distFile := name + "-installed-files.txt" + fmt.Fprintln(w, ".PHONY:", goal) + fmt.Fprintf(w, "$(call dist-for-goals,%s,%s:%s)\n", + goal, a.installedFilesFile.String(), distFile) + fmt.Fprintf(w, "$(call declare-0p-target,%s)\n", a.installedFilesFile.String()) } + for _, dist := range data.Entries.GetDistForGoals(a) { + fmt.Fprintf(w, dist) + } + + distCoverageFiles(w, "ndk_apis_usedby_apex", a.nativeApisUsedByModuleFile.String()) + distCoverageFiles(w, "ndk_apis_backedby_apex", a.nativeApisBackedByModuleFile.String()) + distCoverageFiles(w, "java_apis_used_by_apex", a.javaApisUsedByModuleFile.String()) }} } diff --git a/apex/apex.go b/apex/apex.go index c1c9e5c886..38a166eb3c 100644 --- a/apex/apex.go +++ b/apex/apex.go @@ -18,26 +18,23 @@ package apex import ( "fmt" + "log" "path/filepath" "regexp" "sort" "strings" - "android/soong/bazel/cquery" - + "android/soong/aconfig" "github.com/google/blueprint" - "github.com/google/blueprint/bootstrap" "github.com/google/blueprint/proptools" "android/soong/android" - "android/soong/bazel" "android/soong/bpf" "android/soong/cc" prebuilt_etc "android/soong/etc" "android/soong/filesystem" "android/soong/java" "android/soong/multitree" - "android/soong/python" "android/soong/rust" "android/soong/sh" ) @@ -79,7 +76,6 @@ func RegisterPostDepsMutators(ctx android.RegisterMutatorsContext) { ctx.BottomUp("mark_platform_availability", markPlatformAvailability).Parallel() ctx.BottomUp("apex", apexMutator).Parallel() ctx.BottomUp("apex_directly_in_any", apexDirectlyInAnyMutator).Parallel() - ctx.BottomUp("apex_flattened", apexFlattenedMutator).Parallel() ctx.BottomUp("apex_dcla_deps", apexDCLADepsMutator).Parallel() // Register after apex_info mutator so that it can use ApexVariationName ctx.TopDown("apex_strict_updatability_lint", apexStrictUpdatibilityLintMutator).Parallel() @@ -137,6 +133,11 @@ type apexBundleProperties struct { // List of filesystem images that are embedded inside this APEX bundle. Filesystems []string + // List of module names which we don't want to add as transitive deps. This can be used as + // a workaround when the current implementation collects more than necessary. For example, + // Rust binaries with prefer_rlib:true add unnecessary dependencies. + Unwanted_transitive_deps []string + // The minimum SDK version that this APEX must support at minimum. This is usually set to // the SDK version that the APEX was first introduced. Min_sdk_version *string @@ -166,15 +167,7 @@ type apexBundleProperties struct { // Should be only used in non-system apexes (e.g. vendor: true). Default is false. Use_vndk_as_stable *bool - // The type of APEX to build. Controls what the APEX payload is. Either 'image', 'zip' or - // 'both'. When set to image, contents are stored in a filesystem image inside a zip - // container. When set to zip, contents are stored in a zip container directly. This type is - // mostly for host-side debugging. When set to both, the two types are both built. Default - // is 'image'. - Payload_type *string - - // The type of filesystem to use when the payload_type is 'image'. Either 'ext4', 'f2fs' - // or 'erofs'. Default 'ext4'. + // The type of filesystem to use. Either 'ext4', 'f2fs' or 'erofs'. Default 'ext4'. Payload_fs_type *string // For telling the APEX to ignore special handling for system libraries such as bionic. @@ -216,10 +209,12 @@ type apexBundleProperties struct { HideFromMake bool `blueprint:"mutated"` - // Internal package method for this APEX. When payload_type is image, this can be either - // imageApex or flattenedApex depending on Config.FlattenApex(). When payload_type is zip, - // this becomes zipApex. - ApexType apexPackaging `blueprint:"mutated"` + // Name that dependencies can specify in their apex_available properties to refer to this module. + // If not specified, this defaults to Soong module name. This must be the name of a Soong module. + Apex_available_name *string + + // Variant version of the mainline module. Must be an integer between 0-9 + Variant_version *string } type ApexNativeDependencies struct { @@ -391,7 +386,6 @@ type apexBundle struct { android.ModuleBase android.DefaultableModuleBase android.OverridableModuleBase - android.BazelModuleBase multitree.ExportableModuleBase // Properties @@ -404,7 +398,7 @@ type apexBundle struct { /////////////////////////////////////////////////////////////////////////////////////////// // Inputs - // Keys for apex_paylaod.img + // Keys for apex_payload.img publicKeyFile android.Path privateKeyFile android.Path @@ -416,13 +410,6 @@ type apexBundle struct { testApex bool vndkApex bool - // Tells whether this variant of the APEX bundle is the primary one or not. Only the primary - // one gets installed to the device. - primaryApexType bool - - // Suffix of module name in Android.mk ".flattened", ".apex", ".zipapex", or "" - suffix string - // File system type of apex_payload.img payloadFsType fsType @@ -469,6 +456,9 @@ type apexBundle struct { // Path where this APEX was installed. installedFile android.InstallPath + // fragment for this apex for apexkeys.txt + apexKeysPath android.WritablePath + // Installed locations of symlinks for backward compatibility. compatSymlinks android.InstallPaths @@ -490,8 +480,7 @@ type apexBundle struct { nativeApisBackedByModuleFile android.ModuleOutPath javaApisUsedByModuleFile android.ModuleOutPath - // Collect the module directory for IDE info in java/jdeps.go. - modulePaths []string + aconfigFiles []android.Path } // apexFileClass represents a type of file that can be included in APEX. @@ -501,12 +490,10 @@ const ( app apexFileClass = iota appSet etc - goBinary javaSharedLib nativeExecutable nativeSharedLib nativeTest - pyBinary shBinary ) @@ -515,12 +502,10 @@ var ( "app": app, "appSet": appSet, "etc": etc, - "goBinary": goBinary, "javaSharedLib": javaSharedLib, "nativeExecutable": nativeExecutable, "nativeSharedLib": nativeSharedLib, "nativeTest": nativeTest, - "pyBinary": pyBinary, "shBinary": shBinary, } ) @@ -528,8 +513,7 @@ var ( // apexFile represents a file in an APEX bundle. This is created during the first half of // GenerateAndroidBuildActions by traversing the dependencies of the APEX. Then in the second half // of the function, this is used to create commands that copies the files into a staging directory, -// where they are packaged into the APEX file. This struct is also used for creating Make modules -// for each of the files in case when the APEX is flattened. +// where they are packaged into the APEX file. type apexFile struct { // buildFile is put in the installDir inside the APEX. builtFile android.Path @@ -559,10 +543,6 @@ type apexFile struct { multilib string - isBazelPrebuilt bool - unstrippedBuiltFile android.Path - arch string - // TODO(jiyong): remove this module android.Module } @@ -712,11 +692,10 @@ func addDependenciesForNativeModules(ctx android.BottomUpMutatorContext, nativeM libVariations := append(target.Variations(), blueprint.Variation{Mutator: "link", Variation: "shared"}) rustLibVariations := append(target.Variations(), blueprint.Variation{Mutator: "rust_libraries", Variation: "dylib"}) - if ctx.Device() { - binVariations = append(binVariations, blueprint.Variation{Mutator: "image", Variation: imageVariation}) - libVariations = append(libVariations, blueprint.Variation{Mutator: "image", Variation: imageVariation}) - rustLibVariations = append(rustLibVariations, blueprint.Variation{Mutator: "image", Variation: imageVariation}) - } + // Append "image" variation + binVariations = append(binVariations, blueprint.Variation{Mutator: "image", Variation: imageVariation}) + libVariations = append(libVariations, blueprint.Variation{Mutator: "image", Variation: imageVariation}) + rustLibVariations = append(rustLibVariations, blueprint.Variation{Mutator: "image", Variation: imageVariation}) // Use *FarVariation* to be able to depend on modules having conflicting variations with // this module. This is required since arch variant of an APEX bundle is 'common' but it is @@ -736,16 +715,7 @@ func addDependenciesForNativeModules(ctx android.BottomUpMutatorContext, nativeM } func (a *apexBundle) combineProperties(ctx android.BottomUpMutatorContext) { - if ctx.Device() { - proptools.AppendProperties(&a.properties.Multilib, &a.targetProperties.Target.Android.Multilib, nil) - } else { - proptools.AppendProperties(&a.properties.Multilib, &a.targetProperties.Target.Host.Multilib, nil) - if ctx.Os().Bionic() { - proptools.AppendProperties(&a.properties.Multilib, &a.targetProperties.Target.Linux_bionic.Multilib, nil) - } else { - proptools.AppendProperties(&a.properties.Multilib, &a.targetProperties.Target.Linux_glibc.Multilib, nil) - } - } + proptools.AppendProperties(&a.properties.Multilib, &a.targetProperties.Target.Android.Multilib, nil) } // getImageVariationPair returns a pair for the image variation name as its @@ -766,7 +736,7 @@ func (a *apexBundle) getImageVariationPair(deviceConfig android.DeviceConfig) (s vndkVersion = deviceConfig.VndkVersion() } else if a.ProductSpecific() { prefix = cc.ProductVariationPrefix - vndkVersion = deviceConfig.ProductVndkVersion() + vndkVersion = deviceConfig.PlatformVndkVersion() } } if vndkVersion == "current" { @@ -803,12 +773,6 @@ func (a *apexBundle) DepsMutator(ctx android.BottomUpMutatorContext) { } } for i, target := range targets { - // Don't include artifacts for the host cross targets because there is no way for us - // to run those artifacts natively on host - if target.HostCross { - continue - } - var deps ApexNativeDependencies // Add native modules targeting both ABIs. When multilib.* is omitted for @@ -989,7 +953,7 @@ func (a *apexBundle) ApexInfoMutator(mctx android.TopDownMutatorContext) { // the non-system APEXes because the VNDK libraries won't be included (and duped) in the // APEX, but shared across APEXes via the VNDK APEX. useVndk := a.SocSpecific() || a.DeviceSpecific() || (a.ProductSpecific() && mctx.Config().EnforceProductPartitionInterface()) - excludeVndkLibs := useVndk && proptools.Bool(a.properties.Use_vndk_as_stable) + excludeVndkLibs := useVndk && a.useVndkAsStable(mctx) if proptools.Bool(a.properties.Use_vndk_as_stable) { if !useVndk { mctx.PropertyErrorf("use_vndk_as_stable", "not supported for system/system_ext APEXes") @@ -1028,6 +992,13 @@ func (a *apexBundle) ApexInfoMutator(mctx android.TopDownMutatorContext) { return false } } + + //TODO: b/296491928 Vendor APEX should use libbinder.ndk instead of libbinder once VNDK is fully deprecated. + if useVndk && mctx.Config().IsVndkDeprecated() && child.Name() == "libbinder" { + log.Print("Libbinder is linked from Vendor APEX ", a.Name(), " with module ", parent.Name()) + return false + } + // By default, all the transitive dependencies are collected, unless filtered out // above. return true @@ -1064,6 +1035,10 @@ func (a *apexBundle) ApexInfoMutator(mctx android.TopDownMutatorContext) { apexVariationName := mctx.ModuleName() // could be com.android.foo a.properties.ApexVariationName = apexVariationName + testApexes := []string{} + if a.testApex { + testApexes = []string{apexVariationName} + } apexInfo := android.ApexInfo{ ApexVariationName: apexVariationName, MinSdkVersion: minSdkVersion, @@ -1072,6 +1047,7 @@ func (a *apexBundle) ApexInfoMutator(mctx android.TopDownMutatorContext) { InApexVariants: []string{apexVariationName}, InApexModules: []string{a.Name()}, // could be com.mycompany.android.foo ApexContents: []*android.ApexContents{apexContents}, + TestApexes: testApexes, } mctx.WalkDeps(func(child, parent android.Module) bool { if !continueApexDepsWalk(child, parent) { @@ -1240,8 +1216,8 @@ func apexTestForMutator(mctx android.BottomUpMutatorContext) { // be) available to platform // TODO(jiyong): move this to android/apex.go? func markPlatformAvailability(mctx android.BottomUpMutatorContext) { - // Host and recovery are not considered as platform - if mctx.Host() || mctx.Module().InstallInRecovery() { + // Recovery is not considered as platform + if mctx.Module().InstallInRecovery() { return } @@ -1344,116 +1320,19 @@ func apexDirectlyInAnyMutator(mctx android.BottomUpMutatorContext) { } } -// apexPackaging represents a specific packaging method for an APEX. -type apexPackaging int - -const ( - // imageApex is a packaging method where contents are included in a filesystem image which - // is then included in a zip container. This is the most typical way of packaging. - imageApex apexPackaging = iota - - // zipApex is a packaging method where contents are directly included in the zip container. - // This is used for host-side testing - because the contents are easily accessible by - // unzipping the container. - zipApex - - // flattendApex is a packaging method where contents are not included in the APEX file, but - // installed to /apex/ directory on the device. This packaging method is used for - // old devices where the filesystem-based APEX file can't be supported. - flattenedApex -) - const ( // File extensions of an APEX for different packaging methods imageApexSuffix = ".apex" imageCapexSuffix = ".capex" - zipApexSuffix = ".zipapex" - flattenedSuffix = ".flattened" // variant names each of which is for a packaging method - imageApexType = "image" - zipApexType = "zip" - flattenedApexType = "flattened" + imageApexType = "image" ext4FsType = "ext4" f2fsFsType = "f2fs" erofsFsType = "erofs" ) -// The suffix for the output "file", not the module -func (a apexPackaging) suffix() string { - switch a { - case imageApex: - return imageApexSuffix - case zipApex: - return zipApexSuffix - default: - panic(fmt.Errorf("unknown APEX type %d", a)) - } -} - -func (a apexPackaging) name() string { - switch a { - case imageApex: - return imageApexType - case zipApex: - return zipApexType - default: - panic(fmt.Errorf("unknown APEX type %d", a)) - } -} - -// apexFlattenedMutator creates one or more variations each of which is for a packaging method. -// TODO(jiyong): give a better name to this mutator -func apexFlattenedMutator(mctx android.BottomUpMutatorContext) { - if !mctx.Module().Enabled() { - return - } - if ab, ok := mctx.Module().(*apexBundle); ok { - var variants []string - switch proptools.StringDefault(ab.properties.Payload_type, "image") { - case "image": - // This is the normal case. Note that both image and flattend APEXes are - // created. The image type is installed to the system partition, while the - // flattened APEX is (optionally) installed to the system_ext partition. - // This is mostly for GSI which has to support wide range of devices. If GSI - // is installed on a newer (APEX-capable) device, the image APEX in the - // system will be used. However, if the same GSI is installed on an old - // device which can't support image APEX, the flattened APEX in the - // system_ext partion (which still is part of GSI) is used instead. - variants = append(variants, imageApexType, flattenedApexType) - case "zip": - variants = append(variants, zipApexType) - case "both": - variants = append(variants, imageApexType, zipApexType, flattenedApexType) - default: - mctx.PropertyErrorf("payload_type", "%q is not one of \"image\", \"zip\", or \"both\".", *ab.properties.Payload_type) - return - } - - modules := mctx.CreateLocalVariations(variants...) - - for i, v := range variants { - switch v { - case imageApexType: - modules[i].(*apexBundle).properties.ApexType = imageApex - case zipApexType: - modules[i].(*apexBundle).properties.ApexType = zipApex - case flattenedApexType: - modules[i].(*apexBundle).properties.ApexType = flattenedApex - // See the comment above for why system_ext. - if !mctx.Config().FlattenApex() && ab.Platform() { - modules[i].(*apexBundle).MakeAsSystemExt() - } - } - } - } else if _, ok := mctx.Module().(*OverrideApex); ok { - // payload_type is forcibly overridden to "image" - // TODO(jiyong): is this the right decision? - mctx.CreateVariations(imageApexType, flattenedApexType) - } -} - var _ android.DepIsInSameApex = (*apexBundle)(nil) // Implements android.DepInInSameApex @@ -1485,9 +1364,6 @@ func (a *apexBundle) OutputFiles(tag string) (android.Paths, error) { var _ multitree.Exportable = (*apexBundle)(nil) func (a *apexBundle) Exportable() bool { - if a.properties.ApexType == flattenedApex { - return false - } return true } @@ -1501,7 +1377,7 @@ var _ cc.Coverage = (*apexBundle)(nil) // Implements cc.Coverage func (a *apexBundle) IsNativeCoverageNeeded(ctx android.BaseModuleContext) bool { - return ctx.Device() && ctx.DeviceConfig().NativeCoverageEnabled() + return ctx.DeviceConfig().NativeCoverageEnabled() } // Implements cc.Coverage @@ -1612,13 +1488,9 @@ func (a *apexBundle) IsSanitizerEnabled(config android.Config, sanitizerName str // Then follow the global setting var globalSanitizerNames []string - if a.Host() { - globalSanitizerNames = config.SanitizeHost() - } else { - arches := config.SanitizeDeviceArch() - if len(arches) == 0 || android.InList(a.Arch().ArchType.Name, arches) { - globalSanitizerNames = config.SanitizeDevice() - } + arches := config.SanitizeDeviceArch() + if len(arches) == 0 || android.InList(a.Arch().ArchType.Name, arches) { + globalSanitizerNames = config.SanitizeDevice() } return android.InList(sanitizerName, globalSanitizerNames) } @@ -1626,7 +1498,7 @@ func (a *apexBundle) IsSanitizerEnabled(config android.Config, sanitizerName str func (a *apexBundle) AddSanitizerDependencies(ctx android.BottomUpMutatorContext, sanitizerName string) { // TODO(jiyong): move this info (the sanitizer name, the lib name, etc.) to cc/sanitize.go // Keep only the mechanism here. - if ctx.Device() && sanitizerName == "hwaddress" && strings.HasPrefix(a.Name(), "com.android.runtime") { + if sanitizerName == "hwaddress" && strings.HasPrefix(a.Name(), "com.android.runtime") { imageVariation := a.getImageVariation(ctx) for _, target := range ctx.MultiTargets() { if target.Arch.ArchType.Multilib == "lib64" { @@ -1700,6 +1572,7 @@ func apexFileForRustExecutable(ctx android.BaseModuleContext, rustm *rust.Module if rustm.Target().NativeBridge == android.NativeBridgeEnabled { dirInApex = filepath.Join(dirInApex, rustm.Target().NativeBridgeRelativePath) } + dirInApex = filepath.Join(dirInApex, rustm.RelativeInstallPath()) fileToCopy := android.OutputFileForModule(ctx, rustm, "") androidMkModuleName := rustm.BaseModuleName() + rustm.Properties.SubName af := newApexFile(ctx, fileToCopy, androidMkModuleName, dirInApex, nativeExecutable, rustm) @@ -1719,27 +1592,12 @@ func apexFileForRustLibrary(ctx android.BaseModuleContext, rustm *rust.Module) a if rustm.Target().NativeBridge == android.NativeBridgeEnabled { dirInApex = filepath.Join(dirInApex, rustm.Target().NativeBridgeRelativePath) } + dirInApex = filepath.Join(dirInApex, rustm.RelativeInstallPath()) fileToCopy := android.OutputFileForModule(ctx, rustm, "") androidMkModuleName := rustm.BaseModuleName() + rustm.Properties.SubName return newApexFile(ctx, fileToCopy, androidMkModuleName, dirInApex, nativeSharedLib, rustm) } -func apexFileForPyBinary(ctx android.BaseModuleContext, py *python.PythonBinaryModule) apexFile { - dirInApex := "bin" - fileToCopy := py.HostToolPath().Path() - return newApexFile(ctx, fileToCopy, py.BaseModuleName(), dirInApex, pyBinary, py) -} - -func apexFileForGoBinary(ctx android.BaseModuleContext, depName string, gb bootstrap.GoBinaryTool) apexFile { - dirInApex := "bin" - fileToCopy := android.PathForGoBinary(ctx, gb) - // NB: Since go binaries are static we don't need the module for anything here, which is - // good since the go tool is a blueprint.Module not an android.Module like we would - // normally use. - // - return newApexFile(ctx, fileToCopy, depName, dirInApex, goBinary, nil) -} - func apexFileForShBinary(ctx android.BaseModuleContext, sh *sh.ShBinary) apexFile { dirInApex := filepath.Join("bin", sh.SubDir()) if sh.Target().NativeBridge == android.NativeBridgeEnabled { @@ -1823,6 +1681,7 @@ type androidApp interface { Certificate() java.Certificate BaseModuleName() string LintDepSets() java.LintDepSets + PrivAppAllowlist() android.OptionalPath } var _ androidApp = (*java.AndroidApp)(nil) @@ -1843,7 +1702,7 @@ func sanitizedBuildIdForPath(ctx android.BaseModuleContext) string { return buildId } -func apexFileForAndroidApp(ctx android.BaseModuleContext, aapp androidApp) apexFile { +func apexFilesForAndroidApp(ctx android.BaseModuleContext, aapp androidApp) []apexFile { appDir := "app" if aapp.Privileged() { appDir = "priv-app" @@ -1865,7 +1724,18 @@ func apexFileForAndroidApp(ctx android.BaseModuleContext, aapp androidApp) apexF }); ok { af.overriddenPackageName = app.OverriddenManifestPackageName() } - return af + + apexFiles := []apexFile{} + + if allowlist := aapp.PrivAppAllowlist(); allowlist.Valid() { + dirInApex := filepath.Join("etc", "permissions") + privAppAllowlist := newApexFile(ctx, allowlist.Path(), aapp.BaseModuleName()+"_privapp", dirInApex, etc, aapp) + apexFiles = append(apexFiles, privAppAllowlist) + } + + apexFiles = append(apexFiles, af) + + return apexFiles } func apexFileForRuntimeResourceOverlay(ctx android.BaseModuleContext, rro java.RuntimeResourceOverlayModule) apexFile { @@ -1943,135 +1813,8 @@ func (f fsType) string() string { } } -var _ android.MixedBuildBuildable = (*apexBundle)(nil) - -func (a *apexBundle) IsMixedBuildSupported(ctx android.BaseModuleContext) bool { - return a.properties.ApexType == imageApex -} - -func (a *apexBundle) QueueBazelCall(ctx android.BaseModuleContext) { - bazelCtx := ctx.Config().BazelContext - bazelCtx.QueueBazelRequest(a.GetBazelLabel(ctx, a), cquery.GetApexInfo, android.GetConfigKey(ctx)) -} - -// GetBazelLabel returns the bazel label of this apexBundle, or the label of the -// override_apex module overriding this apexBundle. An apexBundle can be -// overridden by different override_apex modules (e.g. Google or Go variants), -// which is handled by the overrides mutators. -func (a *apexBundle) GetBazelLabel(ctx android.BazelConversionPathContext, module blueprint.Module) string { - if _, ok := ctx.Module().(android.OverridableModule); ok { - return android.MaybeBp2buildLabelOfOverridingModule(ctx) - } - return a.BazelModuleBase.GetBazelLabel(ctx, a) -} - -func (a *apexBundle) ProcessBazelQueryResponse(ctx android.ModuleContext) { - if !a.commonBuildActions(ctx) { - return - } - - a.setApexTypeAndSuffix(ctx) - a.setPayloadFsType(ctx) - a.setSystemLibLink(ctx) - - if a.properties.ApexType != zipApex { - a.compatSymlinks = makeCompatSymlinks(a.BaseModuleName(), ctx, a.primaryApexType) - } - - bazelCtx := ctx.Config().BazelContext - outputs, err := bazelCtx.GetApexInfo(a.GetBazelLabel(ctx, a), android.GetConfigKey(ctx)) - if err != nil { - ctx.ModuleErrorf(err.Error()) - return - } - a.installDir = android.PathForModuleInstall(ctx, "apex") - - // Set the output file to .apex or .capex depending on the compression configuration. - a.setCompression(ctx) - if a.isCompressed { - a.outputApexFile = android.PathForBazelOutRelative(ctx, ctx.ModuleDir(), outputs.SignedCompressedOutput) - } else { - a.outputApexFile = android.PathForBazelOutRelative(ctx, ctx.ModuleDir(), outputs.SignedOutput) - } - a.outputFile = a.outputApexFile - - if len(outputs.TidyFiles) > 0 { - tidyFiles := android.PathsForBazelOut(ctx, outputs.TidyFiles) - a.outputFile = android.AttachValidationActions(ctx, a.outputFile, tidyFiles) - } - - // TODO(b/257829940): These are used by the apex_keys_text singleton; would probably be a clearer - // interface if these were set in a provider rather than the module itself - a.publicKeyFile = android.PathForBazelOut(ctx, outputs.BundleKeyInfo[0]) - a.privateKeyFile = android.PathForBazelOut(ctx, outputs.BundleKeyInfo[1]) - a.containerCertificateFile = android.PathForBazelOut(ctx, outputs.ContainerKeyInfo[0]) - a.containerPrivateKeyFile = android.PathForBazelOut(ctx, outputs.ContainerKeyInfo[1]) - - // Ensure ApexMkInfo.install_to_system make module names are installed as - // part of a bundled build. - a.makeModulesToInstall = append(a.makeModulesToInstall, outputs.MakeModulesToInstall...) - - apexType := a.properties.ApexType - switch apexType { - case imageApex: - a.bundleModuleFile = android.PathForBazelOut(ctx, outputs.BundleFile) - a.nativeApisUsedByModuleFile = android.ModuleOutPath(android.PathForBazelOut(ctx, outputs.SymbolsUsedByApex)) - a.nativeApisBackedByModuleFile = android.ModuleOutPath(android.PathForBazelOut(ctx, outputs.BackingLibs)) - // TODO(b/239084755): Generate the java api using.xml file from Bazel. - a.javaApisUsedByModuleFile = android.ModuleOutPath(android.PathForBazelOut(ctx, outputs.JavaSymbolsUsedByApex)) - a.installedFilesFile = android.ModuleOutPath(android.PathForBazelOut(ctx, outputs.InstalledFiles)) - installSuffix := imageApexSuffix - if a.isCompressed { - installSuffix = imageCapexSuffix - } - a.installedFile = ctx.InstallFile(a.installDir, a.Name()+installSuffix, a.outputFile, - a.compatSymlinks.Paths()...) - default: - panic(fmt.Errorf("internal error: unexpected apex_type for the ProcessBazelQueryResponse: %v", a.properties.ApexType)) - } - - // filesInfo in mixed mode must retrieve all information about the apex's - // contents completely from the Starlark providers. It should never rely on - // Android.bp information, as they might not exist for fully migrated - // dependencies. - // - // Prevent accidental writes to filesInfo in the earlier parts Soong by - // asserting it to be nil. - if a.filesInfo != nil { - panic( - fmt.Errorf("internal error: filesInfo must be nil for an apex handled by Bazel. " + - "Did something else set filesInfo before this line of code?")) - } - for _, f := range outputs.PayloadFilesInfo { - fileInfo := apexFile{ - isBazelPrebuilt: true, - - builtFile: android.PathForBazelOut(ctx, f["built_file"]), - unstrippedBuiltFile: android.PathForBazelOut(ctx, f["unstripped_built_file"]), - androidMkModuleName: f["make_module_name"], - installDir: f["install_dir"], - class: classes[f["class"]], - customStem: f["basename"], - moduleDir: f["package"], - } - - arch := f["arch"] - fileInfo.arch = arch - if len(arch) > 0 { - fileInfo.multilib = "lib32" - if strings.HasSuffix(arch, "64") { - fileInfo.multilib = "lib64" - } - } - - a.filesInfo = append(a.filesInfo, fileInfo) - } -} - func (a *apexBundle) setCompression(ctx android.ModuleContext) { - if a.properties.ApexType != imageApex { - a.isCompressed = false - } else if a.testOnlyShouldForceCompression() { + if a.testOnlyShouldForceCompression() { a.isCompressed = true } else { a.isCompressed = ctx.Config().ApexCompressionEnabled() && a.isCompressable() @@ -2097,12 +1840,7 @@ func (a *apexBundle) setSystemLibLink(ctx android.ModuleContext) { // We don't need the optimization for updatable APEXes, as it might give false signal // to the system health when the APEXes are still bundled (b/149805758). - if !forced && updatable && a.properties.ApexType == imageApex { - a.linkToSystemLib = false - } - - // We also don't want the optimization for host APEXes, because it doesn't make sense. - if ctx.Host() { + if !forced && updatable { a.linkToSystemLib = false } } @@ -2120,39 +1858,7 @@ func (a *apexBundle) setPayloadFsType(ctx android.ModuleContext) { } } -func (a *apexBundle) setApexTypeAndSuffix(ctx android.ModuleContext) { - // Set suffix and primaryApexType depending on the ApexType - buildFlattenedAsDefault := ctx.Config().FlattenApex() - switch a.properties.ApexType { - case imageApex: - if buildFlattenedAsDefault { - a.suffix = imageApexSuffix - } else { - a.suffix = "" - a.primaryApexType = true - - if ctx.Config().InstallExtraFlattenedApexes() { - a.makeModulesToInstall = append(a.makeModulesToInstall, a.Name()+flattenedSuffix) - } - } - case zipApex: - if proptools.String(a.properties.Payload_type) == "zip" { - a.suffix = "" - a.primaryApexType = true - } else { - a.suffix = zipApexSuffix - } - case flattenedApex: - if buildFlattenedAsDefault { - a.suffix = "" - a.primaryApexType = true - } else { - a.suffix = flattenedSuffix - } - } -} - -func (a apexBundle) isCompressable() bool { +func (a *apexBundle) isCompressable() bool { return proptools.BoolDefault(a.overridableProperties.Compressible, false) && !a.testApex } @@ -2181,11 +1887,23 @@ type visitorContext struct { // if true, raise error on duplicate apexFile checkDuplicate bool + + // visitor skips these from this list of module names + unwantedTransitiveDeps []string + + aconfigFiles []android.Path } func (vctx *visitorContext) normalizeFileInfo(mctx android.ModuleContext) { encountered := make(map[string]apexFile) for _, f := range vctx.filesInfo { + // Skips unwanted transitive deps. This happens, for example, with Rust binaries with prefer_rlib:true. + // TODO(b/295593640) + // Needs additional verification for the resulting APEX to ensure that skipped artifacts don't make problems. + // For example, DT_NEEDED modules should be found within the APEX unless they are marked in `requiredNativeLibs`. + if f.transitiveDep && f.module != nil && android.InList(mctx.OtherModuleName(f.module), vctx.unwantedTransitiveDeps) { + continue + } dest := filepath.Join(f.installDir, f.builtFile.Base()) if e, ok := encountered[dest]; !ok { encountered[dest] = f @@ -2198,6 +1916,9 @@ func (vctx *visitorContext) normalizeFileInfo(mctx android.ModuleContext) { // If a module is directly included and also transitively depended on // consider it as directly included. e.transitiveDep = e.transitiveDep && f.transitiveDep + // If a module is added as both a JNI library and a regular shared library, consider it as a + // JNI library. + e.isJniLib = e.isJniLib || f.isJniLib encountered[dest] = e } } @@ -2225,11 +1946,19 @@ func (a *apexBundle) depVisitor(vctx *visitorContext, ctx android.ModuleContext, switch depTag { case sharedLibTag, jniLibTag: isJniLib := depTag == jniLibTag + propertyName := "native_shared_libs" + if isJniLib { + propertyName = "jni_libs" + } switch ch := child.(type) { case *cc.Module: + if ch.IsStubs() { + ctx.PropertyErrorf(propertyName, "%q is a stub. Remove it from the list.", depName) + } fi := apexFileForNativeLibrary(ctx, ch, vctx.handleSpecialLibs) fi.isJniLib = isJniLib vctx.filesInfo = append(vctx.filesInfo, fi) + addAconfigFiles(vctx, ctx, child) // Collect the list of stub-providing libs except: // - VNDK libs are only for vendors // - bootstrap bionic libs are treated as provided by system @@ -2241,29 +1970,20 @@ func (a *apexBundle) depVisitor(vctx *visitorContext, ctx android.ModuleContext, fi := apexFileForRustLibrary(ctx, ch) fi.isJniLib = isJniLib vctx.filesInfo = append(vctx.filesInfo, fi) + addAconfigFiles(vctx, ctx, child) return true // track transitive dependencies default: - propertyName := "native_shared_libs" - if isJniLib { - propertyName = "jni_libs" - } ctx.PropertyErrorf(propertyName, "%q is not a cc_library or cc_library_shared module", depName) } case executableTag: switch ch := child.(type) { case *cc.Module: vctx.filesInfo = append(vctx.filesInfo, apexFileForExecutable(ctx, ch)) + addAconfigFiles(vctx, ctx, child) return true // track transitive dependencies - case *python.PythonBinaryModule: - if ch.HostToolPath().Valid() { - vctx.filesInfo = append(vctx.filesInfo, apexFileForPyBinary(ctx, ch)) - } - case bootstrap.GoBinaryTool: - if a.Host() { - vctx.filesInfo = append(vctx.filesInfo, apexFileForGoBinary(ctx, depName, ch)) - } case *rust.Module: vctx.filesInfo = append(vctx.filesInfo, apexFileForRustExecutable(ctx, ch)) + addAconfigFiles(vctx, ctx, child) return true // track transitive dependencies default: ctx.PropertyErrorf("binaries", @@ -2303,6 +2023,7 @@ func (a *apexBundle) depVisitor(vctx *visitorContext, ctx android.ModuleContext, return false } vctx.filesInfo = append(vctx.filesInfo, af) + addAconfigFiles(vctx, ctx, child) return true // track transitive dependencies default: ctx.PropertyErrorf("java_libs", "%q of type %q is not supported", depName, ctx.OtherModuleType(child)) @@ -2310,12 +2031,13 @@ func (a *apexBundle) depVisitor(vctx *visitorContext, ctx android.ModuleContext, case androidAppTag: switch ap := child.(type) { case *java.AndroidApp: - vctx.filesInfo = append(vctx.filesInfo, apexFileForAndroidApp(ctx, ap)) + vctx.filesInfo = append(vctx.filesInfo, apexFilesForAndroidApp(ctx, ap)...) + addAconfigFiles(vctx, ctx, child) return true // track transitive dependencies case *java.AndroidAppImport: - vctx.filesInfo = append(vctx.filesInfo, apexFileForAndroidApp(ctx, ap)) + vctx.filesInfo = append(vctx.filesInfo, apexFilesForAndroidApp(ctx, ap)...) case *java.AndroidTestHelperApp: - vctx.filesInfo = append(vctx.filesInfo, apexFileForAndroidApp(ctx, ap)) + vctx.filesInfo = append(vctx.filesInfo, apexFilesForAndroidApp(ctx, ap)...) case *java.AndroidAppSet: appDir := "app" if ap.Privileged() { @@ -2416,18 +2138,17 @@ func (a *apexBundle) depVisitor(vctx *visitorContext, ctx android.ModuleContext, // tags used below are private (e.g. `cc.sharedDepTag`). if cc.IsSharedDepTag(depTag) || cc.IsRuntimeDepTag(depTag) { if ch, ok := child.(*cc.Module); ok { - if ch.UseVndk() && proptools.Bool(a.properties.Use_vndk_as_stable) && ch.IsVndk() { + if ch.UseVndk() && a.useVndkAsStable(ctx) && ch.IsVndk() { vctx.requireNativeLibs = append(vctx.requireNativeLibs, ":vndk") return false } - af := apexFileForNativeLibrary(ctx, ch, vctx.handleSpecialLibs) - af.transitiveDep = true - // Always track transitive dependencies for host. - if a.Host() { - vctx.filesInfo = append(vctx.filesInfo, af) - return true + //TODO: b/296491928 Vendor APEX should use libbinder.ndk instead of libbinder once VNDK is fully deprecated. + if ch.UseVndk() && ctx.Config().IsVndkDeprecated() && child.Name() == "libbinder" { + return false } + af := apexFileForNativeLibrary(ctx, ch, vctx.handleSpecialLibs) + af.transitiveDep = true abInfo := ctx.Provider(ApexBundleInfoProvider).(ApexBundleInfo) if !abInfo.Contents.DirectlyInApex(depName) && (ch.IsStubs() || ch.HasStubsVariants()) { @@ -2550,20 +2271,19 @@ func (a *apexBundle) depVisitor(vctx *visitorContext, ctx android.ModuleContext, return false } +func addAconfigFiles(vctx *visitorContext, ctx android.ModuleContext, module blueprint.Module) { + dep := ctx.OtherModuleProvider(module, aconfig.TransitiveDeclarationsInfoProvider).(aconfig.TransitiveDeclarationsInfo) + if len(dep.AconfigFiles) > 0 && dep.AconfigFiles[ctx.ModuleName()] != nil { + vctx.aconfigFiles = append(vctx.aconfigFiles, dep.AconfigFiles[ctx.ModuleName()]...) + } +} + func (a *apexBundle) shouldCheckDuplicate(ctx android.ModuleContext) bool { // TODO(b/263308293) remove this if a.properties.IsCoverageVariant { return false } - // TODO(b/263308515) remove this - if a.testApex { - return false - } - // TODO(b/263309864) remove this - if a.Host() { - return false - } - if a.Device() && ctx.DeviceConfig().DeviceArch() == "" { + if ctx.DeviceConfig().DeviceArch() == "" { return false } return true @@ -2583,63 +2303,74 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) { } //////////////////////////////////////////////////////////////////////////////////////////// // 2) traverse the dependency tree to collect apexFile structs from them. - // Collect the module directory for IDE info in java/jdeps.go. - a.modulePaths = append(a.modulePaths, ctx.ModuleDir()) // TODO(jiyong): do this using WalkPayloadDeps // TODO(jiyong): make this clean!!! vctx := visitorContext{ - handleSpecialLibs: !android.Bool(a.properties.Ignore_system_library_special_case), - checkDuplicate: a.shouldCheckDuplicate(ctx), + handleSpecialLibs: !android.Bool(a.properties.Ignore_system_library_special_case), + checkDuplicate: a.shouldCheckDuplicate(ctx), + unwantedTransitiveDeps: a.properties.Unwanted_transitive_deps, } ctx.WalkDepsBlueprint(func(child, parent blueprint.Module) bool { return a.depVisitor(&vctx, ctx, child, parent) }) vctx.normalizeFileInfo(ctx) if a.privateKeyFile == nil { - ctx.PropertyErrorf("key", "private_key for %q could not be found", String(a.overridableProperties.Key)) - return + if ctx.Config().AllowMissingDependencies() { + // TODO(b/266099037): a better approach for slim manifests. + ctx.AddMissingDependencies([]string{String(a.overridableProperties.Key)}) + // Create placeholder paths for later stages that expect to see those paths, + // though they won't be used. + var unusedPath = android.PathForModuleOut(ctx, "nonexistentprivatekey") + ctx.Build(pctx, android.BuildParams{ + Rule: android.ErrorRule, + Output: unusedPath, + Args: map[string]string{ + "error": "Private key not available", + }, + }) + a.privateKeyFile = unusedPath + } else { + ctx.PropertyErrorf("key", "private_key for %q could not be found", String(a.overridableProperties.Key)) + return + } + } + + if a.publicKeyFile == nil { + if ctx.Config().AllowMissingDependencies() { + // TODO(b/266099037): a better approach for slim manifests. + ctx.AddMissingDependencies([]string{String(a.overridableProperties.Key)}) + // Create placeholder paths for later stages that expect to see those paths, + // though they won't be used. + var unusedPath = android.PathForModuleOut(ctx, "nonexistentpublickey") + ctx.Build(pctx, android.BuildParams{ + Rule: android.ErrorRule, + Output: unusedPath, + Args: map[string]string{ + "error": "Public key not available", + }, + }) + a.publicKeyFile = unusedPath + } else { + ctx.PropertyErrorf("key", "public_key for %q could not be found", String(a.overridableProperties.Key)) + return + } } //////////////////////////////////////////////////////////////////////////////////////////// // 3) some fields in apexBundle struct are configured a.installDir = android.PathForModuleInstall(ctx, "apex") a.filesInfo = vctx.filesInfo + a.aconfigFiles = android.FirstUniquePaths(vctx.aconfigFiles) - a.setApexTypeAndSuffix(ctx) a.setPayloadFsType(ctx) a.setSystemLibLink(ctx) - if a.properties.ApexType != zipApex { - a.compatSymlinks = makeCompatSymlinks(a.BaseModuleName(), ctx, a.primaryApexType) - } + a.compatSymlinks = makeCompatSymlinks(a.BaseModuleName(), ctx) //////////////////////////////////////////////////////////////////////////////////////////// // 4) generate the build rules to create the APEX. This is done in builder.go. a.buildManifest(ctx, vctx.provideNativeLibs, vctx.requireNativeLibs) - if a.properties.ApexType == flattenedApex { - a.buildFlattenedApex(ctx) - } else { - a.buildUnflattenedApex(ctx) - } + a.buildApex(ctx) a.buildApexDependencyInfo(ctx) a.buildLintReports(ctx) - - // Append meta-files to the filesInfo list so that they are reflected in Android.mk as well. - if a.installable() { - // For flattened APEX, make sure that APEX manifest and apex_pubkey are also copied - // along with other ordinary files. (Note that this is done by apexer for - // non-flattened APEXes) - a.filesInfo = append(a.filesInfo, newApexFile(ctx, a.manifestPbOut, "apex_manifest.pb", ".", etc, nil)) - - // Place the public key as apex_pubkey. This is also done by apexer for - // non-flattened APEXes case. - // TODO(jiyong): Why do we need this CP rule? - copiedPubkey := android.PathForModuleOut(ctx, "apex_pubkey") - ctx.Build(pctx, android.BuildParams{ - Rule: android.Cp, - Input: a.publicKeyFile, - Output: copiedPubkey, - }) - a.filesInfo = append(a.filesInfo, newApexFile(ctx, copiedPubkey, "apex_pubkey", ".", etc, nil)) - } } // apexBootclasspathFragmentFiles returns the list of apexFile structures defining the files that @@ -2733,10 +2464,9 @@ func newApexBundle() *apexBundle { module.AddProperties(&module.archProperties) module.AddProperties(&module.overridableProperties) - android.InitAndroidMultiTargetsArchModule(module, android.HostAndDeviceSupported, android.MultilibCommon) + android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon) android.InitDefaultableModule(module) android.InitOverridableModule(module, &module.overridableProperties.Overrides) - android.InitBazelModule(module) multitree.InitExportableModule(module) return module } @@ -2784,7 +2514,6 @@ func DefaultsFactory() android.Module { type OverrideApex struct { android.ModuleBase android.OverrideModuleBase - android.BazelModuleBase } func (o *OverrideApex) GenerateAndroidBuildActions(_ android.ModuleContext) { @@ -2800,104 +2529,9 @@ func OverrideApexFactory() android.Module { android.InitAndroidMultiTargetsArchModule(m, android.DeviceSupported, android.MultilibCommon) android.InitOverrideModule(m) - android.InitBazelModule(m) return m } -func (o *OverrideApex) ConvertWithBp2build(ctx android.TopDownMutatorContext) { - if ctx.ModuleType() != "override_apex" { - return - } - - baseApexModuleName := o.OverrideModuleBase.GetOverriddenModuleName() - baseModule, baseApexExists := ctx.ModuleFromName(baseApexModuleName) - if !baseApexExists { - panic(fmt.Errorf("Base apex module doesn't exist: %s", baseApexModuleName)) - } - - a, baseModuleIsApex := baseModule.(*apexBundle) - if !baseModuleIsApex { - panic(fmt.Errorf("Base module is not apex module: %s", baseApexModuleName)) - } - attrs, props, commonAttrs := convertWithBp2build(a, ctx) - - // We just want the name, not module reference. - baseApexName := strings.TrimPrefix(baseApexModuleName, ":") - attrs.Base_apex_name = &baseApexName - - for _, p := range o.GetProperties() { - overridableProperties, ok := p.(*overridableProperties) - if !ok { - continue - } - - // Manifest is either empty or a file in the directory of base APEX and is not overridable. - // After it is converted in convertWithBp2build(baseApex, ctx), - // the attrs.Manifest.Value.Label is the file path relative to the directory - // of base apex. So the following code converts it to a label that looks like - // : if base apex and override - // apex are not in the same package. - baseApexPackage := ctx.OtherModuleDir(a) - overrideApexPackage := ctx.ModuleDir() - if baseApexPackage != overrideApexPackage { - attrs.Manifest.Value.Label = "//" + baseApexPackage + ":" + attrs.Manifest.Value.Label - } - - // Key - if overridableProperties.Key != nil { - attrs.Key = bazel.LabelAttribute{} - attrs.Key.SetValue(android.BazelLabelForModuleDepSingle(ctx, *overridableProperties.Key)) - } - - // Certificate - if overridableProperties.Certificate == nil { - // If overridableProperties.Certificate is nil, clear this out as - // well with zeroed structs, so the override_apex does not use the - // base apex's certificate. - attrs.Certificate = bazel.LabelAttribute{} - attrs.Certificate_name = bazel.StringAttribute{} - } else { - attrs.Certificate, attrs.Certificate_name = android.BazelStringOrLabelFromProp(ctx, overridableProperties.Certificate) - } - - // Prebuilts - if overridableProperties.Prebuilts != nil { - prebuiltsLabelList := android.BazelLabelForModuleDeps(ctx, overridableProperties.Prebuilts) - attrs.Prebuilts = bazel.MakeLabelListAttribute(prebuiltsLabelList) - } - - // Compressible - if overridableProperties.Compressible != nil { - attrs.Compressible = bazel.BoolAttribute{Value: overridableProperties.Compressible} - } - - // Package name - // - // e.g. com.android.adbd's package name is com.android.adbd, but - // com.google.android.adbd overrides the package name to com.google.android.adbd - // - // TODO: this can be overridden from the product configuration, see - // getOverrideManifestPackageName and - // PRODUCT_MANIFEST_PACKAGE_NAME_OVERRIDES. - // - // Instead of generating the BUILD files differently based on the product config - // at the point of conversion, this should be handled by the BUILD file loading - // from the soong_injection's product_vars, so product config is decoupled from bp2build. - if overridableProperties.Package_name != "" { - attrs.Package_name = &overridableProperties.Package_name - } - - // Logging parent - if overridableProperties.Logging_parent != "" { - attrs.Logging_parent = &overridableProperties.Logging_parent - } - } - - commonAttrs.Name = o.Name() - - ctx.CreateBazelTargetModule(props, commonAttrs, &attrs) -} - /////////////////////////////////////////////////////////////////////////////////////////////////// // Vality check routines // @@ -2924,13 +2558,13 @@ func (a *apexBundle) minSdkVersionValue(ctx android.EarlyModuleContext) string { // Only override the minSdkVersion value on Apexes which already specify // a min_sdk_version (it's optional for non-updatable apexes), and that its // min_sdk_version value is lower than the one to override with. - minApiLevel := minSdkVersionFromValue(ctx, proptools.String(a.properties.Min_sdk_version)) + minApiLevel := android.MinSdkVersionFromValue(ctx, proptools.String(a.properties.Min_sdk_version)) if minApiLevel.IsNone() { return "" } overrideMinSdkValue := ctx.DeviceConfig().ApexGlobalMinSdkVersionOverride() - overrideApiLevel := minSdkVersionFromValue(ctx, overrideMinSdkValue) + overrideApiLevel := android.MinSdkVersionFromValue(ctx, overrideMinSdkValue) if !overrideApiLevel.IsNone() && overrideApiLevel.CompareTo(minApiLevel) > 0 { minApiLevel = overrideApiLevel } @@ -2945,26 +2579,13 @@ func (a *apexBundle) MinSdkVersion(ctx android.EarlyModuleContext) android.ApiLe // Returns apex's min_sdk_version ApiLevel, honoring overrides func (a *apexBundle) minSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel { - return minSdkVersionFromValue(ctx, a.minSdkVersionValue(ctx)) -} - -// Construct ApiLevel object from min_sdk_version string value -func minSdkVersionFromValue(ctx android.EarlyModuleContext, value string) android.ApiLevel { - if value == "" { - return android.NoneApiLevel - } - apiLevel, err := android.ApiLevelFromUser(ctx, value) - if err != nil { - ctx.PropertyErrorf("min_sdk_version", "%s", err.Error()) - return android.NoneApiLevel - } - return apiLevel + return android.MinSdkVersionFromValue(ctx, a.minSdkVersionValue(ctx)) } // Ensures that a lib providing stub isn't statically linked func (a *apexBundle) checkStaticLinkingToStubLibraries(ctx android.ModuleContext) { // Practically, we only care about regular APEXes on the device. - if ctx.Host() || a.testApex || a.vndkApex { + if a.testApex || a.vndkApex { return } @@ -3059,7 +2680,7 @@ func (a *apexBundle) checkJavaStableSdkVersion(ctx android.ModuleContext) { // checkApexAvailability ensures that the all the dependencies are marked as available for this APEX. func (a *apexBundle) checkApexAvailability(ctx android.ModuleContext) { // Let's be practical. Availability for test, host, and the VNDK apex isn't important - if ctx.Host() || a.testApex || a.vndkApex { + if a.testApex || a.vndkApex { return } @@ -3083,6 +2704,13 @@ func (a *apexBundle) checkApexAvailability(ctx android.ModuleContext) { } apexName := ctx.ModuleName() + for _, props := range ctx.Module().GetProperties() { + if apexProps, ok := props.(*apexBundleProperties); ok { + if apexProps.Apex_available_name != nil { + apexName = *apexProps.Apex_available_name + } + } + } fromName := ctx.OtherModuleName(from) toName := ctx.OtherModuleName(to) @@ -3110,11 +2738,6 @@ func (a *apexBundle) checkApexAvailability(ctx android.ModuleContext) { // checkStaticExecutable ensures that executables in an APEX are not static. func (a *apexBundle) checkStaticExecutables(ctx android.ModuleContext) { - // No need to run this for host APEXes - if ctx.Host() { - return - } - ctx.VisitDirectDepsBlueprint(func(module blueprint.Module) { if ctx.OtherModuleDependencyTag(module) != executableTag { return @@ -3148,7 +2771,6 @@ func (a *apexBundle) IDEInfo(dpInfo *android.IdeInfo) { dpInfo.Deps = append(dpInfo.Deps, a.properties.Java_libs...) dpInfo.Deps = append(dpInfo.Deps, a.properties.Bootclasspath_fragments...) dpInfo.Deps = append(dpInfo.Deps, a.properties.Systemserverclasspath_fragments...) - dpInfo.Paths = append(dpInfo.Paths, a.modulePaths...) } var ( @@ -3213,65 +2835,6 @@ func makeApexAvailableBaseline() map[string][]string { // // Module separator // - m["com.android.appsearch"] = []string{ - "icing-java-proto-lite", - "libprotobuf-java-lite", - } - // - // Module separator - // - m["com.android.btservices"] = []string{ - // empty - } - // - // Module separator - // - m["com.android.cellbroadcast"] = []string{"CellBroadcastApp", "CellBroadcastServiceModule"} - // - // Module separator - // - m["com.android.extservices"] = []string{ - "error_prone_annotations", - "ExtServices-core", - "ExtServices", - "libtextclassifier-java", - "libz_current", - "textclassifier-statsd", - "TextClassifierNotificationLibNoManifest", - "TextClassifierServiceLibNoManifest", - } - // - // Module separator - // - m["com.android.neuralnetworks"] = []string{ - "android.hardware.neuralnetworks@1.0", - "android.hardware.neuralnetworks@1.1", - "android.hardware.neuralnetworks@1.2", - "android.hardware.neuralnetworks@1.3", - "android.hidl.allocator@1.0", - "android.hidl.memory.token@1.0", - "android.hidl.memory@1.0", - "android.hidl.safe_union@1.0", - "libarect", - "libbuildversion", - "libmath", - "libprocpartition", - } - // - // Module separator - // - m["com.android.media"] = []string{ - // empty - } - // - // Module separator - // - m["com.android.media.swcodec"] = []string{ - // empty - } - // - // Module separator - // m["com.android.mediaprovider"] = []string{ "MediaProvider", "MediaProviderGoogle", @@ -3284,49 +2847,17 @@ func makeApexAvailableBaseline() map[string][]string { // Module separator // m["com.android.runtime"] = []string{ - "bionic_libc_platform_headers", - "libarm-optimized-routines-math", - "libc_aeabi", - "libc_bionic", - "libc_bionic_ndk", - "libc_bootstrap", - "libc_common", - "libc_common_shared", - "libc_common_static", - "libc_dns", - "libc_dynamic_dispatch", - "libc_fortify", - "libc_freebsd", - "libc_freebsd_large_stack", - "libc_gdtoa", - "libc_init_dynamic", - "libc_init_static", - "libc_jemalloc_wrapper", - "libc_netbsd", - "libc_nomalloc", - "libc_nopthread", - "libc_openbsd", - "libc_openbsd_large_stack", - "libc_openbsd_ndk", - "libc_pthread", - "libc_static_dispatch", - "libc_syscalls", - "libc_tzcode", - "libc_unwind_static", "libdebuggerd", "libdebuggerd_common_headers", "libdebuggerd_handler_core", - "libdebuggerd_handler_fallback", "libdl_static", "libjemalloc5", "liblinker_main", "liblinker_malloc", - "liblz4", "liblzma", "libprocinfo", "libpropertyinfoparser", "libscudo", - "libstdc++", "libsystemproperties", "libtombstoned_client_static", "libunwindstack", @@ -3339,17 +2870,7 @@ func makeApexAvailableBaseline() map[string][]string { m["com.android.tethering"] = []string{ "android.hardware.tetheroffload.config-V1.0-java", "android.hardware.tetheroffload.control-V1.0-java", - "android.hidl.base-V1.0-java", - "libcgrouprc", - "libcgrouprc_format", - "libtetherutilsjni", - "libvndksupport", "net-utils-framework-common", - "netd_aidl_interface-V3-java", - "netlink-client", - "networkstack-aidl-interfaces-java", - "tethering-aidl-interfaces-java", - "TetheringApiCurrentLib", } // // Module separator @@ -3369,57 +2890,22 @@ func makeApexAvailableBaseline() map[string][]string { "android.hardware.wifi.supplicant-V1.1-java", "android.hardware.wifi.supplicant-V1.2-java", "android.hardware.wifi.supplicant-V1.3-java", - "android.hidl.base-V1.0-java", - "android.hidl.manager-V1.0-java", - "android.hidl.manager-V1.1-java", - "android.hidl.manager-V1.2-java", "bouncycastle-unbundled", - "dnsresolver_aidl_interface-V2-java", - "error_prone_annotations", - "framework-wifi-pre-jarjar", "framework-wifi-util-lib", - "ipmemorystore-aidl-interfaces-V3-java", - "ipmemorystore-aidl-interfaces-java", "ksoap2", "libnanohttpd", - "libwifi-jni", - "net-utils-services-common", - "netd_aidl_interface-V2-java", - "netd_aidl_interface-unstable-java", - "netd_event_listener_interface-java", - "netlink-client", - "networkstack-client", - "services.net", "wifi-lite-protos", "wifi-nano-protos", "wifi-service-pre-jarjar", - "wifi-service-resources", - } - // - // Module separator - // - m["com.android.os.statsd"] = []string{ - "libstatssocket", } // // Module separator // m[android.AvailableToAnyApex] = []string{ - // TODO(b/156996905) Set apex_available/min_sdk_version for androidx/extras support libraries - "androidx", - "androidx-constraintlayout_constraintlayout", - "androidx-constraintlayout_constraintlayout-nodeps", - "androidx-constraintlayout_constraintlayout-solver", - "androidx-constraintlayout_constraintlayout-solver-nodeps", - "com.google.android.material_material", - "com.google.android.material_material-nodeps", - - "libclang_rt", "libprofile-clang-extras", "libprofile-clang-extras_ndk", "libprofile-extras", "libprofile-extras_ndk", - "libunwind", } return m } @@ -3500,293 +2986,15 @@ func rBcpPackages() map[string][]string { } } -// For Bazel / bp2build - -type bazelApexBundleAttributes struct { - Manifest bazel.LabelAttribute - Android_manifest bazel.LabelAttribute - File_contexts bazel.LabelAttribute - Canned_fs_config bazel.LabelAttribute - Key bazel.LabelAttribute - Certificate bazel.LabelAttribute // used when the certificate prop is a module - Certificate_name bazel.StringAttribute // used when the certificate prop is a string - Min_sdk_version bazel.StringAttribute - Updatable bazel.BoolAttribute - Installable bazel.BoolAttribute - Binaries bazel.LabelListAttribute - Prebuilts bazel.LabelListAttribute - Native_shared_libs_32 bazel.LabelListAttribute - Native_shared_libs_64 bazel.LabelListAttribute - Compressible bazel.BoolAttribute - Package_name *string - Logging_parent *string - Tests bazel.LabelListAttribute - Base_apex_name *string -} - -type convertedNativeSharedLibs struct { - Native_shared_libs_32 bazel.LabelListAttribute - Native_shared_libs_64 bazel.LabelListAttribute -} - -const ( - minSdkVersionPropName = "Min_sdk_version" -) - -// ConvertWithBp2build performs bp2build conversion of an apex -func (a *apexBundle) ConvertWithBp2build(ctx android.TopDownMutatorContext) { - // We only convert apex and apex_test modules at this time - if ctx.ModuleType() != "apex" && ctx.ModuleType() != "apex_test" { - return - } - - attrs, props, commonAttrs := convertWithBp2build(a, ctx) - commonAttrs.Name = a.Name() - ctx.CreateBazelTargetModule(props, commonAttrs, &attrs) +func (a *apexBundle) IsTestApex() bool { + return a.testApex } -func convertWithBp2build(a *apexBundle, ctx android.TopDownMutatorContext) (bazelApexBundleAttributes, bazel.BazelTargetModuleProperties, android.CommonAttributes) { - var manifestLabelAttribute bazel.LabelAttribute - manifestLabelAttribute.SetValue(android.BazelLabelForModuleSrcSingle(ctx, proptools.StringDefault(a.properties.Manifest, "apex_manifest.json"))) - - var androidManifestLabelAttribute bazel.LabelAttribute - if a.properties.AndroidManifest != nil { - androidManifestLabelAttribute.SetValue(android.BazelLabelForModuleSrcSingle(ctx, *a.properties.AndroidManifest)) - } - - var fileContextsLabelAttribute bazel.LabelAttribute - if a.properties.File_contexts == nil { - // See buildFileContexts(), if file_contexts is not specified the default one is used, which is //system/sepolicy/apex:-file_contexts - fileContextsLabelAttribute.SetValue(android.BazelLabelForModuleDepSingle(ctx, a.Name()+"-file_contexts")) - } else if strings.HasPrefix(*a.properties.File_contexts, ":") { - // File_contexts is a module - fileContextsLabelAttribute.SetValue(android.BazelLabelForModuleDepSingle(ctx, *a.properties.File_contexts)) - } else { - // File_contexts is a file - fileContextsLabelAttribute.SetValue(android.BazelLabelForModuleSrcSingle(ctx, *a.properties.File_contexts)) - } - - var cannedFsConfigAttribute bazel.LabelAttribute - if a.properties.Canned_fs_config != nil { - cannedFsConfigAttribute.SetValue(android.BazelLabelForModuleSrcSingle(ctx, *a.properties.Canned_fs_config)) - } - - productVariableProps := android.ProductVariableProperties(ctx, a) - // TODO(b/219503907) this would need to be set to a.MinSdkVersionValue(ctx) but - // given it's coming via config, we probably don't want to put it in here. - var minSdkVersion bazel.StringAttribute - if a.properties.Min_sdk_version != nil { - minSdkVersion.SetValue(*a.properties.Min_sdk_version) - } - if props, ok := productVariableProps[minSdkVersionPropName]; ok { - for c, p := range props { - if val, ok := p.(*string); ok { - minSdkVersion.SetSelectValue(c.ConfigurationAxis(), c.SelectKey(), val) - } - } - } - - var keyLabelAttribute bazel.LabelAttribute - if a.overridableProperties.Key != nil { - keyLabelAttribute.SetValue(android.BazelLabelForModuleDepSingle(ctx, *a.overridableProperties.Key)) - } - - // Certificate - certificate, certificateName := android.BazelStringOrLabelFromProp(ctx, a.overridableProperties.Certificate) - - nativeSharedLibs := &convertedNativeSharedLibs{ - Native_shared_libs_32: bazel.LabelListAttribute{}, - Native_shared_libs_64: bazel.LabelListAttribute{}, - } - - // https://cs.android.com/android/platform/superproject/+/master:build/soong/android/arch.go;l=698;drc=f05b0d35d2fbe51be9961ce8ce8031f840295c68 - // https://cs.android.com/android/platform/superproject/+/master:build/soong/apex/apex.go;l=2549;drc=ec731a83e3e2d80a1254e32fd4ad7ef85e262669 - // In Soong, decodeMultilib, used to get multilib, return "first" if defaultMultilib is set to "common". - // Since apex sets defaultMultilib to be "common", equivalent compileMultilib in bp2build for apex should be "first" - compileMultilib := "first" - if a.CompileMultilib() != nil { - compileMultilib = *a.CompileMultilib() - } - - // properties.Native_shared_libs is treated as "both" - convertBothLibs(ctx, compileMultilib, a.properties.Native_shared_libs, nativeSharedLibs) - convertBothLibs(ctx, compileMultilib, a.properties.Multilib.Both.Native_shared_libs, nativeSharedLibs) - convert32Libs(ctx, compileMultilib, a.properties.Multilib.Lib32.Native_shared_libs, nativeSharedLibs) - convert64Libs(ctx, compileMultilib, a.properties.Multilib.Lib64.Native_shared_libs, nativeSharedLibs) - convertFirstLibs(ctx, compileMultilib, a.properties.Multilib.First.Native_shared_libs, nativeSharedLibs) - - prebuilts := a.overridableProperties.Prebuilts - prebuiltsLabelList := android.BazelLabelForModuleDeps(ctx, prebuilts) - prebuiltsLabelListAttribute := bazel.MakeLabelListAttribute(prebuiltsLabelList) - - binaries := android.BazelLabelForModuleDeps(ctx, a.properties.ApexNativeDependencies.Binaries) - binariesLabelListAttribute := bazel.MakeLabelListAttribute(binaries) - - var testsAttrs bazel.LabelListAttribute - if a.testApex && len(a.properties.ApexNativeDependencies.Tests) > 0 { - tests := android.BazelLabelForModuleDeps(ctx, a.properties.ApexNativeDependencies.Tests) - testsAttrs = bazel.MakeLabelListAttribute(tests) - } - - var updatableAttribute bazel.BoolAttribute - if a.properties.Updatable != nil { - updatableAttribute.Value = a.properties.Updatable - } - - var installableAttribute bazel.BoolAttribute - if a.properties.Installable != nil { - installableAttribute.Value = a.properties.Installable - } - - var compressibleAttribute bazel.BoolAttribute - if a.overridableProperties.Compressible != nil { - compressibleAttribute.Value = a.overridableProperties.Compressible - } - - var packageName *string - if a.overridableProperties.Package_name != "" { - packageName = &a.overridableProperties.Package_name - } - - var loggingParent *string - if a.overridableProperties.Logging_parent != "" { - loggingParent = &a.overridableProperties.Logging_parent - } - - attrs := bazelApexBundleAttributes{ - Manifest: manifestLabelAttribute, - Android_manifest: androidManifestLabelAttribute, - File_contexts: fileContextsLabelAttribute, - Canned_fs_config: cannedFsConfigAttribute, - Min_sdk_version: minSdkVersion, - Key: keyLabelAttribute, - Certificate: certificate, - Certificate_name: certificateName, - Updatable: updatableAttribute, - Installable: installableAttribute, - Native_shared_libs_32: nativeSharedLibs.Native_shared_libs_32, - Native_shared_libs_64: nativeSharedLibs.Native_shared_libs_64, - Binaries: binariesLabelListAttribute, - Prebuilts: prebuiltsLabelListAttribute, - Compressible: compressibleAttribute, - Package_name: packageName, - Logging_parent: loggingParent, - Tests: testsAttrs, - } - - props := bazel.BazelTargetModuleProperties{ - Rule_class: "apex", - Bzl_load_location: "//build/bazel/rules/apex:apex.bzl", - } - - commonAttrs := android.CommonAttributes{} - if a.testApex { - commonAttrs.Testonly = proptools.BoolPtr(true) - } - - return attrs, props, commonAttrs -} - -// The following conversions are based on this table where the rows are the compile_multilib -// values and the columns are the properties.Multilib.*.Native_shared_libs. Each cell -// represents how the libs should be compiled for a 64-bit/32-bit device: 32 means it -// should be compiled as 32-bit, 64 means it should be compiled as 64-bit, none means it -// should not be compiled. -// multib/compile_multilib, 32, 64, both, first -// 32, 32/32, none/none, 32/32, none/32 -// 64, none/none, 64/none, 64/none, 64/none -// both, 32/32, 64/none, 32&64/32, 64/32 -// first, 32/32, 64/none, 64/32, 64/32 - -func convert32Libs(ctx android.TopDownMutatorContext, compileMultilb string, - libs []string, nativeSharedLibs *convertedNativeSharedLibs) { - libsLabelList := android.BazelLabelForModuleDeps(ctx, libs) - switch compileMultilb { - case "both", "32": - makeNoConfig32SharedLibsAttributes(libsLabelList, nativeSharedLibs) - case "first": - make32SharedLibsAttributes(libsLabelList, nativeSharedLibs) - case "64": - // Incompatible, ignore - default: - invalidCompileMultilib(ctx, compileMultilb) - } -} - -func convert64Libs(ctx android.TopDownMutatorContext, compileMultilb string, - libs []string, nativeSharedLibs *convertedNativeSharedLibs) { - libsLabelList := android.BazelLabelForModuleDeps(ctx, libs) - switch compileMultilb { - case "both", "64", "first": - make64SharedLibsAttributes(libsLabelList, nativeSharedLibs) - case "32": - // Incompatible, ignore - default: - invalidCompileMultilib(ctx, compileMultilb) - } -} - -func convertBothLibs(ctx android.TopDownMutatorContext, compileMultilb string, - libs []string, nativeSharedLibs *convertedNativeSharedLibs) { - libsLabelList := android.BazelLabelForModuleDeps(ctx, libs) - switch compileMultilb { - case "both": - makeNoConfig32SharedLibsAttributes(libsLabelList, nativeSharedLibs) - make64SharedLibsAttributes(libsLabelList, nativeSharedLibs) - case "first": - makeFirstSharedLibsAttributes(libsLabelList, nativeSharedLibs) - case "32": - makeNoConfig32SharedLibsAttributes(libsLabelList, nativeSharedLibs) - case "64": - make64SharedLibsAttributes(libsLabelList, nativeSharedLibs) - default: - invalidCompileMultilib(ctx, compileMultilb) - } -} - -func convertFirstLibs(ctx android.TopDownMutatorContext, compileMultilb string, - libs []string, nativeSharedLibs *convertedNativeSharedLibs) { - libsLabelList := android.BazelLabelForModuleDeps(ctx, libs) - switch compileMultilb { - case "both", "first": - makeFirstSharedLibsAttributes(libsLabelList, nativeSharedLibs) - case "32": - make32SharedLibsAttributes(libsLabelList, nativeSharedLibs) - case "64": - make64SharedLibsAttributes(libsLabelList, nativeSharedLibs) - default: - invalidCompileMultilib(ctx, compileMultilb) +func (a *apexBundle) useVndkAsStable(ctx android.BaseModuleContext) bool { + // VNDK cannot be linked if it is deprecated + if ctx.Config().IsVndkDeprecated() { + return false } -} - -func makeFirstSharedLibsAttributes(libsLabelList bazel.LabelList, nativeSharedLibs *convertedNativeSharedLibs) { - make32SharedLibsAttributes(libsLabelList, nativeSharedLibs) - make64SharedLibsAttributes(libsLabelList, nativeSharedLibs) -} - -func makeNoConfig32SharedLibsAttributes(libsLabelList bazel.LabelList, nativeSharedLibs *convertedNativeSharedLibs) { - list := bazel.LabelListAttribute{} - list.SetSelectValue(bazel.NoConfigAxis, "", libsLabelList) - nativeSharedLibs.Native_shared_libs_32.Append(list) -} - -func make32SharedLibsAttributes(libsLabelList bazel.LabelList, nativeSharedLibs *convertedNativeSharedLibs) { - makeSharedLibsAttributes("x86", libsLabelList, &nativeSharedLibs.Native_shared_libs_32) - makeSharedLibsAttributes("arm", libsLabelList, &nativeSharedLibs.Native_shared_libs_32) -} - -func make64SharedLibsAttributes(libsLabelList bazel.LabelList, nativeSharedLibs *convertedNativeSharedLibs) { - makeSharedLibsAttributes("x86_64", libsLabelList, &nativeSharedLibs.Native_shared_libs_64) - makeSharedLibsAttributes("arm64", libsLabelList, &nativeSharedLibs.Native_shared_libs_64) -} - -func makeSharedLibsAttributes(config string, libsLabelList bazel.LabelList, - labelListAttr *bazel.LabelListAttribute) { - list := bazel.LabelListAttribute{} - list.SetSelectValue(bazel.ArchConfigurationAxis, config, libsLabelList) - labelListAttr.Append(list) -} -func invalidCompileMultilib(ctx android.TopDownMutatorContext, value string) { - ctx.PropertyErrorf("compile_multilib", "Invalid value: %s", value) + return proptools.Bool(a.properties.Use_vndk_as_stable) } diff --git a/apex/apex_singleton.go b/apex/apex_singleton.go index 1581949375..a63344fc10 100644 --- a/apex/apex_singleton.go +++ b/apex/apex_singleton.go @@ -23,7 +23,11 @@ import ( ) func init() { - android.RegisterSingletonType("apex_depsinfo_singleton", apexDepsInfoSingletonFactory) + registerApexDepsInfoComponents(android.InitRegistrationContext) +} + +func registerApexDepsInfoComponents(ctx android.RegistrationContext) { + ctx.RegisterParallelSingletonType("apex_depsinfo_singleton", apexDepsInfoSingletonFactory) } type apexDepsInfoSingleton struct { diff --git a/apex/apex_test.go b/apex/apex_test.go index e12f758b14..abf6b15340 100644 --- a/apex/apex_test.go +++ b/apex/apex_test.go @@ -25,6 +25,8 @@ import ( "strings" "testing" + "android/soong/aconfig/codegen" + "github.com/google/blueprint" "github.com/google/blueprint/proptools" @@ -151,6 +153,7 @@ var prepareForApexTest = android.GroupFixturePreparers( prebuilt_etc.PrepareForTestWithPrebuiltEtc, rust.PrepareForTestWithRustDefaultModules, sh.PrepareForTestWithShBuildComponents, + codegen.PrepareForTestWithAconfigBuildComponents, PrepareForTestWithApexBuildComponents, @@ -390,7 +393,7 @@ func TestBasicApex(t *testing.T) { name: "foo.rust", srcs: ["foo.rs"], rlibs: ["libfoo.rlib.rust"], - dylibs: ["libfoo.dylib.rust"], + rustlibs: ["libfoo.dylib.rust"], apex_available: ["myapex"], } @@ -518,10 +521,10 @@ func TestBasicApex(t *testing.T) { } `) - apexRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexRule") + apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule") // Make sure that Android.mk is created - ab := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle) + ab := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(*apexBundle) data := android.AndroidMkDataForTest(t, ctx, ab) var builder strings.Builder data.Custom(&builder, ab.BaseModuleName(), "TARGET_", "", data) @@ -533,7 +536,7 @@ func TestBasicApex(t *testing.T) { optFlags := apexRule.Args["opt_flags"] ensureContains(t, optFlags, "--pubkey vendor/foo/devkeys/testkey.avbpubkey") // Ensure that the NOTICE output is being packaged as an asset. - ensureContains(t, optFlags, "--assets_dir out/soong/.intermediates/myapex/android_common_myapex_image/NOTICE") + ensureContains(t, optFlags, "--assets_dir out/soong/.intermediates/myapex/android_common_myapex/NOTICE") copyCmds := apexRule.Args["copy_commands"] @@ -595,13 +598,15 @@ func TestBasicApex(t *testing.T) { t.Errorf("Could not find all expected symlinks! foo: %t, foo_link_64: %t. Command was %s", found_foo, found_foo_link_64, copyCmds) } - fullDepsInfo := strings.Split(ctx.ModuleForTests("myapex", "android_common_myapex_image").Output("depsinfo/fulllist.txt").Args["content"], "\\n") + fullDepsInfo := strings.Split(android.ContentFromFileRuleForTests(t, ctx, + ctx.ModuleForTests("myapex", "android_common_myapex").Output("depsinfo/fulllist.txt")), "\n") ensureListContains(t, fullDepsInfo, " myjar(minSdkVersion:(no version)) <- myapex") ensureListContains(t, fullDepsInfo, " mylib2(minSdkVersion:(no version)) <- mylib") ensureListContains(t, fullDepsInfo, " myotherjar(minSdkVersion:(no version)) <- myjar") ensureListContains(t, fullDepsInfo, " mysharedjar(minSdkVersion:(no version)) (external) <- myjar") - flatDepsInfo := strings.Split(ctx.ModuleForTests("myapex", "android_common_myapex_image").Output("depsinfo/flatlist.txt").Args["content"], "\\n") + flatDepsInfo := strings.Split(android.ContentFromFileRuleForTests(t, ctx, + ctx.ModuleForTests("myapex", "android_common_myapex").Output("depsinfo/flatlist.txt")), "\n") ensureListContains(t, flatDepsInfo, "myjar(minSdkVersion:(no version))") ensureListContains(t, flatDepsInfo, "mylib2(minSdkVersion:(no version))") ensureListContains(t, flatDepsInfo, "myotherjar(minSdkVersion:(no version))") @@ -678,7 +683,7 @@ func TestDefaults(t *testing.T) { } `) - ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{ + ensureExactContents(t, ctx, "myapex", "android_common_myapex", []string{ "etc/myetc", "javalib/myjar.jar", "lib64/mylib.so", @@ -705,7 +710,7 @@ func TestApexManifest(t *testing.T) { } `) - module := ctx.ModuleForTests("myapex", "android_common_myapex_image") + module := ctx.ModuleForTests("myapex", "android_common_myapex") args := module.Rule("apexRule").Args if manifest := args["manifest"]; manifest != module.Output("apex_manifest.pb").Output.String() { t.Error("manifest should be apex_manifest.pb, but " + manifest) @@ -776,7 +781,7 @@ func TestApexManifestMinSdkVersion(t *testing.T) { }, } for _, tc := range testCases { - module := ctx.ModuleForTests(tc.module, "android_common_"+tc.module+"_image") + module := ctx.ModuleForTests(tc.module, "android_common_"+tc.module) args := module.Rule("apexRule").Args optFlags := args["opt_flags"] if !strings.Contains(optFlags, "--min_sdk_version "+tc.minSdkVersion) { @@ -786,18 +791,16 @@ func TestApexManifestMinSdkVersion(t *testing.T) { } func TestFileContexts(t *testing.T) { - for _, useFileContextsAsIs := range []bool{true, false} { + for _, vendor := range []bool{true, false} { prop := "" - if useFileContextsAsIs { - prop = "use_file_contexts_as_is: true,\n" + if vendor { + prop = "vendor: true,\n" } ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", - file_contexts: "file_contexts", updatable: false, - vendor: true, `+prop+` } @@ -806,73 +809,21 @@ func TestFileContexts(t *testing.T) { public_key: "testkey.avbpubkey", private_key: "testkey.pem", } - `, withFiles(map[string][]byte{ - "file_contexts": nil, - })) + `) - rule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Output("file_contexts") - forceLabellingCommand := "apex_manifest\\\\.pb u:object_r:system_file:s0" - if useFileContextsAsIs { - android.AssertStringDoesNotContain(t, "should force-label", - rule.RuleParams.Command, forceLabellingCommand) + rule := ctx.ModuleForTests("myapex", "android_common_myapex").Output("file_contexts") + if vendor { + android.AssertStringDoesContain(t, "should force-label as vendor_apex_metadata_file", + rule.RuleParams.Command, + "apex_manifest\\\\.pb u:object_r:vendor_apex_metadata_file:s0") } else { - android.AssertStringDoesContain(t, "shouldn't force-label", - rule.RuleParams.Command, forceLabellingCommand) + android.AssertStringDoesContain(t, "should force-label as system_file", + rule.RuleParams.Command, + "apex_manifest\\\\.pb u:object_r:system_file:s0") } } } -func TestBasicZipApex(t *testing.T) { - ctx := testApex(t, ` - apex { - name: "myapex", - key: "myapex.key", - payload_type: "zip", - native_shared_libs: ["mylib"], - updatable: false, - } - - apex_key { - name: "myapex.key", - public_key: "testkey.avbpubkey", - private_key: "testkey.pem", - } - - cc_library { - name: "mylib", - srcs: ["mylib.cpp"], - shared_libs: ["mylib2"], - system_shared_libs: [], - stl: "none", - apex_available: [ "myapex" ], - } - - cc_library { - name: "mylib2", - srcs: ["mylib.cpp"], - system_shared_libs: [], - stl: "none", - apex_available: [ "myapex" ], - } - `) - - zipApexRule := ctx.ModuleForTests("myapex", "android_common_myapex_zip").Rule("zipApexRule") - copyCmds := zipApexRule.Args["copy_commands"] - - // Ensure that main rule creates an output - ensureContains(t, zipApexRule.Output.String(), "myapex.zipapex.unsigned") - - // Ensure that APEX variant is created for the direct dep - ensureListContains(t, ctx.ModuleVariantsForTests("mylib"), "android_arm64_armv8-a_shared_apex10000") - - // Ensure that APEX variant is created for the indirect dep - ensureListContains(t, ctx.ModuleVariantsForTests("mylib2"), "android_arm64_armv8-a_shared_apex10000") - - // Ensure that both direct and indirect deps are copied into apex - ensureContains(t, copyCmds, "image.zipapex/lib64/mylib.so") - ensureContains(t, copyCmds, "image.zipapex/lib64/mylib2.so") -} - func TestApexWithStubs(t *testing.T) { ctx := testApex(t, ` apex { @@ -949,7 +900,7 @@ func TestApexWithStubs(t *testing.T) { `) - apexRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexRule") + apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule") copyCmds := apexRule.Args["copy_commands"] // Ensure that direct non-stubs dep is always included @@ -990,7 +941,7 @@ func TestApexWithStubs(t *testing.T) { // Ensure that genstub for apex-provided lib is invoked with --apex ensureContains(t, ctx.ModuleForTests("mylib3", "android_arm64_armv8-a_shared_12").Rule("genStubSrc").Args["flags"], "--apex") - ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{ + ensureExactContents(t, ctx, "myapex", "android_common_myapex", []string{ "lib64/mylib.so", "lib64/mylib3.so", "lib64/mylib4.so", @@ -1002,14 +953,40 @@ func TestApexWithStubs(t *testing.T) { // Ensure that stub dependency from a rust module is not included ensureNotContains(t, copyCmds, "image.apex/lib64/libfoo.shared_from_rust.so") // The rust module is linked to the stub cc library - rustDeps := ctx.ModuleForTests("foo.rust", "android_arm64_armv8-a_apex10000").Rule("rustLink").Args["linkFlags"] + rustDeps := ctx.ModuleForTests("foo.rust", "android_arm64_armv8-a_apex10000").Rule("rustc").Args["linkFlags"] ensureContains(t, rustDeps, "libfoo.shared_from_rust/android_arm64_armv8-a_shared_current/libfoo.shared_from_rust.so") ensureNotContains(t, rustDeps, "libfoo.shared_from_rust/android_arm64_armv8-a_shared/libfoo.shared_from_rust.so") - apexManifestRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexManifestRule") + apexManifestRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexManifestRule") ensureListContains(t, names(apexManifestRule.Args["requireNativeLibs"]), "libfoo.shared_from_rust.so") } +func TestApexShouldNotEmbedStubVariant(t *testing.T) { + testApexError(t, `module "myapex" .*: native_shared_libs: "libbar" is a stub`, ` + apex { + name: "myapex", + key: "myapex.key", + vendor: true, + updatable: false, + native_shared_libs: ["libbar"], // should not add an LLNDK stub in a vendor apex + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + cc_library { + name: "libbar", + srcs: ["mylib.cpp"], + llndk: { + symbol_file: "libbar.map.txt", + } + } + `) +} + func TestApexCanUsePrivateApis(t *testing.T) { ctx := testApex(t, ` apex { @@ -1066,7 +1043,7 @@ func TestApexCanUsePrivateApis(t *testing.T) { } `) - apexRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexRule") + apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule") copyCmds := apexRule.Args["copy_commands"] // Ensure that indirect stubs dep is not included @@ -1078,7 +1055,7 @@ func TestApexCanUsePrivateApis(t *testing.T) { mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_apex10000").Rule("ld").Args["libFlags"] ensureNotContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared_current/mylib2.so") ensureContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared/mylib2.so") - rustDeps := ctx.ModuleForTests("foo.rust", "android_arm64_armv8-a_apex10000").Rule("rustLink").Args["linkFlags"] + rustDeps := ctx.ModuleForTests("foo.rust", "android_arm64_armv8-a_apex10000").Rule("rustc").Args["linkFlags"] ensureNotContains(t, rustDeps, "libfoo.shared_from_rust/android_arm64_armv8-a_shared_current/libfoo.shared_from_rust.so") ensureContains(t, rustDeps, "libfoo.shared_from_rust/android_arm64_armv8-a_shared/libfoo.shared_from_rust.so") } @@ -1144,7 +1121,7 @@ func TestApexWithStubsWithMinSdkVersion(t *testing.T) { } `) - apexRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexRule") + apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule") copyCmds := apexRule.Args["copy_commands"] // Ensure that direct non-stubs dep is always included @@ -1175,7 +1152,7 @@ func TestApexWithStubsWithMinSdkVersion(t *testing.T) { // Ensure that genstub is invoked with --systemapi ensureContains(t, ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_shared_29").Rule("genStubSrc").Args["flags"], "--systemapi") - ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{ + ensureExactContents(t, ctx, "myapex", "android_common_myapex", []string{ "lib64/mylib.so", "lib64/mylib3.so", "lib64/mylib4.so", @@ -1311,7 +1288,7 @@ func TestApexWithExplicitStubsDependency(t *testing.T) { `) - apexRule := ctx.ModuleForTests("myapex2", "android_common_myapex2_image").Rule("apexRule") + apexRule := ctx.ModuleForTests("myapex2", "android_common_myapex2").Rule("apexRule") copyCmds := apexRule.Args["copy_commands"] // Ensure that direct non-stubs dep is always included @@ -1335,10 +1312,12 @@ func TestApexWithExplicitStubsDependency(t *testing.T) { // Ensure that libfoo stubs is not linking to libbar (since it is a stubs) ensureNotContains(t, libFooStubsLdFlags, "libbar.so") - fullDepsInfo := strings.Split(ctx.ModuleForTests("myapex2", "android_common_myapex2_image").Output("depsinfo/fulllist.txt").Args["content"], "\\n") + fullDepsInfo := strings.Split(android.ContentFromFileRuleForTests(t, ctx, + ctx.ModuleForTests("myapex2", "android_common_myapex2").Output("depsinfo/fulllist.txt")), "\n") ensureListContains(t, fullDepsInfo, " libfoo(minSdkVersion:(no version)) (external) <- mylib") - flatDepsInfo := strings.Split(ctx.ModuleForTests("myapex2", "android_common_myapex2_image").Output("depsinfo/flatlist.txt").Args["content"], "\\n") + flatDepsInfo := strings.Split(android.ContentFromFileRuleForTests(t, ctx, + ctx.ModuleForTests("myapex2", "android_common_myapex2").Output("depsinfo/flatlist.txt")), "\n") ensureListContains(t, flatDepsInfo, "libfoo(minSdkVersion:(no version)) (external)") } @@ -1368,6 +1347,8 @@ func TestApexWithRuntimeLibsDependency(t *testing.T) { cc_library { name: "mylib", srcs: ["mylib.cpp"], + static_libs: ["libstatic"], + shared_libs: ["libshared"], runtime_libs: ["libfoo", "libbar"], system_shared_libs: [], stl: "none", @@ -1392,9 +1373,42 @@ func TestApexWithRuntimeLibsDependency(t *testing.T) { apex_available: [ "myapex" ], } + cc_library { + name: "libstatic", + srcs: ["mylib.cpp"], + system_shared_libs: [], + stl: "none", + apex_available: [ "myapex" ], + runtime_libs: ["libstatic_to_runtime"], + } + + cc_library { + name: "libshared", + srcs: ["mylib.cpp"], + system_shared_libs: [], + stl: "none", + apex_available: [ "myapex" ], + runtime_libs: ["libshared_to_runtime"], + } + + cc_library { + name: "libstatic_to_runtime", + srcs: ["mylib.cpp"], + system_shared_libs: [], + stl: "none", + apex_available: [ "myapex" ], + } + + cc_library { + name: "libshared_to_runtime", + srcs: ["mylib.cpp"], + system_shared_libs: [], + stl: "none", + apex_available: [ "myapex" ], + } `) - apexRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexRule") + apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule") copyCmds := apexRule.Args["copy_commands"] // Ensure that direct non-stubs dep is always included @@ -1405,11 +1419,14 @@ func TestApexWithRuntimeLibsDependency(t *testing.T) { // Ensure that runtime_libs dep in included ensureContains(t, copyCmds, "image.apex/lib64/libbar.so") + ensureContains(t, copyCmds, "image.apex/lib64/libshared.so") + ensureContains(t, copyCmds, "image.apex/lib64/libshared_to_runtime.so") + + ensureNotContains(t, copyCmds, "image.apex/lib64/libstatic_to_runtime.so") - apexManifestRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexManifestRule") + apexManifestRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexManifestRule") ensureListEmpty(t, names(apexManifestRule.Args["provideNativeLibs"])) ensureListContains(t, names(apexManifestRule.Args["requireNativeLibs"]), "libfoo.so") - } var prepareForTestOfRuntimeApexWithHwasan = android.GroupFixturePreparers( @@ -1461,10 +1478,14 @@ func TestRuntimeApexShouldInstallHwasanIfLibcDependsOnIt(t *testing.T) { sanitize: { never: true, }, + apex_available: [ + "//apex_available:anyapex", + "//apex_available:platform", + ], } `) ctx := result.TestContext - ensureExactContents(t, ctx, "com.android.runtime", "android_common_hwasan_com.android.runtime_image", []string{ + ensureExactContents(t, ctx, "com.android.runtime", "android_common_hwasan_com.android.runtime", []string{ "lib64/bionic/libc.so", "lib64/bionic/libclang_rt.hwasan-aarch64-android.so", }) @@ -1509,11 +1530,15 @@ func TestRuntimeApexShouldInstallHwasanIfHwaddressSanitized(t *testing.T) { sanitize: { never: true, }, + apex_available: [ + "//apex_available:anyapex", + "//apex_available:platform", + ], } `) ctx := result.TestContext - ensureExactContents(t, ctx, "com.android.runtime", "android_common_hwasan_com.android.runtime_image", []string{ + ensureExactContents(t, ctx, "com.android.runtime", "android_common_hwasan_com.android.runtime", []string{ "lib64/bionic/libc.so", "lib64/bionic/libclang_rt.hwasan-aarch64-android.so", }) @@ -1594,12 +1619,12 @@ func TestApexDependsOnLLNDKTransitively(t *testing.T) { ) // Ensure that LLNDK dep is not included - ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{ + ensureExactContents(t, ctx, "myapex", "android_common_myapex", []string{ "lib64/mylib.so", }) // Ensure that LLNDK dep is required - apexManifestRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexManifestRule") + apexManifestRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexManifestRule") ensureListEmpty(t, names(apexManifestRule.Args["provideNativeLibs"])) ensureListContains(t, names(apexManifestRule.Args["requireNativeLibs"]), "libbar.so") @@ -1659,7 +1684,7 @@ func TestApexWithSystemLibsStubs(t *testing.T) { } `) - apexRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexRule") + apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule") copyCmds := apexRule.Args["copy_commands"] // Ensure that mylib, libm, libdl are included. @@ -1981,6 +2006,94 @@ func TestApexMinSdkVersion_InVendorApex(t *testing.T) { android.AssertStringDoesContain(t, "cflags", cflags, "-target aarch64-linux-android29") } +func TestTrackAllowedDeps(t *testing.T) { + ctx := testApex(t, ` + apex { + name: "myapex", + key: "myapex.key", + updatable: true, + native_shared_libs: [ + "mylib", + "yourlib", + ], + min_sdk_version: "29", + } + + apex { + name: "myapex2", + key: "myapex.key", + updatable: false, + native_shared_libs: ["yourlib"], + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + cc_library { + name: "mylib", + srcs: ["mylib.cpp"], + shared_libs: ["libbar"], + min_sdk_version: "29", + apex_available: ["myapex"], + } + + cc_library { + name: "libbar", + stubs: { versions: ["29", "30"] }, + } + + cc_library { + name: "yourlib", + srcs: ["mylib.cpp"], + min_sdk_version: "29", + apex_available: ["myapex", "myapex2", "//apex_available:platform"], + } + `, withFiles(android.MockFS{ + "packages/modules/common/build/allowed_deps.txt": nil, + })) + + depsinfo := ctx.SingletonForTests("apex_depsinfo_singleton") + inputs := depsinfo.Rule("generateApexDepsInfoFilesRule").BuildParams.Inputs.Strings() + android.AssertStringListContains(t, "updatable myapex should generate depsinfo file", inputs, + "out/soong/.intermediates/myapex/android_common_myapex/depsinfo/flatlist.txt") + android.AssertStringListDoesNotContain(t, "non-updatable myapex2 should not generate depsinfo file", inputs, + "out/soong/.intermediates/myapex2/android_common_myapex2/depsinfo/flatlist.txt") + + myapex := ctx.ModuleForTests("myapex", "android_common_myapex") + flatlist := strings.Split(android.ContentFromFileRuleForTests(t, ctx, + myapex.Output("depsinfo/flatlist.txt")), "\n") + android.AssertStringListContains(t, "deps with stubs should be tracked in depsinfo as external dep", + flatlist, "libbar(minSdkVersion:(no version)) (external)") + android.AssertStringListDoesNotContain(t, "do not track if not available for platform", + flatlist, "mylib:(minSdkVersion:29)") + android.AssertStringListContains(t, "track platform-available lib", + flatlist, "yourlib(minSdkVersion:29)") +} + +func TestTrackAllowedDeps_SkipWithoutAllowedDepsTxt(t *testing.T) { + ctx := testApex(t, ` + apex { + name: "myapex", + key: "myapex.key", + updatable: true, + min_sdk_version: "29", + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + `) + depsinfo := ctx.SingletonForTests("apex_depsinfo_singleton") + if nil != depsinfo.MaybeRule("generateApexDepsInfoFilesRule").Output { + t.Error("apex_depsinfo_singleton shouldn't run when allowed_deps.txt doesn't exist") + } +} + func TestPlatformUsesLatestStubsFromApexes(t *testing.T) { ctx := testApex(t, ` apex { @@ -2664,7 +2777,7 @@ func TestFilesInSubDir(t *testing.T) { name: "myapex", key: "myapex.key", native_shared_libs: ["mylib"], - binaries: ["mybin"], + binaries: ["mybin", "mybin.rust"], prebuilts: ["myetc"], compile_multilib: "both", updatable: false, @@ -2699,9 +2812,16 @@ func TestFilesInSubDir(t *testing.T) { stl: "none", apex_available: [ "myapex" ], } + + rust_binary { + name: "mybin.rust", + srcs: ["foo.rs"], + relative_install_path: "rust_subdir", + apex_available: [ "myapex" ], + } `) - generateFsRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("generateFsConfig") + generateFsRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("generateFsConfig") cmd := generateFsRule.RuleParams.Command // Ensure that the subdirectories are all listed @@ -2717,6 +2837,7 @@ func TestFilesInSubDir(t *testing.T) { ensureContains(t, cmd, "/bin ") ensureContains(t, cmd, "/bin/foo ") ensureContains(t, cmd, "/bin/foo/bar ") + ensureContains(t, cmd, "/bin/rust_subdir ") } func TestFilesInSubDirWhenNativeBridgeEnabled(t *testing.T) { @@ -2765,7 +2886,7 @@ func TestFilesInSubDirWhenNativeBridgeEnabled(t *testing.T) { }, } `, withNativeBridgeEnabled) - ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{ + ensureExactContents(t, ctx, "myapex", "android_common_myapex", []string{ "bin/foo/bar/mybin", "bin/foo/bar/mybin64", "bin/arm/foo/bar/mybin", @@ -2805,14 +2926,14 @@ func TestVendorApex(t *testing.T) { } `) - ensureExactContents(t, result.TestContext, "myapex", "android_common_myapex_image", []string{ + ensureExactContents(t, result.TestContext, "myapex", "android_common_myapex", []string{ "bin/mybin", "lib64/libfoo.so", // TODO(b/159195575): Add an option to use VNDK libs from VNDK APEX "lib64/libc++.so", }) - apexBundle := result.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle) + apexBundle := result.ModuleForTests("myapex", "android_common_myapex").Module().(*apexBundle) data := android.AndroidMkDataForTest(t, result.TestContext, apexBundle) name := apexBundle.BaseModuleName() prefix := "TARGET_" @@ -2822,7 +2943,7 @@ func TestVendorApex(t *testing.T) { installPath := "out/target/product/test_device/vendor/apex" ensureContains(t, androidMk, "LOCAL_MODULE_PATH := "+installPath) - apexManifestRule := result.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexManifestRule") + apexManifestRule := result.ModuleForTests("myapex", "android_common_myapex").Rule("apexManifestRule") requireNativeLibs := names(apexManifestRule.Args["requireNativeLibs"]) ensureListNotContains(t, requireNativeLibs, ":vndk") } @@ -2899,7 +3020,11 @@ func TestVendorApex_use_vndk_as_stable(t *testing.T) { vendor: true, shared_libs: ["libvndk", "libvendor"], } - `) + `, + android.FixtureModifyConfig(func(config android.Config) { + config.TestProductVariables.KeepVndk = proptools.BoolPtr(true) + }), + ) vendorVariant := "android_vendor.29_arm64_armv8-a" @@ -2961,10 +3086,10 @@ func TestVendorApex_use_vndk_as_stable(t *testing.T) { ensureListContains(t, libs, lib) } // Check apex contents - ensureExactContents(t, ctx, tc.apexName, "android_common_"+tc.apexName+"_image", tc.contents) + ensureExactContents(t, ctx, tc.apexName, "android_common_"+tc.apexName, tc.contents) // Check "requireNativeLibs" - apexManifestRule := ctx.ModuleForTests(tc.apexName, "android_common_"+tc.apexName+"_image").Rule("apexManifestRule") + apexManifestRule := ctx.ModuleForTests(tc.apexName, "android_common_"+tc.apexName).Rule("apexManifestRule") requireNativeLibs := names(apexManifestRule.Args["requireNativeLibs"]) if tc.requireVndkNamespace { ensureListContains(t, requireNativeLibs, ":vndk") @@ -2997,10 +3122,7 @@ func TestProductVariant(t *testing.T) { apex_available: ["myapex"], srcs: ["foo.cpp"], } - `, android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { - variables.ProductVndkVersion = proptools.StringPtr("current") - }), - ) + `) cflags := strings.Fields( ctx.ModuleForTests("foo", "android_product.29_arm64_armv8-a_myapex").Rule("cc").Args["cFlags"]) @@ -3040,7 +3162,7 @@ func TestApex_withPrebuiltFirmware(t *testing.T) { `+tc.additionalProp+` } `) - ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{ + ensureExactContents(t, ctx, "myapex", "android_common_myapex", []string{ "etc/firmware/myfirmware.bin", }) }) @@ -3069,14 +3191,14 @@ func TestAndroidMk_VendorApexRequired(t *testing.T) { } `) - apexBundle := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle) + apexBundle := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(*apexBundle) data := android.AndroidMkDataForTest(t, ctx, apexBundle) name := apexBundle.BaseModuleName() prefix := "TARGET_" var builder strings.Builder data.Custom(&builder, name, prefix, "", data) androidMk := builder.String() - ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := libc++.vendor.myapex:64 mylib.vendor.myapex:64 apex_manifest.pb.myapex apex_pubkey.myapex libc.vendor libm.vendor libdl.vendor\n") + ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := libc++.vendor.myapex:64 mylib.vendor.myapex:64 libc.vendor libm.vendor libdl.vendor\n") } func TestAndroidMkWritesCommonProperties(t *testing.T) { @@ -3098,7 +3220,7 @@ func TestAndroidMkWritesCommonProperties(t *testing.T) { } `) - apexBundle := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle) + apexBundle := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(*apexBundle) data := android.AndroidMkDataForTest(t, ctx, apexBundle) name := apexBundle.BaseModuleName() prefix := "TARGET_" @@ -3132,10 +3254,7 @@ func TestStaticLinking(t *testing.T) { stubs: { versions: ["1", "2", "3"], }, - apex_available: [ - "//apex_available:platform", - "myapex", - ], + apex_available: ["myapex"], } cc_binary { @@ -3204,7 +3323,7 @@ func TestKeys(t *testing.T) { } // check the APK certs. It should be overridden to myapex.certificate.override - certs := ctx.ModuleForTests("myapex_keytest", "android_common_myapex_keytest_image").Rule("signapk").Args["certificates"] + certs := ctx.ModuleForTests("myapex_keytest", "android_common_myapex_keytest").Rule("signapk").Args["certificates"] if certs != "testkey.override.x509.pem testkey.override.pk8" { t.Errorf("cert and private key %q are not %q", certs, "testkey.override.509.pem testkey.override.pk8") @@ -3224,7 +3343,7 @@ func TestCertificate(t *testing.T) { public_key: "testkey.avbpubkey", private_key: "testkey.pem", }`) - rule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("signapk") + rule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("signapk") expected := "vendor/foo/devkeys/test.x509.pem vendor/foo/devkeys/test.pk8" if actual := rule.Args["certificates"]; actual != expected { t.Errorf("certificates should be %q, not %q", expected, actual) @@ -3247,7 +3366,7 @@ func TestCertificate(t *testing.T) { name: "myapex.certificate.override", certificate: "testkey.override", }`) - rule := ctx.ModuleForTests("myapex_keytest", "android_common_myapex_keytest_image").Rule("signapk") + rule := ctx.ModuleForTests("myapex_keytest", "android_common_myapex_keytest").Rule("signapk") expected := "testkey.override.x509.pem testkey.override.pk8" if actual := rule.Args["certificates"]; actual != expected { t.Errorf("certificates should be %q, not %q", expected, actual) @@ -3270,7 +3389,7 @@ func TestCertificate(t *testing.T) { name: "myapex.certificate", certificate: "testkey", }`) - rule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("signapk") + rule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("signapk") expected := "testkey.x509.pem testkey.pk8" if actual := rule.Args["certificates"]; actual != expected { t.Errorf("certificates should be %q, not %q", expected, actual) @@ -3294,7 +3413,7 @@ func TestCertificate(t *testing.T) { name: "myapex.certificate.override", certificate: "testkey.override", }`) - rule := ctx.ModuleForTests("myapex_keytest", "android_common_myapex_keytest_image").Rule("signapk") + rule := ctx.ModuleForTests("myapex_keytest", "android_common_myapex_keytest").Rule("signapk") expected := "testkey.override.x509.pem testkey.override.pk8" if actual := rule.Args["certificates"]; actual != expected { t.Errorf("certificates should be %q, not %q", expected, actual) @@ -3313,7 +3432,7 @@ func TestCertificate(t *testing.T) { public_key: "testkey.avbpubkey", private_key: "testkey.pem", }`) - rule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("signapk") + rule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("signapk") expected := "vendor/foo/devkeys/testkey.x509.pem vendor/foo/devkeys/testkey.pk8" if actual := rule.Args["certificates"]; actual != expected { t.Errorf("certificates should be %q, not %q", expected, actual) @@ -3337,7 +3456,7 @@ func TestCertificate(t *testing.T) { name: "myapex.certificate.override", certificate: "testkey.override", }`) - rule := ctx.ModuleForTests("myapex_keytest", "android_common_myapex_keytest_image").Rule("signapk") + rule := ctx.ModuleForTests("myapex_keytest", "android_common_myapex_keytest").Rule("signapk") expected := "testkey.override.x509.pem testkey.override.pk8" if actual := rule.Args["certificates"]; actual != expected { t.Errorf("certificates should be %q, not %q", expected, actual) @@ -3518,10 +3637,6 @@ func getFiles(t *testing.T, ctx *android.TestContext, moduleName, variant string module := ctx.ModuleForTests(moduleName, variant) apexRule := module.MaybeRule("apexRule") apexDir := "/image.apex/" - if apexRule.Rule == nil { - apexRule = module.Rule("zipApexRule") - apexDir = "/image.zipapex/" - } copyCmds := apexRule.Args["copy_commands"] var ret []fileInApex for _, cmd := range strings.Split(copyCmds, "&&") { @@ -3731,8 +3846,9 @@ func TestVndkApexCurrent(t *testing.T) { } `+vndkLibrariesTxtFiles("current"), android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { variables.DeviceVndkVersion = proptools.StringPtr(tc.vndkVersion) + variables.KeepVndk = proptools.BoolPtr(true) })) - ensureExactContents(t, ctx, "com.android.vndk.current", "android_common_image", tc.expectedFiles) + ensureExactContents(t, ctx, "com.android.vndk.current", "android_common", tc.expectedFiles) }) } } @@ -3787,7 +3903,7 @@ func TestVndkApexWithPrebuilt(t *testing.T) { "libvndk.so": nil, "libvndk.arm.so": nil, })) - ensureExactContents(t, ctx, "com.android.vndk.current", "android_common_image", []string{ + ensureExactContents(t, ctx, "com.android.vndk.current", "android_common", []string{ "lib/libvndk.so", "lib/libvndk.arm.so", "lib64/libvndk.so", @@ -3800,13 +3916,24 @@ func TestVndkApexWithPrebuilt(t *testing.T) { func vndkLibrariesTxtFiles(vers ...string) (result string) { for _, v := range vers { if v == "current" { - for _, txt := range []string{"llndk", "vndkcore", "vndksp", "vndkprivate", "vndkproduct"} { + for _, txt := range []string{"vndkcore", "vndksp", "vndkprivate", "vndkproduct"} { result += ` ` + txt + `_libraries_txt { name: "` + txt + `.libraries.txt", + insert_vndk_version: true, } ` } + result += ` + llndk_libraries_txt { + name: "llndk.libraries.txt", + } + llndk_libraries_txt_for_apex { + name: "llndk.libraries.txt.apex", + stem: "llndk.libraries.txt", + insert_vndk_version: true, + } + ` } else { for _, txt := range []string{"llndk", "vndkcore", "vndksp", "vndkprivate", "vndkproduct"} { result += ` @@ -3883,7 +4010,7 @@ func TestVndkApexVersion(t *testing.T) { "libvndk27_x86_64.so": nil, })) - ensureExactContents(t, ctx, "com.android.vndk.v27", "android_common_image", []string{ + ensureExactContents(t, ctx, "com.android.vndk.v27", "android_common", []string{ "lib/libvndk27_arm.so", "lib64/libvndk27_arm64.so", "etc/*", @@ -3912,7 +4039,7 @@ func TestVndkApexNameRule(t *testing.T) { }`+vndkLibrariesTxtFiles("28", "current")) assertApexName := func(expected, moduleName string) { - module := ctx.ModuleForTests(moduleName, "android_common_image") + module := ctx.ModuleForTests(moduleName, "android_common") apexManifestRule := module.Rule("apexManifestRule") ensureContains(t, apexManifestRule.Args["opt"], "-v name "+expected) } @@ -3953,7 +4080,7 @@ func TestVndkApexSkipsNativeBridgeSupportedModules(t *testing.T) { `+vndkLibrariesTxtFiles("current"), withNativeBridgeEnabled) - ensureExactContents(t, ctx, "com.android.vndk.current", "android_common_image", []string{ + ensureExactContents(t, ctx, "com.android.vndk.current", "android_common", []string{ "lib/libvndk.so", "lib64/libvndk.so", "lib/libc++.so", @@ -4056,7 +4183,7 @@ func TestVndkApexWithBinder32(t *testing.T) { }), ) - ensureExactContents(t, ctx, "com.android.vndk.v27", "android_common_image", []string{ + ensureExactContents(t, ctx, "com.android.vndk.v27", "android_common", []string{ "lib/libvndk27binder32.so", "etc/*", }) @@ -4093,10 +4220,10 @@ func TestVndkApexShouldNotProvideNativeLibs(t *testing.T) { "libz.map.txt": nil, })) - apexManifestRule := ctx.ModuleForTests("com.android.vndk.current", "android_common_image").Rule("apexManifestRule") + apexManifestRule := ctx.ModuleForTests("com.android.vndk.current", "android_common").Rule("apexManifestRule") provideNativeLibs := names(apexManifestRule.Args["provideNativeLibs"]) ensureListEmpty(t, provideNativeLibs) - ensureExactContents(t, ctx, "com.android.vndk.current", "android_common_image", []string{ + ensureExactContents(t, ctx, "com.android.vndk.current", "android_common", []string{ "out/soong/.intermediates/libz/android_vendor.29_arm64_armv8-a_shared/libz.so:lib64/libz.so", "out/soong/.intermediates/libz/android_vendor.29_arm_armv7-a-neon_shared/libz.so:lib/libz.so", "*/*", @@ -4252,7 +4379,7 @@ func TestVendorApexWithVndkPrebuilts(t *testing.T) { })) // Should embed the prebuilt VNDK libraries in the apex - ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{ + ensureExactContents(t, ctx, "myapex", "android_common_myapex", []string{ "bin/foo", "prebuilts/vndk/libc++.so:lib64/libc++.so", "prebuilts/vndk/libvndk.so:lib64/libvndk.so", @@ -4266,7 +4393,7 @@ func TestVendorApexWithVndkPrebuilts(t *testing.T) { android.AssertStringDoesContain(t, "should link to prebuilt libunwind", ldRule.Args["libFlags"], "prebuilts/vndk/libunwind.a") // Should declare the LLNDK library as a "required" external dependency - manifestRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexManifestRule") + manifestRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexManifestRule") requireNativeLibs := names(manifestRule.Args["requireNativeLibs"]) ensureListContains(t, requireNativeLibs, "libllndk.so") } @@ -4303,7 +4430,7 @@ func TestDependenciesInApexManifest(t *testing.T) { apex { name: "myapex_selfcontained", key: "myapex.key", - native_shared_libs: ["lib_dep", "libfoo"], + native_shared_libs: ["lib_dep_on_bar", "libbar"], compile_multilib: "both", file_contexts: ":myapex-file_contexts", updatable: false, @@ -4336,6 +4463,18 @@ func TestDependenciesInApexManifest(t *testing.T) { ], } + cc_library { + name: "lib_dep_on_bar", + srcs: ["mylib.cpp"], + shared_libs: ["libbar"], + system_shared_libs: [], + stl: "none", + apex_available: [ + "myapex_selfcontained", + ], + } + + cc_library { name: "libfoo", srcs: ["mytest.cpp"], @@ -4346,36 +4485,49 @@ func TestDependenciesInApexManifest(t *testing.T) { stl: "none", apex_available: [ "myapex_provider", + ], + } + + cc_library { + name: "libbar", + srcs: ["mytest.cpp"], + stubs: { + versions: ["1"], + }, + system_shared_libs: [], + stl: "none", + apex_available: [ "myapex_selfcontained", ], } + `) var apexManifestRule android.TestingBuildParams var provideNativeLibs, requireNativeLibs []string - apexManifestRule = ctx.ModuleForTests("myapex_nodep", "android_common_myapex_nodep_image").Rule("apexManifestRule") + apexManifestRule = ctx.ModuleForTests("myapex_nodep", "android_common_myapex_nodep").Rule("apexManifestRule") provideNativeLibs = names(apexManifestRule.Args["provideNativeLibs"]) requireNativeLibs = names(apexManifestRule.Args["requireNativeLibs"]) ensureListEmpty(t, provideNativeLibs) ensureListEmpty(t, requireNativeLibs) - apexManifestRule = ctx.ModuleForTests("myapex_dep", "android_common_myapex_dep_image").Rule("apexManifestRule") + apexManifestRule = ctx.ModuleForTests("myapex_dep", "android_common_myapex_dep").Rule("apexManifestRule") provideNativeLibs = names(apexManifestRule.Args["provideNativeLibs"]) requireNativeLibs = names(apexManifestRule.Args["requireNativeLibs"]) ensureListEmpty(t, provideNativeLibs) ensureListContains(t, requireNativeLibs, "libfoo.so") - apexManifestRule = ctx.ModuleForTests("myapex_provider", "android_common_myapex_provider_image").Rule("apexManifestRule") + apexManifestRule = ctx.ModuleForTests("myapex_provider", "android_common_myapex_provider").Rule("apexManifestRule") provideNativeLibs = names(apexManifestRule.Args["provideNativeLibs"]) requireNativeLibs = names(apexManifestRule.Args["requireNativeLibs"]) ensureListContains(t, provideNativeLibs, "libfoo.so") ensureListEmpty(t, requireNativeLibs) - apexManifestRule = ctx.ModuleForTests("myapex_selfcontained", "android_common_myapex_selfcontained_image").Rule("apexManifestRule") + apexManifestRule = ctx.ModuleForTests("myapex_selfcontained", "android_common_myapex_selfcontained").Rule("apexManifestRule") provideNativeLibs = names(apexManifestRule.Args["provideNativeLibs"]) requireNativeLibs = names(apexManifestRule.Args["requireNativeLibs"]) - ensureListContains(t, provideNativeLibs, "libfoo.so") + ensureListContains(t, provideNativeLibs, "libbar.so") ensureListEmpty(t, requireNativeLibs) } @@ -4408,7 +4560,7 @@ func TestOverrideApexManifestDefaultVersion(t *testing.T) { "OVERRIDE_APEX_MANIFEST_DEFAULT_VERSION": "1234", })) - module := ctx.ModuleForTests("myapex", "android_common_myapex_image") + module := ctx.ModuleForTests("myapex", "android_common_myapex") apexManifestRule := module.Rule("apexManifestRule") ensureContains(t, apexManifestRule.Args["default_version"], "1234") } @@ -4471,7 +4623,7 @@ func TestCompileMultilibProp(t *testing.T) { } `, testCase.compileMultiLibProp), ) - module := ctx.ModuleForTests("myapex", "android_common_myapex_image") + module := ctx.ModuleForTests("myapex", "android_common_myapex") apexRule := module.Rule("apexRule") copyCmds := apexRule.Args["copy_commands"] for _, containedLib := range testCase.containedLibs { @@ -4510,7 +4662,7 @@ func TestNonTestApex(t *testing.T) { } `) - module := ctx.ModuleForTests("myapex", "android_common_myapex_image") + module := ctx.ModuleForTests("myapex", "android_common_myapex") apexRule := module.Rule("apexRule") copyCmds := apexRule.Args["copy_commands"] @@ -4564,7 +4716,7 @@ func TestTestApex(t *testing.T) { } `) - module := ctx.ModuleForTests("myapex", "android_common_myapex_image") + module := ctx.ModuleForTests("myapex", "android_common_myapex") apexRule := module.Rule("apexRule") copyCmds := apexRule.Args["copy_commands"] @@ -4585,6 +4737,72 @@ func TestTestApex(t *testing.T) { ensureListContains(t, ctx.ModuleVariantsForTests("mylib_common_test"), "android_arm64_armv8-a_shared") } +func TestLibzVendorIsntStable(t *testing.T) { + ctx := testApex(t, ` + apex { + name: "myapex", + key: "myapex.key", + updatable: false, + binaries: ["mybin"], + } + apex { + name: "myvendorapex", + key: "myapex.key", + file_contexts: "myvendorapex_file_contexts", + vendor: true, + updatable: false, + binaries: ["mybin"], + } + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + cc_binary { + name: "mybin", + vendor_available: true, + system_shared_libs: [], + stl: "none", + shared_libs: ["libz"], + apex_available: ["//apex_available:anyapex"], + } + cc_library { + name: "libz", + vendor_available: true, + system_shared_libs: [], + stl: "none", + stubs: { + versions: ["28", "30"], + }, + target: { + vendor: { + no_stubs: true, + }, + }, + } + `, withFiles(map[string][]byte{ + "myvendorapex_file_contexts": nil, + })) + + // libz provides stubs for core variant. + { + ensureExactContents(t, ctx, "myapex", "android_common_myapex", []string{ + "bin/mybin", + }) + apexManifestRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexManifestRule") + android.AssertStringEquals(t, "should require libz", apexManifestRule.Args["requireNativeLibs"], "libz.so") + } + // libz doesn't provide stubs for vendor variant. + { + ensureExactContents(t, ctx, "myvendorapex", "android_common_myvendorapex", []string{ + "bin/mybin", + "lib64/libz.so", + }) + apexManifestRule := ctx.ModuleForTests("myvendorapex", "android_common_myvendorapex").Rule("apexManifestRule") + android.AssertStringEquals(t, "should not require libz", apexManifestRule.Args["requireNativeLibs"], "") + } +} + func TestApexWithTarget(t *testing.T) { ctx := testApex(t, ` apex { @@ -4654,7 +4872,7 @@ func TestApexWithTarget(t *testing.T) { } `) - apexRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexRule") + apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule") copyCmds := apexRule.Args["copy_commands"] // Ensure that main rule creates an output @@ -4738,7 +4956,7 @@ func TestApexWithArch(t *testing.T) { } `) - apexRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexRule") + apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule") copyCmds := apexRule.Args["copy_commands"] // Ensure that apex variant is created for the direct dep @@ -4774,7 +4992,7 @@ func TestApexWithShBinary(t *testing.T) { } `) - apexRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexRule") + apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule") copyCmds := apexRule.Args["copy_commands"] ensureContains(t, copyCmds, "image.apex/bin/script/myscript.sh") @@ -4782,17 +5000,17 @@ func TestApexWithShBinary(t *testing.T) { func TestApexInVariousPartition(t *testing.T) { testcases := []struct { - propName, parition, flattenedPartition string + propName, partition string }{ - {"", "system", "system_ext"}, - {"product_specific: true", "product", "product"}, - {"soc_specific: true", "vendor", "vendor"}, - {"proprietary: true", "vendor", "vendor"}, - {"vendor: true", "vendor", "vendor"}, - {"system_ext_specific: true", "system_ext", "system_ext"}, + {"", "system"}, + {"product_specific: true", "product"}, + {"soc_specific: true", "vendor"}, + {"proprietary: true", "vendor"}, + {"vendor: true", "vendor"}, + {"system_ext_specific: true", "system_ext"}, } for _, tc := range testcases { - t.Run(tc.propName+":"+tc.parition, func(t *testing.T) { + t.Run(tc.propName+":"+tc.partition, func(t *testing.T) { ctx := testApex(t, ` apex { name: "myapex", @@ -4808,19 +5026,12 @@ func TestApexInVariousPartition(t *testing.T) { } `) - apex := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle) - expected := "out/soong/target/product/test_device/" + tc.parition + "/apex" + apex := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(*apexBundle) + expected := "out/soong/target/product/test_device/" + tc.partition + "/apex" actual := apex.installDir.RelativeToTop().String() if actual != expected { t.Errorf("wrong install path. expected %q. actual %q", expected, actual) } - - flattened := ctx.ModuleForTests("myapex", "android_common_myapex_flattened").Module().(*apexBundle) - expected = "out/soong/target/product/test_device/" + tc.flattenedPartition + "/apex" - actual = flattened.installDir.RelativeToTop().String() - if actual != expected { - t.Errorf("wrong install path. expected %q. actual %q", expected, actual) - } }) } } @@ -4839,7 +5050,7 @@ func TestFileContexts_FindInDefaultLocationIfNotSet(t *testing.T) { private_key: "testkey.pem", } `) - module := ctx.ModuleForTests("myapex", "android_common_myapex_image") + module := ctx.ModuleForTests("myapex", "android_common_myapex") rule := module.Output("file_contexts") ensureContains(t, rule.RuleParams.Command, "cat system/sepolicy/apex/myapex-file_contexts") } @@ -4897,7 +5108,7 @@ func TestFileContexts_ProductSpecificApexes(t *testing.T) { `, withFiles(map[string][]byte{ "product_specific_file_contexts": nil, })) - module := ctx.ModuleForTests("myapex", "android_common_myapex_image") + module := ctx.ModuleForTests("myapex", "android_common_myapex") rule := module.Output("file_contexts") ensureContains(t, rule.RuleParams.Command, "cat product_specific_file_contexts") } @@ -4925,7 +5136,7 @@ func TestFileContexts_SetViaFileGroup(t *testing.T) { `, withFiles(map[string][]byte{ "product_specific_file_contexts": nil, })) - module := ctx.ModuleForTests("myapex", "android_common_myapex_image") + module := ctx.ModuleForTests("myapex", "android_common_myapex") rule := module.Output("file_contexts") ensureContains(t, rule.RuleParams.Command, "cat product_specific_file_contexts") } @@ -5348,7 +5559,7 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { checkBootDexJarPath := func(t *testing.T, ctx *android.TestContext, stem string, bootDexJarPath string) { t.Helper() - s := ctx.ModuleForTests("platform-bootclasspath", "android_common") + s := ctx.ModuleForTests("dex_bootjars", "android_common") foundLibfooJar := false base := stem + ".jar" for _, output := range s.AllOutputs() { @@ -5442,6 +5653,7 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { checkHiddenAPIIndexFromFlagsInputs(t, ctx, ` my-bootclasspath-fragment/index.csv out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/hiddenapi-monolithic/index-from-classes.csv + out/soong/.intermediates/packages/modules/com.android.art/art-bootclasspath-fragment/android_common_apex10000/modular-hiddenapi/index.csv `) }) @@ -5519,6 +5731,7 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { checkHiddenAPIIndexFromFlagsInputs(t, ctx, ` my-bootclasspath-fragment/index.csv out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/hiddenapi-monolithic/index-from-classes.csv + out/soong/.intermediates/packages/modules/com.android.art/art-bootclasspath-fragment/android_common_apex10000/modular-hiddenapi/index.csv `) myApex := ctx.ModuleForTests("myapex", "android_common_myapex").Module() @@ -5613,7 +5826,29 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { t.Run("prebuilt library preferred with source", func(t *testing.T) { bp := ` - prebuilt_apex { + apex { + name: "myapex", + key: "myapex.key", + updatable: false, + bootclasspath_fragments: ["my-bootclasspath-fragment"], + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + bootclasspath_fragment { + name: "my-bootclasspath-fragment", + contents: ["libfoo", "libbar"], + apex_available: ["myapex"], + hidden_api: { + split_packages: ["*"], + }, + } + + prebuilt_apex { name: "myapex", arch: { arm64: { @@ -5628,6 +5863,7 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { prebuilt_bootclasspath_fragment { name: "my-bootclasspath-fragment", + prefer: true, contents: ["libfoo", "libbar"], apex_available: ["myapex"], hidden_api: { @@ -5652,6 +5888,7 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { name: "libfoo", srcs: ["foo/bar/MyClass.java"], apex_available: ["myapex"], + installable: true, } java_sdk_library_import { @@ -5670,6 +5907,7 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { srcs: ["foo/bar/MyClass.java"], unsafe_ignore_missing_latest_api: true, apex_available: ["myapex"], + compile_dex: true, } ` @@ -5682,6 +5920,7 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { checkHiddenAPIIndexFromFlagsInputs(t, ctx, ` my-bootclasspath-fragment/index.csv out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/hiddenapi-monolithic/index-from-classes.csv + out/soong/.intermediates/packages/modules/com.android.art/art-bootclasspath-fragment/android_common_apex10000/modular-hiddenapi/index.csv `) }) @@ -5690,8 +5929,8 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { apex { name: "myapex", key: "myapex.key", - java_libs: ["libfoo", "libbar"], updatable: false, + bootclasspath_fragments: ["my-bootclasspath-fragment"], } apex_key { @@ -5700,6 +5939,15 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { private_key: "testkey.pem", } + bootclasspath_fragment { + name: "my-bootclasspath-fragment", + contents: ["libfoo", "libbar"], + apex_available: ["myapex"], + hidden_api: { + split_packages: ["*"], + }, + } + prebuilt_apex { name: "myapex", arch: { @@ -5738,6 +5986,7 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { srcs: ["foo/bar/MyClass.java"], apex_available: ["myapex"], permitted_packages: ["foo"], + installable: true, } java_sdk_library_import { @@ -5755,18 +6004,20 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { unsafe_ignore_missing_latest_api: true, apex_available: ["myapex"], permitted_packages: ["bar"], + compile_dex: true, } ` ctx := testDexpreoptWithApexes(t, bp, "", preparer, fragment) - checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/libfoo/android_common_apex10000/hiddenapi/libfoo.jar") - checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/libbar/android_common_myapex/hiddenapi/libbar.jar") + checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/my-bootclasspath-fragment/android_common_myapex/hiddenapi-modular/encoded/libfoo.jar") + checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/my-bootclasspath-fragment/android_common_myapex/hiddenapi-modular/encoded/libbar.jar") // Verify the correct module jars contribute to the hiddenapi index file. checkHiddenAPIIndexFromClassesInputs(t, ctx, ``) checkHiddenAPIIndexFromFlagsInputs(t, ctx, ` - my-bootclasspath-fragment/index.csv out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/hiddenapi-monolithic/index-from-classes.csv + out/soong/.intermediates/my-bootclasspath-fragment/android_common_myapex/modular-hiddenapi/index.csv + out/soong/.intermediates/packages/modules/com.android.art/art-bootclasspath-fragment/android_common_apex10000/modular-hiddenapi/index.csv `) }) @@ -5776,7 +6027,7 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { name: "myapex", enabled: false, key: "myapex.key", - java_libs: ["libfoo", "libbar"], + bootclasspath_fragments: ["my-bootclasspath-fragment"], } apex_key { @@ -5785,6 +6036,16 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { private_key: "testkey.pem", } + bootclasspath_fragment { + name: "my-bootclasspath-fragment", + enabled: false, + contents: ["libfoo", "libbar"], + apex_available: ["myapex"], + hidden_api: { + split_packages: ["*"], + }, + } + prebuilt_apex { name: "myapex", arch: { @@ -5814,7 +6075,6 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { java_import { name: "libfoo", - prefer: true, jars: ["libfoo.jar"], apex_available: ["myapex"], permitted_packages: ["foo"], @@ -5822,13 +6082,14 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { java_library { name: "libfoo", + enabled: false, srcs: ["foo/bar/MyClass.java"], apex_available: ["myapex"], + installable: true, } java_sdk_library_import { name: "libbar", - prefer: true, public: { jars: ["libbar.jar"], }, @@ -5839,9 +6100,11 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { java_sdk_library { name: "libbar", + enabled: false, srcs: ["foo/bar/MyClass.java"], unsafe_ignore_missing_latest_api: true, apex_available: ["myapex"], + compile_dex: true, } ` @@ -5854,71 +6117,11 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { checkHiddenAPIIndexFromFlagsInputs(t, ctx, ` my-bootclasspath-fragment/index.csv out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/hiddenapi-monolithic/index-from-classes.csv + out/soong/.intermediates/packages/modules/com.android.art/art-bootclasspath-fragment/android_common_apex10000/modular-hiddenapi/index.csv `) }) } -func TestPrebuiltSkipsSymbols(t *testing.T) { - testCases := []struct { - name string - usePrebuilt bool - installSymbolFiles bool - }{ - { - name: "Source module build rule doesn't install symbol files", - usePrebuilt: true, - installSymbolFiles: false, - }, - { - name: "Source module is installed with symbols", - usePrebuilt: false, - installSymbolFiles: true, - }, - } - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - preferProperty := "prefer: false" - if tc.usePrebuilt { - preferProperty = "prefer: true" - } - ctx := testApex(t, ` - // Source module - apex { - name: "myapex", - binaries: ["foo"], - key: "myapex.key", - updatable: false, - } - - apex_key { - name: "myapex.key", - public_key: "testkey.avbpubkey", - private_key: "testkey.pem", - } - - apex_set { - name: "myapex", - set: "myapex.apks", - `+preferProperty+` - } - - cc_binary { - name: "foo", - srcs: ["mylib.cpp"], - system_shared_libs: [], - stl: "none", - apex_available: [ "myapex" ], - } - `) - // Symbol files are installed by installing entries under ${OUT}/apex/{apex name} - android.AssertStringListContainsEquals(t, "Installs", - ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().FilesToInstall().Strings(), - filepath.Join(ctx.Config().SoongOutDir(), "target/product/test_device/apex/myapex/bin/foo"), - tc.installSymbolFiles) - }) - } -} - func TestApexWithTests(t *testing.T) { ctx := testApex(t, ` apex_test { @@ -5991,7 +6194,7 @@ func TestApexWithTests(t *testing.T) { } `) - apexRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexRule") + apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule") copyCmds := apexRule.Args["copy_commands"] // Ensure that test dep (and their transitive dependencies) are copied into apex. @@ -6008,7 +6211,7 @@ func TestApexWithTests(t *testing.T) { ensureContains(t, copyCmds, "image.apex/bin/test/mytest3") // Ensure the module is correctly translated. - bundle := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle) + bundle := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(*apexBundle) data := android.AndroidMkDataForTest(t, ctx, bundle) name := bundle.BaseModuleName() prefix := "TARGET_" @@ -6019,42 +6222,7 @@ func TestApexWithTests(t *testing.T) { ensureContains(t, androidMk, "LOCAL_MODULE := mytest1.myapex\n") ensureContains(t, androidMk, "LOCAL_MODULE := mytest2.myapex\n") ensureContains(t, androidMk, "LOCAL_MODULE := mytest3.myapex\n") - ensureContains(t, androidMk, "LOCAL_MODULE := apex_manifest.pb.myapex\n") - ensureContains(t, androidMk, "LOCAL_MODULE := apex_pubkey.myapex\n") ensureContains(t, androidMk, "LOCAL_MODULE := myapex\n") - - flatBundle := ctx.ModuleForTests("myapex", "android_common_myapex_flattened").Module().(*apexBundle) - data = android.AndroidMkDataForTest(t, ctx, flatBundle) - data.Custom(&builder, name, prefix, "", data) - flatAndroidMk := builder.String() - ensureContainsOnce(t, flatAndroidMk, "LOCAL_TEST_DATA := :baz :bar/baz\n") - ensureContainsOnce(t, flatAndroidMk, "LOCAL_TEST_DATA := :testdata/baz\n") -} - -func TestInstallExtraFlattenedApexes(t *testing.T) { - ctx := testApex(t, ` - apex { - name: "myapex", - key: "myapex.key", - updatable: false, - } - apex_key { - name: "myapex.key", - public_key: "testkey.avbpubkey", - private_key: "testkey.pem", - } - `, - android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { - variables.InstallExtraFlattenedApexes = proptools.BoolPtr(true) - }), - ) - ab := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle) - ensureListContains(t, ab.makeModulesToInstall, "myapex.flattened") - mk := android.AndroidMkDataForTest(t, ctx, ab) - var builder strings.Builder - mk.Custom(&builder, ab.Name(), "TARGET_", "", mk) - androidMk := builder.String() - ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := apex_manifest.pb.myapex apex_pubkey.myapex myapex.flattened\n") } func TestErrorsIfDepsAreNotEnabled(t *testing.T) { @@ -6126,7 +6294,7 @@ func TestApexWithJavaImport(t *testing.T) { } `) - module := ctx.ModuleForTests("myapex", "android_common_myapex_image") + module := ctx.ModuleForTests("myapex", "android_common_myapex") apexRule := module.Rule("apexRule") copyCmds := apexRule.Args["copy_commands"] ensureContains(t, copyCmds, "image.apex/javalib/myjavaimport.jar") @@ -6166,6 +6334,7 @@ func TestApexWithApps(t *testing.T) { sdk_version: "current", system_modules: "none", privileged: true, + privapp_allowlist: "privapp_allowlist_com.android.AppFooPriv.xml", stl: "none", apex_available: [ "myapex" ], } @@ -6189,12 +6358,13 @@ func TestApexWithApps(t *testing.T) { } `) - module := ctx.ModuleForTests("myapex", "android_common_myapex_image") + module := ctx.ModuleForTests("myapex", "android_common_myapex") apexRule := module.Rule("apexRule") copyCmds := apexRule.Args["copy_commands"] ensureContains(t, copyCmds, "image.apex/app/AppFoo@TEST.BUILD_ID/AppFoo.apk") ensureContains(t, copyCmds, "image.apex/priv-app/AppFooPriv@TEST.BUILD_ID/AppFooPriv.apk") + ensureContains(t, copyCmds, "image.apex/etc/permissions/privapp_allowlist_com.android.AppFooPriv.xml") appZipRule := ctx.ModuleForTests("AppFoo", "android_common_apex10000").Description("zip jni libs") // JNI libraries are uncompressed @@ -6209,6 +6379,18 @@ func TestApexWithApps(t *testing.T) { // ... and not directly inside the APEX ensureNotContains(t, copyCmds, "image.apex/lib64/"+jni+".so") } + + apexBundle := module.Module().(*apexBundle) + data := android.AndroidMkDataForTest(t, ctx, apexBundle) + var builder strings.Builder + data.Custom(&builder, apexBundle.Name(), "TARGET_", "", data) + androidMk := builder.String() + ensureContains(t, androidMk, "LOCAL_MODULE := AppFooPriv.myapex") + ensureContains(t, androidMk, "LOCAL_MODULE := AppFoo.myapex") + ensureMatches(t, androidMk, "LOCAL_SOONG_INSTALLED_MODULE := \\S+AppFooPriv.apk") + ensureMatches(t, androidMk, "LOCAL_SOONG_INSTALLED_MODULE := \\S+AppFoo.apk") + ensureMatches(t, androidMk, "LOCAL_SOONG_INSTALL_PAIRS := \\S+AppFooPriv.apk") + ensureContains(t, androidMk, "LOCAL_SOONG_INSTALL_PAIRS := privapp_allowlist_com.android.AppFooPriv.xml:$(PRODUCT_OUT)/apex/myapex/etc/permissions/privapp_allowlist_com.android.AppFooPriv.xml") } func TestApexWithAppImportBuildId(t *testing.T) { @@ -6282,7 +6464,7 @@ func TestApexWithAppImports(t *testing.T) { } `) - module := ctx.ModuleForTests("myapex", "android_common_myapex_image") + module := ctx.ModuleForTests("myapex", "android_common_myapex") apexRule := module.Rule("apexRule") copyCmds := apexRule.Args["copy_commands"] @@ -6327,7 +6509,7 @@ func TestApexWithAppImportsPrefer(t *testing.T) { "AppFooPrebuilt.apk": nil, })) - ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{ + ensureExactContents(t, ctx, "myapex", "android_common_myapex", []string{ "app/AppFoo@TEST.BUILD_ID/AppFooPrebuilt.apk", }) } @@ -6357,7 +6539,7 @@ func TestApexWithTestHelperApp(t *testing.T) { `) - module := ctx.ModuleForTests("myapex", "android_common_myapex_image") + module := ctx.ModuleForTests("myapex", "android_common_myapex") apexRule := module.Rule("apexRule") copyCmds := apexRule.Args["copy_commands"] @@ -6482,8 +6664,8 @@ func TestApexAvailable_IndirectDep(t *testing.T) { }`) } -func TestApexAvailable_InvalidApexName(t *testing.T) { - testApexError(t, "\"otherapex\" is not a valid module name", ` +func TestApexAvailable_IndirectStaticDep(t *testing.T) { + testApex(t, ` apex { name: "myapex", key: "myapex.key", @@ -6500,15 +6682,30 @@ func TestApexAvailable_InvalidApexName(t *testing.T) { cc_library { name: "libfoo", stl: "none", + static_libs: ["libbar"], + system_shared_libs: [], + apex_available: ["myapex"], + } + + cc_library { + name: "libbar", + stl: "none", + shared_libs: ["libbaz"], + system_shared_libs: [], + apex_available: ["myapex"], + } + + cc_library { + name: "libbaz", + stl: "none", system_shared_libs: [], - apex_available: ["otherapex"], }`) - testApex(t, ` + testApexError(t, `requires "libbar" that doesn't list the APEX under 'apex_available'.`, ` apex { name: "myapex", key: "myapex.key", - native_shared_libs: ["libfoo", "libbar"], + native_shared_libs: ["libfoo"], updatable: false, } @@ -6521,8 +6718,8 @@ func TestApexAvailable_InvalidApexName(t *testing.T) { cc_library { name: "libfoo", stl: "none", + static_libs: ["libbar"], system_shared_libs: [], - runtime_libs: ["libbaz"], apex_available: ["myapex"], } @@ -6530,25 +6727,15 @@ func TestApexAvailable_InvalidApexName(t *testing.T) { name: "libbar", stl: "none", system_shared_libs: [], - apex_available: ["//apex_available:anyapex"], - } - - cc_library { - name: "libbaz", - stl: "none", - system_shared_libs: [], - stubs: { - versions: ["10", "20", "30"], - }, }`) } -func TestApexAvailable_CheckForPlatform(t *testing.T) { - ctx := testApex(t, ` +func TestApexAvailable_InvalidApexName(t *testing.T) { + testApexError(t, "\"otherapex\" is not a valid module name", ` apex { name: "myapex", key: "myapex.key", - native_shared_libs: ["libbar", "libbaz"], + native_shared_libs: ["libfoo"], updatable: false, } @@ -6562,61 +6749,392 @@ func TestApexAvailable_CheckForPlatform(t *testing.T) { name: "libfoo", stl: "none", system_shared_libs: [], - shared_libs: ["libbar"], - apex_available: ["//apex_available:platform"], + apex_available: ["otherapex"], + }`) + + testApex(t, ` + apex { + name: "myapex", + key: "myapex.key", + native_shared_libs: ["libfoo", "libbar"], + updatable: false, + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", } cc_library { - name: "libfoo2", + name: "libfoo", stl: "none", system_shared_libs: [], - shared_libs: ["libbaz"], - apex_available: ["//apex_available:platform"], + runtime_libs: ["libbaz"], + apex_available: ["myapex"], } cc_library { name: "libbar", stl: "none", system_shared_libs: [], - apex_available: ["myapex"], + apex_available: ["//apex_available:anyapex"], } cc_library { name: "libbaz", stl: "none", system_shared_libs: [], - apex_available: ["myapex"], stubs: { - versions: ["1"], + versions: ["10", "20", "30"], }, }`) +} - // libfoo shouldn't be available to platform even though it has "//apex_available:platform", - // because it depends on libbar which isn't available to platform - libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module().(*cc.Module) - if libfoo.NotAvailableForPlatform() != true { - t.Errorf("%q shouldn't be available to platform", libfoo.String()) - } +func TestApexAvailable_ApexAvailableNameWithVersionCodeError(t *testing.T) { + t.Run("negative variant_version produces error", func(t *testing.T) { + testApexError(t, "expected an integer between 0-9; got -1", ` + apex { + name: "myapex", + key: "myapex.key", + apex_available_name: "com.android.foo", + variant_version: "-1", + updatable: false, + } + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + `) + }) - // libfoo2 however can be available to platform because it depends on libbaz which provides - // stubs - libfoo2 := ctx.ModuleForTests("libfoo2", "android_arm64_armv8-a_shared").Module().(*cc.Module) - if libfoo2.NotAvailableForPlatform() == true { - t.Errorf("%q should be available to platform", libfoo2.String()) - } + t.Run("variant_version greater than 9 produces error", func(t *testing.T) { + testApexError(t, "expected an integer between 0-9; got 10", ` + apex { + name: "myapex", + key: "myapex.key", + apex_available_name: "com.android.foo", + variant_version: "10", + updatable: false, + } + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + `) + }) } -func TestApexAvailable_CreatedForApex(t *testing.T) { - ctx := testApex(t, ` - apex { - name: "myapex", - key: "myapex.key", - native_shared_libs: ["libfoo"], - updatable: false, +func TestApexAvailable_ApexAvailableNameWithVersionCode(t *testing.T) { + context := android.GroupFixturePreparers( + android.PrepareForIntegrationTestWithAndroid, + PrepareForTestWithApexBuildComponents, + android.FixtureMergeMockFs(android.MockFS{ + "system/sepolicy/apex/foo-file_contexts": nil, + "system/sepolicy/apex/bar-file_contexts": nil, + }), + ) + result := context.RunTestWithBp(t, ` + apex { + name: "foo", + key: "myapex.key", + apex_available_name: "com.android.foo", + variant_version: "0", + updatable: false, + } + apex { + name: "bar", + key: "myapex.key", + apex_available_name: "com.android.foo", + variant_version: "3", + updatable: false, + } + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + override_apex { + name: "myoverrideapex", + base: "bar", + } + `) + + fooManifestRule := result.ModuleForTests("foo", "android_common_foo").Rule("apexManifestRule") + fooExpectedDefaultVersion := android.DefaultUpdatableModuleVersion + fooActualDefaultVersion := fooManifestRule.Args["default_version"] + if fooActualDefaultVersion != fooExpectedDefaultVersion { + t.Errorf("expected to find defaultVersion %q; got %q", fooExpectedDefaultVersion, fooActualDefaultVersion) } - apex_key { - name: "myapex.key", + barManifestRule := result.ModuleForTests("bar", "android_common_bar").Rule("apexManifestRule") + defaultVersionInt, _ := strconv.Atoi(android.DefaultUpdatableModuleVersion) + barExpectedDefaultVersion := fmt.Sprint(defaultVersionInt + 3) + barActualDefaultVersion := barManifestRule.Args["default_version"] + if barActualDefaultVersion != barExpectedDefaultVersion { + t.Errorf("expected to find defaultVersion %q; got %q", barExpectedDefaultVersion, barActualDefaultVersion) + } + + overrideBarManifestRule := result.ModuleForTests("bar", "android_common_myoverrideapex_bar").Rule("apexManifestRule") + overrideBarActualDefaultVersion := overrideBarManifestRule.Args["default_version"] + if overrideBarActualDefaultVersion != barExpectedDefaultVersion { + t.Errorf("expected to find defaultVersion %q; got %q", barExpectedDefaultVersion, barActualDefaultVersion) + } +} + +func TestApexAvailable_ApexAvailableName(t *testing.T) { + t.Run("using name of apex that sets apex_available_name is not allowed", func(t *testing.T) { + testApexError(t, "Consider adding \"myapex\" to 'apex_available' property of \"AppFoo\"", ` + apex { + name: "myapex_sminus", + key: "myapex.key", + apps: ["AppFoo"], + apex_available_name: "myapex", + updatable: false, + } + apex { + name: "myapex", + key: "myapex.key", + apps: ["AppFoo"], + updatable: false, + } + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + android_app { + name: "AppFoo", + srcs: ["foo/bar/MyClass.java"], + sdk_version: "none", + system_modules: "none", + apex_available: [ "myapex_sminus" ], + }`, + android.FixtureMergeMockFs(android.MockFS{ + "system/sepolicy/apex/myapex_sminus-file_contexts": nil, + }), + ) + }) + + t.Run("apex_available_name allows module to be used in two different apexes", func(t *testing.T) { + testApex(t, ` + apex { + name: "myapex_sminus", + key: "myapex.key", + apps: ["AppFoo"], + apex_available_name: "myapex", + updatable: false, + } + apex { + name: "myapex", + key: "myapex.key", + apps: ["AppFoo"], + updatable: false, + } + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + android_app { + name: "AppFoo", + srcs: ["foo/bar/MyClass.java"], + sdk_version: "none", + system_modules: "none", + apex_available: [ "myapex" ], + }`, + android.FixtureMergeMockFs(android.MockFS{ + "system/sepolicy/apex/myapex_sminus-file_contexts": nil, + }), + ) + }) + + t.Run("override_apexes work with apex_available_name", func(t *testing.T) { + testApex(t, ` + override_apex { + name: "myoverrideapex_sminus", + base: "myapex_sminus", + key: "myapex.key", + apps: ["AppFooOverride"], + } + override_apex { + name: "myoverrideapex", + base: "myapex", + key: "myapex.key", + apps: ["AppFooOverride"], + } + apex { + name: "myapex_sminus", + key: "myapex.key", + apps: ["AppFoo"], + apex_available_name: "myapex", + updatable: false, + } + apex { + name: "myapex", + key: "myapex.key", + apps: ["AppFoo"], + updatable: false, + } + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + android_app { + name: "AppFooOverride", + srcs: ["foo/bar/MyClass.java"], + sdk_version: "none", + system_modules: "none", + apex_available: [ "myapex" ], + } + android_app { + name: "AppFoo", + srcs: ["foo/bar/MyClass.java"], + sdk_version: "none", + system_modules: "none", + apex_available: [ "myapex" ], + }`, + android.FixtureMergeMockFs(android.MockFS{ + "system/sepolicy/apex/myapex_sminus-file_contexts": nil, + }), + ) + }) +} + +func TestApexAvailable_ApexAvailableNameWithOverrides(t *testing.T) { + context := android.GroupFixturePreparers( + android.PrepareForIntegrationTestWithAndroid, + PrepareForTestWithApexBuildComponents, + java.PrepareForTestWithDexpreopt, + android.FixtureMergeMockFs(android.MockFS{ + "system/sepolicy/apex/myapex-file_contexts": nil, + "system/sepolicy/apex/myapex_sminus-file_contexts": nil, + }), + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.BuildId = proptools.StringPtr("buildid") + }), + ) + context.RunTestWithBp(t, ` + override_apex { + name: "myoverrideapex_sminus", + base: "myapex_sminus", + } + override_apex { + name: "myoverrideapex", + base: "myapex", + } + apex { + name: "myapex", + key: "myapex.key", + apps: ["AppFoo"], + updatable: false, + } + apex { + name: "myapex_sminus", + apex_available_name: "myapex", + key: "myapex.key", + apps: ["AppFoo_sminus"], + updatable: false, + } + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + android_app { + name: "AppFoo", + srcs: ["foo/bar/MyClass.java"], + sdk_version: "none", + system_modules: "none", + apex_available: [ "myapex" ], + } + android_app { + name: "AppFoo_sminus", + srcs: ["foo/bar/MyClass.java"], + sdk_version: "none", + min_sdk_version: "29", + system_modules: "none", + apex_available: [ "myapex" ], + }`) +} + +func TestApexAvailable_CheckForPlatform(t *testing.T) { + ctx := testApex(t, ` + apex { + name: "myapex", + key: "myapex.key", + native_shared_libs: ["libbar", "libbaz"], + updatable: false, + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + cc_library { + name: "libfoo", + stl: "none", + system_shared_libs: [], + shared_libs: ["libbar"], + apex_available: ["//apex_available:platform"], + } + + cc_library { + name: "libfoo2", + stl: "none", + system_shared_libs: [], + shared_libs: ["libbaz"], + apex_available: ["//apex_available:platform"], + } + + cc_library { + name: "libbar", + stl: "none", + system_shared_libs: [], + apex_available: ["myapex"], + } + + cc_library { + name: "libbaz", + stl: "none", + system_shared_libs: [], + apex_available: ["myapex"], + stubs: { + versions: ["1"], + }, + }`) + + // libfoo shouldn't be available to platform even though it has "//apex_available:platform", + // because it depends on libbar which isn't available to platform + libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module().(*cc.Module) + if libfoo.NotAvailableForPlatform() != true { + t.Errorf("%q shouldn't be available to platform", libfoo.String()) + } + + // libfoo2 however can be available to platform because it depends on libbaz which provides + // stubs + libfoo2 := ctx.ModuleForTests("libfoo2", "android_arm64_armv8-a_shared").Module().(*cc.Module) + if libfoo2.NotAvailableForPlatform() == true { + t.Errorf("%q should be available to platform", libfoo2.String()) + } +} + +func TestApexAvailable_CreatedForApex(t *testing.T) { + ctx := testApex(t, ` + apex { + name: "myapex", + key: "myapex.key", + native_shared_libs: ["libfoo"], + updatable: false, + } + + apex_key { + name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } @@ -6719,8 +7237,8 @@ func TestOverrideApex(t *testing.T) { } `, withManifestPackageNameOverrides([]string{"myapex:com.android.myapex"})) - originalVariant := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(android.OverridableModule) - overriddenVariant := ctx.ModuleForTests("myapex", "android_common_override_myapex_myapex_image").Module().(android.OverridableModule) + originalVariant := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(android.OverridableModule) + overriddenVariant := ctx.ModuleForTests("myapex", "android_common_override_myapex_myapex").Module().(android.OverridableModule) if originalVariant.GetOverriddenBy() != "" { t.Errorf("GetOverriddenBy should be empty, but was %q", originalVariant.GetOverriddenBy()) } @@ -6728,7 +7246,7 @@ func TestOverrideApex(t *testing.T) { t.Errorf("GetOverriddenBy should be \"override_myapex\", but was %q", overriddenVariant.GetOverriddenBy()) } - module := ctx.ModuleForTests("myapex", "android_common_override_myapex_myapex_image") + module := ctx.ModuleForTests("myapex", "android_common_override_myapex_myapex") apexRule := module.Rule("apexRule") copyCmds := apexRule.Args["copy_commands"] @@ -6764,13 +7282,11 @@ func TestOverrideApex(t *testing.T) { androidMk := builder.String() ensureContains(t, androidMk, "LOCAL_MODULE := override_app.override_myapex") ensureContains(t, androidMk, "LOCAL_MODULE := overrideBpf.o.override_myapex") - ensureContains(t, androidMk, "LOCAL_MODULE := apex_manifest.pb.override_myapex") ensureContains(t, androidMk, "LOCAL_MODULE_STEM := override_myapex.apex") ensureContains(t, androidMk, "LOCAL_OVERRIDES_MODULES := unknownapex myapex") ensureNotContains(t, androidMk, "LOCAL_MODULE := app.myapex") ensureNotContains(t, androidMk, "LOCAL_MODULE := bpf.myapex") ensureNotContains(t, androidMk, "LOCAL_MODULE := override_app.myapex") - ensureNotContains(t, androidMk, "LOCAL_MODULE := apex_manifest.pb.myapex") ensureNotContains(t, androidMk, "LOCAL_MODULE_STEM := myapex.apex") } @@ -6820,7 +7336,7 @@ func TestMinSdkVersionOverride(t *testing.T) { `, withApexGlobalMinSdkVersionOverride(&minSdkOverride31)) - apexRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexRule") + apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule") copyCmds := apexRule.Args["copy_commands"] // Ensure that direct non-stubs dep is always included @@ -6879,7 +7395,7 @@ func TestMinSdkVersionOverrideToLowerVersionNoOp(t *testing.T) { `, withApexGlobalMinSdkVersionOverride(&minSdkOverride29)) - apexRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexRule") + apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule") copyCmds := apexRule.Args["copy_commands"] // Ensure that direct non-stubs dep is always included @@ -6917,7 +7433,7 @@ func TestLegacyAndroid10Support(t *testing.T) { } `, withUnbundledBuild) - module := ctx.ModuleForTests("myapex", "android_common_myapex_image") + module := ctx.ModuleForTests("myapex", "android_common_myapex") args := module.Rule("apexRule").Args ensureContains(t, args["opt_flags"], "--manifest_json "+module.Output("apex_manifest.json").Output.String()) ensureNotContains(t, args["opt_flags"], "--no_hashtree") @@ -6981,7 +7497,7 @@ func TestJavaSDKLibrary(t *testing.T) { `, withFiles(filesForSdkLibrary)) // java_sdk_library installs both impl jar and permission XML - ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{ + ensureExactContents(t, ctx, "myapex", "android_common_myapex", []string{ "javalib/foo.jar", "etc/permissions/foo.xml", }) @@ -7030,7 +7546,7 @@ func TestJavaSDKLibrary_WithinApex(t *testing.T) { `, withFiles(filesForSdkLibrary)) // java_sdk_library installs both impl jar and permission XML - ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{ + ensureExactContents(t, ctx, "myapex", "android_common_myapex", []string{ "javalib/bar.jar", "javalib/foo.jar", "etc/permissions/foo.xml", @@ -7082,7 +7598,7 @@ func TestJavaSDKLibrary_CrossBoundary(t *testing.T) { `, withFiles(filesForSdkLibrary)) // java_sdk_library installs both impl jar and permission XML - ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{ + ensureExactContents(t, ctx, "myapex", "android_common_myapex", []string{ "javalib/foo.jar", "etc/permissions/foo.xml", }) @@ -7171,7 +7687,7 @@ func TestJavaSDKLibrary_ImportPreferred(t *testing.T) { ) // java_sdk_library installs both impl jar and permission XML - ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{ + ensureExactContents(t, ctx, "myapex", "android_common_myapex", []string{ "javalib/bar.jar", "javalib/foo.jar", "etc/permissions/foo.xml", @@ -7251,7 +7767,7 @@ func TestCompatConfig(t *testing.T) { } `) ctx := result.TestContext - ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{ + ensureExactContents(t, ctx, "myapex", "android_common_myapex", []string{ "etc/compatconfig/myjar-platform-compat-config.xml", "javalib/myjar.jar", }) @@ -7293,6 +7809,42 @@ func TestNoDupeApexFiles(t *testing.T) { `) } +func TestApexUnwantedTransitiveDeps(t *testing.T) { + bp := ` + apex { + name: "myapex", + key: "myapex.key", + native_shared_libs: ["libfoo"], + updatable: false, + unwanted_transitive_deps: ["libbar"], + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + cc_library { + name: "libfoo", + srcs: ["foo.cpp"], + shared_libs: ["libbar"], + apex_available: ["myapex"], + } + + cc_library { + name: "libbar", + srcs: ["bar.cpp"], + apex_available: ["myapex"], + }` + ctx := testApex(t, bp) + ensureExactContents(t, ctx, "myapex", "android_common_myapex", []string{ + "*/libc++.so", + "*/libfoo.so", + // not libbar.so + }) +} + func TestRejectNonInstallableJavaLibrary(t *testing.T) { testApexError(t, `"myjar" is not configured to be compiled into dex`, ` apex { @@ -7346,14 +7898,14 @@ func TestCarryRequiredModuleNames(t *testing.T) { } `) - apexBundle := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle) + apexBundle := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(*apexBundle) data := android.AndroidMkDataForTest(t, ctx, apexBundle) name := apexBundle.BaseModuleName() prefix := "TARGET_" var builder strings.Builder data.Custom(&builder, name, prefix, "", data) androidMk := builder.String() - ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := mylib.myapex:64 apex_manifest.pb.myapex apex_pubkey.myapex a b\n") + ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := mylib.myapex:64 a b\n") ensureContains(t, androidMk, "LOCAL_HOST_REQUIRED_MODULES := c d\n") ensureContains(t, androidMk, "LOCAL_TARGET_REQUIRED_MODULES := e f\n") } @@ -7485,13 +8037,13 @@ func TestSymlinksFromApexToSystem(t *testing.T) { // For unbundled build, symlink shouldn't exist regardless of whether an APEX // is updatable or not ctx := testApex(t, bp, withUnbundledBuild) - files := getFiles(t, ctx, "myapex", "android_common_myapex_image") + files := getFiles(t, ctx, "myapex", "android_common_myapex") ensureRealfileExists(t, files, "javalib/myjar.jar") ensureRealfileExists(t, files, "lib64/mylib.so") ensureRealfileExists(t, files, "lib64/myotherlib.so") ensureRealfileExists(t, files, "lib64/myotherlib_ext.so") - files = getFiles(t, ctx, "myapex.updatable", "android_common_myapex.updatable_image") + files = getFiles(t, ctx, "myapex.updatable", "android_common_myapex.updatable") ensureRealfileExists(t, files, "javalib/myjar.jar") ensureRealfileExists(t, files, "lib64/mylib.so") ensureRealfileExists(t, files, "lib64/myotherlib.so") @@ -7499,13 +8051,13 @@ func TestSymlinksFromApexToSystem(t *testing.T) { // For bundled build, symlink to the system for the non-updatable APEXes only ctx = testApex(t, bp) - files = getFiles(t, ctx, "myapex", "android_common_myapex_image") + files = getFiles(t, ctx, "myapex", "android_common_myapex") ensureRealfileExists(t, files, "javalib/myjar.jar") ensureRealfileExists(t, files, "lib64/mylib.so") ensureSymlinkExists(t, files, "lib64/myotherlib.so", "/system/lib64/myotherlib.so") // this is symlink ensureSymlinkExists(t, files, "lib64/myotherlib_ext.so", "/system_ext/lib64/myotherlib_ext.so") // this is symlink - files = getFiles(t, ctx, "myapex.updatable", "android_common_myapex.updatable_image") + files = getFiles(t, ctx, "myapex.updatable", "android_common_myapex.updatable") ensureRealfileExists(t, files, "javalib/myjar.jar") ensureRealfileExists(t, files, "lib64/mylib.so") ensureRealfileExists(t, files, "lib64/myotherlib.so") // this is a real file @@ -7551,7 +8103,7 @@ func TestSymlinksFromApexToSystemRequiredModuleNames(t *testing.T) { } `) - apexBundle := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle) + apexBundle := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(*apexBundle) data := android.AndroidMkDataForTest(t, ctx, apexBundle) var builder strings.Builder data.Custom(&builder, apexBundle.BaseModuleName(), "TARGET_", "", data) @@ -7561,7 +8113,7 @@ func TestSymlinksFromApexToSystemRequiredModuleNames(t *testing.T) { ensureNotContains(t, androidMk, "LOCAL_MODULE := prebuilt_myotherlib.myapex\n") ensureNotContains(t, androidMk, "LOCAL_MODULE := myotherlib.myapex\n") // `myapex` should have `myotherlib` in its required line, not `prebuilt_myotherlib` - ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := mylib.myapex:64 myotherlib:64 apex_manifest.pb.myapex apex_pubkey.myapex\n") + ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := mylib.myapex:64 myotherlib:64\n") } func TestApexWithJniLibs(t *testing.T) { @@ -7569,7 +8121,8 @@ func TestApexWithJniLibs(t *testing.T) { apex { name: "myapex", key: "myapex.key", - jni_libs: ["mylib", "libfoo.rust"], + binaries: ["mybin"], + jni_libs: ["mylib", "mylib3", "libfoo.rust"], updatable: false, } @@ -7596,6 +8149,24 @@ func TestApexWithJniLibs(t *testing.T) { apex_available: [ "myapex" ], } + // Used as both a JNI library and a regular shared library. + cc_library { + name: "mylib3", + srcs: ["mylib.cpp"], + system_shared_libs: [], + stl: "none", + apex_available: [ "myapex" ], + } + + cc_binary { + name: "mybin", + srcs: ["mybin.cpp"], + shared_libs: ["mylib3"], + system_shared_libs: [], + stl: "none", + apex_available: [ "myapex" ], + } + rust_ffi_shared { name: "libfoo.rust", crate_name: "foo", @@ -7617,12 +8188,14 @@ func TestApexWithJniLibs(t *testing.T) { `) - rule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexManifestRule") + rule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexManifestRule") // Notice mylib2.so (transitive dep) is not added as a jni_lib - ensureEquals(t, rule.Args["opt"], "-a jniLibs libfoo.rust.so mylib.so") - ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{ + ensureEquals(t, rule.Args["opt"], "-a jniLibs libfoo.rust.so mylib.so mylib3.so") + ensureExactContents(t, ctx, "myapex", "android_common_myapex", []string{ + "bin/mybin", "lib64/mylib.so", "lib64/mylib2.so", + "lib64/mylib3.so", "lib64/libfoo.rust.so", "lib64/libc++.so", // auto-added to libfoo.rust by Soong "lib64/liblog.so", // auto-added to libfoo.rust by Soong @@ -7680,8 +8253,8 @@ func TestAppBundle(t *testing.T) { } `, withManifestPackageNameOverrides([]string{"AppFoo:com.android.foo"})) - bundleConfigRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Output("bundle_config.json") - content := bundleConfigRule.Args["content"] + bundleConfigRule := ctx.ModuleForTests("myapex", "android_common_myapex").Output("bundle_config.json") + content := android.ContentFromFileRuleForTests(t, ctx, bundleConfigRule) ensureContains(t, content, `"compression":{"uncompressed_glob":["apex_payload.img","apex_manifest.*"]}`) ensureContains(t, content, `"apex_config":{"apex_embedded_apk_config":[{"package_name":"com.android.foo","path":"app/AppFoo@TEST.BUILD_ID/AppFoo.apk"}]}`) @@ -7706,9 +8279,9 @@ func TestAppSetBundle(t *testing.T) { name: "AppSet", set: "AppSet.apks", }`) - mod := ctx.ModuleForTests("myapex", "android_common_myapex_image") + mod := ctx.ModuleForTests("myapex", "android_common_myapex") bundleConfigRule := mod.Output("bundle_config.json") - content := bundleConfigRule.Args["content"] + content := android.ContentFromFileRuleForTests(t, ctx, bundleConfigRule) ensureContains(t, content, `"compression":{"uncompressed_glob":["apex_payload.img","apex_manifest.*"]}`) s := mod.Rule("apexRule").Args["copy_commands"] copyCmds := regexp.MustCompile(" *&& *").Split(s, -1) @@ -7776,150 +8349,30 @@ func TestApexSetApksModuleAssignment(t *testing.T) { android.AssertArrayString(t, "extractor input", []string{"myapex.apks"}, extractedApex.Inputs.Strings()) } -func testNoUpdatableJarsInBootImage(t *testing.T, errmsg string, preparer android.FixturePreparer, fragments ...java.ApexVariantReference) { +func testDexpreoptWithApexes(t *testing.T, bp, errmsg string, preparer android.FixturePreparer, fragments ...java.ApexVariantReference) *android.TestContext { t.Helper() - bp := ` - java_library { - name: "some-updatable-apex-lib", - srcs: ["a.java"], - sdk_version: "current", - apex_available: [ - "some-updatable-apex", - ], - permitted_packages: ["some.updatable.apex.lib"], - min_sdk_version: "33", - } - - java_library { - name: "some-non-updatable-apex-lib", - srcs: ["a.java"], - apex_available: [ - "some-non-updatable-apex", - ], - compile_dex: true, - permitted_packages: ["some.non.updatable.apex.lib"], - } + fs := android.MockFS{ + "a.java": nil, + "a.jar": nil, + "apex_manifest.json": nil, + "AndroidManifest.xml": nil, + "system/sepolicy/apex/myapex-file_contexts": nil, + "system/sepolicy/apex/some-updatable-apex-file_contexts": nil, + "system/sepolicy/apex/some-non-updatable-apex-file_contexts": nil, + "system/sepolicy/apex/com.android.art.debug-file_contexts": nil, + "framework/aidl/a.aidl": nil, + } - bootclasspath_fragment { - name: "some-non-updatable-fragment", - contents: ["some-non-updatable-apex-lib"], - apex_available: [ - "some-non-updatable-apex", - ], - hidden_api: { - split_packages: ["*"], - }, - } - - java_library { - name: "some-platform-lib", - srcs: ["a.java"], - sdk_version: "current", - installable: true, - } - - java_library { - name: "some-art-lib", - srcs: ["a.java"], - sdk_version: "current", - apex_available: [ - "com.android.art.debug", - ], - hostdex: true, - compile_dex: true, - min_sdk_version: "33", - } - - apex { - name: "some-updatable-apex", - key: "some-updatable-apex.key", - java_libs: ["some-updatable-apex-lib"], - updatable: true, - min_sdk_version: "33", - } - - apex { - name: "some-non-updatable-apex", - key: "some-non-updatable-apex.key", - bootclasspath_fragments: ["some-non-updatable-fragment"], - updatable: false, - } - - apex_key { - name: "some-updatable-apex.key", - } - - apex_key { - name: "some-non-updatable-apex.key", - } - - apex { - name: "com.android.art.debug", - key: "com.android.art.debug.key", - bootclasspath_fragments: ["art-bootclasspath-fragment"], - updatable: true, - min_sdk_version: "33", - } - - bootclasspath_fragment { - name: "art-bootclasspath-fragment", - image_name: "art", - contents: ["some-art-lib"], - apex_available: [ - "com.android.art.debug", - ], - hidden_api: { - split_packages: ["*"], - }, - } - - apex_key { - name: "com.android.art.debug.key", - } - - filegroup { - name: "some-updatable-apex-file_contexts", - srcs: [ - "system/sepolicy/apex/some-updatable-apex-file_contexts", - ], - } - - filegroup { - name: "some-non-updatable-apex-file_contexts", - srcs: [ - "system/sepolicy/apex/some-non-updatable-apex-file_contexts", - ], - } - ` - - testDexpreoptWithApexes(t, bp, errmsg, preparer, fragments...) -} - -func testDexpreoptWithApexes(t *testing.T, bp, errmsg string, preparer android.FixturePreparer, fragments ...java.ApexVariantReference) *android.TestContext { - t.Helper() - - fs := android.MockFS{ - "a.java": nil, - "a.jar": nil, - "apex_manifest.json": nil, - "AndroidManifest.xml": nil, - "system/sepolicy/apex/myapex-file_contexts": nil, - "system/sepolicy/apex/some-updatable-apex-file_contexts": nil, - "system/sepolicy/apex/some-non-updatable-apex-file_contexts": nil, - "system/sepolicy/apex/com.android.art.debug-file_contexts": nil, - "framework/aidl/a.aidl": nil, - } - - errorHandler := android.FixtureExpectsNoErrors - if errmsg != "" { - errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(errmsg) - } + errorHandler := android.FixtureExpectsNoErrors + if errmsg != "" { + errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(errmsg) + } result := android.GroupFixturePreparers( cc.PrepareForTestWithCcDefaultModules, java.PrepareForTestWithHiddenApiBuildComponents, - java.PrepareForTestWithJavaDefaultModules, + java.PrepareForTestWithDexpreopt, java.PrepareForTestWithJavaSdkLibraryFiles, PrepareForTestWithApexBuildComponents, preparer, @@ -7934,12 +8387,16 @@ func testDexpreoptWithApexes(t *testing.T, bp, errmsg string, preparer android.F platform_bootclasspath { name: "platform-bootclasspath", fragments: [ + {apex: "com.android.art", module: "art-bootclasspath-fragment"}, %s ], } `, insert)) } }), + // Dexpreopt for boot jars requires the ART boot image profile. + java.PrepareApexBootJarModule("com.android.art", "core-oj"), + dexpreopt.FixtureSetArtBootJars("com.android.art:core-oj"), dexpreopt.FixtureSetBootImageProfiles("art/build/boot/boot-image-profile.txt"), ). ExtendWithErrorHandler(errorHandler). @@ -8246,126 +8703,6 @@ func TestUpdatable_should_not_set_generate_classpaths_proto(t *testing.T) { ) } -func TestNoUpdatableJarsInBootImage(t *testing.T) { - // Set the BootJars in dexpreopt.GlobalConfig and productVariables to the same value. This can - // result in an invalid configuration as it does not set the ArtApexJars and allows art apex - // modules to be included in the BootJars. - prepareSetBootJars := func(bootJars ...string) android.FixturePreparer { - return android.GroupFixturePreparers( - dexpreopt.FixtureSetBootJars(bootJars...), - android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { - variables.BootJars = android.CreateTestConfiguredJarList(bootJars) - }), - ) - } - - // Set the ArtApexJars and BootJars in dexpreopt.GlobalConfig and productVariables all to the - // same value. This can result in an invalid configuration as it allows non art apex jars to be - // specified in the ArtApexJars configuration. - prepareSetArtJars := func(bootJars ...string) android.FixturePreparer { - return android.GroupFixturePreparers( - dexpreopt.FixtureSetArtBootJars(bootJars...), - dexpreopt.FixtureSetBootJars(bootJars...), - android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { - variables.BootJars = android.CreateTestConfiguredJarList(bootJars) - }), - ) - } - - t.Run("updatable jar from ART apex in the ART boot image => ok", func(t *testing.T) { - preparer := android.GroupFixturePreparers( - java.FixtureConfigureBootJars("com.android.art.debug:some-art-lib"), - java.FixtureConfigureApexBootJars("some-non-updatable-apex:some-non-updatable-apex-lib"), - ) - fragments := []java.ApexVariantReference{ - { - Apex: proptools.StringPtr("com.android.art.debug"), - Module: proptools.StringPtr("art-bootclasspath-fragment"), - }, - { - Apex: proptools.StringPtr("some-non-updatable-apex"), - Module: proptools.StringPtr("some-non-updatable-fragment"), - }, - } - testNoUpdatableJarsInBootImage(t, "", preparer, fragments...) - }) - - t.Run("updatable jar from ART apex in the platform bootclasspath => error", func(t *testing.T) { - err := `module "some-art-lib" from updatable apexes \["com.android.art.debug"\] is not allowed in the platform bootclasspath` - // Update the dexpreopt BootJars directly. - preparer := android.GroupFixturePreparers( - prepareSetBootJars("com.android.art.debug:some-art-lib"), - java.FixtureConfigureApexBootJars("some-non-updatable-apex:some-non-updatable-apex-lib"), - ) - testNoUpdatableJarsInBootImage(t, err, preparer) - }) - - t.Run("updatable jar from some other apex in the ART boot image => error", func(t *testing.T) { - err := `ArtApexJars expects this to be in apex "some-updatable-apex" but this is only in apexes.*"com.android.art.debug"` - // Update the dexpreopt ArtApexJars directly. - preparer := prepareSetArtJars("some-updatable-apex:some-updatable-apex-lib") - testNoUpdatableJarsInBootImage(t, err, preparer) - }) - - t.Run("non-updatable jar from some other apex in the ART boot image => error", func(t *testing.T) { - err := `ArtApexJars expects this to be in apex "some-non-updatable-apex" but this is only in apexes.*"com.android.art.debug"` - // Update the dexpreopt ArtApexJars directly. - preparer := prepareSetArtJars("some-non-updatable-apex:some-non-updatable-apex-lib") - testNoUpdatableJarsInBootImage(t, err, preparer) - }) - - t.Run("updatable jar from some other apex in the platform bootclasspath => error", func(t *testing.T) { - err := `module "some-updatable-apex-lib" from updatable apexes \["some-updatable-apex"\] is not allowed in the platform bootclasspath` - preparer := android.GroupFixturePreparers( - java.FixtureConfigureBootJars("some-updatable-apex:some-updatable-apex-lib"), - java.FixtureConfigureApexBootJars("some-non-updatable-apex:some-non-updatable-apex-lib"), - ) - testNoUpdatableJarsInBootImage(t, err, preparer) - }) - - t.Run("non-updatable jar from some other apex in the platform bootclasspath => ok", func(t *testing.T) { - preparer := java.FixtureConfigureApexBootJars("some-non-updatable-apex:some-non-updatable-apex-lib") - fragment := java.ApexVariantReference{ - Apex: proptools.StringPtr("some-non-updatable-apex"), - Module: proptools.StringPtr("some-non-updatable-fragment"), - } - testNoUpdatableJarsInBootImage(t, "", preparer, fragment) - }) - - t.Run("nonexistent jar in the ART boot image => error", func(t *testing.T) { - err := `"platform-bootclasspath" depends on undefined module "nonexistent"` - preparer := java.FixtureConfigureBootJars("platform:nonexistent") - testNoUpdatableJarsInBootImage(t, err, preparer) - }) - - t.Run("nonexistent jar in the platform bootclasspath => error", func(t *testing.T) { - err := `"platform-bootclasspath" depends on undefined module "nonexistent"` - preparer := java.FixtureConfigureBootJars("platform:nonexistent") - testNoUpdatableJarsInBootImage(t, err, preparer) - }) - - t.Run("platform jar in the ART boot image => error", func(t *testing.T) { - err := `ArtApexJars is invalid as it requests a platform variant of "some-platform-lib"` - // Update the dexpreopt ArtApexJars directly. - preparer := prepareSetArtJars("platform:some-platform-lib") - testNoUpdatableJarsInBootImage(t, err, preparer) - }) - - t.Run("platform jar in the platform bootclasspath => ok", func(t *testing.T) { - preparer := android.GroupFixturePreparers( - java.FixtureConfigureBootJars("platform:some-platform-lib"), - java.FixtureConfigureApexBootJars("some-non-updatable-apex:some-non-updatable-apex-lib"), - ) - fragments := []java.ApexVariantReference{ - { - Apex: proptools.StringPtr("some-non-updatable-apex"), - Module: proptools.StringPtr("some-non-updatable-fragment"), - }, - } - testNoUpdatableJarsInBootImage(t, "", preparer, fragments...) - }) -} - func TestDexpreoptAccessDexFilesFromPrebuiltApex(t *testing.T) { preparer := java.FixtureConfigureApexBootJars("myapex:libfoo") t.Run("prebuilt no source", func(t *testing.T) { @@ -8718,14 +9055,14 @@ func TestTestForForLibInOtherApex(t *testing.T) { apex { name: "com.android.art", key: "myapex.key", - native_shared_libs: ["mylib"], + native_shared_libs: ["libnativebridge"], updatable: false, } apex { name: "com.android.art.debug", key: "myapex.key", - native_shared_libs: ["mylib", "mytestlib"], + native_shared_libs: ["libnativebridge", "libnativebrdige_test"], updatable: false, } @@ -8736,8 +9073,8 @@ func TestTestForForLibInOtherApex(t *testing.T) { } cc_library { - name: "mylib", - srcs: ["mylib.cpp"], + name: "libnativebridge", + srcs: ["libnativebridge.cpp"], system_shared_libs: [], stl: "none", stubs: { @@ -8747,10 +9084,10 @@ func TestTestForForLibInOtherApex(t *testing.T) { } cc_library { - name: "mytestlib", + name: "libnativebrdige_test", srcs: ["mylib.cpp"], system_shared_libs: [], - shared_libs: ["mylib"], + shared_libs: ["libnativebridge"], stl: "none", apex_available: ["com.android.art.debug"], test_for: ["com.android.art"], @@ -8888,9 +9225,9 @@ func TestApexKeysTxt(t *testing.T) { } `) - apexKeysText := ctx.SingletonForTests("apex_keys_text") - content := apexKeysText.MaybeDescription("apexkeys.txt").BuildParams.Args["content"] - ensureContains(t, content, `name="myapex.apex" public_key="vendor/foo/devkeys/testkey.avbpubkey" private_key="vendor/foo/devkeys/testkey.pem" container_certificate="vendor/foo/devkeys/test.x509.pem" container_private_key="vendor/foo/devkeys/test.pk8" partition="system_ext" sign_tool="sign_myapex"`) + myapex := ctx.ModuleForTests("myapex", "android_common_myapex") + content := android.ContentFromFileRuleForTests(t, ctx, myapex.Output("apexkeys.txt")) + ensureContains(t, content, `name="myapex.apex" public_key="vendor/foo/devkeys/testkey.avbpubkey" private_key="vendor/foo/devkeys/testkey.pem" container_certificate="vendor/foo/devkeys/test.x509.pem" container_private_key="vendor/foo/devkeys/test.pk8" partition="system" sign_tool="sign_myapex"`) } func TestApexKeysTxtOverrides(t *testing.T) { @@ -8929,10 +9266,12 @@ func TestApexKeysTxtOverrides(t *testing.T) { } `) - apexKeysText := ctx.SingletonForTests("apex_keys_text") - content := apexKeysText.MaybeDescription("apexkeys.txt").BuildParams.Args["content"] + content := android.ContentFromFileRuleForTests(t, ctx, + ctx.ModuleForTests("myapex", "android_common_myapex").Output("apexkeys.txt")) + ensureContains(t, content, `name="myapex.apex" public_key="vendor/foo/devkeys/testkey.avbpubkey" private_key="vendor/foo/devkeys/testkey.pem" container_certificate="vendor/foo/devkeys/test.x509.pem" container_private_key="vendor/foo/devkeys/test.pk8" partition="system" sign_tool="sign_myapex"`) + content = android.ContentFromFileRuleForTests(t, ctx, + ctx.ModuleForTests("myapex_set", "android_common_myapex_set").Output("apexkeys.txt")) ensureContains(t, content, `name="myapex_set.apex" public_key="PRESIGNED" private_key="PRESIGNED" container_certificate="PRESIGNED" container_private_key="PRESIGNED" partition="system"`) - ensureContains(t, content, `name="myapex.apex" public_key="PRESIGNED" private_key="PRESIGNED" container_certificate="PRESIGNED" container_private_key="PRESIGNED" partition="system"`) } func TestAllowedFiles(t *testing.T) { @@ -8980,12 +9319,12 @@ func TestAllowedFiles(t *testing.T) { `), })) - rule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("diffApexContentRule") + rule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("diffApexContentRule") if expected, actual := "allowed.txt", rule.Args["allowed_files_file"]; expected != actual { t.Errorf("allowed_files_file: expected %q but got %q", expected, actual) } - rule2 := ctx.ModuleForTests("myapex", "android_common_override_myapex_myapex_image").Rule("diffApexContentRule") + rule2 := ctx.ModuleForTests("myapex", "android_common_override_myapex_myapex").Rule("diffApexContentRule") if expected, actual := "sub/allowed.txt", rule2.Args["allowed_files_file"]; expected != actual { t.Errorf("allowed_files_file: expected %q but got %q", expected, actual) } @@ -9046,14 +9385,14 @@ func TestCompressedApex(t *testing.T) { }), ) - compressRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("compressRule") + compressRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("compressRule") ensureContains(t, compressRule.Output.String(), "myapex.capex.unsigned") - signApkRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Description("sign compressedApex") + signApkRule := ctx.ModuleForTests("myapex", "android_common_myapex").Description("sign compressedApex") ensureEquals(t, signApkRule.Input.String(), compressRule.Output.String()) // Make sure output of bundle is .capex - ab := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle) + ab := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(*apexBundle) ensureContains(t, ab.outputFile.String(), "myapex.capex") // Verify android.mk rules @@ -9105,7 +9444,7 @@ func TestPreferredPrebuiltSharedLibDep(t *testing.T) { } `) - ab := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle) + ab := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(*apexBundle) data := android.AndroidMkDataForTest(t, ctx, ab) var builder strings.Builder data.Custom(&builder, ab.BaseModuleName(), "TARGET_", "", data) @@ -9113,7 +9452,7 @@ func TestPreferredPrebuiltSharedLibDep(t *testing.T) { // The make level dependency needs to be on otherlib - prebuilt_otherlib isn't // a thing there. - ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := libc++:64 mylib.myapex:64 apex_manifest.pb.myapex apex_pubkey.myapex otherlib\n") + ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := libc++:64 mylib.myapex:64 otherlib\n") } func TestExcludeDependency(t *testing.T) { @@ -9162,7 +9501,7 @@ func TestExcludeDependency(t *testing.T) { ensureNotContains(t, ldFlags, "mylib2/android_arm64_armv8-a_shared_apex10000/mylib2.so") // It shouldn't appear in the copy cmd as well. - copyCmds := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexRule").Args["copy_commands"] + copyCmds := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule").Args["copy_commands"] ensureNotContains(t, copyCmds, "image.apex/lib64/mylib2.so") } @@ -9327,28 +9666,6 @@ func TestPrebuiltStubLibDep(t *testing.T) { } } -func TestHostApexInHostOnlyBuild(t *testing.T) { - testApex(t, ` - apex { - name: "myapex", - host_supported: true, - key: "myapex.key", - updatable: false, - payload_type: "zip", - } - apex_key { - name: "myapex.key", - public_key: "testkey.avbpubkey", - private_key: "testkey.pem", - } - `, - android.FixtureModifyConfig(func(config android.Config) { - // We may not have device targets in all builds, e.g. in - // prebuilts/build-tools/build-prebuilts.sh - config.Targets[android.Android] = []android.Target{} - })) -} - func TestApexJavaCoverage(t *testing.T) { bp := ` apex { @@ -9502,12 +9819,12 @@ func TestAndroidMk_DexpreoptBuiltInstalledForApex(t *testing.T) { dexpreopt.FixtureSetApexSystemServerJars("myapex:foo"), ) - apexBundle := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle) + apexBundle := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(*apexBundle) data := android.AndroidMkDataForTest(t, ctx, apexBundle) var builder strings.Builder data.Custom(&builder, apexBundle.BaseModuleName(), "TARGET_", "", data) androidMk := builder.String() - ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := foo.myapex apex_manifest.pb.myapex apex_pubkey.myapex foo-dexpreopt-arm64-apex@myapex@javalib@foo.jar@classes.odex foo-dexpreopt-arm64-apex@myapex@javalib@foo.jar@classes.vdex\n") + ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := foo.myapex foo-dexpreopt-arm64-apex@myapex@javalib@foo.jar@classes.odex foo-dexpreopt-arm64-apex@myapex@javalib@foo.jar@classes.vdex\n") } func TestAndroidMk_DexpreoptBuiltInstalledForApex_Prebuilt(t *testing.T) { @@ -9578,12 +9895,12 @@ func TestAndroidMk_RequiredModules(t *testing.T) { } `) - apexBundle := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle) + apexBundle := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(*apexBundle) data := android.AndroidMkDataForTest(t, ctx, apexBundle) var builder strings.Builder data.Custom(&builder, apexBundle.BaseModuleName(), "TARGET_", "", data) androidMk := builder.String() - ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := foo.myapex apex_manifest.pb.myapex apex_pubkey.myapex otherapex") + ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := foo.myapex otherapex") } func TestAndroidMk_RequiredDeps(t *testing.T) { @@ -9601,21 +9918,13 @@ func TestAndroidMk_RequiredDeps(t *testing.T) { } `) - bundle := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle) + bundle := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(*apexBundle) bundle.makeModulesToInstall = append(bundle.makeModulesToInstall, "foo") data := android.AndroidMkDataForTest(t, ctx, bundle) var builder strings.Builder data.Custom(&builder, bundle.BaseModuleName(), "TARGET_", "", data) androidMk := builder.String() - ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := apex_manifest.pb.myapex apex_pubkey.myapex foo\n") - - flattenedBundle := ctx.ModuleForTests("myapex", "android_common_myapex_flattened").Module().(*apexBundle) - flattenedBundle.makeModulesToInstall = append(flattenedBundle.makeModulesToInstall, "foo") - flattenedData := android.AndroidMkDataForTest(t, ctx, flattenedBundle) - var flattenedBuilder strings.Builder - flattenedData.Custom(&flattenedBuilder, flattenedBundle.BaseModuleName(), "TARGET_", "", flattenedData) - flattenedAndroidMk := flattenedBuilder.String() - ensureContains(t, flattenedAndroidMk, "LOCAL_REQUIRED_MODULES := apex_manifest.pb.myapex.flattened apex_pubkey.myapex.flattened foo\n") + ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := foo\n") } func TestApexOutputFileProducer(t *testing.T) { @@ -9627,12 +9936,12 @@ func TestApexOutputFileProducer(t *testing.T) { { name: "test_using_output", ref: ":myapex", - expected_data: []string{"out/soong/.intermediates/myapex/android_common_myapex_image/myapex.capex:myapex.capex"}, + expected_data: []string{"out/soong/.intermediates/myapex/android_common_myapex/myapex.capex:myapex.capex"}, }, { name: "test_using_apex", ref: ":myapex{.apex}", - expected_data: []string{"out/soong/.intermediates/myapex/android_common_myapex_image/myapex.apex:myapex.apex"}, + expected_data: []string{"out/soong/.intermediates/myapex/android_common_myapex/myapex.apex:myapex.apex"}, }, } { t.Run(tc.name, func(t *testing.T) { @@ -10357,6 +10666,8 @@ func TestTrimmedApex(t *testing.T) { src: "libc.so", min_sdk_version: "29", recovery_available: true, + vendor_available: true, + product_available: true, } api_imports { name: "api_imports", @@ -10367,14 +10678,14 @@ func TestTrimmedApex(t *testing.T) { } ` ctx := testApex(t, bp) - module := ctx.ModuleForTests("myapex", "android_common_myapex_image") + module := ctx.ModuleForTests("myapex", "android_common_myapex") apexRule := module.MaybeRule("apexRule") if apexRule.Rule == nil { t.Errorf("Expecting regular apex rule but a non regular apex rule found") } ctx = testApex(t, bp, android.FixtureModifyConfig(android.SetTrimmedApexEnabledForTests)) - trimmedApexRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("TrimmedApexRule") + trimmedApexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("TrimmedApexRule") libs_to_trim := trimmedApexRule.Args["libs_to_trim"] android.AssertStringDoesContain(t, "missing lib to trim", libs_to_trim, "libfoo") android.AssertStringDoesContain(t, "missing lib to trim", libs_to_trim, "libbar") @@ -10394,7 +10705,7 @@ func TestCannedFsConfig(t *testing.T) { public_key: "testkey.avbpubkey", private_key: "testkey.pem", }`) - mod := ctx.ModuleForTests("myapex", "android_common_myapex_image") + mod := ctx.ModuleForTests("myapex", "android_common_myapex") generateFsRule := mod.Rule("generateFsConfig") cmd := generateFsRule.RuleParams.Command @@ -10415,7 +10726,7 @@ func TestCannedFsConfig_HasCustomConfig(t *testing.T) { public_key: "testkey.avbpubkey", private_key: "testkey.pem", }`) - mod := ctx.ModuleForTests("myapex", "android_common_myapex_image") + mod := ctx.ModuleForTests("myapex", "android_common_myapex") generateFsRule := mod.Rule("generateFsConfig") cmd := generateFsRule.RuleParams.Command @@ -10423,6 +10734,80 @@ func TestCannedFsConfig_HasCustomConfig(t *testing.T) { ensureContains(t, cmd, `( echo '/ 1000 1000 0755'; echo '/apex_manifest.json 1000 1000 0644'; echo '/apex_manifest.pb 1000 1000 0644'; cat my_config ) >`) } +func TestStubLibrariesMultipleApexViolation(t *testing.T) { + testCases := []struct { + desc string + hasStubs bool + apexAvailable string + expectedError string + }{ + { + desc: "non-stub library can have multiple apex_available", + hasStubs: false, + apexAvailable: `["myapex", "otherapex"]`, + }, + { + desc: "stub library should not be available to anyapex", + hasStubs: true, + apexAvailable: `["//apex_available:anyapex"]`, + expectedError: "Stub libraries should have a single apex_available.*anyapex", + }, + { + desc: "stub library should not be available to multiple apexes", + hasStubs: true, + apexAvailable: `["myapex", "otherapex"]`, + expectedError: "Stub libraries should have a single apex_available.*myapex.*otherapex", + }, + { + desc: "stub library can be available to a core apex and a test apex", + hasStubs: true, + apexAvailable: `["myapex", "test_myapex"]`, + }, + } + bpTemplate := ` + cc_library { + name: "libfoo", + %v + apex_available: %v, + } + apex { + name: "myapex", + key: "apex.key", + updatable: false, + native_shared_libs: ["libfoo"], + } + apex { + name: "otherapex", + key: "apex.key", + updatable: false, + } + apex_test { + name: "test_myapex", + key: "apex.key", + updatable: false, + native_shared_libs: ["libfoo"], + } + apex_key { + name: "apex.key", + } + ` + for _, tc := range testCases { + stubs := "" + if tc.hasStubs { + stubs = `stubs: {symbol_file: "libfoo.map.txt"},` + } + bp := fmt.Sprintf(bpTemplate, stubs, tc.apexAvailable) + mockFsFixturePreparer := android.FixtureModifyMockFS(func(fs android.MockFS) { + fs["system/sepolicy/apex/test_myapex-file_contexts"] = nil + }) + if tc.expectedError == "" { + testApex(t, bp, mockFsFixturePreparer) + } else { + testApexError(t, tc.expectedError, bp, mockFsFixturePreparer) + } + } +} + func TestFileSystemShouldSkipApexLibraries(t *testing.T) { context := android.GroupFixturePreparers( android.PrepareForIntegrationTestWithAndroid, @@ -10473,3 +10858,549 @@ func TestFileSystemShouldSkipApexLibraries(t *testing.T) { inputs.Strings(), "out/soong/.intermediates/libbar/android_arm64_armv8-a_shared/libbar.so") } + +var apex_default_bp = ` + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + filegroup { + name: "myapex.manifest", + srcs: ["apex_manifest.json"], + } + + filegroup { + name: "myapex.androidmanifest", + srcs: ["AndroidManifest.xml"], + } +` + +func TestAconfigFilesJavaDeps(t *testing.T) { + ctx := testApex(t, apex_default_bp+` + apex { + name: "myapex", + manifest: ":myapex.manifest", + androidManifest: ":myapex.androidmanifest", + key: "myapex.key", + java_libs: [ + "my_java_library_foo", + "my_java_library_bar", + ], + updatable: false, + } + + java_library { + name: "my_java_library_foo", + srcs: ["foo/bar/MyClass.java"], + sdk_version: "none", + system_modules: "none", + static_libs: ["my_java_aconfig_library_foo"], + apex_available: [ + "myapex", + ], + } + + java_library { + name: "my_java_library_bar", + srcs: ["foo/bar/MyClass.java"], + sdk_version: "none", + system_modules: "none", + static_libs: ["my_java_aconfig_library_bar"], + apex_available: [ + "myapex", + ], + } + + aconfig_declarations { + name: "my_aconfig_declarations_foo", + package: "com.example.package", + container: "myapex", + srcs: ["foo.aconfig"], + } + + java_aconfig_library { + name: "my_java_aconfig_library_foo", + aconfig_declarations: "my_aconfig_declarations_foo", + apex_available: [ + "myapex", + ], + } + + aconfig_declarations { + name: "my_aconfig_declarations_bar", + package: "com.example.package", + container: "myapex", + srcs: ["bar.aconfig"], + } + + java_aconfig_library { + name: "my_java_aconfig_library_bar", + aconfig_declarations: "my_aconfig_declarations_bar", + apex_available: [ + "myapex", + ], + } + `) + + mod := ctx.ModuleForTests("myapex", "android_common_myapex") + s := mod.Rule("apexRule").Args["copy_commands"] + copyCmds := regexp.MustCompile(" *&& *").Split(s, -1) + if len(copyCmds) != 5 { + t.Fatalf("Expected 5 commands, got %d in:\n%s", len(copyCmds), s) + } + + ensureMatches(t, copyCmds[4], "^cp -f .*/aconfig_flags.pb .*/image.apex$") + + combineAconfigRule := mod.Rule("All_aconfig_declarations_dump") + s = " " + combineAconfigRule.Args["cache_files"] + aconfigArgs := regexp.MustCompile(" --cache ").Split(s, -1)[1:] + if len(aconfigArgs) != 2 { + t.Fatalf("Expected 2 commands, got %d in:\n%s", len(aconfigArgs), s) + } + android.EnsureListContainsSuffix(t, aconfigArgs, "my_aconfig_declarations_foo/intermediate.pb") + android.EnsureListContainsSuffix(t, aconfigArgs, "my_aconfig_declarations_bar/intermediate.pb") + + buildParams := combineAconfigRule.BuildParams + android.EnsureListContainsSuffix(t, buildParams.Inputs.Strings(), "my_aconfig_declarations_foo/intermediate.pb") + android.EnsureListContainsSuffix(t, buildParams.Inputs.Strings(), "my_aconfig_declarations_bar/intermediate.pb") + ensureContains(t, buildParams.Output.String(), "android_common_myapex/aconfig_flags.pb") +} + +func TestAconfigFilesJavaAndCcDeps(t *testing.T) { + ctx := testApex(t, apex_default_bp+` + apex { + name: "myapex", + manifest: ":myapex.manifest", + androidManifest: ":myapex.androidmanifest", + key: "myapex.key", + java_libs: [ + "my_java_library_foo", + ], + native_shared_libs: [ + "my_cc_library_bar", + ], + binaries: [ + "my_cc_binary_baz", + ], + updatable: false, + } + + java_library { + name: "my_java_library_foo", + srcs: ["foo/bar/MyClass.java"], + sdk_version: "none", + system_modules: "none", + static_libs: ["my_java_aconfig_library_foo"], + apex_available: [ + "myapex", + ], + } + + cc_library { + name: "my_cc_library_bar", + srcs: ["foo/bar/MyClass.cc"], + static_libs: [ + "my_cc_aconfig_library_bar", + "my_cc_aconfig_library_baz", + ], + apex_available: [ + "myapex", + ], + } + + cc_binary { + name: "my_cc_binary_baz", + srcs: ["foo/bar/MyClass.cc"], + static_libs: ["my_cc_aconfig_library_baz"], + apex_available: [ + "myapex", + ], + } + + aconfig_declarations { + name: "my_aconfig_declarations_foo", + package: "com.example.package", + container: "myapex", + srcs: ["foo.aconfig"], + } + + java_aconfig_library { + name: "my_java_aconfig_library_foo", + aconfig_declarations: "my_aconfig_declarations_foo", + apex_available: [ + "myapex", + ], + } + + aconfig_declarations { + name: "my_aconfig_declarations_bar", + package: "com.example.package", + container: "myapex", + srcs: ["bar.aconfig"], + } + + cc_aconfig_library { + name: "my_cc_aconfig_library_bar", + aconfig_declarations: "my_aconfig_declarations_bar", + apex_available: [ + "myapex", + ], + } + + aconfig_declarations { + name: "my_aconfig_declarations_baz", + package: "com.example.package", + container: "myapex", + srcs: ["baz.aconfig"], + } + + cc_aconfig_library { + name: "my_cc_aconfig_library_baz", + aconfig_declarations: "my_aconfig_declarations_baz", + apex_available: [ + "myapex", + ], + } + + cc_library { + name: "server_configurable_flags", + srcs: ["server_configurable_flags.cc"], + } + `) + + mod := ctx.ModuleForTests("myapex", "android_common_myapex") + s := mod.Rule("apexRule").Args["copy_commands"] + copyCmds := regexp.MustCompile(" *&& *").Split(s, -1) + if len(copyCmds) != 9 { + t.Fatalf("Expected 9 commands, got %d in:\n%s", len(copyCmds), s) + } + + ensureMatches(t, copyCmds[8], "^cp -f .*/aconfig_flags.pb .*/image.apex$") + + combineAconfigRule := mod.Rule("All_aconfig_declarations_dump") + s = " " + combineAconfigRule.Args["cache_files"] + aconfigArgs := regexp.MustCompile(" --cache ").Split(s, -1)[1:] + if len(aconfigArgs) != 3 { + t.Fatalf("Expected 3 commands, got %d in:\n%s", len(aconfigArgs), s) + } + android.EnsureListContainsSuffix(t, aconfigArgs, "my_aconfig_declarations_foo/intermediate.pb") + android.EnsureListContainsSuffix(t, aconfigArgs, "my_cc_library_bar/android_arm64_armv8-a_shared_apex10000/aconfig_merged.pb") + android.EnsureListContainsSuffix(t, aconfigArgs, "my_aconfig_declarations_baz/intermediate.pb") + + buildParams := combineAconfigRule.BuildParams + if len(buildParams.Inputs) != 3 { + t.Fatalf("Expected 3 input, got %d", len(buildParams.Inputs)) + } + android.EnsureListContainsSuffix(t, buildParams.Inputs.Strings(), "my_aconfig_declarations_foo/intermediate.pb") + android.EnsureListContainsSuffix(t, buildParams.Inputs.Strings(), "my_cc_library_bar/android_arm64_armv8-a_shared_apex10000/aconfig_merged.pb") + android.EnsureListContainsSuffix(t, buildParams.Inputs.Strings(), "my_aconfig_declarations_baz/intermediate.pb") + ensureContains(t, buildParams.Output.String(), "android_common_myapex/aconfig_flags.pb") +} + +func TestAconfigFilesRustDeps(t *testing.T) { + ctx := testApex(t, apex_default_bp+` + apex { + name: "myapex", + manifest: ":myapex.manifest", + androidManifest: ":myapex.androidmanifest", + key: "myapex.key", + native_shared_libs: [ + "libmy_rust_library", + ], + binaries: [ + "my_rust_binary", + ], + rust_dyn_libs: [ + "libmy_rust_dylib", + ], + updatable: false, + } + + rust_library { + name: "libflags_rust", // test mock + crate_name: "flags_rust", + srcs: ["lib.rs"], + apex_available: [ + "myapex", + ], + } + + rust_library { + name: "liblazy_static", // test mock + crate_name: "lazy_static", + srcs: ["src/lib.rs"], + apex_available: [ + "myapex", + ], + } + + rust_ffi_shared { + name: "libmy_rust_library", + srcs: ["src/lib.rs"], + rustlibs: ["libmy_rust_aconfig_library_foo"], + crate_name: "my_rust_library", + apex_available: [ + "myapex", + ], + } + + rust_library_dylib { + name: "libmy_rust_dylib", + srcs: ["foo/bar/MyClass.rs"], + rustlibs: ["libmy_rust_aconfig_library_bar"], + crate_name: "my_rust_dylib", + apex_available: [ + "myapex", + ], + } + + rust_binary { + name: "my_rust_binary", + srcs: ["foo/bar/MyClass.rs"], + rustlibs: [ + "libmy_rust_aconfig_library_baz", + "libmy_rust_dylib", + ], + apex_available: [ + "myapex", + ], + } + + aconfig_declarations { + name: "my_aconfig_declarations_foo", + package: "com.example.package", + container: "myapex", + srcs: ["foo.aconfig"], + } + + aconfig_declarations { + name: "my_aconfig_declarations_bar", + package: "com.example.package", + container: "myapex", + srcs: ["bar.aconfig"], + } + + aconfig_declarations { + name: "my_aconfig_declarations_baz", + package: "com.example.package", + container: "myapex", + srcs: ["baz.aconfig"], + } + + rust_aconfig_library { + name: "libmy_rust_aconfig_library_foo", + aconfig_declarations: "my_aconfig_declarations_foo", + crate_name: "my_rust_aconfig_library_foo", + apex_available: [ + "myapex", + ], + } + + rust_aconfig_library { + name: "libmy_rust_aconfig_library_bar", + aconfig_declarations: "my_aconfig_declarations_bar", + crate_name: "my_rust_aconfig_library_bar", + apex_available: [ + "myapex", + ], + } + + rust_aconfig_library { + name: "libmy_rust_aconfig_library_baz", + aconfig_declarations: "my_aconfig_declarations_baz", + crate_name: "my_rust_aconfig_library_baz", + apex_available: [ + "myapex", + ], + } + `) + + mod := ctx.ModuleForTests("myapex", "android_common_myapex") + s := mod.Rule("apexRule").Args["copy_commands"] + copyCmds := regexp.MustCompile(" *&& *").Split(s, -1) + if len(copyCmds) != 23 { + t.Fatalf("Expected 23 commands, got %d in:\n%s", len(copyCmds), s) + } + + ensureMatches(t, copyCmds[22], "^cp -f .*/aconfig_flags.pb .*/image.apex$") + + combineAconfigRule := mod.Rule("All_aconfig_declarations_dump") + s = " " + combineAconfigRule.Args["cache_files"] + aconfigArgs := regexp.MustCompile(" --cache ").Split(s, -1)[1:] + if len(aconfigArgs) != 2 { + t.Fatalf("Expected 2 commands, got %d in:\n%s", len(aconfigArgs), s) + } + android.EnsureListContainsSuffix(t, aconfigArgs, "my_aconfig_declarations_foo/intermediate.pb") + android.EnsureListContainsSuffix(t, aconfigArgs, "my_rust_binary/android_arm64_armv8-a_apex10000/aconfig_merged.pb") + + buildParams := combineAconfigRule.BuildParams + if len(buildParams.Inputs) != 2 { + t.Fatalf("Expected 3 input, got %d", len(buildParams.Inputs)) + } + android.EnsureListContainsSuffix(t, buildParams.Inputs.Strings(), "my_aconfig_declarations_foo/intermediate.pb") + android.EnsureListContainsSuffix(t, buildParams.Inputs.Strings(), "my_rust_binary/android_arm64_armv8-a_apex10000/aconfig_merged.pb") + ensureContains(t, buildParams.Output.String(), "android_common_myapex/aconfig_flags.pb") +} + +func TestAconfigFilesOnlyMatchCurrentApex(t *testing.T) { + ctx := testApex(t, apex_default_bp+` + apex { + name: "myapex", + manifest: ":myapex.manifest", + androidManifest: ":myapex.androidmanifest", + key: "myapex.key", + java_libs: [ + "my_java_library_foo", + "other_java_library_bar", + ], + updatable: false, + } + + java_library { + name: "my_java_library_foo", + srcs: ["foo/bar/MyClass.java"], + sdk_version: "none", + system_modules: "none", + static_libs: ["my_java_aconfig_library_foo"], + apex_available: [ + "myapex", + ], + } + + java_library { + name: "other_java_library_bar", + srcs: ["foo/bar/MyClass.java"], + sdk_version: "none", + system_modules: "none", + static_libs: ["other_java_aconfig_library_bar"], + apex_available: [ + "myapex", + ], + } + + aconfig_declarations { + name: "my_aconfig_declarations_foo", + package: "com.example.package", + container: "myapex", + srcs: ["foo.aconfig"], + } + + java_aconfig_library { + name: "my_java_aconfig_library_foo", + aconfig_declarations: "my_aconfig_declarations_foo", + apex_available: [ + "myapex", + ], + } + + aconfig_declarations { + name: "other_aconfig_declarations_bar", + package: "com.example.package", + container: "otherapex", + srcs: ["bar.aconfig"], + } + + java_aconfig_library { + name: "other_java_aconfig_library_bar", + aconfig_declarations: "other_aconfig_declarations_bar", + apex_available: [ + "myapex", + ], + } + `) + + mod := ctx.ModuleForTests("myapex", "android_common_myapex") + combineAconfigRule := mod.Rule("All_aconfig_declarations_dump") + s := " " + combineAconfigRule.Args["cache_files"] + aconfigArgs := regexp.MustCompile(" --cache ").Split(s, -1)[1:] + if len(aconfigArgs) != 1 { + t.Fatalf("Expected 1 commands, got %d in:\n%s", len(aconfigArgs), s) + } + android.EnsureListContainsSuffix(t, aconfigArgs, "my_aconfig_declarations_foo/intermediate.pb") + + buildParams := combineAconfigRule.BuildParams + if len(buildParams.Inputs) != 1 { + t.Fatalf("Expected 1 input, got %d", len(buildParams.Inputs)) + } + android.EnsureListContainsSuffix(t, buildParams.Inputs.Strings(), "my_aconfig_declarations_foo/intermediate.pb") + ensureContains(t, buildParams.Output.String(), "android_common_myapex/aconfig_flags.pb") +} + +func TestAconfigFilesRemoveDuplicates(t *testing.T) { + ctx := testApex(t, apex_default_bp+` + apex { + name: "myapex", + manifest: ":myapex.manifest", + androidManifest: ":myapex.androidmanifest", + key: "myapex.key", + java_libs: [ + "my_java_library_foo", + "my_java_library_bar", + ], + updatable: false, + } + + java_library { + name: "my_java_library_foo", + srcs: ["foo/bar/MyClass.java"], + sdk_version: "none", + system_modules: "none", + static_libs: ["my_java_aconfig_library_foo"], + apex_available: [ + "myapex", + ], + } + + java_library { + name: "my_java_library_bar", + srcs: ["foo/bar/MyClass.java"], + sdk_version: "none", + system_modules: "none", + static_libs: ["my_java_aconfig_library_bar"], + apex_available: [ + "myapex", + ], + } + + aconfig_declarations { + name: "my_aconfig_declarations_foo", + package: "com.example.package", + container: "myapex", + srcs: ["foo.aconfig"], + } + + java_aconfig_library { + name: "my_java_aconfig_library_foo", + aconfig_declarations: "my_aconfig_declarations_foo", + apex_available: [ + "myapex", + ], + } + + java_aconfig_library { + name: "my_java_aconfig_library_bar", + aconfig_declarations: "my_aconfig_declarations_foo", + apex_available: [ + "myapex", + ], + } + `) + + mod := ctx.ModuleForTests("myapex", "android_common_myapex") + combineAconfigRule := mod.Rule("All_aconfig_declarations_dump") + s := " " + combineAconfigRule.Args["cache_files"] + aconfigArgs := regexp.MustCompile(" --cache ").Split(s, -1)[1:] + if len(aconfigArgs) != 1 { + t.Fatalf("Expected 1 commands, got %d in:\n%s", len(aconfigArgs), s) + } + android.EnsureListContainsSuffix(t, aconfigArgs, "my_aconfig_declarations_foo/intermediate.pb") + + buildParams := combineAconfigRule.BuildParams + if len(buildParams.Inputs) != 1 { + t.Fatalf("Expected 1 input, got %d", len(buildParams.Inputs)) + } + android.EnsureListContainsSuffix(t, buildParams.Inputs.Strings(), "my_aconfig_declarations_foo/intermediate.pb") + ensureContains(t, buildParams.Output.String(), "android_common_myapex/aconfig_flags.pb") +} diff --git a/apex/bootclasspath_fragment_test.go b/apex/bootclasspath_fragment_test.go index 1b52886961..43be310bb9 100644 --- a/apex/bootclasspath_fragment_test.go +++ b/apex/bootclasspath_fragment_test.go @@ -46,78 +46,6 @@ var prepareForTestWithArtApex = android.GroupFixturePreparers( dexpreopt.FixtureSetBootImageProfiles("art/build/boot/boot-image-profile.txt"), ) -func TestBootclasspathFragments(t *testing.T) { - result := android.GroupFixturePreparers( - prepareForTestWithBootclasspathFragment, - // Configure some libraries in the art bootclasspath_fragment and platform_bootclasspath. - java.FixtureConfigureBootJars("com.android.art:baz", "com.android.art:quuz", "platform:foo", "platform:bar"), - prepareForTestWithArtApex, - - java.PrepareForTestWithJavaSdkLibraryFiles, - java.FixtureWithLastReleaseApis("foo"), - ).RunTestWithBp(t, ` - java_sdk_library { - name: "foo", - srcs: ["b.java"], - } - - java_library { - name: "bar", - srcs: ["b.java"], - installable: true, - } - - apex { - name: "com.android.art", - key: "com.android.art.key", - bootclasspath_fragments: ["art-bootclasspath-fragment"], - updatable: false, - } - - apex_key { - name: "com.android.art.key", - public_key: "com.android.art.avbpubkey", - private_key: "com.android.art.pem", - } - - java_library { - name: "baz", - apex_available: [ - "com.android.art", - ], - srcs: ["b.java"], - compile_dex: true, - } - - java_library { - name: "quuz", - apex_available: [ - "com.android.art", - ], - srcs: ["b.java"], - compile_dex: true, - } - - bootclasspath_fragment { - name: "art-bootclasspath-fragment", - image_name: "art", - // Must match the "com.android.art:" entries passed to FixtureConfigureBootJars above. - contents: ["baz", "quuz"], - apex_available: [ - "com.android.art", - ], - hidden_api: { - split_packages: ["*"], - }, - } -`, - ) - - // Make sure that the art-bootclasspath-fragment is using the correct configuration. - checkBootclasspathFragment(t, result, "art-bootclasspath-fragment", "android_common_apex10000", - "com.android.art:baz,com.android.art:quuz") -} - func TestBootclasspathFragments_FragmentDependency(t *testing.T) { result := android.GroupFixturePreparers( prepareForTestWithBootclasspathFragment, @@ -248,16 +176,6 @@ func TestBootclasspathFragments_FragmentDependency(t *testing.T) { checkAPIScopeStubs("other", otherInfo, java.CorePlatformHiddenAPIScope) } -func checkBootclasspathFragment(t *testing.T, result *android.TestResult, moduleName, variantName string, expectedConfiguredModules string) { - t.Helper() - - bootclasspathFragment := result.ModuleForTests(moduleName, variantName).Module().(*java.BootclasspathFragmentModule) - - bootclasspathFragmentInfo := result.ModuleProvider(bootclasspathFragment, java.BootclasspathFragmentApexContentInfoProvider).(java.BootclasspathFragmentApexContentInfo) - modules := bootclasspathFragmentInfo.Modules() - android.AssertStringEquals(t, "invalid modules for "+moduleName, expectedConfiguredModules, modules.String()) -} - func TestBootclasspathFragmentInArtApex(t *testing.T) { commonPreparer := android.GroupFixturePreparers( prepareForTestWithBootclasspathFragment, @@ -268,10 +186,10 @@ func TestBootclasspathFragmentInArtApex(t *testing.T) { name: "com.android.art", key: "com.android.art.key", bootclasspath_fragments: [ - "mybootclasspathfragment", + "art-bootclasspath-fragment", ], // bar (like foo) should be transitively included in this apex because it is part of the - // mybootclasspathfragment bootclasspath_fragment. + // art-bootclasspath-fragment bootclasspath_fragment. updatable: false, } @@ -280,42 +198,6 @@ func TestBootclasspathFragmentInArtApex(t *testing.T) { public_key: "testkey.avbpubkey", private_key: "testkey.pem", } - - java_library { - name: "foo", - srcs: ["b.java"], - installable: true, - apex_available: [ - "com.android.art", - ], - } - - java_library { - name: "bar", - srcs: ["b.java"], - installable: true, - apex_available: [ - "com.android.art", - ], - } - - java_import { - name: "foo", - jars: ["foo.jar"], - apex_available: [ - "com.android.art", - ], - compile_dex: true, - } - - java_import { - name: "bar", - jars: ["bar.jar"], - apex_available: [ - "com.android.art", - ], - compile_dex: true, - } `), ) @@ -330,7 +212,7 @@ func TestBootclasspathFragmentInArtApex(t *testing.T) { addSource := func(contents ...string) android.FixturePreparer { text := fmt.Sprintf(` bootclasspath_fragment { - name: "mybootclasspathfragment", + name: "art-bootclasspath-fragment", image_name: "art", %s apex_available: [ @@ -342,6 +224,19 @@ func TestBootclasspathFragmentInArtApex(t *testing.T) { } `, contentsInsert(contents)) + for _, content := range contents { + text += fmt.Sprintf(` + java_library { + name: "%[1]s", + srcs: ["%[1]s.java"], + installable: true, + apex_available: [ + "com.android.art", + ], + } + `, content) + } + return android.FixtureAddTextFile("art/build/boot/Android.bp", text) } @@ -357,11 +252,11 @@ func TestBootclasspathFragmentInArtApex(t *testing.T) { src: "com.android.art-arm.apex", }, }, - exported_bootclasspath_fragments: ["mybootclasspathfragment"], + exported_bootclasspath_fragments: ["art-bootclasspath-fragment"], } prebuilt_bootclasspath_fragment { - name: "mybootclasspathfragment", + name: "art-bootclasspath-fragment", image_name: "art", %s prefer: %t, @@ -369,14 +264,29 @@ func TestBootclasspathFragmentInArtApex(t *testing.T) { "com.android.art", ], hidden_api: { - annotation_flags: "mybootclasspathfragment/annotation-flags.csv", - metadata: "mybootclasspathfragment/metadata.csv", - index: "mybootclasspathfragment/index.csv", - stub_flags: "mybootclasspathfragment/stub-flags.csv", - all_flags: "mybootclasspathfragment/all-flags.csv", + annotation_flags: "hiddenapi/annotation-flags.csv", + metadata: "hiddenapi/metadata.csv", + index: "hiddenapi/index.csv", + stub_flags: "hiddenapi/stub-flags.csv", + all_flags: "hiddenapi/all-flags.csv", }, } `, contentsInsert(contents), prefer) + + for _, content := range contents { + text += fmt.Sprintf(` + java_import { + name: "%[1]s", + prefer: %[2]t, + jars: ["%[1]s.jar"], + apex_available: [ + "com.android.art", + ], + compile_dex: true, + } + `, content, prefer) + } + return android.FixtureAddTextFile("prebuilts/module_sdk/art/Android.bp", text) } @@ -387,25 +297,26 @@ func TestBootclasspathFragmentInArtApex(t *testing.T) { // Configure some libraries in the art bootclasspath_fragment that match the source // bootclasspath_fragment's contents property. java.FixtureConfigureBootJars("com.android.art:foo", "com.android.art:bar"), + dexpreopt.FixtureSetTestOnlyArtBootImageJars("com.android.art:foo", "com.android.art:bar"), addSource("foo", "bar"), java.FixtureSetBootImageInstallDirOnDevice("art", "apex/com.android.art/javalib"), ).RunTest(t) - ensureExactContents(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{ + ensureExactContents(t, result.TestContext, "com.android.art", "android_common_com.android.art", []string{ "etc/boot-image.prof", "etc/classpaths/bootclasspath.pb", "javalib/bar.jar", "javalib/foo.jar", }) - java.CheckModuleDependencies(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{ + java.CheckModuleDependencies(t, result.TestContext, "com.android.art", "android_common_com.android.art", []string{ + `art-bootclasspath-fragment`, `com.android.art.key`, - `mybootclasspathfragment`, }) // Make sure that the source bootclasspath_fragment copies its dex files to the predefined // locations for the art image. - module := result.ModuleForTests("mybootclasspathfragment", "android_common_apex10000") + module := result.ModuleForTests("dex_bootjars", "android_common") checkCopiesToPredefinedLocationForArt(t, result.Config, module, "bar", "foo") }) @@ -421,7 +332,7 @@ func TestBootclasspathFragmentInArtApex(t *testing.T) { dexpreopt.FixtureDisableDexpreoptBootImages(true), ).RunTest(t) - ensureExactContents(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{ + ensureExactContents(t, result.TestContext, "com.android.art", "android_common_com.android.art", []string{ "etc/boot-image.prof", "etc/classpaths/bootclasspath.pb", "javalib/bar.jar", @@ -440,7 +351,7 @@ func TestBootclasspathFragmentInArtApex(t *testing.T) { dexpreopt.FixtureDisableGenerateProfile(true), ).RunTest(t) - files := getFiles(t, result.TestContext, "com.android.art", "android_common_com.android.art_image") + files := getFiles(t, result.TestContext, "com.android.art", "android_common_com.android.art") for _, file := range files { matched, _ := path.Match("etc/boot-image.prof", file.path) android.AssertBoolEquals(t, "\"etc/boot-image.prof\" should not be in the APEX", matched, false) @@ -454,6 +365,7 @@ func TestBootclasspathFragmentInArtApex(t *testing.T) { // Configure some libraries in the art bootclasspath_fragment that match the source // bootclasspath_fragment's contents property. java.FixtureConfigureBootJars("com.android.art:foo", "com.android.art:bar"), + dexpreopt.FixtureSetTestOnlyArtBootImageJars("com.android.art:foo", "com.android.art:bar"), addSource("foo", "bar"), // Make sure that a preferred prebuilt with consistent contents doesn't affect the apex. @@ -468,15 +380,15 @@ func TestBootclasspathFragmentInArtApex(t *testing.T) { "javalib/foo.jar", }) - java.CheckModuleDependencies(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{ + java.CheckModuleDependencies(t, result.TestContext, "com.android.art", "android_common_com.android.art", []string{ + `art-bootclasspath-fragment`, `com.android.art.key`, - `mybootclasspathfragment`, `prebuilt_com.android.art`, }) // Make sure that the prebuilt bootclasspath_fragment copies its dex files to the predefined // locations for the art image. - module := result.ModuleForTests("prebuilt_mybootclasspathfragment", "android_common_com.android.art") + module := result.ModuleForTests("dex_bootjars", "android_common") checkCopiesToPredefinedLocationForArt(t, result.Config, module, "bar", "foo") }) @@ -552,6 +464,7 @@ func TestBootclasspathFragmentInPrebuiltArtApex(t *testing.T) { // Configure some libraries in the art bootclasspath_fragment. java.FixtureConfigureBootJars("com.android.art:foo", "com.android.art:bar"), + dexpreopt.FixtureSetTestOnlyArtBootImageJars("com.android.art:foo", "com.android.art:bar"), java.FixtureSetBootImageInstallDirOnDevice("art", "apex/com.android.art/javalib"), ) @@ -566,7 +479,7 @@ func TestBootclasspathFragmentInPrebuiltArtApex(t *testing.T) { src: "com.android.art-arm.apex", }, }, - exported_bootclasspath_fragments: ["mybootclasspathfragment"], + exported_bootclasspath_fragments: ["art-bootclasspath-fragment"], } java_import { @@ -586,7 +499,7 @@ func TestBootclasspathFragmentInPrebuiltArtApex(t *testing.T) { } prebuilt_bootclasspath_fragment { - name: "mybootclasspathfragment", + name: "art-bootclasspath-fragment", image_name: "art", // Must match the "com.android.art:" entries passed to FixtureConfigureBootJars above. contents: ["foo", "bar"], @@ -594,11 +507,11 @@ func TestBootclasspathFragmentInPrebuiltArtApex(t *testing.T) { "com.android.art", ], hidden_api: { - annotation_flags: "mybootclasspathfragment/annotation-flags.csv", - metadata: "mybootclasspathfragment/metadata.csv", - index: "mybootclasspathfragment/index.csv", - stub_flags: "mybootclasspathfragment/stub-flags.csv", - all_flags: "mybootclasspathfragment/all-flags.csv", + annotation_flags: "hiddenapi/annotation-flags.csv", + metadata: "hiddenapi/metadata.csv", + index: "hiddenapi/index.csv", + stub_flags: "hiddenapi/stub-flags.csv", + all_flags: "hiddenapi/all-flags.csv", }, } @@ -608,7 +521,7 @@ func TestBootclasspathFragmentInPrebuiltArtApex(t *testing.T) { apex_name: "com.android.art", %s src: "com.mycompany.android.art.apex", - exported_bootclasspath_fragments: ["mybootclasspathfragment"], + exported_bootclasspath_fragments: ["art-bootclasspath-fragment"], } ` @@ -617,17 +530,17 @@ func TestBootclasspathFragmentInPrebuiltArtApex(t *testing.T) { java.CheckModuleDependencies(t, result.TestContext, "com.android.art", "android_common_com.android.art", []string{ `com.android.art.apex.selector`, - `prebuilt_mybootclasspathfragment`, + `prebuilt_art-bootclasspath-fragment`, }) - java.CheckModuleDependencies(t, result.TestContext, "mybootclasspathfragment", "android_common_com.android.art", []string{ + java.CheckModuleDependencies(t, result.TestContext, "art-bootclasspath-fragment", "android_common_com.android.art", []string{ `com.android.art.deapexer`, `dex2oatd`, `prebuilt_bar`, `prebuilt_foo`, }) - module := result.ModuleForTests("mybootclasspathfragment", "android_common_com.android.art") + module := result.ModuleForTests("dex_bootjars", "android_common") checkCopiesToPredefinedLocationForArt(t, result.Config, module, "bar", "foo") }) @@ -722,7 +635,7 @@ func TestBootclasspathFragmentContentsNoName(t *testing.T) { } `) - ensureExactContents(t, result.TestContext, "myapex", "android_common_myapex_image", []string{ + ensureExactContents(t, result.TestContext, "myapex", "android_common_myapex", []string{ // This does not include art, oat or vdex files as they are only included for the art boot // image. "etc/classpaths/bootclasspath.pb", @@ -730,12 +643,12 @@ func TestBootclasspathFragmentContentsNoName(t *testing.T) { "javalib/foo.jar", }) - java.CheckModuleDependencies(t, result.TestContext, "myapex", "android_common_myapex_image", []string{ + java.CheckModuleDependencies(t, result.TestContext, "myapex", "android_common_myapex", []string{ `myapex.key`, `mybootclasspathfragment`, }) - apex := result.ModuleForTests("myapex", "android_common_myapex_image") + apex := result.ModuleForTests("myapex", "android_common_myapex") apexRule := apex.Rule("apexRule") copyCommands := apexRule.Args["copy_commands"] @@ -752,7 +665,7 @@ func TestBootclasspathFragmentContentsNoName(t *testing.T) { } android.AssertPathRelativeToTopEquals(t, name+" dex", expectedDexJar, dexJar) - expectedCopyCommand := fmt.Sprintf("&& cp -f %s out/soong/.intermediates/myapex/android_common_myapex_image/image.apex/javalib/%s.jar", expectedDexJar, name) + expectedCopyCommand := fmt.Sprintf("&& cp -f %s out/soong/.intermediates/myapex/android_common_myapex/image.apex/javalib/%s.jar", expectedDexJar, name) android.AssertStringDoesContain(t, name+" apex copy command", copyCommands, expectedCopyCommand) } @@ -919,7 +832,7 @@ func TestBootclasspathFragment_HiddenAPIList(t *testing.T) { // TestBootclasspathFragment_AndroidNonUpdatable checks to make sure that setting // additional_stubs: ["android-non-updatable"] causes the source android-non-updatable modules to be // added to the hiddenapi list tool. -func TestBootclasspathFragment_AndroidNonUpdatable(t *testing.T) { +func TestBootclasspathFragment_AndroidNonUpdatable_FromSource(t *testing.T) { result := android.GroupFixturePreparers( prepareForTestWithBootclasspathFragment, prepareForTestWithArtApex, @@ -930,6 +843,9 @@ func TestBootclasspathFragment_AndroidNonUpdatable(t *testing.T) { // Make sure that the frameworks/base/Android.bp file exists as otherwise hidden API encoding // is disabled. android.FixtureAddTextFile("frameworks/base/Android.bp", ""), + android.FixtureModifyConfig(func(config android.Config) { + config.SetBuildFromTextStub(false) + }), java.PrepareForTestWithJavaSdkLibraryFiles, java.FixtureWithLastReleaseApis("foo", "android-non-updatable"), @@ -1087,6 +1003,168 @@ func TestBootclasspathFragment_AndroidNonUpdatable(t *testing.T) { android.AssertStringDoesContain(t, "test", command, "--test-stub-classpath="+nonUpdatableTestStubs) } +func TestBootclasspathFragment_AndroidNonUpdatable_FromText(t *testing.T) { + result := android.GroupFixturePreparers( + prepareForTestWithBootclasspathFragment, + prepareForTestWithArtApex, + prepareForTestWithMyapex, + // Configure bootclasspath jars to ensure that hidden API encoding is performed on them. + java.FixtureConfigureBootJars("com.android.art:baz", "com.android.art:quuz"), + java.FixtureConfigureApexBootJars("myapex:foo", "myapex:bar"), + // Make sure that the frameworks/base/Android.bp file exists as otherwise hidden API encoding + // is disabled. + android.FixtureAddTextFile("frameworks/base/Android.bp", ""), + android.FixtureModifyConfig(func(config android.Config) { + config.SetBuildFromTextStub(true) + }), + + java.PrepareForTestWithJavaSdkLibraryFiles, + java.FixtureWithLastReleaseApis("foo", "android-non-updatable"), + ).RunTestWithBp(t, ` + java_sdk_library { + name: "android-non-updatable", + srcs: ["b.java"], + compile_dex: true, + public: { + enabled: true, + }, + system: { + enabled: true, + }, + test: { + enabled: true, + }, + module_lib: { + enabled: true, + }, + } + + apex { + name: "com.android.art", + key: "com.android.art.key", + bootclasspath_fragments: ["art-bootclasspath-fragment"], + updatable: false, + } + + apex_key { + name: "com.android.art.key", + public_key: "com.android.art.avbpubkey", + private_key: "com.android.art.pem", + } + + java_library { + name: "baz", + apex_available: [ + "com.android.art", + ], + srcs: ["b.java"], + compile_dex: true, + } + + java_library { + name: "quuz", + apex_available: [ + "com.android.art", + ], + srcs: ["b.java"], + compile_dex: true, + } + + bootclasspath_fragment { + name: "art-bootclasspath-fragment", + image_name: "art", + // Must match the "com.android.art:" entries passed to FixtureConfigureBootJars above. + contents: ["baz", "quuz"], + apex_available: [ + "com.android.art", + ], + hidden_api: { + split_packages: ["*"], + }, + } + + apex { + name: "myapex", + key: "myapex.key", + bootclasspath_fragments: [ + "mybootclasspathfragment", + ], + updatable: false, + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + java_sdk_library { + name: "foo", + srcs: ["b.java"], + shared_library: false, + public: {enabled: true}, + apex_available: [ + "myapex", + ], + } + + java_library { + name: "bar", + srcs: ["b.java"], + installable: true, + apex_available: [ + "myapex", + ], + } + + bootclasspath_fragment { + name: "mybootclasspathfragment", + contents: [ + "foo", + "bar", + ], + apex_available: [ + "myapex", + ], + additional_stubs: ["android-non-updatable"], + fragments: [ + { + apex: "com.android.art", + module: "art-bootclasspath-fragment", + }, + ], + hidden_api: { + split_packages: ["*"], + }, + } + `) + + java.CheckModuleDependencies(t, result.TestContext, "mybootclasspathfragment", "android_common_apex10000", []string{ + "android-non-updatable.stubs", + "android-non-updatable.stubs.system", + "android-non-updatable.stubs.test", + "android-non-updatable.stubs.test_module_lib", + "art-bootclasspath-fragment", + "bar", + "dex2oatd", + "foo", + }) + + nonUpdatableTestModuleLibStubs := getDexJarPath(result, "android-non-updatable.stubs.test_module_lib") + + // Make sure that the fragment uses the android-non-updatable modules when generating the hidden + // API flags. + fragment := result.ModuleForTests("mybootclasspathfragment", "android_common_apex10000") + + rule := fragment.Rule("modularHiddenAPIStubFlagsFile") + command := rule.RuleParams.Command + android.AssertStringDoesContain(t, "check correct rule", command, "hiddenapi list") + + // Make sure that the test_module_lib non-updatable stubs are available for resolving references from + // the implementation boot dex jars provided by this module. + android.AssertStringDoesContain(t, "android-non-updatable widest", command, "--dependency-stub-dex="+nonUpdatableTestModuleLibStubs) +} + // TestBootclasspathFragment_AndroidNonUpdatable_AlwaysUsePrebuiltSdks checks to make sure that // setting additional_stubs: ["android-non-updatable"] causes the prebuilt android-non-updatable // modules to be added to the hiddenapi list tool. diff --git a/apex/bp2build.go b/apex/bp2build.go deleted file mode 100644 index a3dda83b18..0000000000 --- a/apex/bp2build.go +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (C) 2022 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 apex - -import ( - "android/soong/android" - "encoding/json" - "strings" -) - -// This file contains the bp2build integration for the apex package. - -// Export constants as Starlark using bp2build to Bazel. -func BazelApexToolchainVars() (string, error) { - marshalled, err := json.Marshal(apexAvailBaseline) - if err != nil { - return "", err - } - content := []string{ - "# GENERATED BY SOONG. DO NOT EDIT.", - "default_manifest_version = " + android.DefaultUpdatableModuleVersion, // constants.go is different in every branch. - "apex_available_baseline = json.decode('''" + string(marshalled) + "''')", - } - return strings.Join(content, "\n"), nil -} diff --git a/apex/bp2build_test.go b/apex/bp2build_test.go deleted file mode 100644 index 2a0f6e9e25..0000000000 --- a/apex/bp2build_test.go +++ /dev/null @@ -1,461 +0,0 @@ -// Copyright 2022 Google Inc. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -package apex - -import ( - "android/soong/android" - "android/soong/bazel/cquery" - "strings" - "testing" -) - -func TestApexImageInMixedBuilds(t *testing.T) { - bp := ` -apex_key{ - name: "foo_key", -} - -apex { - name: "foo", - key: "foo_key", - updatable: true, - min_sdk_version: "31", - file_contexts: ":myapex-file_contexts", - bazel_module: { label: "//:foo" }, -}` - - outputBaseDir := "out/bazel" - result := android.GroupFixturePreparers( - prepareForApexTest, - android.FixtureModifyConfig(func(config android.Config) { - config.BazelContext = android.MockBazelContext{ - OutputBaseDir: outputBaseDir, - LabelToApexInfo: map[string]cquery.ApexInfo{ - "//:foo": cquery.ApexInfo{ - // ApexInfo Starlark provider. - SignedOutput: "signed_out.apex", - SignedCompressedOutput: "signed_out.capex", - UnsignedOutput: "unsigned_out.apex", - BundleKeyInfo: []string{"public_key", "private_key"}, - ContainerKeyInfo: []string{"container_cert", "container_private"}, - SymbolsUsedByApex: "foo_using.txt", - JavaSymbolsUsedByApex: "foo_using.xml", - BundleFile: "apex_bundle.zip", - InstalledFiles: "installed-files.txt", - RequiresLibs: []string{"//path/c:c", "//path/d:d"}, - - // unused - PackageName: "pkg_name", - ProvidesLibs: []string{"a", "b"}, - - // ApexMkInfo Starlark provider - PayloadFilesInfo: []map[string]string{ - { - "built_file": "bazel-out/adbd", - "install_dir": "bin", - "class": "nativeExecutable", - "make_module_name": "adbd", - "basename": "adbd", - "package": "foo", - }, - }, - MakeModulesToInstall: []string{"c"}, // d deliberately omitted - }, - }, - } - }), - ).RunTestWithBp(t, bp) - - m := result.ModuleForTests("foo", "android_common_foo_image").Module() - ab, ok := m.(*apexBundle) - - if !ok { - t.Fatalf("Expected module to be an apexBundle, was not") - } - - // TODO: refactor to android.AssertStringEquals - if w, g := "out/bazel/execroot/__main__/public_key", ab.publicKeyFile.String(); w != g { - t.Errorf("Expected public key %q, got %q", w, g) - } - - if w, g := "out/bazel/execroot/__main__/private_key", ab.privateKeyFile.String(); w != g { - t.Errorf("Expected private key %q, got %q", w, g) - } - - if w, g := "out/bazel/execroot/__main__/container_cert", ab.containerCertificateFile.String(); w != g { - t.Errorf("Expected public container key %q, got %q", w, g) - } - - if w, g := "out/bazel/execroot/__main__/container_private", ab.containerPrivateKeyFile.String(); w != g { - t.Errorf("Expected private container key %q, got %q", w, g) - } - - if w, g := "out/bazel/execroot/__main__/signed_out.apex", ab.outputFile.String(); w != g { - t.Errorf("Expected output file %q, got %q", w, g) - } - - if w, g := "out/bazel/execroot/__main__/foo_using.txt", ab.nativeApisUsedByModuleFile.String(); w != g { - t.Errorf("Expected output file %q, got %q", w, g) - } - - if w, g := "out/bazel/execroot/__main__/foo_using.xml", ab.javaApisUsedByModuleFile.String(); w != g { - t.Errorf("Expected output file %q, got %q", w, g) - } - - if w, g := "out/bazel/execroot/__main__/installed-files.txt", ab.installedFilesFile.String(); w != g { - t.Errorf("Expected installed-files.txt %q, got %q", w, g) - } - - mkData := android.AndroidMkDataForTest(t, result.TestContext, m) - var builder strings.Builder - mkData.Custom(&builder, "foo", "BAZEL_TARGET_", "", mkData) - - data := builder.String() - if w := "ALL_MODULES.$(my_register_name).BUNDLE := out/bazel/execroot/__main__/apex_bundle.zip"; !strings.Contains(data, w) { - t.Errorf("Expected %q in androidmk data, but did not find %q", w, data) - } - if w := "$(call dist-for-goals,checkbuild,out/bazel/execroot/__main__/installed-files.txt:foo-installed-files.txt)"; !strings.Contains(data, w) { - t.Errorf("Expected %q in androidmk data, but did not find %q", w, data) - } - - // make modules to be installed to system - if len(ab.makeModulesToInstall) != 1 && ab.makeModulesToInstall[0] != "c" { - t.Errorf("Expected makeModulesToInstall slice to only contain 'c', got %q", ab.makeModulesToInstall) - } - if w := "LOCAL_REQUIRED_MODULES := adbd.foo c"; !strings.Contains(data, w) { - t.Errorf("Expected %q in androidmk data, but did not find it in %q", w, data) - } -} - -func TestApexImageCreatesFilesInfoForMake(t *testing.T) { - bp := ` -apex_key{ - name: "foo_key", -} - -apex { - name: "foo", - key: "foo_key", - updatable: true, - min_sdk_version: "31", - file_contexts: ":myapex-file_contexts", - bazel_module: { label: "//:foo" }, -}` - - outputBaseDir := "out/bazel" - result := android.GroupFixturePreparers( - prepareForApexTest, - android.FixtureModifyConfig(func(config android.Config) { - config.BazelContext = android.MockBazelContext{ - OutputBaseDir: outputBaseDir, - LabelToApexInfo: map[string]cquery.ApexInfo{ - "//:foo": { - // ApexInfo Starlark provider. Necessary for the test. - SignedOutput: "signed_out.apex", - BundleKeyInfo: []string{"public_key", "private_key"}, - ContainerKeyInfo: []string{"container_cert", "container_private"}, - - // ApexMkInfo Starlark provider - PayloadFilesInfo: []map[string]string{ - { - "arch": "arm64", - "basename": "libcrypto.so", - "built_file": "bazel-out/64/libcrypto.so", - "class": "nativeSharedLib", - "install_dir": "lib64", - "make_module_name": "libcrypto", - "package": "foo/bar", - "unstripped_built_file": "bazel-out/64/unstripped_libcrypto.so", - }, - { - "arch": "arm", - "basename": "libcrypto.so", - "built_file": "bazel-out/32/libcrypto.so", - "class": "nativeSharedLib", - "install_dir": "lib", - "make_module_name": "libcrypto", - "package": "foo/bar", - }, - { - "arch": "arm64", - "basename": "adbd", - "built_file": "bazel-out/adbd", - "class": "nativeExecutable", - "install_dir": "bin", - "make_module_name": "adbd", - "package": "foo", - }, - }, - }, - }, - } - }), - ).RunTestWithBp(t, bp) - - m := result.ModuleForTests("foo", "android_common_foo_image").Module() - ab, ok := m.(*apexBundle) - - if !ok { - t.Fatalf("Expected module to be an apexBundle, was not") - } - - expectedFilesInfo := []apexFile{ - { - androidMkModuleName: "libcrypto", - builtFile: android.PathForTesting("out/bazel/execroot/__main__/bazel-out/64/libcrypto.so"), - class: nativeSharedLib, - customStem: "libcrypto.so", - installDir: "lib64", - moduleDir: "foo/bar", - arch: "arm64", - unstrippedBuiltFile: android.PathForTesting("out/bazel/execroot/__main__/bazel-out/64/unstripped_libcrypto.so"), - }, - { - androidMkModuleName: "libcrypto", - builtFile: android.PathForTesting("out/bazel/execroot/__main__/bazel-out/32/libcrypto.so"), - class: nativeSharedLib, - customStem: "libcrypto.so", - installDir: "lib", - moduleDir: "foo/bar", - arch: "arm", - }, - { - androidMkModuleName: "adbd", - builtFile: android.PathForTesting("out/bazel/execroot/__main__/bazel-out/adbd"), - class: nativeExecutable, - customStem: "adbd", - installDir: "bin", - moduleDir: "foo", - arch: "arm64", - }, - } - - if len(ab.filesInfo) != len(expectedFilesInfo) { - t.Errorf("Expected %d entries in ab.filesInfo, but got %d", len(ab.filesInfo), len(expectedFilesInfo)) - } - - for idx, f := range ab.filesInfo { - expected := expectedFilesInfo[idx] - android.AssertSame(t, "different class", expected.class, f.class) - android.AssertStringEquals(t, "different built file", expected.builtFile.String(), f.builtFile.String()) - android.AssertStringEquals(t, "different custom stem", expected.customStem, f.customStem) - android.AssertStringEquals(t, "different install dir", expected.installDir, f.installDir) - android.AssertStringEquals(t, "different make module name", expected.androidMkModuleName, f.androidMkModuleName) - android.AssertStringEquals(t, "different moduleDir", expected.moduleDir, f.moduleDir) - android.AssertStringEquals(t, "different arch", expected.arch, f.arch) - if expected.unstrippedBuiltFile != nil { - if f.unstrippedBuiltFile == nil { - t.Errorf("expected an unstripped built file path.") - } - android.AssertStringEquals(t, "different unstripped built file", expected.unstrippedBuiltFile.String(), f.unstrippedBuiltFile.String()) - } - } -} - -func TestCompressedApexImageInMixedBuilds(t *testing.T) { - bp := ` -apex_key{ - name: "foo_key", -} -apex { - name: "foo", - key: "foo_key", - updatable: true, - min_sdk_version: "31", - file_contexts: ":myapex-file_contexts", - bazel_module: { label: "//:foo" }, - test_only_force_compression: true, // force compression -}` - - outputBaseDir := "out/bazel" - result := android.GroupFixturePreparers( - prepareForApexTest, - android.FixtureModifyConfig(func(config android.Config) { - config.BazelContext = android.MockBazelContext{ - OutputBaseDir: outputBaseDir, - LabelToApexInfo: map[string]cquery.ApexInfo{ - "//:foo": cquery.ApexInfo{ - SignedOutput: "signed_out.apex", - SignedCompressedOutput: "signed_out.capex", - BundleKeyInfo: []string{"public_key", "private_key"}, - ContainerKeyInfo: []string{"container_cert", "container_private"}, - }, - }, - } - }), - ).RunTestWithBp(t, bp) - - m := result.ModuleForTests("foo", "android_common_foo_image").Module() - ab, ok := m.(*apexBundle) - if !ok { - t.Fatalf("Expected module to be an apexBundle, was not") - } - - if w, g := "out/bazel/execroot/__main__/signed_out.capex", ab.outputFile.String(); w != g { - t.Errorf("Expected output file to be compressed apex %q, got %q", w, g) - } - - mkData := android.AndroidMkDataForTest(t, result.TestContext, m) - var builder strings.Builder - mkData.Custom(&builder, "foo", "BAZEL_TARGET_", "", mkData) - - data := builder.String() - - expectedAndroidMk := []string{ - "LOCAL_PREBUILT_MODULE_FILE := out/bazel/execroot/__main__/signed_out.capex", - - // Check that the source install file is the capex. The dest is not important. - "LOCAL_SOONG_INSTALL_PAIRS := out/bazel/execroot/__main__/signed_out.capex:", - } - for _, androidMk := range expectedAndroidMk { - if !strings.Contains(data, androidMk) { - t.Errorf("Expected %q in androidmk data, but did not find %q", androidMk, data) - } - } -} - -func TestOverrideApexImageInMixedBuilds(t *testing.T) { - bp := ` -apex_key{ - name: "foo_key", -} -apex_key{ - name: "override_foo_key", -} -apex { - name: "foo", - key: "foo_key", - updatable: true, - min_sdk_version: "31", - package_name: "pkg_name", - file_contexts: ":myapex-file_contexts", - bazel_module: { label: "//:foo" }, -} -override_apex { - name: "override_foo", - key: "override_foo_key", - package_name: "override_pkg_name", - base: "foo", - bazel_module: { label: "//:override_foo" }, -} -` - - outputBaseDir := "out/bazel" - result := android.GroupFixturePreparers( - prepareForApexTest, - android.FixtureModifyConfig(func(config android.Config) { - config.BazelContext = android.MockBazelContext{ - OutputBaseDir: outputBaseDir, - LabelToApexInfo: map[string]cquery.ApexInfo{ - "//:foo": cquery.ApexInfo{ - // ApexInfo Starlark provider - SignedOutput: "signed_out.apex", - UnsignedOutput: "unsigned_out.apex", - BundleKeyInfo: []string{"public_key", "private_key"}, - ContainerKeyInfo: []string{"container_cert", "container_private"}, - SymbolsUsedByApex: "foo_using.txt", - JavaSymbolsUsedByApex: "foo_using.xml", - BundleFile: "apex_bundle.zip", - InstalledFiles: "installed-files.txt", - RequiresLibs: []string{"//path/c:c", "//path/d:d"}, - - // unused - PackageName: "pkg_name", - ProvidesLibs: []string{"a", "b"}, - - // ApexMkInfo Starlark provider - MakeModulesToInstall: []string{"c"}, // d deliberately omitted - }, - "//:override_foo": cquery.ApexInfo{ - // ApexInfo Starlark provider - SignedOutput: "override_signed_out.apex", - UnsignedOutput: "override_unsigned_out.apex", - BundleKeyInfo: []string{"override_public_key", "override_private_key"}, - ContainerKeyInfo: []string{"override_container_cert", "override_container_private"}, - SymbolsUsedByApex: "override_foo_using.txt", - JavaSymbolsUsedByApex: "override_foo_using.xml", - BundleFile: "override_apex_bundle.zip", - InstalledFiles: "override_installed-files.txt", - RequiresLibs: []string{"//path/c:c", "//path/d:d"}, - - // unused - PackageName: "override_pkg_name", - ProvidesLibs: []string{"a", "b"}, - - // ApexMkInfo Starlark provider - MakeModulesToInstall: []string{"c"}, // d deliberately omitted - }, - }, - } - }), - ).RunTestWithBp(t, bp) - - m := result.ModuleForTests("foo", "android_common_override_foo_foo_image").Module() - ab, ok := m.(*apexBundle) - if !ok { - t.Fatalf("Expected module to be an apexBundle, was not") - } - - if w, g := "out/bazel/execroot/__main__/override_public_key", ab.publicKeyFile.String(); w != g { - t.Errorf("Expected public key %q, got %q", w, g) - } - - if w, g := "out/bazel/execroot/__main__/override_private_key", ab.privateKeyFile.String(); w != g { - t.Errorf("Expected private key %q, got %q", w, g) - } - - if w, g := "out/bazel/execroot/__main__/override_container_cert", ab.containerCertificateFile.String(); w != g { - t.Errorf("Expected public container key %q, got %q", w, g) - } - - if w, g := "out/bazel/execroot/__main__/override_container_private", ab.containerPrivateKeyFile.String(); w != g { - t.Errorf("Expected private container key %q, got %q", w, g) - } - - if w, g := "out/bazel/execroot/__main__/override_signed_out.apex", ab.outputFile.String(); w != g { - t.Errorf("Expected output file %q, got %q", w, g) - } - - if w, g := "out/bazel/execroot/__main__/override_foo_using.txt", ab.nativeApisUsedByModuleFile.String(); w != g { - t.Errorf("Expected output file %q, got %q", w, g) - } - - if w, g := "out/bazel/execroot/__main__/override_foo_using.xml", ab.javaApisUsedByModuleFile.String(); w != g { - t.Errorf("Expected output file %q, got %q", w, g) - } - - if w, g := "out/bazel/execroot/__main__/override_installed-files.txt", ab.installedFilesFile.String(); w != g { - t.Errorf("Expected installed-files.txt %q, got %q", w, g) - } - - mkData := android.AndroidMkDataForTest(t, result.TestContext, m) - var builder strings.Builder - mkData.Custom(&builder, "override_foo", "BAZEL_TARGET_", "", mkData) - - data := builder.String() - if w := "ALL_MODULES.$(my_register_name).BUNDLE := out/bazel/execroot/__main__/override_apex_bundle.zip"; !strings.Contains(data, w) { - t.Errorf("Expected %q in androidmk data, but did not find %q", w, data) - } - if w := "$(call dist-for-goals,checkbuild,out/bazel/execroot/__main__/override_installed-files.txt:override_foo-installed-files.txt)"; !strings.Contains(data, w) { - t.Errorf("Expected %q in androidmk data, but did not find %q", w, data) - } - - // make modules to be installed to system - if len(ab.makeModulesToInstall) != 1 && ab.makeModulesToInstall[0] != "c" { - t.Errorf("Expected makeModulestoInstall slice to only contain 'c', got %q", ab.makeModulesToInstall) - } - if w := "LOCAL_REQUIRED_MODULES := c"; !strings.Contains(data, w) { - t.Errorf("Expected %q in androidmk data, but did not find it in %q", w, data) - } -} diff --git a/apex/builder.go b/apex/builder.go index 3c7671b00b..30788630b2 100644 --- a/apex/builder.go +++ b/apex/builder.go @@ -17,12 +17,14 @@ package apex import ( "encoding/json" "fmt" + "path" "path/filepath" "runtime" "sort" "strconv" "strings" + "android/soong/aconfig" "android/soong/android" "android/soong/java" @@ -35,6 +37,7 @@ var ( ) func init() { + pctx.Import("android/soong/aconfig") pctx.Import("android/soong/android") pctx.Import("android/soong/cc/config") pctx.Import("android/soong/java") @@ -74,7 +77,12 @@ func init() { pctx.HostBinToolVariable("apex_sepolicy_tests", "apex_sepolicy_tests") pctx.HostBinToolVariable("deapexer", "deapexer") pctx.HostBinToolVariable("debugfs_static", "debugfs_static") + pctx.HostBinToolVariable("fsck_erofs", "fsck.erofs") pctx.SourcePathVariable("genNdkUsedbyApexPath", "build/soong/scripts/gen_ndk_usedby_apex.sh") + pctx.HostBinToolVariable("conv_linker_config", "conv_linker_config") + pctx.HostBinToolVariable("assemble_vintf", "assemble_vintf") + pctx.HostBinToolVariable("apex_elf_checker", "apex_elf_checker") + pctx.HostBinToolVariable("aconfig", "aconfig") } var ( @@ -179,19 +187,6 @@ var ( }, "tool_path", "image_dir", "copy_commands", "file_contexts", "canned_fs_config", "key", "opt_flags", "manifest", "libs_to_trim") - zipApexRule = pctx.StaticRule("zipApexRule", blueprint.RuleParams{ - Command: `rm -rf ${image_dir} && mkdir -p ${image_dir} && ` + - `(. ${out}.copy_commands) && ` + - `APEXER_TOOL_PATH=${tool_path} ` + - `${apexer} --force --manifest ${manifest} ` + - `--payload_type zip ` + - `${image_dir} ${out} `, - CommandDeps: []string{"${apexer}", "${merge_zips}", "${soong_zip}", "${zipalign}", "${aapt2}"}, - Rspfile: "${out}.copy_commands", - RspfileContent: "${copy_commands}", - Description: "ZipAPEX ${image_dir} => ${out}", - }, "tool_path", "image_dir", "copy_commands", "manifest") - apexProtoConvertRule = pctx.AndroidStaticRule("apexProtoConvertRule", blueprint.RuleParams{ Command: `${aapt2} convert --output-format proto $in -o $out`, @@ -231,10 +226,28 @@ var ( apexSepolicyTestsRule = pctx.StaticRule("apexSepolicyTestsRule", blueprint.RuleParams{ Command: `${deapexer} --debugfs_path ${debugfs_static} list -Z ${in} > ${out}.fc` + - `&& ${apex_sepolicy_tests} -f ${out}.fc && touch ${out}`, + ` && ${apex_sepolicy_tests} -f ${out}.fc && touch ${out}`, CommandDeps: []string{"${apex_sepolicy_tests}", "${deapexer}", "${debugfs_static}"}, Description: "run apex_sepolicy_tests", }) + + apexLinkerconfigValidationRule = pctx.StaticRule("apexLinkerconfigValidationRule", blueprint.RuleParams{ + Command: `${conv_linker_config} validate --type apex ${image_dir} && touch ${out}`, + CommandDeps: []string{"${conv_linker_config}"}, + Description: "run apex_linkerconfig_validation", + }, "image_dir") + + assembleVintfRule = pctx.StaticRule("assembleVintfRule", blueprint.RuleParams{ + Command: `rm -f $out && VINTF_IGNORE_TARGET_FCM_VERSION=true ${assemble_vintf} -i $in -o $out`, + CommandDeps: []string{"${assemble_vintf}"}, + Description: "run assemble_vintf", + }) + + apexElfCheckerUnwantedRule = pctx.StaticRule("apexElfCheckerUnwantedRule", blueprint.RuleParams{ + Command: `${apex_elf_checker} --tool_path ${tool_path} --unwanted ${unwanted} ${in} && touch ${out}`, + CommandDeps: []string{"${apex_elf_checker}", "${deapexer}", "${debugfs_static}", "${fsck_erofs}", "${config.ClangBin}/llvm-readelf"}, + Description: "run apex_elf_checker --unwanted", + }, "tool_path", "unwanted") ) // buildManifest creates buile rules to modify the input apex_manifest.json to add information @@ -275,6 +288,22 @@ func (a *apexBundle) buildManifest(ctx android.ModuleContext, provideNativeLibs, manifestJsonFullOut := android.PathForModuleOut(ctx, "apex_manifest_full.json") defaultVersion := android.DefaultUpdatableModuleVersion + if a.properties.Variant_version != nil { + defaultVersionInt, err := strconv.Atoi(defaultVersion) + if err != nil { + ctx.ModuleErrorf("expected DefaultUpdatableModuleVersion to be an int, but got %s", defaultVersion) + } + if defaultVersionInt%10 != 0 { + ctx.ModuleErrorf("expected DefaultUpdatableModuleVersion to end in a zero, but got %s", defaultVersion) + } + variantVersion := []rune(*a.properties.Variant_version) + if len(variantVersion) != 1 || variantVersion[0] < '0' || variantVersion[0] > '9' { + ctx.PropertyErrorf("variant_version", "expected an integer between 0-9; got %s", *a.properties.Variant_version) + } + defaultVersionRunes := []rune(defaultVersion) + defaultVersionRunes[len(defaultVersion)-1] = []rune(variantVersion)[0] + defaultVersion = string(defaultVersionRunes) + } if override := ctx.Config().Getenv("OVERRIDE_APEX_MANIFEST_DEFAULT_VERSION"); override != "" { defaultVersion = override } @@ -314,14 +343,16 @@ func (a *apexBundle) buildManifest(ctx android.ModuleContext, provideNativeLibs, // buildFileContexts create build rules to append an entry for apex_manifest.pb to the file_contexts // file for this APEX which is either from /systme/sepolicy/apex/-file_contexts or from // the file_contexts property of this APEX. This is to make sure that the manifest file is correctly -// labeled as system_file. +// labeled as system_file or vendor_apex_metadata_file. func (a *apexBundle) buildFileContexts(ctx android.ModuleContext) android.OutputPath { var fileContexts android.Path var fileContextsDir string + isFileContextsModule := false if a.properties.File_contexts == nil { fileContexts = android.PathForSource(ctx, "system/sepolicy/apex", ctx.ModuleName()+"-file_contexts") } else { if m, t := android.SrcIsModuleWithTag(*a.properties.File_contexts); m != "" { + isFileContextsModule = true otherModule := android.GetModuleFromPathDep(ctx, m, t) fileContextsDir = ctx.OtherModuleDir(otherModule) } @@ -337,7 +368,7 @@ func (a *apexBundle) buildFileContexts(ctx android.ModuleContext) android.Output ctx.PropertyErrorf("file_contexts", "should be under system/sepolicy, but found in %q", fileContextsDir) } } - if !android.ExistentPathForSource(ctx, fileContexts.String()).Valid() { + if !isFileContextsModule && !android.ExistentPathForSource(ctx, fileContexts.String()).Valid() { ctx.PropertyErrorf("file_contexts", "cannot find file_contexts file: %q", fileContexts.String()) } @@ -346,38 +377,23 @@ func (a *apexBundle) buildFileContexts(ctx android.ModuleContext) android.Output output := android.PathForModuleOut(ctx, "file_contexts") rule := android.NewRuleBuilder(pctx, ctx) - switch a.properties.ApexType { - case imageApex: - // remove old file - rule.Command().Text("rm").FlagWithOutput("-f ", output) - // copy file_contexts - rule.Command().Text("cat").Input(fileContexts).Text(">>").Output(output) - // new line - rule.Command().Text("echo").Text(">>").Output(output) - if !useFileContextsAsIs { - // force-label /apex_manifest.pb and / as system_file so that apexd can read them - rule.Command().Text("echo").Flag("/apex_manifest\\\\.pb u:object_r:system_file:s0").Text(">>").Output(output) - rule.Command().Text("echo").Flag("/ u:object_r:system_file:s0").Text(">>").Output(output) - } - case flattenedApex: - // For flattened apexes, install path should be prepended. - // File_contexts file should be emiited to make via LOCAL_FILE_CONTEXTS - // so that it can be merged into file_contexts.bin - apexPath := android.InstallPathToOnDevicePath(ctx, a.installDir.Join(ctx, a.Name())) - apexPath = strings.ReplaceAll(apexPath, ".", `\\.`) - // remove old file - rule.Command().Text("rm").FlagWithOutput("-f ", output) - // copy file_contexts - rule.Command().Text("awk").Text(`'/object_r/{printf("` + apexPath + `%s\n", $0)}'`).Input(fileContexts).Text(">").Output(output) - // new line - rule.Command().Text("echo").Text(">>").Output(output) - if !useFileContextsAsIs { - // force-label /apex_manifest.pb and / as system_file so that apexd can read them - rule.Command().Text("echo").Flag(apexPath + `/apex_manifest\\.pb u:object_r:system_file:s0`).Text(">>").Output(output) - rule.Command().Text("echo").Flag(apexPath + "/ u:object_r:system_file:s0").Text(">>").Output(output) - } - default: - panic(fmt.Errorf("unsupported type %v", a.properties.ApexType)) + labelForRoot := "u:object_r:system_file:s0" + labelForManifest := "u:object_r:system_file:s0" + if a.SocSpecific() && !a.vndkApex { + // APEX on /vendor should label ./ and ./apex_manifest.pb as vendor file. + labelForRoot = "u:object_r:vendor_file:s0" + labelForManifest = "u:object_r:vendor_apex_metadata_file:s0" + } + // remove old file + rule.Command().Text("rm").FlagWithOutput("-f ", output) + // copy file_contexts + rule.Command().Text("cat").Input(fileContexts).Text(">>").Output(output) + // new line + rule.Command().Text("echo").Text(">>").Output(output) + if !useFileContextsAsIs { + // force-label /apex_manifest.pb and / + rule.Command().Text("echo").Text("/apex_manifest\\\\.pb").Text(labelForManifest).Text(">>").Output(output) + rule.Command().Text("echo").Text("/").Text(labelForRoot).Text(">>").Output(output) } rule.Build("file_contexts."+a.Name(), "Generate file_contexts") @@ -456,10 +472,25 @@ func markManifestTestOnly(ctx android.ModuleContext, androidManifestFile android }) } -// buildUnflattendApex creates build rules to build an APEX using apexer. -func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext) { - apexType := a.properties.ApexType - suffix := apexType.suffix() +func isVintfFragment(fi apexFile) bool { + isVintfFragment, _ := path.Match("etc/vintf/*.xml", fi.path()) + return isVintfFragment +} + +func runAssembleVintf(ctx android.ModuleContext, vintfFragment android.Path) android.Path { + processed := android.PathForModuleOut(ctx, "vintf", vintfFragment.Base()) + ctx.Build(pctx, android.BuildParams{ + Rule: assembleVintfRule, + Input: vintfFragment, + Output: processed, + Description: "run assemble_vintf for VINTF in APEX", + }) + return processed +} + +// buildApex creates build rules to build an APEX using apexer. +func (a *apexBundle) buildApex(ctx android.ModuleContext) { + suffix := imageApexSuffix apexName := a.BaseModuleName() //////////////////////////////////////////////////////////////////////////////////////////// @@ -468,10 +499,6 @@ func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext) { imageDir := android.PathForModuleOut(ctx, "image"+suffix) installSymbolFiles := (!ctx.Config().KatiEnabled() || a.ExportedToMake()) && a.installable() - // We can't install symbol files when prebuilt is used. - if a.IsReplacedByPrebuilt() { - installSymbolFiles = false - } // set of dependency module:location mappings installMapSet := make(map[string]bool) @@ -498,7 +525,15 @@ func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext) { copyCommands = append(copyCommands, "ln -sfn "+pathOnDevice+" "+destPath) } else { // Copy the file into APEX - copyCommands = append(copyCommands, "cp -f "+fi.builtFile.String()+" "+destPath) + if !a.testApex && isVintfFragment(fi) { + // copy the output of assemble_vintf instead of the original + vintfFragment := runAssembleVintf(ctx, fi.builtFile) + copyCommands = append(copyCommands, "cp -f "+vintfFragment.String()+" "+destPath) + implicitInputs = append(implicitInputs, vintfFragment) + } else { + copyCommands = append(copyCommands, "cp -f "+fi.builtFile.String()+" "+destPath) + implicitInputs = append(implicitInputs, fi.builtFile) + } var installedPath android.InstallPath if fi.class == appSet { @@ -516,7 +551,6 @@ func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext) { installedPath = ctx.InstallFile(apexDir.Join(ctx, fi.installDir), fi.stem(), fi.builtFile) } } - implicitInputs = append(implicitInputs, fi.builtFile) // Create additional symlinks pointing the file inside the APEX (if any). Note that // this is independent from the symlink optimization. @@ -534,13 +568,8 @@ func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext) { // Copy the test files (if any) for _, d := range fi.dataPaths { // TODO(eakammer): This is now the third repetition of ~this logic for test paths, refactoring should be possible - relPath := d.SrcPath.Rel() - dataPath := d.SrcPath.String() - if !strings.HasSuffix(dataPath, relPath) { - panic(fmt.Errorf("path %q does not end with %q", dataPath, relPath)) - } - - dataDest := imageDir.Join(ctx, fi.apexRelativePath(relPath), d.RelativeInstallPath).String() + relPath := d.ToRelativeInstallPath() + dataDest := imageDir.Join(ctx, fi.apexRelativePath(relPath)).String() copyCommands = append(copyCommands, "cp -f "+d.SrcPath.String()+" "+dataDest) implicitInputs = append(implicitInputs, d.SrcPath) @@ -548,6 +577,7 @@ func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext) { installMapSet[installMapPath.String()+":"+fi.installDir+"/"+fi.builtFile.Base()] = true } + implicitInputs = append(implicitInputs, a.manifestPbOut) if len(installMapSet) > 0 { @@ -602,263 +632,265 @@ func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext) { outHostBinDir := ctx.Config().HostToolPath(ctx, "").String() prebuiltSdkToolsBinDir := filepath.Join("prebuilts", "sdk", "tools", runtime.GOOS, "bin") - if apexType == imageApex { + defaultReadOnlyFiles := []string{"apex_manifest.json", "apex_manifest.pb"} + if len(a.aconfigFiles) > 0 { + apexAconfigFile := android.PathForModuleOut(ctx, "aconfig_flags.pb") + ctx.Build(pctx, android.BuildParams{ + Rule: aconfig.AllDeclarationsRule, + Inputs: a.aconfigFiles, + Output: apexAconfigFile, + Description: "combine_aconfig_declarations", + Args: map[string]string{ + "cache_files": android.JoinPathsWithPrefix(a.aconfigFiles, "--cache "), + }, + }) - //////////////////////////////////////////////////////////////////////////////////// - // Step 2: create canned_fs_config which encodes filemode,uid,gid of each files - // in this APEX. The file will be used by apexer in later steps. - cannedFsConfig := a.buildCannedFsConfig(ctx) - implicitInputs = append(implicitInputs, cannedFsConfig) + copyCommands = append(copyCommands, "cp -f "+apexAconfigFile.String()+" "+imageDir.String()) + implicitInputs = append(implicitInputs, apexAconfigFile) + defaultReadOnlyFiles = append(defaultReadOnlyFiles, apexAconfigFile.Base()) + } - //////////////////////////////////////////////////////////////////////////////////// - // Step 3: Prepare option flags for apexer and invoke it to create an unsigned APEX. - // TODO(jiyong): use the RuleBuilder - optFlags := []string{} + //////////////////////////////////////////////////////////////////////////////////// + // Step 2: create canned_fs_config which encodes filemode,uid,gid of each files + // in this APEX. The file will be used by apexer in later steps. + cannedFsConfig := a.buildCannedFsConfig(ctx, defaultReadOnlyFiles) + implicitInputs = append(implicitInputs, cannedFsConfig) - fileContexts := a.buildFileContexts(ctx) - implicitInputs = append(implicitInputs, fileContexts) + //////////////////////////////////////////////////////////////////////////////////// + // Step 3: Prepare option flags for apexer and invoke it to create an unsigned APEX. + // TODO(jiyong): use the RuleBuilder + optFlags := []string{} - implicitInputs = append(implicitInputs, a.privateKeyFile, a.publicKeyFile) - optFlags = append(optFlags, "--pubkey "+a.publicKeyFile.String()) + fileContexts := a.buildFileContexts(ctx) + implicitInputs = append(implicitInputs, fileContexts) - manifestPackageName := a.getOverrideManifestPackageName(ctx) - if manifestPackageName != "" { - optFlags = append(optFlags, "--override_apk_package_name "+manifestPackageName) - } + implicitInputs = append(implicitInputs, a.privateKeyFile, a.publicKeyFile) + optFlags = append(optFlags, "--pubkey "+a.publicKeyFile.String()) - if a.properties.AndroidManifest != nil { - androidManifestFile := android.PathForModuleSrc(ctx, proptools.String(a.properties.AndroidManifest)) + manifestPackageName := a.getOverrideManifestPackageName(ctx) + if manifestPackageName != "" { + optFlags = append(optFlags, "--override_apk_package_name "+manifestPackageName) + } - if a.testApex { - androidManifestFile = markManifestTestOnly(ctx, androidManifestFile) - } + if a.properties.AndroidManifest != nil { + androidManifestFile := android.PathForModuleSrc(ctx, proptools.String(a.properties.AndroidManifest)) - implicitInputs = append(implicitInputs, androidManifestFile) - optFlags = append(optFlags, "--android_manifest "+androidManifestFile.String()) - } else if a.testApex { - optFlags = append(optFlags, "--test_only") + if a.testApex { + androidManifestFile = markManifestTestOnly(ctx, androidManifestFile) } - // Determine target/min sdk version from the context - // TODO(jiyong): make this as a function - moduleMinSdkVersion := a.minSdkVersion(ctx) - minSdkVersion := moduleMinSdkVersion.String() + implicitInputs = append(implicitInputs, androidManifestFile) + optFlags = append(optFlags, "--android_manifest "+androidManifestFile.String()) + } else if a.testApex { + optFlags = append(optFlags, "--test_only") + } - // bundletool doesn't understand what "current" is. We need to transform it to - // codename - if moduleMinSdkVersion.IsCurrent() || moduleMinSdkVersion.IsNone() { - minSdkVersion = ctx.Config().DefaultAppTargetSdk(ctx).String() + // Determine target/min sdk version from the context + // TODO(jiyong): make this as a function + moduleMinSdkVersion := a.minSdkVersion(ctx) + minSdkVersion := moduleMinSdkVersion.String() - if java.UseApiFingerprint(ctx) { - minSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", java.ApiFingerprintPath(ctx).String()) - implicitInputs = append(implicitInputs, java.ApiFingerprintPath(ctx)) - } - } - // apex module doesn't have a concept of target_sdk_version, hence for the time - // being targetSdkVersion == default targetSdkVersion of the branch. - targetSdkVersion := strconv.Itoa(ctx.Config().DefaultAppTargetSdk(ctx).FinalOrFutureInt()) + // bundletool doesn't understand what "current" is. We need to transform it to + // codename + if moduleMinSdkVersion.IsCurrent() || moduleMinSdkVersion.IsNone() { + minSdkVersion = ctx.Config().DefaultAppTargetSdk(ctx).String() if java.UseApiFingerprint(ctx) { - targetSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", java.ApiFingerprintPath(ctx).String()) + minSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", java.ApiFingerprintPath(ctx).String()) implicitInputs = append(implicitInputs, java.ApiFingerprintPath(ctx)) } - optFlags = append(optFlags, "--target_sdk_version "+targetSdkVersion) - optFlags = append(optFlags, "--min_sdk_version "+minSdkVersion) - - if a.overridableProperties.Logging_parent != "" { - optFlags = append(optFlags, "--logging_parent ", a.overridableProperties.Logging_parent) - } - - // Create a NOTICE file, and embed it as an asset file in the APEX. - htmlGzNotice := android.PathForModuleOut(ctx, "NOTICE.html.gz") - android.BuildNoticeHtmlOutputFromLicenseMetadata( - ctx, htmlGzNotice, "", "", - []string{ - android.PathForModuleInstall(ctx).String() + "/", - android.PathForModuleInPartitionInstall(ctx, "apex").String() + "/", - }) - noticeAssetPath := android.PathForModuleOut(ctx, "NOTICE", "NOTICE.html.gz") - builder := android.NewRuleBuilder(pctx, ctx) - builder.Command().Text("cp"). - Input(htmlGzNotice). - Output(noticeAssetPath) - builder.Build("notice_dir", "Building notice dir") - implicitInputs = append(implicitInputs, noticeAssetPath) - optFlags = append(optFlags, "--assets_dir "+filepath.Dir(noticeAssetPath.String())) - - // Apexes which are supposed to be installed in builtin dirs(/system, etc) - // don't need hashtree for activation. Therefore, by removing hashtree from - // apex bundle (filesystem image in it, to be specific), we can save storage. - needHashTree := moduleMinSdkVersion.LessThanOrEqualTo(android.SdkVersion_Android10) || - a.shouldGenerateHashtree() - if ctx.Config().ApexCompressionEnabled() && a.isCompressable() { - needHashTree = true - } - if !needHashTree { - optFlags = append(optFlags, "--no_hashtree") - } + } + // apex module doesn't have a concept of target_sdk_version, hence for the time + // being targetSdkVersion == default targetSdkVersion of the branch. + targetSdkVersion := strconv.Itoa(ctx.Config().DefaultAppTargetSdk(ctx).FinalOrFutureInt()) - if a.testOnlyShouldSkipPayloadSign() { - optFlags = append(optFlags, "--unsigned_payload") - } + if java.UseApiFingerprint(ctx) { + targetSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", java.ApiFingerprintPath(ctx).String()) + implicitInputs = append(implicitInputs, java.ApiFingerprintPath(ctx)) + } + optFlags = append(optFlags, "--target_sdk_version "+targetSdkVersion) + optFlags = append(optFlags, "--min_sdk_version "+minSdkVersion) - if moduleMinSdkVersion == android.SdkVersion_Android10 { - implicitInputs = append(implicitInputs, a.manifestJsonOut) - optFlags = append(optFlags, "--manifest_json "+a.manifestJsonOut.String()) - } + if a.overridableProperties.Logging_parent != "" { + optFlags = append(optFlags, "--logging_parent ", a.overridableProperties.Logging_parent) + } - optFlags = append(optFlags, "--payload_fs_type "+a.payloadFsType.string()) - - if a.dynamic_common_lib_apex() { - ctx.Build(pctx, android.BuildParams{ - Rule: DCLAApexRule, - Implicits: implicitInputs, - Output: unsignedOutputFile, - Description: "apex (" + apexType.name() + ")", - Args: map[string]string{ - "tool_path": outHostBinDir + ":" + prebuiltSdkToolsBinDir, - "image_dir": imageDir.String(), - "copy_commands": strings.Join(copyCommands, " && "), - "manifest": a.manifestPbOut.String(), - "file_contexts": fileContexts.String(), - "canned_fs_config": cannedFsConfig.String(), - "key": a.privateKeyFile.String(), - "opt_flags": strings.Join(optFlags, " "), - }, - }) - } else if ctx.Config().ApexTrimEnabled() && len(a.libs_to_trim(ctx)) > 0 { - ctx.Build(pctx, android.BuildParams{ - Rule: TrimmedApexRule, - Implicits: implicitInputs, - Output: unsignedOutputFile, - Description: "apex (" + apexType.name() + ")", - Args: map[string]string{ - "tool_path": outHostBinDir + ":" + prebuiltSdkToolsBinDir, - "image_dir": imageDir.String(), - "copy_commands": strings.Join(copyCommands, " && "), - "manifest": a.manifestPbOut.String(), - "file_contexts": fileContexts.String(), - "canned_fs_config": cannedFsConfig.String(), - "key": a.privateKeyFile.String(), - "opt_flags": strings.Join(optFlags, " "), - "libs_to_trim": strings.Join(a.libs_to_trim(ctx), ","), - }, - }) - } else { - ctx.Build(pctx, android.BuildParams{ - Rule: apexRule, - Implicits: implicitInputs, - Output: unsignedOutputFile, - Description: "apex (" + apexType.name() + ")", - Args: map[string]string{ - "tool_path": outHostBinDir + ":" + prebuiltSdkToolsBinDir, - "image_dir": imageDir.String(), - "copy_commands": strings.Join(copyCommands, " && "), - "manifest": a.manifestPbOut.String(), - "file_contexts": fileContexts.String(), - "canned_fs_config": cannedFsConfig.String(), - "key": a.privateKeyFile.String(), - "opt_flags": strings.Join(optFlags, " "), - }, - }) - } + // Create a NOTICE file, and embed it as an asset file in the APEX. + htmlGzNotice := android.PathForModuleOut(ctx, "NOTICE.html.gz") + android.BuildNoticeHtmlOutputFromLicenseMetadata( + ctx, htmlGzNotice, "", "", + []string{ + android.PathForModuleInstall(ctx).String() + "/", + android.PathForModuleInPartitionInstall(ctx, "apex").String() + "/", + }) + noticeAssetPath := android.PathForModuleOut(ctx, "NOTICE", "NOTICE.html.gz") + builder := android.NewRuleBuilder(pctx, ctx) + builder.Command().Text("cp"). + Input(htmlGzNotice). + Output(noticeAssetPath) + builder.Build("notice_dir", "Building notice dir") + implicitInputs = append(implicitInputs, noticeAssetPath) + optFlags = append(optFlags, "--assets_dir "+filepath.Dir(noticeAssetPath.String())) + + // Apexes which are supposed to be installed in builtin dirs(/system, etc) + // don't need hashtree for activation. Therefore, by removing hashtree from + // apex bundle (filesystem image in it, to be specific), we can save storage. + needHashTree := moduleMinSdkVersion.LessThanOrEqualTo(android.SdkVersion_Android10) || + a.shouldGenerateHashtree() + if ctx.Config().ApexCompressionEnabled() && a.isCompressable() { + needHashTree = true + } + if !needHashTree { + optFlags = append(optFlags, "--no_hashtree") + } - // TODO(jiyong): make the two rules below as separate functions - apexProtoFile := android.PathForModuleOut(ctx, a.Name()+".pb"+suffix) - bundleModuleFile := android.PathForModuleOut(ctx, a.Name()+suffix+"-base.zip") - a.bundleModuleFile = bundleModuleFile + if a.testOnlyShouldSkipPayloadSign() { + optFlags = append(optFlags, "--unsigned_payload") + } - ctx.Build(pctx, android.BuildParams{ - Rule: apexProtoConvertRule, - Input: unsignedOutputFile, - Output: apexProtoFile, - Description: "apex proto convert", - }) + if moduleMinSdkVersion == android.SdkVersion_Android10 { + implicitInputs = append(implicitInputs, a.manifestJsonOut) + optFlags = append(optFlags, "--manifest_json "+a.manifestJsonOut.String()) + } - implicitInputs = append(implicitInputs, unsignedOutputFile) + optFlags = append(optFlags, "--payload_fs_type "+a.payloadFsType.string()) - // Run coverage analysis - apisUsedbyOutputFile := android.PathForModuleOut(ctx, a.Name()+"_using.txt") + if a.dynamic_common_lib_apex() { ctx.Build(pctx, android.BuildParams{ - Rule: generateAPIsUsedbyApexRule, + Rule: DCLAApexRule, Implicits: implicitInputs, - Description: "coverage", - Output: apisUsedbyOutputFile, + Output: unsignedOutputFile, + Description: "apex", Args: map[string]string{ - "image_dir": imageDir.String(), - "readelf": "${config.ClangBin}/llvm-readelf", + "tool_path": outHostBinDir + ":" + prebuiltSdkToolsBinDir, + "image_dir": imageDir.String(), + "copy_commands": strings.Join(copyCommands, " && "), + "manifest": a.manifestPbOut.String(), + "file_contexts": fileContexts.String(), + "canned_fs_config": cannedFsConfig.String(), + "key": a.privateKeyFile.String(), + "opt_flags": strings.Join(optFlags, " "), }, }) - a.nativeApisUsedByModuleFile = apisUsedbyOutputFile - - var nativeLibNames []string - for _, f := range a.filesInfo { - if f.class == nativeSharedLib { - nativeLibNames = append(nativeLibNames, f.stem()) - } - } - apisBackedbyOutputFile := android.PathForModuleOut(ctx, a.Name()+"_backing.txt") - rule := android.NewRuleBuilder(pctx, ctx) - rule.Command(). - Tool(android.PathForSource(ctx, "build/soong/scripts/gen_ndk_backedby_apex.sh")). - Output(apisBackedbyOutputFile). - Flags(nativeLibNames) - rule.Build("ndk_backedby_list", "Generate API libraries backed by Apex") - a.nativeApisBackedByModuleFile = apisBackedbyOutputFile - - var javaLibOrApkPath []android.Path - for _, f := range a.filesInfo { - if f.class == javaSharedLib || f.class == app { - javaLibOrApkPath = append(javaLibOrApkPath, f.builtFile) - } - } - javaApiUsedbyOutputFile := android.PathForModuleOut(ctx, a.Name()+"_using.xml") - javaUsedByRule := android.NewRuleBuilder(pctx, ctx) - javaUsedByRule.Command(). - Tool(android.PathForSource(ctx, "build/soong/scripts/gen_java_usedby_apex.sh")). - BuiltTool("dexdeps"). - Output(javaApiUsedbyOutputFile). - Inputs(javaLibOrApkPath) - javaUsedByRule.Build("java_usedby_list", "Generate Java APIs used by Apex") - a.javaApisUsedByModuleFile = javaApiUsedbyOutputFile - - bundleConfig := a.buildBundleConfig(ctx) - - var abis []string - for _, target := range ctx.MultiTargets() { - if len(target.Arch.Abi) > 0 { - abis = append(abis, target.Arch.Abi[0]) - } - } - - abis = android.FirstUniqueStrings(abis) - + } else if ctx.Config().ApexTrimEnabled() && len(a.libs_to_trim(ctx)) > 0 { ctx.Build(pctx, android.BuildParams{ - Rule: apexBundleRule, - Input: apexProtoFile, - Implicit: bundleConfig, - Output: a.bundleModuleFile, - Description: "apex bundle module", + Rule: TrimmedApexRule, + Implicits: implicitInputs, + Output: unsignedOutputFile, + Description: "apex", Args: map[string]string{ - "abi": strings.Join(abis, "."), - "config": bundleConfig.String(), + "tool_path": outHostBinDir + ":" + prebuiltSdkToolsBinDir, + "image_dir": imageDir.String(), + "copy_commands": strings.Join(copyCommands, " && "), + "manifest": a.manifestPbOut.String(), + "file_contexts": fileContexts.String(), + "canned_fs_config": cannedFsConfig.String(), + "key": a.privateKeyFile.String(), + "opt_flags": strings.Join(optFlags, " "), + "libs_to_trim": strings.Join(a.libs_to_trim(ctx), ","), }, }) - } else { // zipApex + } else { ctx.Build(pctx, android.BuildParams{ - Rule: zipApexRule, + Rule: apexRule, Implicits: implicitInputs, Output: unsignedOutputFile, - Description: "apex (" + apexType.name() + ")", + Description: "apex", Args: map[string]string{ - "tool_path": outHostBinDir + ":" + prebuiltSdkToolsBinDir, - "image_dir": imageDir.String(), - "copy_commands": strings.Join(copyCommands, " && "), - "manifest": a.manifestPbOut.String(), + "tool_path": outHostBinDir + ":" + prebuiltSdkToolsBinDir, + "image_dir": imageDir.String(), + "copy_commands": strings.Join(copyCommands, " && "), + "manifest": a.manifestPbOut.String(), + "file_contexts": fileContexts.String(), + "canned_fs_config": cannedFsConfig.String(), + "key": a.privateKeyFile.String(), + "opt_flags": strings.Join(optFlags, " "), }, }) } + // TODO(jiyong): make the two rules below as separate functions + apexProtoFile := android.PathForModuleOut(ctx, a.Name()+".pb"+suffix) + bundleModuleFile := android.PathForModuleOut(ctx, a.Name()+suffix+"-base.zip") + a.bundleModuleFile = bundleModuleFile + + ctx.Build(pctx, android.BuildParams{ + Rule: apexProtoConvertRule, + Input: unsignedOutputFile, + Output: apexProtoFile, + Description: "apex proto convert", + }) + + implicitInputs = append(implicitInputs, unsignedOutputFile) + + // Run coverage analysis + apisUsedbyOutputFile := android.PathForModuleOut(ctx, a.Name()+"_using.txt") + ctx.Build(pctx, android.BuildParams{ + Rule: generateAPIsUsedbyApexRule, + Implicits: implicitInputs, + Description: "coverage", + Output: apisUsedbyOutputFile, + Args: map[string]string{ + "image_dir": imageDir.String(), + "readelf": "${config.ClangBin}/llvm-readelf", + }, + }) + a.nativeApisUsedByModuleFile = apisUsedbyOutputFile + + var nativeLibNames []string + for _, f := range a.filesInfo { + if f.class == nativeSharedLib { + nativeLibNames = append(nativeLibNames, f.stem()) + } + } + apisBackedbyOutputFile := android.PathForModuleOut(ctx, a.Name()+"_backing.txt") + rb := android.NewRuleBuilder(pctx, ctx) + rb.Command(). + Tool(android.PathForSource(ctx, "build/soong/scripts/gen_ndk_backedby_apex.sh")). + Output(apisBackedbyOutputFile). + Flags(nativeLibNames) + rb.Build("ndk_backedby_list", "Generate API libraries backed by Apex") + a.nativeApisBackedByModuleFile = apisBackedbyOutputFile + + var javaLibOrApkPath []android.Path + for _, f := range a.filesInfo { + if f.class == javaSharedLib || f.class == app { + javaLibOrApkPath = append(javaLibOrApkPath, f.builtFile) + } + } + javaApiUsedbyOutputFile := android.PathForModuleOut(ctx, a.Name()+"_using.xml") + javaUsedByRule := android.NewRuleBuilder(pctx, ctx) + javaUsedByRule.Command(). + Tool(android.PathForSource(ctx, "build/soong/scripts/gen_java_usedby_apex.sh")). + BuiltTool("dexdeps"). + Output(javaApiUsedbyOutputFile). + Inputs(javaLibOrApkPath) + javaUsedByRule.Build("java_usedby_list", "Generate Java APIs used by Apex") + a.javaApisUsedByModuleFile = javaApiUsedbyOutputFile + + bundleConfig := a.buildBundleConfig(ctx) + + var abis []string + for _, target := range ctx.MultiTargets() { + if len(target.Arch.Abi) > 0 { + abis = append(abis, target.Arch.Abi[0]) + } + } + + abis = android.FirstUniqueStrings(abis) + + ctx.Build(pctx, android.BuildParams{ + Rule: apexBundleRule, + Input: apexProtoFile, + Implicit: bundleConfig, + Output: a.bundleModuleFile, + Description: "apex bundle module", + Args: map[string]string{ + "abi": strings.Join(abis, "."), + "config": bundleConfig.String(), + }, + }) + //////////////////////////////////////////////////////////////////////////////////// // Step 4: Sign the APEX using signapk signedOutputFile := android.PathForModuleOut(ctx, a.Name()+suffix) @@ -876,9 +908,15 @@ func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext) { args["outCommaList"] = signedOutputFile.String() } var validations android.Paths - if suffix == imageApexSuffix { + validations = append(validations, runApexLinkerconfigValidation(ctx, unsignedOutputFile.OutputPath, imageDir.OutputPath)) + // TODO(b/279688635) deapexer supports [ext4] + if suffix == imageApexSuffix && ext4 == a.payloadFsType { validations = append(validations, runApexSepolicyTests(ctx, unsignedOutputFile.OutputPath)) } + if !a.testApex && len(a.properties.Unwanted_transitive_deps) > 0 { + validations = append(validations, + runApexElfCheckerUnwanted(ctx, unsignedOutputFile.OutputPath, a.properties.Unwanted_transitive_deps)) + } ctx.Build(pctx, android.BuildParams{ Rule: rule, Description: "signapk", @@ -937,53 +975,12 @@ func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext) { // Install to $OUT/soong/{target,host}/.../apex. a.installedFile = ctx.InstallFile(a.installDir, a.Name()+installSuffix, a.outputFile, - a.compatSymlinks.Paths()...) + a.compatSymlinks...) // installed-files.txt is dist'ed a.installedFilesFile = a.buildInstalledFilesFile(ctx, a.outputFile, imageDir) -} - -// buildFlattenedApex creates rules for a flattened APEX. Flattened APEX actually doesn't have a -// single output file. It is a phony target for all the files under /system/apex/ directory. -// This function creates the installation rules for the files. -func (a *apexBundle) buildFlattenedApex(ctx android.ModuleContext) { - bundleName := a.Name() - installedSymlinks := append(android.InstallPaths(nil), a.compatSymlinks...) - if a.installable() { - for _, fi := range a.filesInfo { - dir := filepath.Join("apex", bundleName, fi.installDir) - installDir := android.PathForModuleInstall(ctx, dir) - if a.linkToSystemLib && fi.transitiveDep && fi.availableToPlatform() { - pathOnDevice := filepath.Join("/", fi.partition, fi.path()) - installedSymlinks = append(installedSymlinks, - ctx.InstallAbsoluteSymlink(installDir, fi.stem(), pathOnDevice)) - } else { - if fi.class == appSet { - as := fi.module.(*java.AndroidAppSet) - ctx.InstallFileWithExtraFilesZip(installDir, as.BaseModuleName()+".apk", - as.OutputFile(), as.PackedAdditionalOutputs()) - } else { - target := ctx.InstallFile(installDir, fi.stem(), fi.builtFile) - for _, sym := range fi.symlinks { - installedSymlinks = append(installedSymlinks, - ctx.InstallSymlink(installDir, sym, target)) - } - } - } - } - - // Create install rules for the files added in GenerateAndroidBuildActions after - // buildFlattenedApex is called. Add the links to system libs (if any) as dependencies - // of the apex_manifest.pb file since it is always present. - dir := filepath.Join("apex", bundleName) - installDir := android.PathForModuleInstall(ctx, dir) - ctx.InstallFile(installDir, "apex_manifest.pb", a.manifestPbOut, installedSymlinks.Paths()...) - ctx.InstallFile(installDir, "apex_pubkey", a.publicKeyFile) - } - a.fileContexts = a.buildFileContexts(ctx) - - a.outputFile = android.PathForModuleInstall(ctx, "apex", bundleName) + a.apexKeysPath = writeApexKeys(ctx, a) } // getCertificateAndPrivateKey retrieves the cert and the private key that will be used to sign @@ -1027,21 +1024,12 @@ func (a *apexBundle) getOverrideManifestPackageName(ctx android.ModuleContext) s } func (a *apexBundle) buildApexDependencyInfo(ctx android.ModuleContext) { - if !a.primaryApexType { - return - } - if a.properties.IsCoverageVariant { // Otherwise, we will have duplicated rules for coverage and // non-coverage variants of the same APEX return } - if ctx.Host() { - // No need to generate dependency info for host variant - return - } - depInfos := android.DepNameToDepInfoMap{} a.WalkPayloadDeps(ctx, func(ctx android.ModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool { if from.Name() == to.Name() { @@ -1116,8 +1104,8 @@ func (a *apexBundle) buildLintReports(ctx android.ModuleContext) { a.lintReports = java.BuildModuleLintReportZips(ctx, depSetsBuilder.Build()) } -func (a *apexBundle) buildCannedFsConfig(ctx android.ModuleContext) android.OutputPath { - var readOnlyPaths = []string{"apex_manifest.json", "apex_manifest.pb"} +func (a *apexBundle) buildCannedFsConfig(ctx android.ModuleContext, defaultReadOnlyFiles []string) android.OutputPath { + var readOnlyPaths = defaultReadOnlyFiles var executablePaths []string // this also includes dirs var appSetDirs []string appSetFiles := make(map[string]android.Path) @@ -1126,7 +1114,8 @@ func (a *apexBundle) buildCannedFsConfig(ctx android.ModuleContext) android.Outp if f.installDir == "bin" || strings.HasPrefix(f.installDir, "bin/") { executablePaths = append(executablePaths, pathInApex) for _, d := range f.dataPaths { - readOnlyPaths = append(readOnlyPaths, filepath.Join(f.installDir, d.RelativeInstallPath, d.SrcPath.Rel())) + rel := d.ToRelativeInstallPath() + readOnlyPaths = append(readOnlyPaths, filepath.Join(f.installDir, rel)) } for _, s := range f.symlinks { executablePaths = append(executablePaths, filepath.Join(f.installDir, s)) @@ -1181,6 +1170,19 @@ func (a *apexBundle) buildCannedFsConfig(ctx android.ModuleContext) android.Outp return cannedFsConfig.OutputPath } +func runApexLinkerconfigValidation(ctx android.ModuleContext, apexFile android.OutputPath, imageDir android.OutputPath) android.Path { + timestamp := android.PathForModuleOut(ctx, "apex_linkerconfig_validation.timestamp") + ctx.Build(pctx, android.BuildParams{ + Rule: apexLinkerconfigValidationRule, + Input: apexFile, + Output: timestamp, + Args: map[string]string{ + "image_dir": imageDir.String(), + }, + }) + return timestamp +} + // Runs apex_sepolicy_tests // // $ deapexer list -Z {apex_file} > {file_contexts} @@ -1194,3 +1196,17 @@ func runApexSepolicyTests(ctx android.ModuleContext, apexFile android.OutputPath }) return timestamp } + +func runApexElfCheckerUnwanted(ctx android.ModuleContext, apexFile android.OutputPath, unwanted []string) android.Path { + timestamp := android.PathForModuleOut(ctx, "apex_elf_unwanted.timestamp") + ctx.Build(pctx, android.BuildParams{ + Rule: apexElfCheckerUnwantedRule, + Input: apexFile, + Output: timestamp, + Args: map[string]string{ + "unwanted": android.JoinWithSuffixAndSeparator(unwanted, ".so", ":"), + "tool_path": ctx.Config().HostToolPath(ctx, "").String() + ":${config.ClangBin}", + }, + }) + return timestamp +} diff --git a/apex/deapexer.go b/apex/deapexer.go index fed9cd1f02..3b7c77dc96 100644 --- a/apex/deapexer.go +++ b/apex/deapexer.go @@ -140,7 +140,6 @@ func (p *Deapexer) GenerateAndroidBuildActions(ctx android.ModuleContext) { Tool(android.PathForSource(ctx, "build/soong/scripts/unpack-prebuilt-apex.sh")). BuiltTool("deapexer"). BuiltTool("debugfs"). - BuiltTool("blkid"). BuiltTool("fsck.erofs"). Input(p.inputApex). Text(deapexerOutput.String()) diff --git a/apex/dexpreopt_bootjars_test.go b/apex/dexpreopt_bootjars_test.go index bba8bb6cb7..2e828caded 100644 --- a/apex/dexpreopt_bootjars_test.go +++ b/apex/dexpreopt_bootjars_test.go @@ -138,8 +138,8 @@ func testDexpreoptBoot(t *testing.T, ruleFile string, expectedInputs, expectedOu prepareForTestWithArtApex, ).RunTestWithBp(t, fmt.Sprintf(bp, preferPrebuilt)) - platformBootclasspath := result.ModuleForTests("platform-bootclasspath", "android_common") - rule := platformBootclasspath.Output(ruleFile) + dexBootJars := result.ModuleForTests("dex_bootjars", "android_common") + rule := dexBootJars.Output(ruleFile) inputs := rule.Implicits.Strings() sort.Strings(inputs) @@ -155,15 +155,15 @@ func testDexpreoptBoot(t *testing.T, ruleFile string, expectedInputs, expectedOu } func TestDexpreoptBootJarsWithSourceArtApex(t *testing.T) { - ruleFile := "boot.art" + ruleFile := "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.art" expectedInputs := []string{ "out/soong/dexpreopt_arm64/dex_bootjars_input/core-oj.jar", "out/soong/dexpreopt_arm64/dex_bootjars_input/foo.jar", "out/soong/dexpreopt_arm64/dex_bootjars_input/bar.jar", "out/soong/dexpreopt_arm64/dex_bootjars_input/baz.jar", - "out/soong/dexpreopt_arm64/dex_artjars/boot.prof", - "out/soong/dexpreopt_arm64/dex_bootjars/boot.prof", + "out/soong/.intermediates/art-bootclasspath-fragment/android_common_apex10000/art-bootclasspath-fragment/boot.prof", + "out/soong/.intermediates/default/java/dex_bootjars/android_common/boot/boot.prof", } expectedOutputs := []string{ @@ -192,7 +192,7 @@ func TestDexpreoptBootJarsWithSourceArtApex(t *testing.T) { // The only difference is that the ART profile should be deapexed from the prebuilt APEX. Other // inputs and outputs should be the same as above. func TestDexpreoptBootJarsWithPrebuiltArtApex(t *testing.T) { - ruleFile := "boot.art" + ruleFile := "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.art" expectedInputs := []string{ "out/soong/dexpreopt_arm64/dex_bootjars_input/core-oj.jar", @@ -200,7 +200,7 @@ func TestDexpreoptBootJarsWithPrebuiltArtApex(t *testing.T) { "out/soong/dexpreopt_arm64/dex_bootjars_input/bar.jar", "out/soong/dexpreopt_arm64/dex_bootjars_input/baz.jar", "out/soong/.intermediates/com.android.art.deapexer/android_common/deapexer/etc/boot-image.prof", - "out/soong/dexpreopt_arm64/dex_bootjars/boot.prof", + "out/soong/.intermediates/default/java/dex_bootjars/android_common/boot/boot.prof", } expectedOutputs := []string{ diff --git a/apex/key.go b/apex/key.go index 0a7e80f8fc..e4214f0e05 100644 --- a/apex/key.go +++ b/apex/key.go @@ -16,12 +16,8 @@ package apex import ( "fmt" - "sort" - "strings" "android/soong/android" - "android/soong/bazel" - "github.com/google/blueprint/proptools" ) @@ -33,12 +29,10 @@ func init() { func registerApexKeyBuildComponents(ctx android.RegistrationContext) { ctx.RegisterModuleType("apex_key", ApexKeyFactory) - ctx.RegisterSingletonType("apex_keys_text", apexKeysTextFactory) } type apexKey struct { android.ModuleBase - android.BazelModuleBase properties apexKeyProperties @@ -60,8 +54,7 @@ type apexKeyProperties struct { func ApexKeyFactory() android.Module { module := &apexKey{} module.AddProperties(&module.properties) - android.InitAndroidArchModule(module, android.HostAndDeviceDefault, android.MultilibCommon) - android.InitBazelModule(module) + android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon) return module } @@ -102,135 +95,63 @@ func (m *apexKey) GenerateAndroidBuildActions(ctx android.ModuleContext) { } } -// ////////////////////////////////////////////////////////////////////// -// apex_keys_text -type apexKeysText struct { - output android.OutputPath +type apexKeyEntry struct { + name string + presigned bool + publicKey string + privateKey string + containerCertificate string + containerPrivateKey string + partition string + signTool string } -func (s *apexKeysText) GenerateBuildActions(ctx android.SingletonContext) { - s.output = android.PathForOutput(ctx, "apexkeys.txt") - type apexKeyEntry struct { - name string - presigned bool - publicKey string - privateKey string - containerCertificate string - containerPrivateKey string - partition string - signTool string +func (e apexKeyEntry) String() string { + signTool := "" + if e.signTool != "" { + signTool = fmt.Sprintf(" sign_tool=%q", e.signTool) } - toString := func(e apexKeyEntry) string { - signTool := "" - if e.signTool != "" { - signTool = fmt.Sprintf(" sign_tool=%q", e.signTool) - } - format := "name=%q public_key=%q private_key=%q container_certificate=%q container_private_key=%q partition=%q%s\n" - if e.presigned { - return fmt.Sprintf(format, e.name, "PRESIGNED", "PRESIGNED", "PRESIGNED", "PRESIGNED", e.partition, signTool) - } else { - return fmt.Sprintf(format, e.name, e.publicKey, e.privateKey, e.containerCertificate, e.containerPrivateKey, e.partition, signTool) - } + format := "name=%q public_key=%q private_key=%q container_certificate=%q container_private_key=%q partition=%q%s\n" + if e.presigned { + return fmt.Sprintf(format, e.name, "PRESIGNED", "PRESIGNED", "PRESIGNED", "PRESIGNED", e.partition, signTool) + } else { + return fmt.Sprintf(format, e.name, e.publicKey, e.privateKey, e.containerCertificate, e.containerPrivateKey, e.partition, signTool) } +} - apexKeyMap := make(map[string]apexKeyEntry) - ctx.VisitAllModules(func(module android.Module) { - if m, ok := module.(*apexBundle); ok && m.Enabled() && m.installable() { - pem, key := m.getCertificateAndPrivateKey(ctx) - apexKeyMap[m.Name()] = apexKeyEntry{ - name: m.Name() + ".apex", - presigned: false, - publicKey: m.publicKeyFile.String(), - privateKey: m.privateKeyFile.String(), - containerCertificate: pem.String(), - containerPrivateKey: key.String(), - partition: m.PartitionTag(ctx.DeviceConfig()), - signTool: proptools.String(m.properties.Custom_sign_tool), - } +func apexKeyEntryFor(ctx android.ModuleContext, module android.Module) apexKeyEntry { + switch m := module.(type) { + case *apexBundle: + pem, key := m.getCertificateAndPrivateKey(ctx) + return apexKeyEntry{ + name: m.Name() + ".apex", + presigned: false, + publicKey: m.publicKeyFile.String(), + privateKey: m.privateKeyFile.String(), + containerCertificate: pem.String(), + containerPrivateKey: key.String(), + partition: m.PartitionTag(ctx.DeviceConfig()), + signTool: proptools.String(m.properties.Custom_sign_tool), } - }) - - // Find prebuilts and let them override apexBundle if they are preferred - ctx.VisitAllModules(func(module android.Module) { - if m, ok := module.(*Prebuilt); ok && m.Enabled() && m.installable() && - m.Prebuilt().UsePrebuilt() { - apexKeyMap[m.BaseModuleName()] = apexKeyEntry{ - name: m.InstallFilename(), - presigned: true, - partition: m.PartitionTag(ctx.DeviceConfig()), - } + case *Prebuilt: + return apexKeyEntry{ + name: m.InstallFilename(), + presigned: true, + partition: m.PartitionTag(ctx.DeviceConfig()), } - }) - - // Find apex_set and let them override apexBundle or prebuilts. This is done in a separate pass - // so that apex_set are not overridden by prebuilts. - ctx.VisitAllModules(func(module android.Module) { - if m, ok := module.(*ApexSet); ok && m.Enabled() { - entry := apexKeyEntry{ - name: m.InstallFilename(), - presigned: true, - partition: m.PartitionTag(ctx.DeviceConfig()), - } - apexKeyMap[m.BaseModuleName()] = entry + case *ApexSet: + return apexKeyEntry{ + name: m.InstallFilename(), + presigned: true, + partition: m.PartitionTag(ctx.DeviceConfig()), } - }) - - // iterating over map does not give consistent ordering in golang - var moduleNames []string - for key, _ := range apexKeyMap { - moduleNames = append(moduleNames, key) } - sort.Strings(moduleNames) - - var filecontent strings.Builder - for _, name := range moduleNames { - filecontent.WriteString(toString(apexKeyMap[name])) - } - android.WriteFileRule(ctx, s.output, filecontent.String()) -} - -func apexKeysTextFactory() android.Singleton { - return &apexKeysText{} + panic(fmt.Errorf("unknown type(%t) for apexKeyEntry", module)) } -func (s *apexKeysText) MakeVars(ctx android.MakeVarsContext) { - ctx.Strict("SOONG_APEX_KEYS_FILE", s.output.String()) -} - -// For Bazel / bp2build - -type bazelApexKeyAttributes struct { - Public_key bazel.LabelAttribute - Public_key_name bazel.StringAttribute - - Private_key bazel.LabelAttribute - Private_key_name bazel.StringAttribute -} - -// ConvertWithBp2build performs conversion apexKey for bp2build -func (m *apexKey) ConvertWithBp2build(ctx android.TopDownMutatorContext) { - apexKeyBp2BuildInternal(ctx, m) -} - -func apexKeyBp2BuildInternal(ctx android.TopDownMutatorContext, module *apexKey) { - privateKeyLabelAttribute, privateKeyNameAttribute := - android.BazelStringOrLabelFromProp(ctx, module.properties.Private_key) - - publicKeyLabelAttribute, publicKeyNameAttribute := - android.BazelStringOrLabelFromProp(ctx, module.properties.Public_key) - - attrs := &bazelApexKeyAttributes{ - Private_key: privateKeyLabelAttribute, - Private_key_name: privateKeyNameAttribute, - - Public_key: publicKeyLabelAttribute, - Public_key_name: publicKeyNameAttribute, - } - - props := bazel.BazelTargetModuleProperties{ - Rule_class: "apex_key", - Bzl_load_location: "//build/bazel/rules/apex:apex_key.bzl", - } - - ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: module.Name()}, attrs) +func writeApexKeys(ctx android.ModuleContext, module android.Module) android.WritablePath { + path := android.PathForModuleOut(ctx, "apexkeys.txt") + entry := apexKeyEntryFor(ctx, module) + android.WriteFileRuleVerbatim(ctx, path, entry.String()) + return path } diff --git a/apex/metadata.go b/apex/metadata.go deleted file mode 100644 index b1dff3e3f5..0000000000 --- a/apex/metadata.go +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) 2023 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 apex - -import ( - "encoding/json" - - "github.com/google/blueprint" - - "android/soong/android" -) - -var ( - mtctx = android.NewPackageContext("android/soong/multitree_apex") -) - -func init() { - RegisterModulesSingleton(android.InitRegistrationContext) -} - -func RegisterModulesSingleton(ctx android.RegistrationContext) { - ctx.RegisterSingletonType("apex_multitree_singleton", multitreeAnalysisSingletonFactory) -} - -var PrepareForTestWithApexMultitreeSingleton = android.FixtureRegisterWithContext(RegisterModulesSingleton) - -func multitreeAnalysisSingletonFactory() android.Singleton { - return &multitreeAnalysisSingleton{} -} - -type multitreeAnalysisSingleton struct { - multitreeApexMetadataPath android.OutputPath -} - -type ApexMultitreeMetadataEntry struct { - // The name of the apex. - Name string - - // TODO: Add other properties as needed. -} - -type ApexMultitreeMetadata struct { - // Information about the installable apexes. - Apexes map[string]ApexMultitreeMetadataEntry -} - -func (p *multitreeAnalysisSingleton) GenerateBuildActions(context android.SingletonContext) { - data := ApexMultitreeMetadata{ - Apexes: make(map[string]ApexMultitreeMetadataEntry, 0), - } - context.VisitAllModules(func(module android.Module) { - // If this module is not being installed, ignore it. - if !module.Enabled() || module.IsSkipInstall() { - return - } - // Actual apexes provide ApexBundleInfoProvider. - if _, ok := context.ModuleProvider(module, ApexBundleInfoProvider).(ApexBundleInfo); !ok { - return - } - bundle, ok := module.(*apexBundle) - if ok && !bundle.testApex && !bundle.vndkApex && bundle.primaryApexType { - name := module.Name() - entry := ApexMultitreeMetadataEntry{ - Name: name, - } - data.Apexes[name] = entry - } - }) - p.multitreeApexMetadataPath = android.PathForOutput(context, "multitree_apex_metadata.json") - - jsonStr, err := json.Marshal(data) - if err != nil { - context.Errorf(err.Error()) - } - android.WriteFileRule(context, p.multitreeApexMetadataPath, string(jsonStr)) - // This seems cleaner, but doesn't emit the phony rule in testing. - // context.Phony("multitree_apex_metadata", p.multitreeApexMetadataPath) - - context.Build(mtctx, android.BuildParams{ - Rule: blueprint.Phony, - Description: "phony rule for multitree_apex_metadata", - Inputs: []android.Path{p.multitreeApexMetadataPath}, - Output: android.PathForPhony(context, "multitree_apex_metadata"), - }) -} diff --git a/apex/metadata_test.go b/apex/metadata_test.go deleted file mode 100644 index fed5beae7a..0000000000 --- a/apex/metadata_test.go +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2023 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 apex - -import ( - "strings" - "testing" - - "android/soong/android" - "android/soong/java" -) - -func TestModulesSingleton(t *testing.T) { - result := android.GroupFixturePreparers( - PrepareForTestWithApexMultitreeSingleton, - java.PrepareForTestWithJavaDefaultModules, - PrepareForTestWithApexBuildComponents, - java.FixtureConfigureApexBootJars("myapex:foo"), - java.PrepareForTestWithJavaSdkLibraryFiles, - ).RunTestWithBp(t, ` - prebuilt_apex { - name: "myapex", - src: "myapex.apex", - exported_bootclasspath_fragments: ["mybootclasspath-fragment"], - } - - // A prebuilt java_sdk_library_import that is not preferred by default but will be preferred - // because AlwaysUsePrebuiltSdks() is true. - java_sdk_library_import { - name: "foo", - prefer: false, - shared_library: false, - permitted_packages: ["foo"], - public: { - jars: ["sdk_library/public/foo-stubs.jar"], - stub_srcs: ["sdk_library/public/foo_stub_sources"], - current_api: "sdk_library/public/foo.txt", - removed_api: "sdk_library/public/foo-removed.txt", - sdk_version: "current", - }, - apex_available: ["myapex"], - } - - prebuilt_bootclasspath_fragment { - name: "mybootclasspath-fragment", - apex_available: [ - "myapex", - ], - contents: [ - "foo", - ], - hidden_api: { - stub_flags: "prebuilt-stub-flags.csv", - annotation_flags: "prebuilt-annotation-flags.csv", - metadata: "prebuilt-metadata.csv", - index: "prebuilt-index.csv", - all_flags: "prebuilt-all-flags.csv", - }, - } - - platform_bootclasspath { - name: "myplatform-bootclasspath", - fragments: [ - { - apex: "myapex", - module:"mybootclasspath-fragment", - }, - ], - } -`, - ) - - outputs := result.SingletonForTests("apex_multitree_singleton").AllOutputs() - for _, output := range outputs { - testingBuildParam := result.SingletonForTests("apex_multitree_singleton").Output(output) - switch { - case strings.Contains(output, "soong/multitree_apex_metadata.json"): - android.AssertStringEquals(t, "Invalid build rule", "android/soong/android.writeFile", testingBuildParam.Rule.String()) - android.AssertIntEquals(t, "Invalid input", len(testingBuildParam.Inputs), 0) - android.AssertStringDoesContain(t, "Invalid output path", output, "soong/multitree_apex_metadata.json") - - case strings.HasSuffix(output, "multitree_apex_metadata"): - android.AssertStringEquals(t, "Invalid build rule", ":phony", testingBuildParam.Rule.String()) - android.AssertStringEquals(t, "Invalid input", testingBuildParam.Inputs[0].String(), "out/soong/multitree_apex_metadata.json") - android.AssertStringEquals(t, "Invalid output path", output, "multitree_apex_metadata") - android.AssertIntEquals(t, "Invalid args", len(testingBuildParam.Args), 0) - } - } -} diff --git a/apex/prebuilt.go b/apex/prebuilt.go index 0d83830f16..7d339d54ea 100644 --- a/apex/prebuilt.go +++ b/apex/prebuilt.go @@ -60,6 +60,9 @@ type prebuiltCommon struct { installedFile android.InstallPath outputApex android.WritablePath + // fragment for this apex for apexkeys.txt + apexKeysPath android.WritablePath + // A list of apexFile objects created in prebuiltCommon.initApexFilesForAndroidMk which are used // to create make modules in prebuiltCommon.AndroidMkEntries. apexFilesForAndroidMk []apexFile @@ -133,9 +136,7 @@ func (p *prebuiltCommon) isForceDisabled() bool { } func (p *prebuiltCommon) checkForceDisable(ctx android.ModuleContext) bool { - // If the device is configured to use flattened APEX, force disable the prebuilt because - // the prebuilt is a non-flattened one. - forceDisable := ctx.Config().FlattenApex() + forceDisable := false // Force disable the prebuilts when we are doing unbundled build. We do unbundled build // to build the prebuilts themselves. @@ -237,8 +238,10 @@ func (p *prebuiltCommon) AndroidMkEntries() []android.AndroidMkEntries { entries.SetString("LOCAL_MODULE_STEM", p.installFilename) entries.SetPath("LOCAL_SOONG_INSTALLED_MODULE", p.installedFile) entries.SetString("LOCAL_SOONG_INSTALL_PAIRS", p.outputApex.String()+":"+p.installedFile.String()) + entries.AddStrings("LOCAL_SOONG_INSTALL_SYMLINKS", p.compatSymlinks.Strings()...) entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", !p.installable()) entries.AddStrings("LOCAL_OVERRIDES_MODULES", p.prebuiltCommonProperties.Overrides...) + entries.SetString("LOCAL_APEX_KEY_PATH", p.apexKeysPath.String()) p.addRequiredModules(entries) }, }, @@ -760,6 +763,7 @@ func (p *Prebuilt) ApexInfoMutator(mctx android.TopDownMutatorContext) { } func (p *Prebuilt) GenerateAndroidBuildActions(ctx android.ModuleContext) { + p.apexKeysPath = writeApexKeys(ctx, p) // TODO(jungjw): Check the key validity. p.inputApex = android.OptionalPathForModuleSrc(ctx, p.prebuiltCommonProperties.Selected_apex).Path() p.installDir = android.PathForModuleInstall(ctx, "apex") @@ -783,14 +787,14 @@ func (p *Prebuilt) GenerateAndroidBuildActions(ctx android.ModuleContext) { p.initApexFilesForAndroidMk(ctx) // in case that prebuilt_apex replaces source apex (using prefer: prop) - p.compatSymlinks = makeCompatSymlinks(p.BaseModuleName(), ctx, true) + p.compatSymlinks = makeCompatSymlinks(p.BaseModuleName(), ctx) // or that prebuilt_apex overrides other apexes (using overrides: prop) for _, overridden := range p.prebuiltCommonProperties.Overrides { - p.compatSymlinks = append(p.compatSymlinks, makeCompatSymlinks(overridden, ctx, true)...) + p.compatSymlinks = append(p.compatSymlinks, makeCompatSymlinks(overridden, ctx)...) } if p.installable() { - p.installedFile = ctx.InstallFile(p.installDir, p.installFilename, p.inputApex, p.compatSymlinks.Paths()...) + p.installedFile = ctx.InstallFile(p.installDir, p.installFilename, p.inputApex, p.compatSymlinks...) p.provenanceMetaDataFile = provenance.GenerateArtifactProvenanceMetaData(ctx, p.inputApex, p.installedFile) } } @@ -878,12 +882,7 @@ func (e *ApexExtractorProperties) prebuiltSrcs(ctx android.BaseModuleContext) [] srcs = append(srcs, *e.Set) } - var sanitizers []string - if ctx.Host() { - sanitizers = ctx.Config().SanitizeHost() - } else { - sanitizers = ctx.Config().SanitizeDevice() - } + sanitizers := ctx.Config().SanitizeDevice() if android.InList("address", sanitizers) && e.Sanitized.Address.Set != nil { srcs = append(srcs, *e.Sanitized.Address.Set) @@ -981,6 +980,7 @@ func (a *ApexSet) ApexInfoMutator(mctx android.TopDownMutatorContext) { } func (a *ApexSet) GenerateAndroidBuildActions(ctx android.ModuleContext) { + a.apexKeysPath = writeApexKeys(ctx, a) a.installFilename = a.InstallFilename() if !strings.HasSuffix(a.installFilename, imageApexSuffix) && !strings.HasSuffix(a.installFilename, imageCapexSuffix) { ctx.ModuleErrorf("filename should end in %s or %s for apex_set", imageApexSuffix, imageCapexSuffix) @@ -1008,10 +1008,10 @@ func (a *ApexSet) GenerateAndroidBuildActions(ctx android.ModuleContext) { } // in case that apex_set replaces source apex (using prefer: prop) - a.compatSymlinks = makeCompatSymlinks(a.BaseModuleName(), ctx, true) + a.compatSymlinks = makeCompatSymlinks(a.BaseModuleName(), ctx) // or that apex_set overrides other apexes (using overrides: prop) for _, overridden := range a.prebuiltCommonProperties.Overrides { - a.compatSymlinks = append(a.compatSymlinks, makeCompatSymlinks(overridden, ctx, true)...) + a.compatSymlinks = append(a.compatSymlinks, makeCompatSymlinks(overridden, ctx)...) } } diff --git a/apex/systemserver_classpath_fragment_test.go b/apex/systemserver_classpath_fragment_test.go index f94e50f4f6..40d05814e1 100644 --- a/apex/systemserver_classpath_fragment_test.go +++ b/apex/systemserver_classpath_fragment_test.go @@ -97,7 +97,7 @@ func TestSystemserverclasspathFragmentContents(t *testing.T) { ctx := result.TestContext - ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{ + ensureExactContents(t, ctx, "myapex", "android_common_myapex", []string{ "etc/classpaths/systemserverclasspath.pb", "javalib/foo.jar", "javalib/bar.jar", @@ -105,7 +105,7 @@ func TestSystemserverclasspathFragmentContents(t *testing.T) { "javalib/baz.jar", }) - java.CheckModuleDependencies(t, ctx, "myapex", "android_common_myapex_image", []string{ + java.CheckModuleDependencies(t, ctx, "myapex", "android_common_myapex", []string{ `myapex.key`, `mysystemserverclasspathfragment`, }) @@ -157,11 +157,11 @@ func TestSystemserverclasspathFragmentNoGeneratedProto(t *testing.T) { } `) - ensureExactContents(t, result.TestContext, "myapex", "android_common_myapex_image", []string{ + ensureExactContents(t, result.TestContext, "myapex", "android_common_myapex", []string{ "javalib/foo.jar", }) - java.CheckModuleDependencies(t, result.TestContext, "myapex", "android_common_myapex_image", []string{ + java.CheckModuleDependencies(t, result.TestContext, "myapex", "android_common_myapex", []string{ `myapex.key`, `mysystemserverclasspathfragment`, }) @@ -361,7 +361,7 @@ func TestSystemserverclasspathFragmentStandaloneContents(t *testing.T) { ctx := result.TestContext - ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{ + ensureExactContents(t, ctx, "myapex", "android_common_myapex", []string{ "etc/classpaths/systemserverclasspath.pb", "javalib/foo.jar", "javalib/bar.jar", diff --git a/apex/testing.go b/apex/testing.go index 69bd73e5d1..3b200f05bb 100644 --- a/apex/testing.go +++ b/apex/testing.go @@ -19,6 +19,7 @@ import "android/soong/android" var PrepareForTestWithApexBuildComponents = android.GroupFixturePreparers( android.FixtureRegisterWithContext(registerApexBuildComponents), android.FixtureRegisterWithContext(registerApexKeyBuildComponents), + android.FixtureRegisterWithContext(registerApexDepsInfoComponents), // Additional files needed in tests that disallow non-existent source files. // This includes files that are needed by all, or at least most, instances of an apex module type. android.MockFS{ diff --git a/apex/vndk.go b/apex/vndk.go index 095e89db35..26c60edc8e 100644 --- a/apex/vndk.go +++ b/apex/vndk.go @@ -80,6 +80,10 @@ func apexVndkMutator(mctx android.TopDownMutatorContext) { // config targets the 'current' VNDK (see `vndkVersion`). ab.Disable() } + if proptools.String(ab.vndkProperties.Vndk_version) != "" && + apiLevel.GreaterThanOrEqualTo(android.ApiLevelOrPanic(mctx, mctx.DeviceConfig().PlatformVndkVersion())) { + ab.Disable() + } } } @@ -103,19 +107,15 @@ func apexVndkDepsMutator(mctx android.BottomUpMutatorContext) { } } else if a, ok := mctx.Module().(*apexBundle); ok && a.vndkApex { vndkVersion := proptools.StringDefault(a.vndkProperties.Vndk_version, "current") - mctx.AddDependency(mctx.Module(), prebuiltTag, cc.VndkLibrariesTxtModules(vndkVersion)...) + mctx.AddDependency(mctx.Module(), prebuiltTag, cc.VndkLibrariesTxtModules(vndkVersion, mctx)...) } } // name is module.BaseModuleName() which is used as LOCAL_MODULE_NAME and also LOCAL_OVERRIDES_* -func makeCompatSymlinks(name string, ctx android.ModuleContext, primaryApex bool) (symlinks android.InstallPaths) { +func makeCompatSymlinks(name string, ctx android.ModuleContext) (symlinks android.InstallPaths) { // small helper to add symlink commands addSymlink := func(target string, dir android.InstallPath, linkName string) { - if primaryApex { - symlinks = append(symlinks, ctx.InstallAbsoluteSymlink(dir, linkName, target)) - } else { - symlinks = append(symlinks, dir.Join(ctx, linkName)) - } + symlinks = append(symlinks, ctx.InstallAbsoluteSymlink(dir, linkName, target)) } // TODO(b/142911355): [VNDK APEX] Fix hard-coded references to /system/lib/vndk diff --git a/apex/vndk_test.go b/apex/vndk_test.go index 21526c3eba..e2aee96e77 100644 --- a/apex/vndk_test.go +++ b/apex/vndk_test.go @@ -51,10 +51,11 @@ func TestVndkApexForVndkLite(t *testing.T) { `+vndkLibrariesTxtFiles("current"), android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { variables.DeviceVndkVersion = proptools.StringPtr("") + variables.KeepVndk = proptools.BoolPtr(true) }), ) // VNDK-Lite contains only core variants of VNDK-Sp libraries - ensureExactContents(t, ctx, "com.android.vndk.current", "android_common_image", []string{ + ensureExactContents(t, ctx, "com.android.vndk.current", "android_common", []string{ "lib/libvndksp.so", "lib/libc++.so", "lib64/libvndksp.so", @@ -109,19 +110,14 @@ func TestVndkApexUsesVendorVariant(t *testing.T) { } // VNDK APEX doesn't create apex variant - files := getFiles(t, ctx, "com.android.vndk.current", "android_common_image") + files := getFiles(t, ctx, "com.android.vndk.current", "android_common") ensureFileSrc(t, files, "lib/libfoo.so", "libfoo/android_vendor.29_arm_armv7-a-neon_shared/libfoo.so") }) t.Run("VNDK APEX gathers only vendor variants even if product variants are available", func(t *testing.T) { - ctx := testApex(t, bp, - android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { - // Now product variant is available - variables.ProductVndkVersion = proptools.StringPtr("current") - }), - ) + ctx := testApex(t, bp) - files := getFiles(t, ctx, "com.android.vndk.current", "android_common_image") + files := getFiles(t, ctx, "com.android.vndk.current", "android_common") ensureFileSrc(t, files, "lib/libfoo.so", "libfoo/android_vendor.29_arm_armv7-a-neon_shared/libfoo.so") }) @@ -133,10 +129,10 @@ func TestVndkApexUsesVendorVariant(t *testing.T) { }), ) - files := getFiles(t, ctx, "com.android.vndk.current", "android_common_image") + files := getFiles(t, ctx, "com.android.vndk.current", "android_common") ensureFileSrc(t, files, "lib/libfoo.so", "libfoo/android_vendor.29_arm_armv7-a-neon_shared/libfoo.so") - files = getFiles(t, ctx, "com.android.vndk.current", "android_common_cov_image") + files = getFiles(t, ctx, "com.android.vndk.current", "android_common_cov") ensureFileSrc(t, files, "lib/libfoo.so", "libfoo/android_vendor.29_arm_armv7-a-neon_shared_cov/libfoo.so") }) } diff --git a/bazel/aquery.go b/bazel/aquery.go index 4d39e8f555..35942bc321 100644 --- a/bazel/aquery.go +++ b/bazel/aquery.go @@ -17,6 +17,8 @@ package bazel import ( "crypto/sha256" "encoding/base64" + "encoding/json" + "errors" "fmt" "path/filepath" "reflect" @@ -35,19 +37,6 @@ type artifactId int type depsetId int type pathFragmentId int -// 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 artifactId - PathFragmentId pathFragmentId -} - -type pathFragment struct { - Id pathFragmentId - Label string - ParentId pathFragmentId -} - // KeyValuePair represents Bazel's aquery proto, KeyValuePair. type KeyValuePair struct { Key string @@ -68,37 +57,6 @@ type AqueryDepset struct { TransitiveDepSetHashes []string } -// depSetOfFiles contains relevant portions of Bazel's aquery proto, DepSetOfFiles. -// 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 depsetId - DirectArtifactIds []artifactId - TransitiveDepSetIds []depsetId -} - -// action contains relevant portions of Bazel's aquery proto, Action. -// Represents a single command line invocation in the Bazel build graph. -type action struct { - Arguments []string - EnvironmentVariables []KeyValuePair - InputDepSetIds []depsetId - Mnemonic string - OutputIds []artifactId - TemplateContent string - Substitutions []KeyValuePair - FileContents string -} - -// actionGraphContainer contains relevant portions of Bazel's aquery proto, ActionGraphContainer. -// An aquery response from Bazel contains a single ActionGraphContainer proto. -type actionGraphContainer struct { - Artifacts []artifact - Actions []action - DepSetOfFiles []depSetOfFiles - PathFragments []pathFragment -} - // BuildStatement contains information to register a build statement corresponding (one to one) // with a Bazel action from Bazel's action graph. type BuildStatement struct { @@ -116,6 +74,14 @@ type BuildStatement struct { InputDepsetHashes []string InputPaths []string FileContents string + // If ShouldRunInSbox is true, Soong will use sbox to created an isolated environment + // and run the mixed build action there + ShouldRunInSbox bool + // A list of files to add as implicit deps to the outputs of this BuildStatement. + // Unlike most properties in BuildStatement, these paths must be relative to the root of + // the whole out/ folder, instead of relative to ctx.Config().BazelContext.OutputBase() + ImplicitDeps []string + IsExecutable bool } // A helper type for aquery processing which facilitates retrieval of path IDs from their @@ -169,6 +135,21 @@ func newAqueryHandler(aqueryResult *analysis_v2_proto.ActionGraphContainer) (*aq if err != nil { return nil, err } + if artifact.IsTreeArtifact && + !strings.HasPrefix(artifactPath, "bazel-out/io_bazel_rules_go/") && + !strings.HasPrefix(artifactPath, "bazel-out/rules_java_builtin/") { + // Since we're using ninja as an executor, we can't use tree artifacts. Ninja only + // considers a file/directory "dirty" when it's mtime changes. Directories' mtimes will + // only change when a file in the directory is added/removed, but not when files in + // the directory are changed, or when files in subdirectories are changed/added/removed. + // Bazel handles this by walking the directory and generating a hash for it after the + // action runs, which we would have to do as well if we wanted to support these + // artifacts in mixed builds. + // + // However, there are some bazel built-in rules that use tree artifacts. Allow those, + // but keep in mind that they'll have incrementality issues. + return nil, fmt.Errorf("tree artifacts are currently not supported in mixed builds: " + artifactPath) + } artifactIdToPath[artifactId(artifact.Id)] = artifactPath } @@ -347,13 +328,20 @@ func AqueryBuildStatements(aqueryJsonProto []byte, eventHandler *metrics.EventHa defer eventHandler.End("build_statements") wg := sync.WaitGroup{} var errOnce sync.Once - + id2targets := make(map[uint32]string, len(aqueryProto.Targets)) + for _, t := range aqueryProto.Targets { + id2targets[t.GetId()] = t.GetLabel() + } for i, actionEntry := range aqueryProto.Actions { wg.Add(1) go func(i int, actionEntry *analysis_v2_proto.Action) { - buildStatement, aErr := aqueryHandler.actionToBuildStatement(actionEntry) - if aErr != nil { + if strings.HasPrefix(id2targets[actionEntry.TargetId], "@bazel_tools//") { + // bazel_tools are removed depsets in `populateDepsetMaps()` so skipping + // conversion to build statements as well + buildStatements[i] = nil + } else if buildStatement, aErr := aqueryHandler.actionToBuildStatement(actionEntry); aErr != nil { errOnce.Do(func() { + aErr = fmt.Errorf("%s: [%s] [%s]", aErr.Error(), actionEntry.GetMnemonic(), id2targets[actionEntry.TargetId]) err = aErr }) } else { @@ -453,8 +441,27 @@ func (a *aqueryArtifactHandler) depsetContentHashes(inputDepsetIds []uint32) ([] return hashes, nil } +// escapes the args received from aquery and creates a command string +func commandString(actionEntry *analysis_v2_proto.Action) string { + argsEscaped := make([]string, len(actionEntry.Arguments)) + for i, arg := range actionEntry.Arguments { + if arg == "" { + // If this is an empty string, add '' + // And not + // 1. (literal empty) + // 2. `''\'''\'''` (escaped version of '') + // + // If we had used (1), then this would appear as a whitespace when we strings.Join + argsEscaped[i] = "''" + } else { + argsEscaped[i] = proptools.ShellEscapeIncludingSpaces(arg) + } + } + return strings.Join(argsEscaped, " ") +} + func (a *aqueryArtifactHandler) normalActionBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) { - command := strings.Join(proptools.ShellEscapeListIncludingSpaces(actionEntry.Arguments), " ") + command := commandString(actionEntry) inputDepsetHashes, err := a.depsetContentHashes(actionEntry.InputDepSetIds) if err != nil { return nil, err @@ -472,6 +479,12 @@ func (a *aqueryArtifactHandler) normalActionBuildStatement(actionEntry *analysis Env: actionEntry.EnvironmentVariables, Mnemonic: actionEntry.Mnemonic, } + if buildStatement.Mnemonic == "GoToolchainBinaryBuild" { + // Unlike b's execution root, mixed build execution root contains a symlink to prebuilts/go + // This causes issues for `GOCACHE=$(mktemp -d) go build ...` + // To prevent this, sandbox this action in mixed builds as well + buildStatement.ShouldRunInSbox = true + } return buildStatement, nil } @@ -523,6 +536,7 @@ func (a *aqueryArtifactHandler) fileWriteActionBuildStatement(actionEntry *analy Mnemonic: actionEntry.Mnemonic, InputDepsetHashes: depsetHashes, FileContents: actionEntry.FileContents, + IsExecutable: actionEntry.IsExecutable, }, nil } @@ -548,6 +562,72 @@ func (a *aqueryArtifactHandler) symlinkTreeActionBuildStatement(actionEntry *ana }, nil } +type bazelSandwichJson struct { + Target string `json:"target"` + DependOnTarget *bool `json:"depend_on_target,omitempty"` + ImplicitDeps []string `json:"implicit_deps"` +} + +func (a *aqueryArtifactHandler) unresolvedSymlinkActionBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) { + outputPaths, depfile, err := a.getOutputPaths(actionEntry) + if err != nil { + return nil, err + } + if len(actionEntry.InputDepSetIds) != 0 || len(outputPaths) != 1 { + return nil, fmt.Errorf("expected 0 inputs and 1 output to symlink action, got: input %q, output %q", actionEntry.InputDepSetIds, outputPaths) + } + target := actionEntry.UnresolvedSymlinkTarget + if target == "" { + return nil, fmt.Errorf("expected an unresolved_symlink_target, but didn't get one") + } + if filepath.Clean(target) != target { + return nil, fmt.Errorf("expected %q, got %q", filepath.Clean(target), target) + } + if strings.HasPrefix(target, "/") { + return nil, fmt.Errorf("no absolute symlinks allowed: %s", target) + } + + out := outputPaths[0] + outDir := filepath.Dir(out) + var implicitDeps []string + if strings.HasPrefix(target, "bazel_sandwich:") { + j := bazelSandwichJson{} + err := json.Unmarshal([]byte(target[len("bazel_sandwich:"):]), &j) + if err != nil { + return nil, err + } + if proptools.BoolDefault(j.DependOnTarget, true) { + implicitDeps = append(implicitDeps, j.Target) + } + implicitDeps = append(implicitDeps, j.ImplicitDeps...) + dotDotsToReachCwd := "" + if outDir != "." { + dotDotsToReachCwd = strings.Repeat("../", strings.Count(outDir, "/")+1) + } + target = proptools.ShellEscapeIncludingSpaces(j.Target) + target = "{DOTDOTS_TO_OUTPUT_ROOT}" + dotDotsToReachCwd + target + } else { + target = proptools.ShellEscapeIncludingSpaces(target) + } + + outDir = proptools.ShellEscapeIncludingSpaces(outDir) + out = proptools.ShellEscapeIncludingSpaces(out) + // Use absolute paths, because some soong actions don't play well with relative paths (for example, `cp -d`). + command := fmt.Sprintf("mkdir -p %[1]s && rm -f %[2]s && ln -sf %[3]s %[2]s", outDir, out, target) + symlinkPaths := outputPaths[:] + + buildStatement := &BuildStatement{ + Command: command, + Depfile: depfile, + OutputPaths: outputPaths, + Env: actionEntry.EnvironmentVariables, + Mnemonic: actionEntry.Mnemonic, + SymlinkPaths: symlinkPaths, + ImplicitDeps: implicitDeps, + } + return buildStatement, nil +} + func (a *aqueryArtifactHandler) symlinkActionBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) { outputPaths, depfile, err := a.getOutputPaths(actionEntry) if err != nil { @@ -653,14 +733,16 @@ func (a *aqueryArtifactHandler) actionToBuildStatement(actionEntry *analysis_v2_ if len(actionEntry.Arguments) < 1 { return a.templateExpandActionBuildStatement(actionEntry) } - case "FileWrite", "SourceSymlinkManifest": + case "FileWrite", "SourceSymlinkManifest", "RepoMappingManifest": return a.fileWriteActionBuildStatement(actionEntry) case "SymlinkTree": return a.symlinkTreeActionBuildStatement(actionEntry) + case "UnresolvedSymlink": + return a.unresolvedSymlinkActionBuildStatement(actionEntry) } if len(actionEntry.Arguments) < 1 { - return nil, fmt.Errorf("received action with no command: [%s]", actionEntry.Mnemonic) + return nil, errors.New("received action with no command") } return a.normalActionBuildStatement(actionEntry) diff --git a/bazel/aquery_test.go b/bazel/aquery_test.go index 19a584f233..cbd27919cd 100644 --- a/bazel/aquery_test.go +++ b/bazel/aquery_test.go @@ -178,8 +178,8 @@ func TestInvalidOutputId(t *testing.T) { { "id": 2, "path_fragment_id": 2 }], "actions": [{ "target_id": 1, - "action_key": "x", - "mnemonic": "x", + "action_key": "action_x", + "mnemonic": "X", "arguments": ["touch", "foo"], "input_dep_set_ids": [1], "output_ids": [3], @@ -198,7 +198,7 @@ func TestInvalidOutputId(t *testing.T) { return } _, _, err = AqueryBuildStatements(data, &metrics.EventHandler{}) - assertError(t, err, "undefined outputId 3") + assertError(t, err, "undefined outputId 3: [X] []") } func TestInvalidInputDepsetIdFromAction(t *testing.T) { @@ -209,13 +209,17 @@ func TestInvalidInputDepsetIdFromAction(t *testing.T) { { "id": 2, "path_fragment_id": 2 }], "actions": [{ "target_id": 1, - "action_key": "x", - "mnemonic": "x", + "action_key": "action_x", + "mnemonic": "X", "arguments": ["touch", "foo"], "input_dep_set_ids": [2], "output_ids": [1], "primary_output_id": 1 }], + "targets": [{ + "id": 1, + "label": "target_x" + }], "dep_set_of_files": [ { "id": 1, "direct_artifact_ids": [1, 2] }], "path_fragments": [ @@ -229,7 +233,7 @@ func TestInvalidInputDepsetIdFromAction(t *testing.T) { return } _, _, err = AqueryBuildStatements(data, &metrics.EventHandler{}) - assertError(t, err, "undefined (not even empty) input depsetId 2") + assertError(t, err, "undefined (not even empty) input depsetId 2: [X] [target_x]") } func TestInvalidInputDepsetIdFromDepset(t *testing.T) { @@ -357,9 +361,11 @@ func TestDepfiles(t *testing.T) { actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{}) if err != nil { t.Errorf("Unexpected error %q", err) + return } if expected := 1; len(actual) != expected { t.Fatalf("Expected %d build statements, got %d", expected, len(actual)) + return } bs := actual[0] @@ -381,8 +387,8 @@ func TestMultipleDepfiles(t *testing.T) { { "id": 4, "path_fragment_id": 4 }], "actions": [{ "target_id": 1, - "action_key": "x", - "mnemonic": "x", + "action_key": "action_x", + "mnemonic": "X", "arguments": ["touch", "foo"], "input_dep_set_ids": [1], "output_ids": [2,3,4], @@ -405,7 +411,7 @@ func TestMultipleDepfiles(t *testing.T) { return } _, _, err = AqueryBuildStatements(data, &metrics.EventHandler{}) - assertError(t, err, `found multiple potential depfiles "two.d", "other.d"`) + assertError(t, err, `found multiple potential depfiles "two.d", "other.d": [X] []`) } func TestTransitiveInputDepsets(t *testing.T) { @@ -544,6 +550,7 @@ func TestSymlinkTree(t *testing.T) { actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{}) if err != nil { t.Errorf("Unexpected error %q", err) + return } assertBuildStatements(t, []*BuildStatement{ &BuildStatement{ @@ -556,7 +563,7 @@ func TestSymlinkTree(t *testing.T) { }, actual) } -func TestBazelOutRemovalFromInputDepsets(t *testing.T) { +func TestBazelToolsRemovalFromInputDepsets(t *testing.T) { const inputString = `{ "artifacts": [ { "id": 1, "path_fragment_id": 10 }, @@ -634,7 +641,55 @@ func TestBazelOutRemovalFromInputDepsets(t *testing.T) { } } -func TestBazelOutRemovalFromTransitiveInputDepsets(t *testing.T) { +func TestBazelToolsRemovalFromTargets(t *testing.T) { + const inputString = `{ + "artifacts": [{ "id": 1, "path_fragment_id": 10 }], + "targets": [ + { "id": 100, "label": "targetX" }, + { "id": 200, "label": "@bazel_tools//tool_y" } +], + "actions": [{ + "target_id": 100, + "action_key": "actionX", + "arguments": ["bogus", "command"], + "mnemonic" : "x", + "output_ids": [1] + }, { + "target_id": 200, + "action_key": "y" + }], + "path_fragments": [{ "id": 10, "label": "outputX"}] +}` + data, err := JsonToActionGraphContainer(inputString) + if err != nil { + t.Error(err) + return + } + actualBuildStatements, actualDepsets, _ := AqueryBuildStatements(data, &metrics.EventHandler{}) + if len(actualDepsets) != 0 { + t.Errorf("expected 0 depset but found %#v", actualDepsets) + return + } + expectedBuildStatement := &BuildStatement{ + Command: "bogus command", + OutputPaths: []string{"outputX"}, + Mnemonic: "x", + SymlinkPaths: []string{}, + } + buildStatementFound := false + for _, actualBuildStatement := range actualBuildStatements { + if buildStatementEquals(actualBuildStatement, expectedBuildStatement) == "" { + buildStatementFound = true + break + } + } + if !buildStatementFound { + t.Errorf("expected but missing %#v in %#v build statements", expectedBuildStatement, len(actualBuildStatements)) + return + } +} + +func TestBazelToolsRemovalFromTransitiveInputDepsets(t *testing.T) { const inputString = `{ "artifacts": [ { "id": 1, "path_fragment_id": 10 }, @@ -756,9 +811,11 @@ func TestMiddlemenAction(t *testing.T) { actualBuildStatements, actualDepsets, err := AqueryBuildStatements(data, &metrics.EventHandler{}) if err != nil { t.Errorf("Unexpected error %q", err) + return } if expected := 2; len(actualBuildStatements) != expected { t.Fatalf("Expected %d build statements, got %d %#v", expected, len(actualBuildStatements), actualBuildStatements) + return } expectedDepsetFiles := [][]string{ @@ -859,6 +916,7 @@ func TestSimpleSymlink(t *testing.T) { if err != nil { t.Errorf("Unexpected error %q", err) + return } expectedBuildStatements := []*BuildStatement{ @@ -907,6 +965,7 @@ func TestSymlinkQuotesPaths(t *testing.T) { actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{}) if err != nil { t.Errorf("Unexpected error %q", err) + return } expectedBuildStatements := []*BuildStatement{ @@ -932,7 +991,7 @@ func TestSymlinkMultipleInputs(t *testing.T) { { "id": 3, "path_fragment_id": 3 }], "actions": [{ "target_id": 1, - "action_key": "x", + "action_key": "action_x", "mnemonic": "Symlink", "input_dep_set_ids": [1], "output_ids": [3], @@ -951,7 +1010,7 @@ func TestSymlinkMultipleInputs(t *testing.T) { return } _, _, err = AqueryBuildStatements(data, &metrics.EventHandler{}) - assertError(t, err, `Expect 1 input and 1 output to symlink action, got: input ["file" "other_file"], output ["symlink"]`) + assertError(t, err, `Expect 1 input and 1 output to symlink action, got: input ["file" "other_file"], output ["symlink"]: [Symlink] []`) } func TestSymlinkMultipleOutputs(t *testing.T) { @@ -982,7 +1041,7 @@ func TestSymlinkMultipleOutputs(t *testing.T) { return } _, _, err = AqueryBuildStatements(data, &metrics.EventHandler{}) - assertError(t, err, "undefined outputId 2") + assertError(t, err, "undefined outputId 2: [Symlink] []") } func TestTemplateExpandActionSubstitutions(t *testing.T) { @@ -1017,6 +1076,7 @@ func TestTemplateExpandActionSubstitutions(t *testing.T) { actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{}) if err != nil { t.Errorf("Unexpected error %q", err) + return } expectedBuildStatements := []*BuildStatement{ @@ -1058,7 +1118,7 @@ func TestTemplateExpandActionNoOutput(t *testing.T) { return } _, _, err = AqueryBuildStatements(data, &metrics.EventHandler{}) - assertError(t, err, `Expect 1 output to template expand action, got: output []`) + assertError(t, err, `Expect 1 output to template expand action, got: output []: [TemplateExpand] []`) } func TestFileWrite(t *testing.T) { @@ -1088,6 +1148,7 @@ func TestFileWrite(t *testing.T) { actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{}) if err != nil { t.Errorf("Unexpected error %q", err) + return } assertBuildStatements(t, []*BuildStatement{ &BuildStatement{ @@ -1126,6 +1187,7 @@ func TestSourceSymlinkManifest(t *testing.T) { actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{}) if err != nil { t.Errorf("Unexpected error %q", err) + return } assertBuildStatements(t, []*BuildStatement{ &BuildStatement{ @@ -1136,6 +1198,126 @@ func TestSourceSymlinkManifest(t *testing.T) { }, actual) } +func TestUnresolvedSymlink(t *testing.T) { + const inputString = ` +{ + "artifacts": [ + { "id": 1, "path_fragment_id": 1 } + ], + "actions": [{ + "target_id": 1, + "action_key": "x", + "mnemonic": "UnresolvedSymlink", + "configuration_id": 1, + "output_ids": [1], + "primary_output_id": 1, + "execution_platform": "//build/bazel/platforms:linux_x86_64", + "unresolved_symlink_target": "symlink/target" + }], + "path_fragments": [ + { "id": 1, "label": "path/to/symlink" } + ] +} +` + data, err := JsonToActionGraphContainer(inputString) + if err != nil { + t.Error(err) + return + } + actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{}) + if err != nil { + t.Errorf("Unexpected error %q", err) + return + } + assertBuildStatements(t, []*BuildStatement{{ + Command: "mkdir -p path/to && rm -f path/to/symlink && ln -sf symlink/target path/to/symlink", + OutputPaths: []string{"path/to/symlink"}, + Mnemonic: "UnresolvedSymlink", + SymlinkPaths: []string{"path/to/symlink"}, + }}, actual) +} + +func TestUnresolvedSymlinkBazelSandwich(t *testing.T) { + const inputString = ` +{ + "artifacts": [ + { "id": 1, "path_fragment_id": 1 } + ], + "actions": [{ + "target_id": 1, + "action_key": "x", + "mnemonic": "UnresolvedSymlink", + "configuration_id": 1, + "output_ids": [1], + "primary_output_id": 1, + "execution_platform": "//build/bazel/platforms:linux_x86_64", + "unresolved_symlink_target": "bazel_sandwich:{\"target\":\"target/product/emulator_x86_64/system\"}" + }], + "path_fragments": [ + { "id": 1, "label": "path/to/symlink" } + ] +} +` + data, err := JsonToActionGraphContainer(inputString) + if err != nil { + t.Error(err) + return + } + actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{}) + if err != nil { + t.Errorf("Unexpected error %q", err) + return + } + assertBuildStatements(t, []*BuildStatement{{ + Command: "mkdir -p path/to && rm -f path/to/symlink && ln -sf {DOTDOTS_TO_OUTPUT_ROOT}../../target/product/emulator_x86_64/system path/to/symlink", + OutputPaths: []string{"path/to/symlink"}, + Mnemonic: "UnresolvedSymlink", + SymlinkPaths: []string{"path/to/symlink"}, + ImplicitDeps: []string{"target/product/emulator_x86_64/system"}, + }}, actual) +} + +func TestUnresolvedSymlinkBazelSandwichWithAlternativeDeps(t *testing.T) { + const inputString = ` +{ + "artifacts": [ + { "id": 1, "path_fragment_id": 1 } + ], + "actions": [{ + "target_id": 1, + "action_key": "x", + "mnemonic": "UnresolvedSymlink", + "configuration_id": 1, + "output_ids": [1], + "primary_output_id": 1, + "execution_platform": "//build/bazel/platforms:linux_x86_64", + "unresolved_symlink_target": "bazel_sandwich:{\"depend_on_target\":false,\"implicit_deps\":[\"target/product/emulator_x86_64/obj/PACKAGING/systemimage_intermediates/staging_dir.stamp\"],\"target\":\"target/product/emulator_x86_64/system\"}" + }], + "path_fragments": [ + { "id": 1, "label": "path/to/symlink" } + ] +} +` + data, err := JsonToActionGraphContainer(inputString) + if err != nil { + t.Error(err) + return + } + actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{}) + if err != nil { + t.Errorf("Unexpected error %q", err) + return + } + assertBuildStatements(t, []*BuildStatement{{ + Command: "mkdir -p path/to && rm -f path/to/symlink && ln -sf {DOTDOTS_TO_OUTPUT_ROOT}../../target/product/emulator_x86_64/system path/to/symlink", + OutputPaths: []string{"path/to/symlink"}, + Mnemonic: "UnresolvedSymlink", + SymlinkPaths: []string{"path/to/symlink"}, + // Note that the target of the symlink, target/product/emulator_x86_64/system, is not listed here + ImplicitDeps: []string{"target/product/emulator_x86_64/obj/PACKAGING/systemimage_intermediates/staging_dir.stamp"}, + }}, actual) +} + func assertError(t *testing.T, err error, expected string) { t.Helper() if err == nil { @@ -1201,6 +1383,9 @@ func buildStatementEquals(first *BuildStatement, second *BuildStatement) string if !reflect.DeepEqual(sortedStrings(first.SymlinkPaths), sortedStrings(second.SymlinkPaths)) { return "SymlinkPaths" } + if !reflect.DeepEqual(sortedStrings(first.ImplicitDeps), sortedStrings(second.ImplicitDeps)) { + return "ImplicitDeps" + } if first.Depfile != second.Depfile { return "Depfile" } diff --git a/bazel/bazel_proxy.go b/bazel/bazel_proxy.go index d7f5e64649..229818da00 100644 --- a/bazel/bazel_proxy.go +++ b/bazel/bazel_proxy.go @@ -26,10 +26,11 @@ import ( "time" ) -// Logs fatal events of ProxyServer. +// Logs events of ProxyServer. type ServerLogger interface { Fatal(v ...interface{}) Fatalf(format string, v ...interface{}) + Println(v ...interface{}) } // CmdRequest is a request to the Bazel Proxy server. @@ -71,9 +72,10 @@ type ProxyClient struct { // The ProxyServer will only live as long as soong_ui does; the // underlying Bazel server will live past the duration of the build. type ProxyServer struct { - logger ServerLogger - outDir string - workspaceDir string + logger ServerLogger + outDir string + workspaceDir string + bazeliskVersion string // The server goroutine will listen on this channel and stop handling requests // once it is written to. done chan struct{} @@ -119,13 +121,36 @@ func (b *ProxyClient) IssueCommand(req CmdRequest) (CmdResponse, error) { } // NewProxyServer is a constructor for a ProxyServer. -func NewProxyServer(logger ServerLogger, outDir string, workspaceDir string) *ProxyServer { +func NewProxyServer(logger ServerLogger, outDir string, workspaceDir string, bazeliskVersion string) *ProxyServer { + if len(bazeliskVersion) > 0 { + logger.Println("** Using Bazelisk for this build, due to env var USE_BAZEL_VERSION=" + bazeliskVersion + " **") + } + return &ProxyServer{ - logger: logger, - outDir: outDir, - workspaceDir: workspaceDir, - done: make(chan struct{}), + logger: logger, + outDir: outDir, + workspaceDir: workspaceDir, + done: make(chan struct{}), + bazeliskVersion: bazeliskVersion, + } +} + +func ExecBazel(bazelPath string, workspaceDir string, request CmdRequest) (stdout []byte, stderr []byte, cmdErr error) { + bazelCmd := exec.Command(bazelPath, request.Argv...) + bazelCmd.Dir = workspaceDir + bazelCmd.Env = request.Env + + stderrBuffer := &bytes.Buffer{} + bazelCmd.Stderr = stderrBuffer + + if output, err := bazelCmd.Output(); err != nil { + cmdErr = fmt.Errorf("bazel command failed: %s\n---command---\n%s\n---env---\n%s\n---stderr---\n%s---", + err, bazelCmd, strings.Join(bazelCmd.Env, "\n"), stderrBuffer) + } else { + stdout = output } + stderr = stderrBuffer.Bytes() + return } func (b *ProxyServer) handleRequest(conn net.Conn) error { @@ -137,23 +162,16 @@ func (b *ProxyServer) handleRequest(conn net.Conn) error { return fmt.Errorf("Error decoding request: %s", err) } - bazelCmd := exec.Command("./build/bazel/bin/bazel", req.Argv...) - bazelCmd.Dir = b.workspaceDir - bazelCmd.Env = req.Env - - stderr := &bytes.Buffer{} - bazelCmd.Stderr = stderr - var stdout string - var bazelErrString string - - if output, err := bazelCmd.Output(); err != nil { - bazelErrString = fmt.Sprintf("bazel command failed: %s\n---command---\n%s\n---env---\n%s\n---stderr---\n%s---", - err, bazelCmd, strings.Join(bazelCmd.Env, "\n"), stderr) - } else { - stdout = string(output) + if len(b.bazeliskVersion) > 0 { + req.Env = append(req.Env, "USE_BAZEL_VERSION="+b.bazeliskVersion) + } + stdout, stderr, cmdErr := ExecBazel("./build/bazel/bin/bazel", b.workspaceDir, req) + errorString := "" + if cmdErr != nil { + errorString = cmdErr.Error() } - resp := CmdResponse{stdout, string(stderr.Bytes()), bazelErrString} + resp := CmdResponse{string(stdout), string(stderr), errorString} enc := gob.NewEncoder(conn) if err := enc.Encode(&resp); err != nil { return fmt.Errorf("Error encoding response: %s", err) diff --git a/bazel/configurability.go b/bazel/configurability.go index 46802565c2..2c9a5364a3 100644 --- a/bazel/configurability.go +++ b/bazel/configurability.go @@ -31,15 +31,15 @@ const ( // OsType names in arch.go OsAndroid = "android" - osDarwin = "darwin" - osLinux = "linux_glibc" + OsDarwin = "darwin" + OsLinux = "linux_glibc" osLinuxMusl = "linux_musl" osLinuxBionic = "linux_bionic" - osWindows = "windows" + OsWindows = "windows" // Targets in arch.go osArchAndroidArm = "android_arm" - osArchAndroidArm64 = "android_arm64" + OsArchAndroidArm64 = "android_arm64" osArchAndroidRiscv64 = "android_riscv64" osArchAndroidX86 = "android_x86" osArchAndroidX86_64 = "android_x86_64" @@ -67,13 +67,18 @@ const ( ConditionsDefaultSelectKey = "//conditions:default" - productVariableBazelPackage = "//build/bazel/product_variables" + productVariableBazelPackage = "//build/bazel/product_config/config_settings" - AndroidAndInApex = "android-in_apex" - AndroidAndNonApex = "android-non_apex" + AndroidAndInApex = "android-in_apex" + AndroidPlatform = "system" + Unbundled_app = "unbundled_app" InApex = "in_apex" NonApex = "non_apex" + + ErrorproneDisabled = "errorprone_disabled" + // TODO: b/294868620 - Remove when completing the bug + SanitizersEnabled = "sanitizers_enabled" ) func PowerSetWithoutEmptySet[T any](items []T) [][]T { @@ -128,7 +133,7 @@ func createPlatformArchMap() map[string]string { } result := make(map[string]string) for arch, allFeatures := range archFeatures { - result[arch] = "//build/bazel/platforms/arch:" + arch + result[arch] = "//build/bazel_common_rules/platforms/arch:" + arch // Sometimes we want to select on multiple features being active, so // add the power set of all possible features to the map. More details // in android.ModuleBase.GetArchVariantProperties @@ -155,33 +160,33 @@ var ( // A map of target operating systems to the Bazel label of the // constraint_value for the @platforms//os:os constraint_setting platformOsMap = map[string]string{ - OsAndroid: "//build/bazel/platforms/os:android", - osDarwin: "//build/bazel/platforms/os:darwin", - osLinux: "//build/bazel/platforms/os:linux_glibc", - osLinuxMusl: "//build/bazel/platforms/os:linux_musl", - osLinuxBionic: "//build/bazel/platforms/os:linux_bionic", - osWindows: "//build/bazel/platforms/os:windows", + OsAndroid: "//build/bazel_common_rules/platforms/os:android", + OsDarwin: "//build/bazel_common_rules/platforms/os:darwin", + OsLinux: "//build/bazel_common_rules/platforms/os:linux_glibc", + osLinuxMusl: "//build/bazel_common_rules/platforms/os:linux_musl", + osLinuxBionic: "//build/bazel_common_rules/platforms/os:linux_bionic", + OsWindows: "//build/bazel_common_rules/platforms/os:windows", ConditionsDefaultConfigKey: ConditionsDefaultSelectKey, // The default condition of an os select map. } platformOsArchMap = map[string]string{ - osArchAndroidArm: "//build/bazel/platforms/os_arch:android_arm", - osArchAndroidArm64: "//build/bazel/platforms/os_arch:android_arm64", - osArchAndroidRiscv64: "//build/bazel/platforms/os_arch:android_riscv64", - osArchAndroidX86: "//build/bazel/platforms/os_arch:android_x86", - osArchAndroidX86_64: "//build/bazel/platforms/os_arch:android_x86_64", - osArchDarwinArm64: "//build/bazel/platforms/os_arch:darwin_arm64", - osArchDarwinX86_64: "//build/bazel/platforms/os_arch:darwin_x86_64", - osArchLinuxX86: "//build/bazel/platforms/os_arch:linux_glibc_x86", - osArchLinuxX86_64: "//build/bazel/platforms/os_arch:linux_glibc_x86_64", - osArchLinuxMuslArm: "//build/bazel/platforms/os_arch:linux_musl_arm", - osArchLinuxMuslArm64: "//build/bazel/platforms/os_arch:linux_musl_arm64", - osArchLinuxMuslX86: "//build/bazel/platforms/os_arch:linux_musl_x86", - osArchLinuxMuslX86_64: "//build/bazel/platforms/os_arch:linux_musl_x86_64", - osArchLinuxBionicArm64: "//build/bazel/platforms/os_arch:linux_bionic_arm64", - osArchLinuxBionicX86_64: "//build/bazel/platforms/os_arch:linux_bionic_x86_64", - osArchWindowsX86: "//build/bazel/platforms/os_arch:windows_x86", - osArchWindowsX86_64: "//build/bazel/platforms/os_arch:windows_x86_64", + osArchAndroidArm: "//build/bazel_common_rules/platforms/os_arch:android_arm", + OsArchAndroidArm64: "//build/bazel_common_rules/platforms/os_arch:android_arm64", + osArchAndroidRiscv64: "//build/bazel_common_rules/platforms/os_arch:android_riscv64", + osArchAndroidX86: "//build/bazel_common_rules/platforms/os_arch:android_x86", + osArchAndroidX86_64: "//build/bazel_common_rules/platforms/os_arch:android_x86_64", + osArchDarwinArm64: "//build/bazel_common_rules/platforms/os_arch:darwin_arm64", + osArchDarwinX86_64: "//build/bazel_common_rules/platforms/os_arch:darwin_x86_64", + osArchLinuxX86: "//build/bazel_common_rules/platforms/os_arch:linux_glibc_x86", + osArchLinuxX86_64: "//build/bazel_common_rules/platforms/os_arch:linux_glibc_x86_64", + osArchLinuxMuslArm: "//build/bazel_common_rules/platforms/os_arch:linux_musl_arm", + osArchLinuxMuslArm64: "//build/bazel_common_rules/platforms/os_arch:linux_musl_arm64", + osArchLinuxMuslX86: "//build/bazel_common_rules/platforms/os_arch:linux_musl_x86", + osArchLinuxMuslX86_64: "//build/bazel_common_rules/platforms/os_arch:linux_musl_x86_64", + osArchLinuxBionicArm64: "//build/bazel_common_rules/platforms/os_arch:linux_bionic_arm64", + osArchLinuxBionicX86_64: "//build/bazel_common_rules/platforms/os_arch:linux_bionic_x86_64", + osArchWindowsX86: "//build/bazel_common_rules/platforms/os_arch:windows_x86", + osArchWindowsX86_64: "//build/bazel_common_rules/platforms/os_arch:windows_x86_64", ConditionsDefaultConfigKey: ConditionsDefaultSelectKey, // The default condition of an os select map. } @@ -192,22 +197,23 @@ var ( // in a cyclic dependency. osToArchMap = map[string][]string{ OsAndroid: {archArm, archArm64, archRiscv64, archX86, archX86_64}, - osLinux: {archX86, archX86_64}, + OsLinux: {archX86, archX86_64}, osLinuxMusl: {archX86, archX86_64}, - osDarwin: {archArm64, archX86_64}, + OsDarwin: {archArm64, archX86_64}, osLinuxBionic: {archArm64, archX86_64}, // TODO(cparsons): According to arch.go, this should contain archArm, archArm64, as well. - osWindows: {archX86, archX86_64}, + OsWindows: {archX86, archX86_64}, } osAndInApexMap = map[string]string{ AndroidAndInApex: "//build/bazel/rules/apex:android-in_apex", - AndroidAndNonApex: "//build/bazel/rules/apex:android-non_apex", - osDarwin: "//build/bazel/platforms/os:darwin", - osLinux: "//build/bazel/platforms/os:linux_glibc", - osLinuxMusl: "//build/bazel/platforms/os:linux_musl", - osLinuxBionic: "//build/bazel/platforms/os:linux_bionic", - osWindows: "//build/bazel/platforms/os:windows", + AndroidPlatform: "//build/bazel/rules/apex:system", + Unbundled_app: "//build/bazel/rules/apex:unbundled_app", + OsDarwin: "//build/bazel_common_rules/platforms/os:darwin", + OsLinux: "//build/bazel_common_rules/platforms/os:linux_glibc", + osLinuxMusl: "//build/bazel_common_rules/platforms/os:linux_musl", + osLinuxBionic: "//build/bazel_common_rules/platforms/os:linux_bionic", + OsWindows: "//build/bazel_common_rules/platforms/os:windows", ConditionsDefaultConfigKey: ConditionsDefaultSelectKey, } @@ -216,6 +222,17 @@ var ( NonApex: "//build/bazel/rules/apex:non_apex", ConditionsDefaultConfigKey: ConditionsDefaultSelectKey, } + + errorProneMap = map[string]string{ + ErrorproneDisabled: "//build/bazel/rules/java/errorprone:errorprone_globally_disabled", + ConditionsDefaultConfigKey: ConditionsDefaultSelectKey, + } + + // TODO: b/294868620 - Remove when completing the bug + sanitizersEnabledMap = map[string]string{ + SanitizersEnabled: "//build/bazel/rules/cc:sanitizers_enabled", + ConditionsDefaultConfigKey: ConditionsDefaultSelectKey, + } ) // basic configuration types @@ -229,6 +246,9 @@ const ( productVariables osAndInApex inApex + errorProneDisabled + // TODO: b/294868620 - Remove when completing the bug + sanitizersEnabled ) func osArchString(os string, arch string) string { @@ -237,13 +257,16 @@ func osArchString(os string, arch string) string { func (ct configurationType) String() string { return map[configurationType]string{ - noConfig: "no_config", - arch: "arch", - os: "os", - osArch: "arch_os", - productVariables: "product_variables", - osAndInApex: "os_in_apex", - inApex: "in_apex", + noConfig: "no_config", + arch: "arch", + os: "os", + osArch: "arch_os", + productVariables: "product_variables", + osAndInApex: "os_in_apex", + inApex: "in_apex", + errorProneDisabled: "errorprone_disabled", + // TODO: b/294868620 - Remove when completing the bug + sanitizersEnabled: "sanitizers_enabled", }[ct] } @@ -268,13 +291,21 @@ func (ct configurationType) validateConfig(config string) { case productVariables: // do nothing case osAndInApex: - if _, ok := osAndInApexMap[config]; !ok { - panic(fmt.Errorf("Unknown os+in_apex config: %s", config)) - } + // do nothing + // this axis can contain additional per-apex keys case inApex: if _, ok := inApexMap[config]; !ok { panic(fmt.Errorf("Unknown in_apex config: %s", config)) } + case errorProneDisabled: + if _, ok := errorProneMap[config]; !ok { + panic(fmt.Errorf("Unknown errorprone config: %s", config)) + } + // TODO: b/294868620 - Remove when completing the bug + case sanitizersEnabled: + if _, ok := sanitizersEnabledMap[config]; !ok { + panic(fmt.Errorf("Unknown sanitizers_enabled config: %s", config)) + } default: panic(fmt.Errorf("Unrecognized ConfigurationType %d", ct)) } @@ -293,15 +324,22 @@ func (ca ConfigurationAxis) SelectKey(config string) string { case osArch: return platformOsArchMap[config] case productVariables: - if strings.HasSuffix(config, ConditionsDefaultConfigKey) { - // e.g. "acme__feature1__conditions_default" or "android__board__conditions_default" + if config == ConditionsDefaultConfigKey { return ConditionsDefaultSelectKey } return fmt.Sprintf("%s:%s", productVariableBazelPackage, config) case osAndInApex: - return osAndInApexMap[config] + if ret, exists := osAndInApexMap[config]; exists { + return ret + } + return config case inApex: return inApexMap[config] + case errorProneDisabled: + return errorProneMap[config] + // TODO: b/294868620 - Remove when completing the bug + case sanitizersEnabled: + return sanitizersEnabledMap[config] default: panic(fmt.Errorf("Unrecognized ConfigurationType %d", ca.configurationType)) } @@ -320,14 +358,19 @@ var ( OsAndInApexAxis = ConfigurationAxis{configurationType: osAndInApex} // An axis for in_apex-specific configurations InApexAxis = ConfigurationAxis{configurationType: inApex} + + ErrorProneAxis = ConfigurationAxis{configurationType: errorProneDisabled} + + // TODO: b/294868620 - Remove when completing the bug + SanitizersEnabledAxis = ConfigurationAxis{configurationType: sanitizersEnabled} ) // ProductVariableConfigurationAxis returns an axis for the given product variable -func ProductVariableConfigurationAxis(variable string, outerAxis ConfigurationAxis) ConfigurationAxis { +func ProductVariableConfigurationAxis(archVariant bool, variable string) ConfigurationAxis { return ConfigurationAxis{ configurationType: productVariables, subType: variable, - outerAxisType: outerAxis.configurationType, + archVariant: archVariant, } } @@ -338,8 +381,8 @@ type ConfigurationAxis struct { // some configuration types (e.g. productVariables) have multiple independent axes, subType helps // distinguish between them without needing to list all 17 product variables. subType string - // used to keep track of which product variables are arch variant - outerAxisType configurationType + + archVariant bool } func (ca *ConfigurationAxis) less(other ConfigurationAxis) bool { diff --git a/bazel/cquery/request_type.go b/bazel/cquery/request_type.go index 6a3b3c82c7..791c6bc230 100644 --- a/bazel/cquery/request_type.go +++ b/bazel/cquery/request_type.go @@ -8,10 +8,10 @@ import ( var ( GetOutputFiles = &getOutputFilesRequestType{} - GetPythonBinary = &getPythonBinaryRequestType{} GetCcInfo = &getCcInfoType{} GetApexInfo = &getApexInfoType{} GetCcUnstrippedInfo = &getCcUnstrippedInfoType{} + GetPrebuiltFileInfo = &getPrebuiltFileInfo{} ) type CcAndroidMkInfo struct { @@ -45,8 +45,6 @@ type CcInfo struct { type getOutputFilesRequestType struct{} -type getPythonBinaryRequestType struct{} - // Name returns a string name for this request type. Such request type names must be unique, // and must only consist of alphanumeric characters. func (g getOutputFilesRequestType) Name() string { @@ -72,31 +70,6 @@ func (g getOutputFilesRequestType) ParseResult(rawString string) []string { return splitOrEmpty(rawString, ", ") } -// Name returns a string name for this request type. Such request type names must be unique, -// and must only consist of alphanumeric characters. -func (g getPythonBinaryRequestType) Name() string { - return "getPythonBinary" -} - -// StarlarkFunctionBody returns a starlark function body to process this request type. -// The returned string is the body of a Starlark function which obtains -// all request-relevant information about a target and returns a string containing -// this information. -// The function should have the following properties: -// - The arguments are `target` (a configured target) and `id_string` (the label + configuration). -// - The return value must be a string. -// - The function body should not be indented outside of its own scope. -func (g getPythonBinaryRequestType) StarlarkFunctionBody() string { - return "return providers(target)['FilesToRunProvider'].executable.path" -} - -// ParseResult returns a value obtained by parsing the result of the request's Starlark function. -// The given rawString must correspond to the string output which was created by evaluating the -// Starlark given in StarlarkFunctionBody. -func (g getPythonBinaryRequestType) ParseResult(rawString string) string { - return rawString -} - type getCcInfoType struct{} // Name returns a string name for this request type. Such request type names must be unique, @@ -403,3 +376,51 @@ func parseJson(jsonString string, info interface{}) error { } return nil } + +type getPrebuiltFileInfo struct{} + +// Name returns a string name for this request type. Such request type names must be unique, +// and must only consist of alphanumeric characters. +func (g getPrebuiltFileInfo) Name() string { + return "getPrebuiltFileInfo" +} + +// StarlarkFunctionBody returns a starlark function body to process this request type. +// The returned string is the body of a Starlark function which obtains +// all request-relevant information about a target and returns a string containing +// this information. +// The function should have the following properties: +// - The arguments are `target` (a configured target) and `id_string` (the label + configuration). +// - The return value must be a string. +// - The function body should not be indented outside of its own scope. +func (g getPrebuiltFileInfo) StarlarkFunctionBody() string { + return ` +p = providers(target) +prebuilt_file_info = p.get("//build/bazel/rules:prebuilt_file.bzl%PrebuiltFileInfo") +if not prebuilt_file_info: + fail("%s did not provide PrebuiltFileInfo" % id_string) + +return json.encode({ + "Src": prebuilt_file_info.src.path, + "Dir": prebuilt_file_info.dir, + "Filename": prebuilt_file_info.filename, + "Installable": prebuilt_file_info.installable, +})` +} + +type PrebuiltFileInfo struct { + // TODO: b/207489266 - Fully support all properties in prebuilt_file + Src string + Dir string + Filename string + Installable bool +} + +// ParseResult returns a value obtained by parsing the result of the request's Starlark function. +// The given rawString must correspond to the string output which was created by evaluating the +// Starlark given in StarlarkFunctionBody. +func (g getPrebuiltFileInfo) ParseResult(rawString string) (PrebuiltFileInfo, error) { + var info PrebuiltFileInfo + err := parseJson(rawString, &info) + return info, err +} diff --git a/bazel/cquery/request_type_test.go b/bazel/cquery/request_type_test.go index 7003ce193d..e772bb7d60 100644 --- a/bazel/cquery/request_type_test.go +++ b/bazel/cquery/request_type_test.go @@ -40,34 +40,6 @@ func TestGetOutputFilesParseResults(t *testing.T) { } } -func TestGetPythonBinaryParseResults(t *testing.T) { - t.Parallel() - testCases := []struct { - description string - input string - expectedOutput string - }{ - { - description: "no result", - input: "", - expectedOutput: "", - }, - { - description: "one result", - input: "test", - expectedOutput: "test", - }, - } - for _, tc := range testCases { - t.Run(tc.description, func(t *testing.T) { - actualOutput := GetPythonBinary.ParseResult(tc.input) - if !reflect.DeepEqual(tc.expectedOutput, actualOutput) { - t.Errorf("expected %#v != actual %#v", tc.expectedOutput, actualOutput) - } - }) - } -} - func TestGetCcInfoParseResults(t *testing.T) { t.Parallel() testCases := []struct { diff --git a/bazel/properties.go b/bazel/properties.go index 40d0ba37a2..9c63bc04b2 100644 --- a/bazel/properties.go +++ b/bazel/properties.go @@ -194,14 +194,7 @@ func (ll *LabelList) Partition(predicate func(label Label) bool) (LabelList, Lab // UniqueSortedBazelLabels takes a []Label and deduplicates the labels, and returns // the slice in a sorted order. func UniqueSortedBazelLabels(originalLabels []Label) []Label { - uniqueLabelsSet := make(map[Label]bool) - for _, l := range originalLabels { - uniqueLabelsSet[l] = true - } - var uniqueLabels []Label - for l, _ := range uniqueLabelsSet { - uniqueLabels = append(uniqueLabels, l) - } + uniqueLabels := FirstUniqueBazelLabels(originalLabels) sort.SliceStable(uniqueLabels, func(i, j int) bool { return uniqueLabels[i].Label < uniqueLabels[j].Label }) @@ -210,13 +203,13 @@ func UniqueSortedBazelLabels(originalLabels []Label) []Label { func FirstUniqueBazelLabels(originalLabels []Label) []Label { var labels []Label - found := make(map[Label]bool, len(originalLabels)) + found := make(map[string]bool, len(originalLabels)) for _, l := range originalLabels { - if _, ok := found[l]; ok { + if _, ok := found[l.Label]; ok { continue } labels = append(labels, l) - found[l] = true + found[l.Label] = true } return labels } @@ -288,6 +281,41 @@ func SubtractBazelLabelList(haystack LabelList, needle LabelList) LabelList { return result } +// FirstUniqueBazelLabelListAttribute takes a LabelListAttribute and makes the LabelList for +// each axis/configuration by keeping the first instance of a Label and omitting all subsequent +// repetitions. +func FirstUniqueBazelLabelListAttribute(attr LabelListAttribute) LabelListAttribute { + var result LabelListAttribute + result.Value = FirstUniqueBazelLabelList(attr.Value) + if attr.HasConfigurableValues() { + result.ConfigurableValues = make(configurableLabelLists) + } + for axis, configToLabels := range attr.ConfigurableValues { + for c, l := range configToLabels { + result.SetSelectValue(axis, c, FirstUniqueBazelLabelList(l)) + } + } + + return result +} + +// SubtractBazelLabelListAttribute subtract needle from haystack for LabelList in each +// axis/configuration. +func SubtractBazelLabelListAttribute(haystack LabelListAttribute, needle LabelListAttribute) LabelListAttribute { + var result LabelListAttribute + result.Value = SubtractBazelLabelList(haystack.Value, needle.Value) + if haystack.HasConfigurableValues() { + result.ConfigurableValues = make(configurableLabelLists) + } + for axis, configToLabels := range haystack.ConfigurableValues { + for haystackConfig, haystackLabels := range configToLabels { + result.SetSelectValue(axis, haystackConfig, SubtractBazelLabelList(haystackLabels, needle.SelectValue(axis, haystackConfig))) + } + } + + return result +} + type Attribute interface { HasConfigurableValues() bool } @@ -334,7 +362,7 @@ func (la *LabelAttribute) Collapse() error { if containsArch { allProductVariablesAreArchVariant := true for k := range la.ConfigurableValues { - if k.configurationType == productVariables && k.outerAxisType != arch { + if k.configurationType == productVariables && !k.archVariant { allProductVariablesAreArchVariant = false } } @@ -398,7 +426,7 @@ func (la *LabelAttribute) SetSelectValue(axis ConfigurationAxis, config string, switch axis.configurationType { case noConfig: la.Value = &value - case arch, os, osArch, productVariables, osAndInApex: + case arch, os, osArch, productVariables, osAndInApex, sanitizersEnabled: if la.ConfigurableValues == nil { la.ConfigurableValues = make(configurableLabels) } @@ -414,7 +442,7 @@ func (la *LabelAttribute) SelectValue(axis ConfigurationAxis, config string) *La switch axis.configurationType { case noConfig: return la.Value - case arch, os, osArch, productVariables, osAndInApex: + case arch, os, osArch, productVariables, osAndInApex, sanitizersEnabled: return la.ConfigurableValues[axis][config] default: panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis)) @@ -484,7 +512,7 @@ func (ba *BoolAttribute) SetSelectValue(axis ConfigurationAxis, config string, v switch axis.configurationType { case noConfig: ba.Value = value - case arch, os, osArch, productVariables, osAndInApex: + case arch, os, osArch, productVariables, osAndInApex, sanitizersEnabled: if ba.ConfigurableValues == nil { ba.ConfigurableValues = make(configurableBools) } @@ -631,7 +659,7 @@ func (ba BoolAttribute) SelectValue(axis ConfigurationAxis, config string) *bool switch axis.configurationType { case noConfig: return ba.Value - case arch, os, osArch, productVariables, osAndInApex: + case arch, os, osArch, productVariables, osAndInApex, sanitizersEnabled: if v, ok := ba.ConfigurableValues[axis][config]; ok { return &v } else { @@ -766,7 +794,7 @@ func (lla *LabelListAttribute) SetSelectValue(axis ConfigurationAxis, config str switch axis.configurationType { case noConfig: lla.Value = list - case arch, os, osArch, productVariables, osAndInApex, inApex: + case arch, os, osArch, productVariables, osAndInApex, inApex, errorProneDisabled, sanitizersEnabled: if lla.ConfigurableValues == nil { lla.ConfigurableValues = make(configurableLabelLists) } @@ -782,7 +810,7 @@ func (lla *LabelListAttribute) SelectValue(axis ConfigurationAxis, config string switch axis.configurationType { case noConfig: return lla.Value - case arch, os, osArch, productVariables, osAndInApex, inApex: + case arch, os, osArch, productVariables, osAndInApex, inApex, errorProneDisabled, sanitizersEnabled: return lla.ConfigurableValues[axis][config] default: panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis)) @@ -1140,7 +1168,7 @@ func (sa *StringAttribute) SetSelectValue(axis ConfigurationAxis, config string, switch axis.configurationType { case noConfig: sa.Value = str - case arch, os, osArch, productVariables: + case arch, os, osArch, productVariables, sanitizersEnabled: if sa.ConfigurableValues == nil { sa.ConfigurableValues = make(configurableStrings) } @@ -1156,7 +1184,7 @@ func (sa *StringAttribute) SelectValue(axis ConfigurationAxis, config string) *s switch axis.configurationType { case noConfig: return sa.Value - case arch, os, osArch, productVariables: + case arch, os, osArch, productVariables, sanitizersEnabled: if v, ok := sa.ConfigurableValues[axis][config]; ok { return v } else { @@ -1346,7 +1374,7 @@ func (sla *StringListAttribute) SetSelectValue(axis ConfigurationAxis, config st switch axis.configurationType { case noConfig: sla.Value = list - case arch, os, osArch, productVariables, osAndInApex: + case arch, os, osArch, productVariables, osAndInApex, errorProneDisabled, sanitizersEnabled: if sla.ConfigurableValues == nil { sla.ConfigurableValues = make(configurableStringLists) } @@ -1362,7 +1390,7 @@ func (sla *StringListAttribute) SelectValue(axis ConfigurationAxis, config strin switch axis.configurationType { case noConfig: return sla.Value - case arch, os, osArch, productVariables, osAndInApex: + case arch, os, osArch, productVariables, osAndInApex, errorProneDisabled, sanitizersEnabled: return sla.ConfigurableValues[axis][config] default: panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis)) @@ -1424,3 +1452,16 @@ func TryVariableSubstitution(s string, productVariable string) (string, bool) { sub := productVariableSubstitutionPattern.ReplaceAllString(s, "$("+productVariable+")") return sub, s != sub } + +// StringMapAttribute is a map of strings. +// The use case for this is storing the flag_values in a config_setting object. +// Bazel rules do not support map attributes, and this should NOT be used in Bazel rules. +type StringMapAttribute map[string]string + +// ConfigSettingAttributes stores the keys of a config_setting object. +type ConfigSettingAttributes struct { + // Each key in Flag_values is a label to a custom string_setting + Flag_values StringMapAttribute + // Each element in Constraint_values is a label to a constraint_value + Constraint_values LabelListAttribute +} diff --git a/bazel/properties_test.go b/bazel/properties_test.go index cf03eb5f61..751cb8b307 100644 --- a/bazel/properties_test.go +++ b/bazel/properties_test.go @@ -33,8 +33,12 @@ func TestUniqueBazelLabels(t *testing.T) { {Label: "b"}, {Label: "a"}, {Label: "c"}, + // namespaces + {Label: "//foo:bar", OriginalModuleName: "bar"}, // when referenced from foo namespace + {Label: "//foo:bar", OriginalModuleName: "//foo:bar"}, // when reference from root namespace }, expectedUniqueLabels: []Label{ + {Label: "//foo:bar", OriginalModuleName: "bar"}, {Label: "a"}, {Label: "b"}, {Label: "c"}, @@ -125,6 +129,63 @@ func TestSubtractBazelLabelList(t *testing.T) { } } } + +func TestSubtractBazelLabelListAttribute(t *testing.T) { + testCases := []struct { + haystack LabelListAttribute + needle LabelListAttribute + expected LabelListAttribute + }{ + { + haystack: LabelListAttribute{ + Value: makeLabelList( + []string{"a", "b", "a", "c"}, + []string{"x", "x", "y", "z"}, + ), + ConfigurableValues: configurableLabelLists{ + ArchConfigurationAxis: labelListSelectValues{ + "arm": makeLabelList([]string{"arm_1", "arm_2"}, []string{}), + "x86": makeLabelList([]string{"x86_3", "x86_4", "x86_5"}, []string{"x86_5"}), + }, + }, + }, + needle: LabelListAttribute{ + Value: makeLabelList( + []string{"d", "a"}, + []string{"x", "y2", "z2"}, + ), + ConfigurableValues: configurableLabelLists{ + ArchConfigurationAxis: labelListSelectValues{ + "arm": makeLabelList([]string{"arm_1", "arm_3"}, []string{}), + "x86": makeLabelList([]string{"x86_3", "x86_4"}, []string{"x86_6"}), + }, + }, + }, + expected: LabelListAttribute{ + Value: makeLabelList( + []string{"b", "c"}, + []string{"x", "x", "y", "z"}, + ), + ConfigurableValues: configurableLabelLists{ + ArchConfigurationAxis: labelListSelectValues{ + "arm": makeLabelList([]string{"arm_2"}, []string{}), + "x86": makeLabelList([]string{"x86_5"}, []string{"x86_5"}), + }, + }, + ForceSpecifyEmptyList: false, + EmitEmptyList: false, + Prepend: false, + }, + }, + } + for _, tc := range testCases { + got := SubtractBazelLabelListAttribute(tc.haystack, tc.needle) + if !reflect.DeepEqual(tc.expected, got) { + t.Fatalf("Expected\n%v, but got\n%v", tc.expected, got) + } + } +} + func TestFirstUniqueBazelLabelList(t *testing.T) { testCases := []struct { originalLabelList LabelList @@ -137,6 +198,9 @@ func TestFirstUniqueBazelLabelList(t *testing.T) { {Label: "b"}, {Label: "a"}, {Label: "c"}, + // namespaces + {Label: "//foo:bar", OriginalModuleName: "bar"}, // when referenced from foo namespace + {Label: "//foo:bar", OriginalModuleName: "//foo:bar"}, // when referenced from root namespace }, Excludes: []Label{ {Label: "x"}, @@ -150,6 +214,7 @@ func TestFirstUniqueBazelLabelList(t *testing.T) { {Label: "a"}, {Label: "b"}, {Label: "c"}, + {Label: "//foo:bar", OriginalModuleName: "bar"}, }, Excludes: []Label{ {Label: "x"}, @@ -167,6 +232,46 @@ func TestFirstUniqueBazelLabelList(t *testing.T) { } } +func TestFirstUniqueBazelLabelListAttribute(t *testing.T) { + testCases := []struct { + originalLabelList LabelListAttribute + expectedUniqueLabelList LabelListAttribute + }{ + { + originalLabelList: LabelListAttribute{ + Value: makeLabelList( + []string{"a", "b", "a", "c"}, + []string{"x", "x", "y", "z"}, + ), + ConfigurableValues: configurableLabelLists{ + ArchConfigurationAxis: labelListSelectValues{ + "arm": makeLabelList([]string{"1", "2", "1"}, []string{}), + "x86": makeLabelList([]string{"3", "4", "4"}, []string{"5", "5"}), + }, + }, + }, + expectedUniqueLabelList: LabelListAttribute{ + Value: makeLabelList( + []string{"a", "b", "c"}, + []string{"x", "y", "z"}, + ), + ConfigurableValues: configurableLabelLists{ + ArchConfigurationAxis: labelListSelectValues{ + "arm": makeLabelList([]string{"1", "2"}, []string{}), + "x86": makeLabelList([]string{"3", "4"}, []string{"5"}), + }, + }, + }, + }, + } + for _, tc := range testCases { + actualUniqueLabelList := FirstUniqueBazelLabelListAttribute(tc.originalLabelList) + if !reflect.DeepEqual(tc.expectedUniqueLabelList, actualUniqueLabelList) { + t.Fatalf("Expected %v, got %v", tc.expectedUniqueLabelList, actualUniqueLabelList) + } + } +} + func TestUniqueSortedBazelLabelList(t *testing.T) { testCases := []struct { originalLabelList LabelList @@ -248,13 +353,13 @@ func TestResolveExcludes(t *testing.T) { OsArchConfigurationAxis: labelListSelectValues{ "linux_x86": makeLabelList([]string{"linux_x86_include"}, []string{}), }, - ProductVariableConfigurationAxis("product_with_defaults", NoConfigAxis): labelListSelectValues{ + ProductVariableConfigurationAxis(false, "product_with_defaults"): labelListSelectValues{ "a": makeLabelList([]string{}, []string{"not_in_value"}), "b": makeLabelList([]string{"b_val"}, []string{}), "c": makeLabelList([]string{"c_val"}, []string{}), ConditionsDefaultConfigKey: makeLabelList([]string{"c_val", "default", "default2", "all_exclude"}, []string{}), }, - ProductVariableConfigurationAxis("product_only_with_excludes", NoConfigAxis): labelListSelectValues{ + ProductVariableConfigurationAxis(false, "product_only_with_excludes"): labelListSelectValues{ "a": makeLabelList([]string{}, []string{"product_config_exclude"}), }, }, @@ -282,13 +387,13 @@ func TestResolveExcludes(t *testing.T) { "linux_x86": makeLabels("linux_x86_include"), ConditionsDefaultConfigKey: nilLabels, }, - ProductVariableConfigurationAxis("product_with_defaults", NoConfigAxis): { + ProductVariableConfigurationAxis(false, "product_with_defaults"): { "a": nilLabels, "b": makeLabels("b_val"), "c": makeLabels("c_val"), ConditionsDefaultConfigKey: makeLabels("c_val", "default", "default2"), }, - ProductVariableConfigurationAxis("product_only_with_excludes", NoConfigAxis): { + ProductVariableConfigurationAxis(false, "product_only_with_excludes"): { "a": nilLabels, ConditionsDefaultConfigKey: makeLabels("product_config_exclude"), }, @@ -679,7 +784,7 @@ func TestDeduplicateAxesFromBase(t *testing.T) { OsArchConfigurationAxis: stringListSelectValues{ "linux_x86": {"linux_x86_include"}, }, - ProductVariableConfigurationAxis("a", NoConfigAxis): stringListSelectValues{ + ProductVariableConfigurationAxis(false, "a"): stringListSelectValues{ "a": []string{"not_in_value"}, }, }, @@ -704,7 +809,7 @@ func TestDeduplicateAxesFromBase(t *testing.T) { "linux": []string{"linux_include"}, }, OsArchConfigurationAxis: stringListSelectValues{}, - ProductVariableConfigurationAxis("a", NoConfigAxis): stringListSelectValues{ + ProductVariableConfigurationAxis(false, "a"): stringListSelectValues{ "a": []string{"not_in_value"}, }, } diff --git a/bloaty/bloaty.go b/bloaty/bloaty.go index 50f241f44c..3cff60fe6d 100644 --- a/bloaty/bloaty.go +++ b/bloaty/bloaty.go @@ -51,7 +51,7 @@ func init() { pctx.VariableConfigMethod("hostPrebuiltTag", android.Config.PrebuiltOS) pctx.SourcePathVariable("bloaty", "prebuilts/build-tools/${hostPrebuiltTag}/bin/bloaty") pctx.HostBinToolVariable("bloatyMerger", "bloaty_merger") - android.RegisterSingletonType("file_metrics", fileSizesSingleton) + android.RegisterParallelSingletonType("file_metrics", fileSizesSingleton) fileSizeMeasurerKey = blueprint.NewProvider(measuredFiles{}) } diff --git a/bp2build/Android.bp b/bp2build/Android.bp index b6635c4308..ba12682144 100644 --- a/bp2build/Android.bp +++ b/bp2build/Android.bp @@ -7,18 +7,16 @@ bootstrap_go_package { pkgPath: "android/soong/bp2build", srcs: [ "androidbp_to_build_templates.go", - "bp2build.go", - "bp2build_product_config.go", "build_conversion.go", "bzl_conversion.go", "configurability.go", "constants.go", "conversion.go", - "metrics.go", - "symlink_forest.go", - "testing.go", ], deps: [ + "blueprint-bootstrap", + "soong-aidl-library", + "soong-aconfig", "soong-android", "soong-android-allowlists", "soong-android-soongconfig", @@ -30,55 +28,14 @@ bootstrap_go_package { "soong-genrule", "soong-linkerconfig", "soong-python", + "soong-rust", "soong-sh", "soong-shared", "soong-starlark-format", "soong-ui-metrics", ], testSrcs: [ - "aar_conversion_test.go", - "android_app_certificate_conversion_test.go", - "android_app_conversion_test.go", - "apex_conversion_test.go", - "apex_key_conversion_test.go", - "build_conversion_test.go", - "bzl_conversion_test.go", - "cc_binary_conversion_test.go", - "cc_library_conversion_test.go", - "cc_library_headers_conversion_test.go", - "cc_library_shared_conversion_test.go", - "cc_library_static_conversion_test.go", - "cc_object_conversion_test.go", - "cc_prebuilt_library_conversion_test.go", - "cc_prebuilt_library_shared_test.go", - "cc_prebuilt_library_static_test.go", - "cc_prebuilt_object_conversion_test.go", - "cc_test_conversion_test.go", - "cc_yasm_conversion_test.go", "conversion_test.go", - "droidstubs_conversion_test.go", - "filegroup_conversion_test.go", - "genrule_conversion_test.go", - "gensrcs_conversion_test.go", - "java_binary_host_conversion_test.go", - "java_host_for_device_conversion_test.go", - "java_import_conversion_test.go", - "java_library_conversion_test.go", - "java_library_host_conversion_test.go", - "java_plugin_conversion_test.go", - "java_proto_conversion_test.go", - "license_conversion_test.go", - "license_kind_conversion_test.go", - "linker_config_conversion_test.go", - "ndk_headers_conversion_test.go", - "package_conversion_test.go", - "performance_test.go", - "prebuilt_etc_conversion_test.go", - "python_binary_conversion_test.go", - "python_library_conversion_test.go", - "python_test_conversion_test.go", - "sh_conversion_test.go", - "soong_config_module_type_conversion_test.go", ], pluginFor: [ "soong_build", diff --git a/bp2build/aar_conversion_test.go b/bp2build/aar_conversion_test.go deleted file mode 100644 index 09d9dc1038..0000000000 --- a/bp2build/aar_conversion_test.go +++ /dev/null @@ -1,212 +0,0 @@ -// Copyright 2022 Google Inc. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package bp2build - -import ( - "android/soong/android" - "android/soong/java" - "fmt" - - "testing" -) - -func TestConvertAndroidLibrary(t *testing.T) { - t.Helper() - RunBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, Bp2buildTestCase{ - Description: "Android Library - simple example", - ModuleTypeUnderTest: "android_library", - ModuleTypeUnderTestFactory: java.AndroidLibraryFactory, - Filesystem: map[string]string{ - "lib.java": "", - "arm.java": "", - "x86.java": "", - "res/res.png": "", - "manifest/AndroidManifest.xml": "", - }, - Blueprint: simpleModuleDoNotConvertBp2build("android_library", "static_lib_dep") + ` -android_library { - name: "TestLib", - srcs: ["lib.java"], - arch: { - arm: { - srcs: ["arm.java"], - }, - x86: { - srcs: ["x86.java"], - } - }, - manifest: "manifest/AndroidManifest.xml", - static_libs: ["static_lib_dep"], - java_version: "7", -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget( - "android_library", - "TestLib", - AttrNameToString{ - "srcs": `["lib.java"] + select({ - "//build/bazel/platforms/arch:arm": ["arm.java"], - "//build/bazel/platforms/arch:x86": ["x86.java"], - "//conditions:default": [], - })`, - "manifest": `"manifest/AndroidManifest.xml"`, - "resource_files": `["res/res.png"]`, - "deps": `[":static_lib_dep"]`, - "exports": `[":static_lib_dep"]`, - "java_version": `"7"`, - }), - MakeNeverlinkDuplicateTargetWithAttrs( - "android_library", - "TestLib", - AttrNameToString{"java_version": `"7"`}), - }}) -} - -func TestConvertAndroidLibraryWithNoSources(t *testing.T) { - t.Helper() - RunBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, Bp2buildTestCase{ - Description: "Android Library - modules with deps must have sources", - ModuleTypeUnderTest: "android_library", - ModuleTypeUnderTestFactory: java.AndroidLibraryFactory, - Filesystem: map[string]string{ - "res/res.png": "", - "AndroidManifest.xml": "", - }, - Blueprint: simpleModuleDoNotConvertBp2build("android_library", "lib_dep") + ` -android_library { - name: "TestLib", - srcs: [], - manifest: "AndroidManifest.xml", - libs: ["lib_dep"], -} -`, - ExpectedErr: fmt.Errorf("Module has direct dependencies but no sources. Bazel will not allow this."), - ExpectedBazelTargets: []string{}, - }) -} - -func TestConvertAndroidLibraryImport(t *testing.T) { - t.Helper() - RunBp2BuildTestCase( - t, - func(ctx android.RegistrationContext) { - ctx.RegisterModuleType("android_library", java.AndroidLibraryFactory) - }, - Bp2buildTestCase{ - Description: "Android Library Import", - ModuleTypeUnderTest: "android_library_import", - ModuleTypeUnderTestFactory: java.AARImportFactory, - Filesystem: map[string]string{ - "import.aar": "", - }, - // Bazel's aar_import can only export *_import targets, so we expect - // only "static_import_dep" in exports, but both "static_lib_dep" and - // "static_import_dep" in deps - Blueprint: simpleModuleDoNotConvertBp2build("android_library", "static_lib_dep") + - simpleModuleDoNotConvertBp2build("android_library_import", "static_import_dep") + ` -android_library_import { - name: "TestImport", - aars: ["import.aar"], - static_libs: ["static_lib_dep", "static_import_dep"], -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget( - "aar_import", - "TestImport", - AttrNameToString{ - "aar": `"import.aar"`, - "deps": `[ - ":static_lib_dep", - ":static_import_dep", - ]`, - "exports": `[":static_import_dep"]`, - }, - ), - MakeNeverlinkDuplicateTarget("android_library", "TestImport"), - }, - }, - ) -} - -func TestConvertAndroidLibraryKotlin(t *testing.T) { - t.Helper() - RunBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, Bp2buildTestCase{ - Description: "Android Library with .kt srcs and common_srcs attribute", - ModuleTypeUnderTest: "android_library", - ModuleTypeUnderTestFactory: java.AndroidLibraryFactory, - Filesystem: map[string]string{ - "AndroidManifest.xml": "", - }, - Blueprint: ` -android_library { - name: "TestLib", - srcs: ["a.java", "b.kt"], - common_srcs: ["c.kt"], -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget( - "android_library", - "TestLib", - AttrNameToString{ - "srcs": `[ - "a.java", - "b.kt", - ]`, - "common_srcs": `["c.kt"]`, - "manifest": `"AndroidManifest.xml"`, - "resource_files": `[]`, - }), - MakeNeverlinkDuplicateTarget("android_library", "TestLib"), - }}) -} - -func TestConvertAndroidLibraryKotlinCflags(t *testing.T) { - t.Helper() - RunBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, Bp2buildTestCase{ - Description: "Android Library with .kt srcs and kotlincflags ", - ModuleTypeUnderTest: "android_library", - ModuleTypeUnderTestFactory: java.AndroidLibraryFactory, - Filesystem: map[string]string{ - "AndroidManifest.xml": "", - }, - Blueprint: ` -android_library { - name: "TestLib", - srcs: ["a.java", "b.kt"], - kotlincflags: ["-flag1", "-flag2"], -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget( - "android_library", - "TestLib", - AttrNameToString{ - "srcs": `[ - "a.java", - "b.kt", - ]`, - "kotlincflags": `[ - "-flag1", - "-flag2", - ]`, - "manifest": `"AndroidManifest.xml"`, - "resource_files": `[]`, - }), - MakeNeverlinkDuplicateTarget("android_library", "TestLib"), - }}) -} diff --git a/bp2build/android_app_certificate_conversion_test.go b/bp2build/android_app_certificate_conversion_test.go deleted file mode 100644 index 01045134c2..0000000000 --- a/bp2build/android_app_certificate_conversion_test.go +++ /dev/null @@ -1,49 +0,0 @@ -// 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 bp2build - -import ( - "android/soong/android" - "android/soong/java" - - "testing" -) - -func runAndroidAppCertificateTestCase(t *testing.T, tc Bp2buildTestCase) { - t.Helper() - RunBp2BuildTestCase(t, registerAndroidAppCertificateModuleTypes, tc) -} - -func registerAndroidAppCertificateModuleTypes(ctx android.RegistrationContext) { -} - -func TestAndroidAppCertificateSimple(t *testing.T) { - runAndroidAppCertificateTestCase(t, Bp2buildTestCase{ - Description: "Android app certificate - simple example", - ModuleTypeUnderTest: "android_app_certificate", - ModuleTypeUnderTestFactory: java.AndroidAppCertificateFactory, - Filesystem: map[string]string{}, - Blueprint: ` -android_app_certificate { - name: "com.android.apogee.cert", - certificate: "chamber_of_secrets_dir", -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTargetNoRestrictions("android_app_certificate", "com.android.apogee.cert", AttrNameToString{ - "certificate": `"chamber_of_secrets_dir"`, - }), - }}) -} diff --git a/bp2build/android_app_conversion_test.go b/bp2build/android_app_conversion_test.go deleted file mode 100644 index 928a1f2e18..0000000000 --- a/bp2build/android_app_conversion_test.go +++ /dev/null @@ -1,398 +0,0 @@ -// 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 bp2build - -import ( - "android/soong/android" - "android/soong/java" - - "testing" -) - -func runAndroidAppTestCase(t *testing.T, tc Bp2buildTestCase) { - t.Helper() - RunBp2BuildTestCase(t, registerAndroidAppModuleTypes, tc) -} - -func registerAndroidAppModuleTypes(ctx android.RegistrationContext) { - ctx.RegisterModuleType("filegroup", android.FileGroupFactory) - ctx.RegisterModuleType("java_library", java.LibraryFactory) -} - -func TestMinimalAndroidApp(t *testing.T) { - runAndroidAppTestCase(t, Bp2buildTestCase{ - Description: "Android app - simple example", - ModuleTypeUnderTest: "android_app", - ModuleTypeUnderTestFactory: java.AndroidAppFactory, - Filesystem: map[string]string{ - "app.java": "", - "res/res.png": "", - "AndroidManifest.xml": "", - }, - Blueprint: ` -android_app { - name: "TestApp", - srcs: ["app.java"], - sdk_version: "current", -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("android_binary", "TestApp", AttrNameToString{ - "srcs": `["app.java"]`, - "manifest": `"AndroidManifest.xml"`, - "resource_files": `["res/res.png"]`, - "sdk_version": `"current"`, - }), - }}) -} - -func TestAndroidAppAllSupportedFields(t *testing.T) { - runAndroidAppTestCase(t, Bp2buildTestCase{ - Description: "Android app - all supported fields", - ModuleTypeUnderTest: "android_app", - ModuleTypeUnderTestFactory: java.AndroidAppFactory, - Filesystem: map[string]string{ - "app.java": "", - "resa/res.png": "", - "resb/res.png": "", - "manifest/AndroidManifest.xml": "", - }, - Blueprint: simpleModuleDoNotConvertBp2build("android_app", "static_lib_dep") + ` -android_app { - name: "TestApp", - srcs: ["app.java"], - sdk_version: "current", - package_name: "com.google", - resource_dirs: ["resa", "resb"], - manifest: "manifest/AndroidManifest.xml", - static_libs: ["static_lib_dep"], - java_version: "7", - certificate: "foocert", -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("android_binary", "TestApp", AttrNameToString{ - "srcs": `["app.java"]`, - "manifest": `"manifest/AndroidManifest.xml"`, - "resource_files": `[ - "resa/res.png", - "resb/res.png", - ]`, - "custom_package": `"com.google"`, - "deps": `[":static_lib_dep"]`, - "java_version": `"7"`, - "sdk_version": `"current"`, - "certificate_name": `"foocert"`, - }), - }}) -} - -func TestAndroidAppArchVariantSrcs(t *testing.T) { - runAndroidAppTestCase(t, Bp2buildTestCase{ - Description: "Android app - arch variant srcs", - ModuleTypeUnderTest: "android_app", - ModuleTypeUnderTestFactory: java.AndroidAppFactory, - Filesystem: map[string]string{ - "arm.java": "", - "x86.java": "", - "res/res.png": "", - "AndroidManifest.xml": "", - }, - Blueprint: ` -android_app { - name: "TestApp", - sdk_version: "current", - arch: { - arm: { - srcs: ["arm.java"], - }, - x86: { - srcs: ["x86.java"], - } - } -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("android_binary", "TestApp", AttrNameToString{ - "srcs": `select({ - "//build/bazel/platforms/arch:arm": ["arm.java"], - "//build/bazel/platforms/arch:x86": ["x86.java"], - "//conditions:default": [], - })`, - "manifest": `"AndroidManifest.xml"`, - "resource_files": `["res/res.png"]`, - "sdk_version": `"current"`, - }), - }}) -} - -func TestAndroidAppCertIsModule(t *testing.T) { - runAndroidAppTestCase(t, Bp2buildTestCase{ - Description: "Android app - cert is module", - ModuleTypeUnderTest: "android_app", - ModuleTypeUnderTestFactory: java.AndroidAppFactory, - Filesystem: map[string]string{}, - Blueprint: simpleModuleDoNotConvertBp2build("filegroup", "foocert") + ` -android_app { - name: "TestApp", - certificate: ":foocert", -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("android_binary", "TestApp", AttrNameToString{ - "certificate": `":foocert"`, - "manifest": `"AndroidManifest.xml"`, - "resource_files": `[]`, - }), - }}) -} - -func TestAndroidAppCertIsSrcFile(t *testing.T) { - runAndroidAppTestCase(t, Bp2buildTestCase{ - Description: "Android app - cert is src file", - ModuleTypeUnderTest: "android_app", - ModuleTypeUnderTestFactory: java.AndroidAppFactory, - Filesystem: map[string]string{ - "foocert": "", - }, - Blueprint: ` -android_app { - name: "TestApp", - certificate: "foocert", -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("android_binary", "TestApp", AttrNameToString{ - "certificate": `"foocert"`, - "manifest": `"AndroidManifest.xml"`, - "resource_files": `[]`, - }), - }}) -} - -func TestAndroidAppCertIsNotSrcOrModule(t *testing.T) { - runAndroidAppTestCase(t, Bp2buildTestCase{ - Description: "Android app - cert is not src or module", - ModuleTypeUnderTest: "android_app", - ModuleTypeUnderTestFactory: java.AndroidAppFactory, - Filesystem: map[string]string{ - // deliberate empty - }, - Blueprint: ` -android_app { - name: "TestApp", - certificate: "foocert", -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("android_binary", "TestApp", AttrNameToString{ - "certificate_name": `"foocert"`, - "manifest": `"AndroidManifest.xml"`, - "resource_files": `[]`, - }), - }}) -} - -func TestAndroidAppLibs(t *testing.T) { - runAndroidAppTestCase(t, Bp2buildTestCase{ - Description: "Android app with libs", - ModuleTypeUnderTest: "android_app", - ModuleTypeUnderTestFactory: java.AndroidAppFactory, - Filesystem: map[string]string{}, - Blueprint: simpleModuleDoNotConvertBp2build("filegroup", "foocert") + ` -android_app { - name: "foo", - libs: ["barLib"] -} -java_library{ - name: "barLib", -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("java_library", "barLib", AttrNameToString{}), - MakeNeverlinkDuplicateTarget("java_library", "barLib"), - MakeBazelTarget("android_binary", "foo", AttrNameToString{ - "manifest": `"AndroidManifest.xml"`, - "resource_files": `[]`, - "deps": `[":barLib-neverlink"]`, - }), - }}) -} - -func TestAndroidAppKotlinSrcs(t *testing.T) { - runAndroidAppTestCase(t, Bp2buildTestCase{ - Description: "Android app with kotlin sources and common_srcs", - ModuleTypeUnderTest: "android_app", - ModuleTypeUnderTestFactory: java.AndroidAppFactory, - Filesystem: map[string]string{ - "res/res.png": "", - }, - Blueprint: simpleModuleDoNotConvertBp2build("filegroup", "foocert") + ` -android_app { - name: "foo", - srcs: ["a.java", "b.kt"], - certificate: ":foocert", - manifest: "fooManifest.xml", - libs: ["barLib"] -} -java_library{ - name: "barLib", -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("java_library", "barLib", AttrNameToString{}), - MakeNeverlinkDuplicateTarget("java_library", "barLib"), - MakeBazelTarget("android_library", "foo_kt", AttrNameToString{ - "srcs": `[ - "a.java", - "b.kt", - ]`, - "manifest": `"fooManifest.xml"`, - "resource_files": `["res/res.png"]`, - "deps": `[":barLib-neverlink"]`, - }), - MakeBazelTarget("android_binary", "foo", AttrNameToString{ - "deps": `[":foo_kt"]`, - "certificate": `":foocert"`, - "manifest": `"fooManifest.xml"`, - }), - }}) -} - -func TestAndroidAppCommonSrcs(t *testing.T) { - runAndroidAppTestCase(t, Bp2buildTestCase{ - Description: "Android app with common_srcs", - ModuleTypeUnderTest: "android_app", - ModuleTypeUnderTestFactory: java.AndroidAppFactory, - Filesystem: map[string]string{ - "res/res.png": "", - }, - Blueprint: simpleModuleDoNotConvertBp2build("filegroup", "foocert") + ` -android_app { - name: "foo", - srcs: ["a.java"], - common_srcs: ["b.kt"], - certificate: "foocert", - manifest: "fooManifest.xml", - libs: ["barLib"], -} -java_library{ - name: "barLib", -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("java_library", "barLib", AttrNameToString{}), - MakeNeverlinkDuplicateTarget("java_library", "barLib"), - MakeBazelTarget("android_library", "foo_kt", AttrNameToString{ - "srcs": `["a.java"]`, - "common_srcs": `["b.kt"]`, - "manifest": `"fooManifest.xml"`, - "resource_files": `["res/res.png"]`, - "deps": `[":barLib-neverlink"]`, - }), - MakeBazelTarget("android_binary", "foo", AttrNameToString{ - "deps": `[":foo_kt"]`, - "certificate_name": `"foocert"`, - "manifest": `"fooManifest.xml"`, - }), - }}) -} - -func TestAndroidAppKotlinCflags(t *testing.T) { - runAndroidAppTestCase(t, Bp2buildTestCase{ - Description: "Android app with kotlincflags", - ModuleTypeUnderTest: "android_app", - ModuleTypeUnderTestFactory: java.AndroidAppFactory, - Filesystem: map[string]string{ - "res/res.png": "", - }, - Blueprint: simpleModuleDoNotConvertBp2build("filegroup", "foocert") + ` -android_app { - name: "foo", - srcs: ["a.java", "b.kt"], - certificate: ":foocert", - manifest: "fooManifest.xml", - kotlincflags: ["-flag1", "-flag2"], -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("android_library", "foo_kt", AttrNameToString{ - "srcs": `[ - "a.java", - "b.kt", - ]`, - "manifest": `"fooManifest.xml"`, - "resource_files": `["res/res.png"]`, - "kotlincflags": `[ - "-flag1", - "-flag2", - ]`, - }), - MakeBazelTarget("android_binary", "foo", AttrNameToString{ - "deps": `[":foo_kt"]`, - "certificate": `":foocert"`, - "manifest": `"fooManifest.xml"`, - }), - }}) -} - -func TestAndroidAppMinSdkProvided(t *testing.T) { - runAndroidAppTestCase(t, Bp2buildTestCase{ - Description: "Android app with value for min_sdk_version", - ModuleTypeUnderTest: "android_app", - ModuleTypeUnderTestFactory: java.AndroidAppFactory, - Filesystem: map[string]string{}, - Blueprint: simpleModuleDoNotConvertBp2build("filegroup", "foocert") + ` -android_app { - name: "foo", - sdk_version: "current", - min_sdk_version: "24", -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("android_binary", "foo", AttrNameToString{ - "manifest": `"AndroidManifest.xml"`, - "resource_files": `[]`, - "manifest_values": `{ - "minSdkVersion": "24", - }`, - "sdk_version": `"current"`, - }), - }}) -} - -func TestAndroidAppMinSdkDefaultToSdkVersion(t *testing.T) { - runAndroidAppTestCase(t, Bp2buildTestCase{ - Description: "Android app with value for sdk_version", - ModuleTypeUnderTest: "android_app", - ModuleTypeUnderTestFactory: java.AndroidAppFactory, - Filesystem: map[string]string{}, - Blueprint: simpleModuleDoNotConvertBp2build("filegroup", "foocert") + ` -android_app { - name: "foo", - sdk_version: "30", -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("android_binary", "foo", AttrNameToString{ - "manifest": `"AndroidManifest.xml"`, - "resource_files": `[]`, - "manifest_values": `{ - "minSdkVersion": "30", - }`, - "sdk_version": `"30"`, - }), - }}) -} diff --git a/bp2build/apex_conversion_test.go b/bp2build/apex_conversion_test.go deleted file mode 100644 index 1cc3f22ed3..0000000000 --- a/bp2build/apex_conversion_test.go +++ /dev/null @@ -1,1590 +0,0 @@ -// 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 bp2build - -import ( - "android/soong/android" - "android/soong/apex" - "android/soong/cc" - "android/soong/etc" - "android/soong/java" - "android/soong/sh" - - "fmt" - "testing" -) - -func runApexTestCase(t *testing.T, tc Bp2buildTestCase) { - t.Helper() - RunBp2BuildTestCase(t, registerApexModuleTypes, tc) -} - -func registerApexModuleTypes(ctx android.RegistrationContext) { - // CC module types needed as they can be APEX dependencies - cc.RegisterCCBuildComponents(ctx) - - ctx.RegisterModuleType("sh_binary", sh.ShBinaryFactory) - ctx.RegisterModuleType("cc_binary", cc.BinaryFactory) - ctx.RegisterModuleType("cc_library", cc.LibraryFactory) - ctx.RegisterModuleType("apex_key", apex.ApexKeyFactory) - ctx.RegisterModuleType("android_app_certificate", java.AndroidAppCertificateFactory) - ctx.RegisterModuleType("filegroup", android.FileGroupFactory) - ctx.RegisterModuleType("prebuilt_etc", etc.PrebuiltEtcFactory) - ctx.RegisterModuleType("cc_test", cc.TestFactory) -} - -func runOverrideApexTestCase(t *testing.T, tc Bp2buildTestCase) { - t.Helper() - RunBp2BuildTestCase(t, registerOverrideApexModuleTypes, tc) -} - -func registerOverrideApexModuleTypes(ctx android.RegistrationContext) { - // CC module types needed as they can be APEX dependencies - cc.RegisterCCBuildComponents(ctx) - - ctx.RegisterModuleType("sh_binary", sh.ShBinaryFactory) - ctx.RegisterModuleType("cc_binary", cc.BinaryFactory) - ctx.RegisterModuleType("cc_library", cc.LibraryFactory) - ctx.RegisterModuleType("apex_key", apex.ApexKeyFactory) - ctx.RegisterModuleType("apex_test", apex.TestApexBundleFactory) - ctx.RegisterModuleType("android_app_certificate", java.AndroidAppCertificateFactory) - ctx.RegisterModuleType("filegroup", android.FileGroupFactory) - ctx.RegisterModuleType("apex", apex.BundleFactory) - ctx.RegisterModuleType("apex_defaults", apex.DefaultsFactory) - ctx.RegisterModuleType("prebuilt_etc", etc.PrebuiltEtcFactory) - ctx.RegisterModuleType("soong_config_module_type", android.SoongConfigModuleTypeFactory) - ctx.RegisterModuleType("soong_config_string_variable", android.SoongConfigStringVariableDummyFactory) -} - -func TestApexBundleSimple(t *testing.T) { - runApexTestCase(t, Bp2buildTestCase{ - Description: "apex - example with all props, file_context is a module in same Android.bp", - ModuleTypeUnderTest: "apex", - ModuleTypeUnderTestFactory: apex.BundleFactory, - Filesystem: map[string]string{}, - Blueprint: ` -apex_key { - name: "com.android.apogee.key", - public_key: "com.android.apogee.avbpubkey", - private_key: "com.android.apogee.pem", - bazel_module: { bp2build_available: false }, -} - -android_app_certificate { - name: "com.android.apogee.certificate", - certificate: "com.android.apogee", - bazel_module: { bp2build_available: false }, -} - -cc_library { - name: "native_shared_lib_1", - bazel_module: { bp2build_available: false }, -} - -cc_library { - name: "native_shared_lib_2", - bazel_module: { bp2build_available: false }, -} - -prebuilt_etc { - name: "prebuilt_1", - bazel_module: { bp2build_available: false }, -} - -prebuilt_etc { - name: "prebuilt_2", - bazel_module: { bp2build_available: false }, -} - -filegroup { - name: "com.android.apogee-file_contexts", - srcs: [ - "com.android.apogee-file_contexts", - ], - bazel_module: { bp2build_available: false }, -} - -cc_binary { name: "cc_binary_1", bazel_module: { bp2build_available: false } } -sh_binary { name: "sh_binary_2", bazel_module: { bp2build_available: false } } - -apex { - name: "com.android.apogee", - manifest: "apogee_manifest.json", - androidManifest: "ApogeeAndroidManifest.xml", - file_contexts: ":com.android.apogee-file_contexts", - min_sdk_version: "29", - key: "com.android.apogee.key", - certificate: ":com.android.apogee.certificate", - updatable: false, - installable: false, - compressible: false, - native_shared_libs: [ - "native_shared_lib_1", - "native_shared_lib_2", - ], - binaries: [ - "cc_binary_1", - "sh_binary_2", - ], - prebuilts: [ - "prebuilt_1", - "prebuilt_2", - ], - package_name: "com.android.apogee.test.package", - logging_parent: "logging.parent", -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("apex", "com.android.apogee", AttrNameToString{ - "android_manifest": `"ApogeeAndroidManifest.xml"`, - "binaries": `[ - ":cc_binary_1", - ":sh_binary_2", - ]`, - "certificate": `":com.android.apogee.certificate"`, - "file_contexts": `":com.android.apogee-file_contexts"`, - "installable": "False", - "key": `":com.android.apogee.key"`, - "manifest": `"apogee_manifest.json"`, - "min_sdk_version": `"29"`, - "native_shared_libs_32": `select({ - "//build/bazel/platforms/arch:arm": [ - ":native_shared_lib_1", - ":native_shared_lib_2", - ], - "//build/bazel/platforms/arch:x86": [ - ":native_shared_lib_1", - ":native_shared_lib_2", - ], - "//conditions:default": [], - })`, - "native_shared_libs_64": `select({ - "//build/bazel/platforms/arch:arm64": [ - ":native_shared_lib_1", - ":native_shared_lib_2", - ], - "//build/bazel/platforms/arch:x86_64": [ - ":native_shared_lib_1", - ":native_shared_lib_2", - ], - "//conditions:default": [], - })`, - "prebuilts": `[ - ":prebuilt_1", - ":prebuilt_2", - ]`, - "updatable": "False", - "compressible": "False", - "package_name": `"com.android.apogee.test.package"`, - "logging_parent": `"logging.parent"`, - }), - }}) -} - -func TestApexBundleSimple_fileContextsInAnotherAndroidBp(t *testing.T) { - runApexTestCase(t, Bp2buildTestCase{ - Description: "apex - file contexts is a module in another Android.bp", - ModuleTypeUnderTest: "apex", - ModuleTypeUnderTestFactory: apex.BundleFactory, - Filesystem: map[string]string{ - "a/b/Android.bp": ` -filegroup { - name: "com.android.apogee-file_contexts", - srcs: [ - "com.android.apogee-file_contexts", - ], - bazel_module: { bp2build_available: false }, -} -`, - }, - Blueprint: ` -apex { - name: "com.android.apogee", - file_contexts: ":com.android.apogee-file_contexts", -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("apex", "com.android.apogee", AttrNameToString{ - "file_contexts": `"//a/b:com.android.apogee-file_contexts"`, - "manifest": `"apex_manifest.json"`, - }), - }}) -} - -func TestApexBundleSimple_fileContextsIsFile(t *testing.T) { - runApexTestCase(t, Bp2buildTestCase{ - Description: "apex - file contexts is a file", - ModuleTypeUnderTest: "apex", - ModuleTypeUnderTestFactory: apex.BundleFactory, - Filesystem: map[string]string{}, - Blueprint: ` -apex { - name: "com.android.apogee", - file_contexts: "file_contexts_file", -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("apex", "com.android.apogee", AttrNameToString{ - "file_contexts": `"file_contexts_file"`, - "manifest": `"apex_manifest.json"`, - }), - }}) -} - -func TestApexBundleSimple_fileContextsIsNotSpecified(t *testing.T) { - runApexTestCase(t, Bp2buildTestCase{ - Description: "apex - file contexts is not specified", - ModuleTypeUnderTest: "apex", - ModuleTypeUnderTestFactory: apex.BundleFactory, - Filesystem: map[string]string{ - "system/sepolicy/apex/Android.bp": ` -filegroup { - name: "com.android.apogee-file_contexts", - srcs: [ - "com.android.apogee-file_contexts", - ], - bazel_module: { bp2build_available: false }, -} -`, - }, - Blueprint: ` -apex { - name: "com.android.apogee", -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("apex", "com.android.apogee", AttrNameToString{ - "file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`, - "manifest": `"apex_manifest.json"`, - }), - }}) -} - -func TestApexBundleCompileMultilibBoth(t *testing.T) { - runApexTestCase(t, Bp2buildTestCase{ - Description: "apex - example with compile_multilib=both", - ModuleTypeUnderTest: "apex", - ModuleTypeUnderTestFactory: apex.BundleFactory, - Filesystem: map[string]string{ - "system/sepolicy/apex/Android.bp": ` -filegroup { - name: "com.android.apogee-file_contexts", - srcs: [ "apogee-file_contexts", ], - bazel_module: { bp2build_available: false }, -} -`, - }, - Blueprint: createMultilibBlueprint(`compile_multilib: "both",`), - ExpectedBazelTargets: []string{ - MakeBazelTarget("apex", "com.android.apogee", AttrNameToString{ - "native_shared_libs_32": `[ - ":unnested_native_shared_lib", - ":native_shared_lib_for_both", - ":native_shared_lib_for_lib32", - ] + select({ - "//build/bazel/platforms/arch:arm": [":native_shared_lib_for_first"], - "//build/bazel/platforms/arch:x86": [":native_shared_lib_for_first"], - "//conditions:default": [], - })`, - "native_shared_libs_64": `select({ - "//build/bazel/platforms/arch:arm64": [ - ":unnested_native_shared_lib", - ":native_shared_lib_for_both", - ":native_shared_lib_for_lib64", - ":native_shared_lib_for_first", - ], - "//build/bazel/platforms/arch:x86_64": [ - ":unnested_native_shared_lib", - ":native_shared_lib_for_both", - ":native_shared_lib_for_lib64", - ":native_shared_lib_for_first", - ], - "//conditions:default": [], - })`, - "file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`, - "manifest": `"apex_manifest.json"`, - }), - }}) -} - -func TestApexBundleCompileMultilibFirstAndDefaultValue(t *testing.T) { - expectedBazelTargets := []string{ - MakeBazelTarget("apex", "com.android.apogee", AttrNameToString{ - "native_shared_libs_32": `select({ - "//build/bazel/platforms/arch:arm": [ - ":unnested_native_shared_lib", - ":native_shared_lib_for_both", - ":native_shared_lib_for_lib32", - ":native_shared_lib_for_first", - ], - "//build/bazel/platforms/arch:x86": [ - ":unnested_native_shared_lib", - ":native_shared_lib_for_both", - ":native_shared_lib_for_lib32", - ":native_shared_lib_for_first", - ], - "//conditions:default": [], - })`, - "native_shared_libs_64": `select({ - "//build/bazel/platforms/arch:arm64": [ - ":unnested_native_shared_lib", - ":native_shared_lib_for_both", - ":native_shared_lib_for_lib64", - ":native_shared_lib_for_first", - ], - "//build/bazel/platforms/arch:x86_64": [ - ":unnested_native_shared_lib", - ":native_shared_lib_for_both", - ":native_shared_lib_for_lib64", - ":native_shared_lib_for_first", - ], - "//conditions:default": [], - })`, - "file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`, - "manifest": `"apex_manifest.json"`, - }), - } - - // "first" is the default value of compile_multilib prop so `compile_multilib_: "first"` and unset compile_multilib - // should result to the same bp2build output - compileMultiLibPropValues := []string{`compile_multilib: "first",`, ""} - for _, compileMultiLibProp := range compileMultiLibPropValues { - descriptionSuffix := compileMultiLibProp - if descriptionSuffix == "" { - descriptionSuffix = "compile_multilib unset" - } - runApexTestCase(t, Bp2buildTestCase{ - Description: "apex - example with " + compileMultiLibProp, - ModuleTypeUnderTest: "apex", - ModuleTypeUnderTestFactory: apex.BundleFactory, - Filesystem: map[string]string{ - "system/sepolicy/apex/Android.bp": ` - filegroup { - name: "com.android.apogee-file_contexts", - srcs: [ "apogee-file_contexts", ], - bazel_module: { bp2build_available: false }, - } - `, - }, - Blueprint: createMultilibBlueprint(compileMultiLibProp), - ExpectedBazelTargets: expectedBazelTargets, - }) - } -} - -func TestApexBundleCompileMultilib32(t *testing.T) { - runApexTestCase(t, Bp2buildTestCase{ - Description: "apex - example with compile_multilib=32", - ModuleTypeUnderTest: "apex", - ModuleTypeUnderTestFactory: apex.BundleFactory, - Filesystem: map[string]string{ - "system/sepolicy/apex/Android.bp": ` -filegroup { - name: "com.android.apogee-file_contexts", - srcs: [ "apogee-file_contexts", ], - bazel_module: { bp2build_available: false }, -} -`, - }, - Blueprint: createMultilibBlueprint(`compile_multilib: "32",`), - ExpectedBazelTargets: []string{ - MakeBazelTarget("apex", "com.android.apogee", AttrNameToString{ - "native_shared_libs_32": `[ - ":unnested_native_shared_lib", - ":native_shared_lib_for_both", - ":native_shared_lib_for_lib32", - ] + select({ - "//build/bazel/platforms/arch:arm": [":native_shared_lib_for_first"], - "//build/bazel/platforms/arch:x86": [":native_shared_lib_for_first"], - "//conditions:default": [], - })`, - "file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`, - "manifest": `"apex_manifest.json"`, - }), - }}) -} - -func TestApexBundleCompileMultilib64(t *testing.T) { - runApexTestCase(t, Bp2buildTestCase{ - Description: "apex - example with compile_multilib=64", - ModuleTypeUnderTest: "apex", - ModuleTypeUnderTestFactory: apex.BundleFactory, - Filesystem: map[string]string{ - "system/sepolicy/apex/Android.bp": ` -filegroup { - name: "com.android.apogee-file_contexts", - srcs: [ "apogee-file_contexts", ], - bazel_module: { bp2build_available: false }, -} -`, - }, - Blueprint: createMultilibBlueprint(`compile_multilib: "64",`), - ExpectedBazelTargets: []string{ - MakeBazelTarget("apex", "com.android.apogee", AttrNameToString{ - "native_shared_libs_64": `select({ - "//build/bazel/platforms/arch:arm64": [ - ":unnested_native_shared_lib", - ":native_shared_lib_for_both", - ":native_shared_lib_for_lib64", - ":native_shared_lib_for_first", - ], - "//build/bazel/platforms/arch:x86_64": [ - ":unnested_native_shared_lib", - ":native_shared_lib_for_both", - ":native_shared_lib_for_lib64", - ":native_shared_lib_for_first", - ], - "//conditions:default": [], - })`, - "file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`, - "manifest": `"apex_manifest.json"`, - }), - }}) -} - -func createMultilibBlueprint(compile_multilib string) string { - return fmt.Sprintf(` -cc_library { - name: "native_shared_lib_for_both", - bazel_module: { bp2build_available: false }, -} - -cc_library { - name: "native_shared_lib_for_first", - bazel_module: { bp2build_available: false }, -} - -cc_library { - name: "native_shared_lib_for_lib32", - bazel_module: { bp2build_available: false }, -} - -cc_library { - name: "native_shared_lib_for_lib64", - bazel_module: { bp2build_available: false }, -} - -cc_library { - name: "unnested_native_shared_lib", - bazel_module: { bp2build_available: false }, -} - -apex { - name: "com.android.apogee", - %s - native_shared_libs: ["unnested_native_shared_lib"], - multilib: { - both: { - native_shared_libs: [ - "native_shared_lib_for_both", - ], - }, - first: { - native_shared_libs: [ - "native_shared_lib_for_first", - ], - }, - lib32: { - native_shared_libs: [ - "native_shared_lib_for_lib32", - ], - }, - lib64: { - native_shared_libs: [ - "native_shared_lib_for_lib64", - ], - }, - }, -}`, compile_multilib) -} - -func TestApexBundleDefaultPropertyValues(t *testing.T) { - runApexTestCase(t, Bp2buildTestCase{ - Description: "apex - default property values", - ModuleTypeUnderTest: "apex", - ModuleTypeUnderTestFactory: apex.BundleFactory, - Filesystem: map[string]string{ - "system/sepolicy/apex/Android.bp": ` -filegroup { - name: "com.android.apogee-file_contexts", - srcs: [ "apogee-file_contexts", ], - bazel_module: { bp2build_available: false }, -} -`, - }, - Blueprint: ` -apex { - name: "com.android.apogee", - manifest: "apogee_manifest.json", -} -`, - ExpectedBazelTargets: []string{MakeBazelTarget("apex", "com.android.apogee", AttrNameToString{ - "manifest": `"apogee_manifest.json"`, - "file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`, - }), - }}) -} - -func TestApexBundleHasBazelModuleProps(t *testing.T) { - runApexTestCase(t, Bp2buildTestCase{ - Description: "apex - has bazel module props", - ModuleTypeUnderTest: "apex", - ModuleTypeUnderTestFactory: apex.BundleFactory, - Filesystem: map[string]string{ - "system/sepolicy/apex/Android.bp": ` -filegroup { - name: "apogee-file_contexts", - srcs: [ "apogee-file_contexts", ], - bazel_module: { bp2build_available: false }, -} -`, - }, - Blueprint: ` -apex { - name: "apogee", - manifest: "manifest.json", - bazel_module: { bp2build_available: true }, -} -`, - ExpectedBazelTargets: []string{MakeBazelTarget("apex", "apogee", AttrNameToString{ - "manifest": `"manifest.json"`, - "file_contexts": `"//system/sepolicy/apex:apogee-file_contexts"`, - }), - }}) -} - -func TestBp2BuildOverrideApex(t *testing.T) { - runOverrideApexTestCase(t, Bp2buildTestCase{ - Description: "override_apex", - ModuleTypeUnderTest: "override_apex", - ModuleTypeUnderTestFactory: apex.OverrideApexFactory, - Filesystem: map[string]string{}, - Blueprint: ` -apex_key { - name: "com.android.apogee.key", - public_key: "com.android.apogee.avbpubkey", - private_key: "com.android.apogee.pem", - bazel_module: { bp2build_available: false }, -} - -android_app_certificate { - name: "com.android.apogee.certificate", - certificate: "com.android.apogee", - bazel_module: { bp2build_available: false }, -} - -cc_library { - name: "native_shared_lib_1", - bazel_module: { bp2build_available: false }, -} - -cc_library { - name: "native_shared_lib_2", - bazel_module: { bp2build_available: false }, -} - -prebuilt_etc { - name: "prebuilt_1", - bazel_module: { bp2build_available: false }, -} - -prebuilt_etc { - name: "prebuilt_2", - bazel_module: { bp2build_available: false }, -} - -filegroup { - name: "com.android.apogee-file_contexts", - srcs: [ - "com.android.apogee-file_contexts", - ], - bazel_module: { bp2build_available: false }, -} - -cc_binary { name: "cc_binary_1", bazel_module: { bp2build_available: false } } -sh_binary { name: "sh_binary_2", bazel_module: { bp2build_available: false } } - -apex { - name: "com.android.apogee", - manifest: "apogee_manifest.json", - androidManifest: "ApogeeAndroidManifest.xml", - file_contexts: ":com.android.apogee-file_contexts", - min_sdk_version: "29", - key: "com.android.apogee.key", - certificate: ":com.android.apogee.certificate", - updatable: false, - installable: false, - compressible: false, - native_shared_libs: [ - "native_shared_lib_1", - "native_shared_lib_2", - ], - binaries: [ - "cc_binary_1", - "sh_binary_2", - ], - prebuilts: [ - "prebuilt_1", - "prebuilt_2", - ], - bazel_module: { bp2build_available: false }, -} - -apex_key { - name: "com.google.android.apogee.key", - public_key: "com.google.android.apogee.avbpubkey", - private_key: "com.google.android.apogee.pem", - bazel_module: { bp2build_available: false }, -} - -android_app_certificate { - name: "com.google.android.apogee.certificate", - certificate: "com.google.android.apogee", - bazel_module: { bp2build_available: false }, -} - -override_apex { - name: "com.google.android.apogee", - base: ":com.android.apogee", - key: "com.google.android.apogee.key", - certificate: ":com.google.android.apogee.certificate", - prebuilts: [], - compressible: true, -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{ - "android_manifest": `"ApogeeAndroidManifest.xml"`, - "base_apex_name": `"com.android.apogee"`, - "binaries": `[ - ":cc_binary_1", - ":sh_binary_2", - ]`, - "certificate": `":com.google.android.apogee.certificate"`, - "file_contexts": `":com.android.apogee-file_contexts"`, - "installable": "False", - "key": `":com.google.android.apogee.key"`, - "manifest": `"apogee_manifest.json"`, - "min_sdk_version": `"29"`, - "native_shared_libs_32": `select({ - "//build/bazel/platforms/arch:arm": [ - ":native_shared_lib_1", - ":native_shared_lib_2", - ], - "//build/bazel/platforms/arch:x86": [ - ":native_shared_lib_1", - ":native_shared_lib_2", - ], - "//conditions:default": [], - })`, - "native_shared_libs_64": `select({ - "//build/bazel/platforms/arch:arm64": [ - ":native_shared_lib_1", - ":native_shared_lib_2", - ], - "//build/bazel/platforms/arch:x86_64": [ - ":native_shared_lib_1", - ":native_shared_lib_2", - ], - "//conditions:default": [], - })`, - "prebuilts": `[]`, - "updatable": "False", - "compressible": "True", - }), - }}) -} - -func TestOverrideApexTest(t *testing.T) { - runOverrideApexTestCase(t, Bp2buildTestCase{ - Description: "override_apex", - ModuleTypeUnderTest: "override_apex", - ModuleTypeUnderTestFactory: apex.OverrideApexFactory, - Filesystem: map[string]string{}, - Blueprint: ` -apex_key { - name: "com.android.apogee.key", - public_key: "com.android.apogee.avbpubkey", - private_key: "com.android.apogee.pem", - bazel_module: { bp2build_available: false }, -} - -android_app_certificate { - name: "com.android.apogee.certificate", - certificate: "com.android.apogee", - bazel_module: { bp2build_available: false }, -} - -cc_library { - name: "native_shared_lib_1", - bazel_module: { bp2build_available: false }, -} - -prebuilt_etc { - name: "prebuilt_1", - bazel_module: { bp2build_available: false }, -} - -filegroup { - name: "com.android.apogee-file_contexts", - srcs: [ - "com.android.apogee-file_contexts", - ], - bazel_module: { bp2build_available: false }, -} - -cc_binary { name: "cc_binary_1", bazel_module: { bp2build_available: false } } -sh_binary { name: "sh_binary_2", bazel_module: { bp2build_available: false } } - -apex_test { - name: "com.android.apogee", - manifest: "apogee_manifest.json", - androidManifest: "ApogeeAndroidManifest.xml", - file_contexts: ":com.android.apogee-file_contexts", - min_sdk_version: "29", - key: "com.android.apogee.key", - certificate: ":com.android.apogee.certificate", - updatable: false, - installable: false, - compressible: false, - native_shared_libs: [ - "native_shared_lib_1", - ], - binaries: [ - "cc_binary_1", - "sh_binary_2", - ], - prebuilts: [ - "prebuilt_1", - ], - bazel_module: { bp2build_available: false }, -} - -apex_key { - name: "com.google.android.apogee.key", - public_key: "com.google.android.apogee.avbpubkey", - private_key: "com.google.android.apogee.pem", - bazel_module: { bp2build_available: false }, -} - -android_app_certificate { - name: "com.google.android.apogee.certificate", - certificate: "com.google.android.apogee", - bazel_module: { bp2build_available: false }, -} - -override_apex { - name: "com.google.android.apogee", - base: ":com.android.apogee", - key: "com.google.android.apogee.key", - certificate: ":com.google.android.apogee.certificate", - prebuilts: [], - compressible: true, -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{ - "android_manifest": `"ApogeeAndroidManifest.xml"`, - "base_apex_name": `"com.android.apogee"`, - "binaries": `[ - ":cc_binary_1", - ":sh_binary_2", - ]`, - "certificate": `":com.google.android.apogee.certificate"`, - "file_contexts": `":com.android.apogee-file_contexts"`, - "installable": "False", - "key": `":com.google.android.apogee.key"`, - "manifest": `"apogee_manifest.json"`, - "min_sdk_version": `"29"`, - "native_shared_libs_32": `select({ - "//build/bazel/platforms/arch:arm": [":native_shared_lib_1"], - "//build/bazel/platforms/arch:x86": [":native_shared_lib_1"], - "//conditions:default": [], - })`, - "native_shared_libs_64": `select({ - "//build/bazel/platforms/arch:arm64": [":native_shared_lib_1"], - "//build/bazel/platforms/arch:x86_64": [":native_shared_lib_1"], - "//conditions:default": [], - })`, - "testonly": "True", - "prebuilts": `[]`, - "updatable": "False", - "compressible": "True", - }), - }}) -} - -func TestApexBundleSimple_manifestIsEmpty_baseApexOverrideApexInDifferentAndroidBp(t *testing.T) { - runOverrideApexTestCase(t, Bp2buildTestCase{ - Description: "override_apex - manifest of base apex is empty, base apex and override_apex is in different Android.bp", - ModuleTypeUnderTest: "override_apex", - ModuleTypeUnderTestFactory: apex.OverrideApexFactory, - Filesystem: map[string]string{ - "system/sepolicy/apex/Android.bp": ` -filegroup { - name: "com.android.apogee-file_contexts", - srcs: [ "apogee-file_contexts", ], - bazel_module: { bp2build_available: false }, -}`, - "a/b/Android.bp": ` -apex { - name: "com.android.apogee", - bazel_module: { bp2build_available: false }, -} -`, - }, - Blueprint: ` -override_apex { - name: "com.google.android.apogee", - base: ":com.android.apogee", -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{ - "base_apex_name": `"com.android.apogee"`, - "file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`, - "manifest": `"//a/b:apex_manifest.json"`, - }), - }}) -} - -func TestApexBundleSimple_manifestIsSet_baseApexOverrideApexInDifferentAndroidBp(t *testing.T) { - runOverrideApexTestCase(t, Bp2buildTestCase{ - Description: "override_apex - manifest of base apex is set, base apex and override_apex is in different Android.bp", - ModuleTypeUnderTest: "override_apex", - ModuleTypeUnderTestFactory: apex.OverrideApexFactory, - Filesystem: map[string]string{ - "system/sepolicy/apex/Android.bp": ` -filegroup { - name: "com.android.apogee-file_contexts", - srcs: [ "apogee-file_contexts", ], - bazel_module: { bp2build_available: false }, -}`, - "a/b/Android.bp": ` -apex { - name: "com.android.apogee", - manifest: "apogee_manifest.json", - bazel_module: { bp2build_available: false }, -} -`, - }, - Blueprint: ` -override_apex { - name: "com.google.android.apogee", - base: ":com.android.apogee", -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{ - "base_apex_name": `"com.android.apogee"`, - "file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`, - "manifest": `"//a/b:apogee_manifest.json"`, - }), - }}) -} - -func TestApexBundleSimple_manifestIsEmpty_baseApexOverrideApexInSameAndroidBp(t *testing.T) { - runOverrideApexTestCase(t, Bp2buildTestCase{ - Description: "override_apex - manifest of base apex is empty, base apex and override_apex is in same Android.bp", - ModuleTypeUnderTest: "override_apex", - ModuleTypeUnderTestFactory: apex.OverrideApexFactory, - Filesystem: map[string]string{ - "system/sepolicy/apex/Android.bp": ` -filegroup { - name: "com.android.apogee-file_contexts", - srcs: [ "apogee-file_contexts", ], - bazel_module: { bp2build_available: false }, -}`, - }, - Blueprint: ` -apex { - name: "com.android.apogee", - bazel_module: { bp2build_available: false }, -} - -override_apex { - name: "com.google.android.apogee", - base: ":com.android.apogee", -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{ - "base_apex_name": `"com.android.apogee"`, - "file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`, - "manifest": `"apex_manifest.json"`, - }), - }}) -} - -func TestApexBundleSimple_manifestIsSet_baseApexOverrideApexInSameAndroidBp(t *testing.T) { - runOverrideApexTestCase(t, Bp2buildTestCase{ - Description: "override_apex - manifest of base apex is set, base apex and override_apex is in same Android.bp", - ModuleTypeUnderTest: "override_apex", - ModuleTypeUnderTestFactory: apex.OverrideApexFactory, - Filesystem: map[string]string{ - "system/sepolicy/apex/Android.bp": ` -filegroup { - name: "com.android.apogee-file_contexts", - srcs: [ "apogee-file_contexts", ], - bazel_module: { bp2build_available: false }, -}`, - }, - Blueprint: ` -apex { - name: "com.android.apogee", - manifest: "apogee_manifest.json", - bazel_module: { bp2build_available: false }, -} - -override_apex { - name: "com.google.android.apogee", - base: ":com.android.apogee", -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{ - "base_apex_name": `"com.android.apogee"`, - "file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`, - "manifest": `"apogee_manifest.json"`, - }), - }}) -} - -func TestApexBundleSimple_packageNameOverride(t *testing.T) { - runOverrideApexTestCase(t, Bp2buildTestCase{ - Description: "override_apex - override package name", - ModuleTypeUnderTest: "override_apex", - ModuleTypeUnderTestFactory: apex.OverrideApexFactory, - Filesystem: map[string]string{ - "system/sepolicy/apex/Android.bp": ` -filegroup { - name: "com.android.apogee-file_contexts", - srcs: [ "apogee-file_contexts", ], - bazel_module: { bp2build_available: false }, -}`, - }, - Blueprint: ` -apex { - name: "com.android.apogee", - bazel_module: { bp2build_available: false }, -} - -override_apex { - name: "com.google.android.apogee", - base: ":com.android.apogee", - package_name: "com.google.android.apogee", -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{ - "base_apex_name": `"com.android.apogee"`, - "file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`, - "manifest": `"apex_manifest.json"`, - "package_name": `"com.google.android.apogee"`, - }), - }}) -} - -func TestApexBundleSimple_NoPrebuiltsOverride(t *testing.T) { - runOverrideApexTestCase(t, Bp2buildTestCase{ - Description: "override_apex - no override", - ModuleTypeUnderTest: "override_apex", - ModuleTypeUnderTestFactory: apex.OverrideApexFactory, - Filesystem: map[string]string{ - "system/sepolicy/apex/Android.bp": ` -filegroup { - name: "com.android.apogee-file_contexts", - srcs: [ "apogee-file_contexts", ], - bazel_module: { bp2build_available: false }, -}`, - }, - Blueprint: ` -prebuilt_etc { - name: "prebuilt_file", - bazel_module: { bp2build_available: false }, -} - -apex { - name: "com.android.apogee", - bazel_module: { bp2build_available: false }, - prebuilts: ["prebuilt_file"] -} - -override_apex { - name: "com.google.android.apogee", - base: ":com.android.apogee", -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{ - "base_apex_name": `"com.android.apogee"`, - "file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`, - "manifest": `"apex_manifest.json"`, - "prebuilts": `[":prebuilt_file"]`, - }), - }}) -} - -func TestApexBundleSimple_PrebuiltsOverride(t *testing.T) { - runOverrideApexTestCase(t, Bp2buildTestCase{ - Description: "override_apex - ooverride", - ModuleTypeUnderTest: "override_apex", - ModuleTypeUnderTestFactory: apex.OverrideApexFactory, - Filesystem: map[string]string{ - "system/sepolicy/apex/Android.bp": ` -filegroup { - name: "com.android.apogee-file_contexts", - srcs: [ "apogee-file_contexts", ], - bazel_module: { bp2build_available: false }, -}`, - }, - Blueprint: ` -prebuilt_etc { - name: "prebuilt_file", - bazel_module: { bp2build_available: false }, -} - -prebuilt_etc { - name: "prebuilt_file2", - bazel_module: { bp2build_available: false }, -} - -apex { - name: "com.android.apogee", - bazel_module: { bp2build_available: false }, - prebuilts: ["prebuilt_file"] -} - -override_apex { - name: "com.google.android.apogee", - base: ":com.android.apogee", - prebuilts: ["prebuilt_file2"] -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{ - "base_apex_name": `"com.android.apogee"`, - "file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`, - "manifest": `"apex_manifest.json"`, - "prebuilts": `[":prebuilt_file2"]`, - }), - }}) -} - -func TestApexBundleSimple_PrebuiltsOverrideEmptyList(t *testing.T) { - runOverrideApexTestCase(t, Bp2buildTestCase{ - Description: "override_apex - override with empty list", - ModuleTypeUnderTest: "override_apex", - ModuleTypeUnderTestFactory: apex.OverrideApexFactory, - Filesystem: map[string]string{ - "system/sepolicy/apex/Android.bp": ` -filegroup { - name: "com.android.apogee-file_contexts", - srcs: [ "apogee-file_contexts", ], - bazel_module: { bp2build_available: false }, -}`, - }, - Blueprint: ` -prebuilt_etc { - name: "prebuilt_file", - bazel_module: { bp2build_available: false }, -} - -apex { - name: "com.android.apogee", - bazel_module: { bp2build_available: false }, - prebuilts: ["prebuilt_file"] -} - -override_apex { - name: "com.google.android.apogee", - base: ":com.android.apogee", - prebuilts: [], -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{ - "base_apex_name": `"com.android.apogee"`, - "file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`, - "manifest": `"apex_manifest.json"`, - "prebuilts": `[]`, - }), - }}) -} - -func TestApexBundleSimple_NoLoggingParentOverride(t *testing.T) { - runOverrideApexTestCase(t, Bp2buildTestCase{ - Description: "override_apex - logging_parent - no override", - ModuleTypeUnderTest: "override_apex", - ModuleTypeUnderTestFactory: apex.OverrideApexFactory, - Filesystem: map[string]string{ - "system/sepolicy/apex/Android.bp": ` -filegroup { - name: "com.android.apogee-file_contexts", - srcs: [ "apogee-file_contexts", ], - bazel_module: { bp2build_available: false }, -}`, - }, - Blueprint: ` -apex { - name: "com.android.apogee", - bazel_module: { bp2build_available: false }, - logging_parent: "foo.bar.baz", -} - -override_apex { - name: "com.google.android.apogee", - base: ":com.android.apogee", -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{ - "base_apex_name": `"com.android.apogee"`, - "file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`, - "manifest": `"apex_manifest.json"`, - "logging_parent": `"foo.bar.baz"`, - }), - }}) -} - -func TestApexBundleSimple_LoggingParentOverride(t *testing.T) { - runOverrideApexTestCase(t, Bp2buildTestCase{ - Description: "override_apex - logging_parent - override", - ModuleTypeUnderTest: "override_apex", - ModuleTypeUnderTestFactory: apex.OverrideApexFactory, - Filesystem: map[string]string{ - "system/sepolicy/apex/Android.bp": ` -filegroup { - name: "com.android.apogee-file_contexts", - srcs: [ "apogee-file_contexts", ], - bazel_module: { bp2build_available: false }, -}`, - }, - Blueprint: ` -apex { - name: "com.android.apogee", - bazel_module: { bp2build_available: false }, - logging_parent: "foo.bar.baz", -} - -override_apex { - name: "com.google.android.apogee", - base: ":com.android.apogee", - logging_parent: "foo.bar.baz.override", -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{ - "base_apex_name": `"com.android.apogee"`, - "file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`, - "manifest": `"apex_manifest.json"`, - "logging_parent": `"foo.bar.baz.override"`, - }), - }}) -} - -func TestBp2BuildOverrideApex_CertificateNil(t *testing.T) { - runOverrideApexTestCase(t, Bp2buildTestCase{ - Description: "override_apex - don't set default certificate", - ModuleTypeUnderTest: "override_apex", - ModuleTypeUnderTestFactory: apex.OverrideApexFactory, - Filesystem: map[string]string{}, - Blueprint: ` -android_app_certificate { - name: "com.android.apogee.certificate", - certificate: "com.android.apogee", - bazel_module: { bp2build_available: false }, -} - -filegroup { - name: "com.android.apogee-file_contexts", - srcs: [ - "com.android.apogee-file_contexts", - ], - bazel_module: { bp2build_available: false }, -} - -apex { - name: "com.android.apogee", - manifest: "apogee_manifest.json", - file_contexts: ":com.android.apogee-file_contexts", - certificate: ":com.android.apogee.certificate", - bazel_module: { bp2build_available: false }, -} - -override_apex { - name: "com.google.android.apogee", - base: ":com.android.apogee", - // certificate is deliberately omitted, and not converted to bazel, - // because the overridden apex shouldn't be using the base apex's cert. -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{ - "base_apex_name": `"com.android.apogee"`, - "file_contexts": `":com.android.apogee-file_contexts"`, - "manifest": `"apogee_manifest.json"`, - }), - }}) -} - -func TestApexCertificateIsModule(t *testing.T) { - runApexTestCase(t, Bp2buildTestCase{ - Description: "apex - certificate is module", - ModuleTypeUnderTest: "apex", - ModuleTypeUnderTestFactory: apex.BundleFactory, - Filesystem: map[string]string{}, - Blueprint: ` -android_app_certificate { - name: "com.android.apogee.certificate", - certificate: "com.android.apogee", - bazel_module: { bp2build_available: false }, -} - -apex { - name: "com.android.apogee", - manifest: "apogee_manifest.json", - file_contexts: ":com.android.apogee-file_contexts", - certificate: ":com.android.apogee.certificate", -} -` + simpleModuleDoNotConvertBp2build("filegroup", "com.android.apogee-file_contexts"), - ExpectedBazelTargets: []string{ - MakeBazelTarget("apex", "com.android.apogee", AttrNameToString{ - "certificate": `":com.android.apogee.certificate"`, - "file_contexts": `":com.android.apogee-file_contexts"`, - "manifest": `"apogee_manifest.json"`, - }), - }}) -} - -func TestApexWithStubLib(t *testing.T) { - runApexTestCase(t, Bp2buildTestCase{ - Description: "apex - static variant of stub lib should not have apex_available tag", - ModuleTypeUnderTest: "apex", - ModuleTypeUnderTestFactory: apex.BundleFactory, - Filesystem: map[string]string{}, - Blueprint: ` -cc_library{ - name: "foo", - stubs: { symbol_file: "foo.map.txt", versions: ["28", "29", "current"] }, - apex_available: ["myapex"], -} - -cc_binary{ - name: "bar", - static_libs: ["foo"], - apex_available: ["myapex"], -} - -apex { - name: "myapex", - manifest: "myapex_manifest.json", - file_contexts: ":myapex-file_contexts", - binaries: ["bar"], - native_shared_libs: ["foo"], -} -` + simpleModuleDoNotConvertBp2build("filegroup", "myapex-file_contexts"), - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_binary", "bar", AttrNameToString{ - "local_includes": `["."]`, - "deps": `[":foo_bp2build_cc_library_static"]`, - "tags": `["apex_available=myapex"]`, - }), - MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{ - "local_includes": `["."]`, - }), - MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ - "local_includes": `["."]`, - "stubs_symbol_file": `"foo.map.txt"`, - "tags": `["apex_available=myapex"]`, - }), - MakeBazelTarget("cc_stub_suite", "foo_stub_libs", AttrNameToString{ - "soname": `"foo.so"`, - "source_library_label": `"//:foo"`, - "symbol_file": `"foo.map.txt"`, - "versions": `[ - "28", - "29", - "current", - ]`, - }), - MakeBazelTarget("apex", "myapex", AttrNameToString{ - "file_contexts": `":myapex-file_contexts"`, - "manifest": `"myapex_manifest.json"`, - "binaries": `[":bar"]`, - "native_shared_libs_32": `select({ - "//build/bazel/platforms/arch:arm": [":foo"], - "//build/bazel/platforms/arch:x86": [":foo"], - "//conditions:default": [], - })`, - "native_shared_libs_64": `select({ - "//build/bazel/platforms/arch:arm64": [":foo"], - "//build/bazel/platforms/arch:x86_64": [":foo"], - "//conditions:default": [], - })`, - }), - }, - }) -} - -func TestApexCertificateIsSrc(t *testing.T) { - runApexTestCase(t, Bp2buildTestCase{ - Description: "apex - certificate is src", - ModuleTypeUnderTest: "apex", - ModuleTypeUnderTestFactory: apex.BundleFactory, - Filesystem: map[string]string{}, - Blueprint: ` -apex { - name: "com.android.apogee", - manifest: "apogee_manifest.json", - file_contexts: ":com.android.apogee-file_contexts", - certificate: "com.android.apogee.certificate", -} -` + simpleModuleDoNotConvertBp2build("filegroup", "com.android.apogee-file_contexts"), - ExpectedBazelTargets: []string{ - MakeBazelTarget("apex", "com.android.apogee", AttrNameToString{ - "certificate_name": `"com.android.apogee.certificate"`, - "file_contexts": `":com.android.apogee-file_contexts"`, - "manifest": `"apogee_manifest.json"`, - }), - }}) -} - -func TestBp2BuildOverrideApex_CertificateIsModule(t *testing.T) { - runOverrideApexTestCase(t, Bp2buildTestCase{ - Description: "override_apex - certificate is module", - ModuleTypeUnderTest: "override_apex", - ModuleTypeUnderTestFactory: apex.OverrideApexFactory, - Filesystem: map[string]string{}, - Blueprint: ` -android_app_certificate { - name: "com.android.apogee.certificate", - certificate: "com.android.apogee", - bazel_module: { bp2build_available: false }, -} - -filegroup { - name: "com.android.apogee-file_contexts", - srcs: [ - "com.android.apogee-file_contexts", - ], - bazel_module: { bp2build_available: false }, -} - -apex { - name: "com.android.apogee", - manifest: "apogee_manifest.json", - file_contexts: ":com.android.apogee-file_contexts", - certificate: ":com.android.apogee.certificate", - bazel_module: { bp2build_available: false }, -} - -android_app_certificate { - name: "com.google.android.apogee.certificate", - certificate: "com.google.android.apogee", - bazel_module: { bp2build_available: false }, -} - -override_apex { - name: "com.google.android.apogee", - base: ":com.android.apogee", - certificate: ":com.google.android.apogee.certificate", -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{ - "base_apex_name": `"com.android.apogee"`, - "file_contexts": `":com.android.apogee-file_contexts"`, - "certificate": `":com.google.android.apogee.certificate"`, - "manifest": `"apogee_manifest.json"`, - }), - }}) -} - -func TestBp2BuildOverrideApex_CertificateIsSrc(t *testing.T) { - runOverrideApexTestCase(t, Bp2buildTestCase{ - Description: "override_apex - certificate is src", - ModuleTypeUnderTest: "override_apex", - ModuleTypeUnderTestFactory: apex.OverrideApexFactory, - Filesystem: map[string]string{}, - Blueprint: ` -android_app_certificate { - name: "com.android.apogee.certificate", - certificate: "com.android.apogee", - bazel_module: { bp2build_available: false }, -} - -filegroup { - name: "com.android.apogee-file_contexts", - srcs: [ - "com.android.apogee-file_contexts", - ], - bazel_module: { bp2build_available: false }, -} - -apex { - name: "com.android.apogee", - manifest: "apogee_manifest.json", - file_contexts: ":com.android.apogee-file_contexts", - certificate: ":com.android.apogee.certificate", - bazel_module: { bp2build_available: false }, -} - -override_apex { - name: "com.google.android.apogee", - base: ":com.android.apogee", - certificate: "com.google.android.apogee.certificate", -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{ - "base_apex_name": `"com.android.apogee"`, - "file_contexts": `":com.android.apogee-file_contexts"`, - "certificate_name": `"com.google.android.apogee.certificate"`, - "manifest": `"apogee_manifest.json"`, - }), - }}) -} - -func TestApexTestBundleSimple(t *testing.T) { - runApexTestCase(t, Bp2buildTestCase{ - Description: "apex_test - simple", - ModuleTypeUnderTest: "apex_test", - ModuleTypeUnderTestFactory: apex.TestApexBundleFactory, - Filesystem: map[string]string{}, - Blueprint: ` -cc_test { name: "cc_test_1", bazel_module: { bp2build_available: false } } - -apex_test { - name: "test_com.android.apogee", - file_contexts: "file_contexts_file", - tests: ["cc_test_1"], -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("apex", "test_com.android.apogee", AttrNameToString{ - "file_contexts": `"file_contexts_file"`, - "manifest": `"apex_manifest.json"`, - "testonly": `True`, - "tests": `[":cc_test_1"]`, - }), - }}) -} - -func TestApexBundle_overridePlusProductVars(t *testing.T) { - // Reproduction of b/271424349 - // Tests that overriding an apex that uses product variables correctly copies the product var - // selects over to the override. - runOverrideApexTestCase(t, Bp2buildTestCase{ - Description: "apex - overriding a module that uses product vars", - ModuleTypeUnderTest: "override_apex", - ModuleTypeUnderTestFactory: apex.OverrideApexFactory, - Blueprint: ` -soong_config_string_variable { - name: "library_linking_strategy", - values: [ - "prefer_static", - ], -} - -soong_config_module_type { - name: "library_linking_strategy_apex_defaults", - module_type: "apex_defaults", - config_namespace: "ANDROID", - variables: ["library_linking_strategy"], - properties: [ - "manifest", - "min_sdk_version", - ], -} - -library_linking_strategy_apex_defaults { - name: "higher_min_sdk_when_prefer_static", - soong_config_variables: { - library_linking_strategy: { - // Use the R min_sdk_version - prefer_static: {}, - // Override the R min_sdk_version to min_sdk_version that supports dcla - conditions_default: { - min_sdk_version: "31", - }, - }, - }, -} - -filegroup { - name: "foo-file_contexts", - srcs: [ - "com.android.apogee-file_contexts", - ], - bazel_module: { bp2build_available: false }, -} - -apex { - name: "foo", - defaults: ["higher_min_sdk_when_prefer_static"], - min_sdk_version: "30", - package_name: "pkg_name", - file_contexts: ":foo-file_contexts", -} -override_apex { - name: "override_foo", - base: ":foo", - package_name: "override_pkg_name", -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("apex", "foo", AttrNameToString{ - "file_contexts": `":foo-file_contexts"`, - "manifest": `"apex_manifest.json"`, - "min_sdk_version": `select({ - "//build/bazel/product_variables:android__library_linking_strategy__prefer_static": "30", - "//conditions:default": "31", - })`, - "package_name": `"pkg_name"`, - }), MakeBazelTarget("apex", "override_foo", AttrNameToString{ - "base_apex_name": `"foo"`, - "file_contexts": `":foo-file_contexts"`, - "manifest": `"apex_manifest.json"`, - "min_sdk_version": `select({ - "//build/bazel/product_variables:android__library_linking_strategy__prefer_static": "30", - "//conditions:default": "31", - })`, - "package_name": `"override_pkg_name"`, - }), - }}) -} - -func TestApexBundleSimple_customCannedFsConfig(t *testing.T) { - runApexTestCase(t, Bp2buildTestCase{ - Description: "apex - custom canned_fs_config", - ModuleTypeUnderTest: "apex", - ModuleTypeUnderTestFactory: apex.BundleFactory, - Filesystem: map[string]string{}, - Blueprint: ` -apex { - name: "com.android.apogee", - canned_fs_config: "custom.canned_fs_config", - file_contexts: "file_contexts_file", -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("apex", "com.android.apogee", AttrNameToString{ - "canned_fs_config": `"custom.canned_fs_config"`, - "file_contexts": `"file_contexts_file"`, - "manifest": `"apex_manifest.json"`, - }), - }}) -} diff --git a/bp2build/apex_key_conversion_test.go b/bp2build/apex_key_conversion_test.go deleted file mode 100644 index f9a68c918a..0000000000 --- a/bp2build/apex_key_conversion_test.go +++ /dev/null @@ -1,97 +0,0 @@ -// 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 bp2build - -import ( - "android/soong/android" - "android/soong/apex" - - "testing" -) - -func runApexKeyTestCase(t *testing.T, tc Bp2buildTestCase) { - t.Helper() - RunBp2BuildTestCase(t, registerApexKeyModuleTypes, tc) -} - -func registerApexKeyModuleTypes(ctx android.RegistrationContext) { - ctx.RegisterModuleType("filegroup", android.FileGroupFactory) -} - -func TestApexKeySimple_KeysAreSrcFilesInSameDir(t *testing.T) { - runApexKeyTestCase(t, Bp2buildTestCase{ - Description: "apex key - keys are src files, use key attributes", - ModuleTypeUnderTest: "apex_key", - ModuleTypeUnderTestFactory: apex.ApexKeyFactory, - Filesystem: map[string]string{ - "com.android.apogee.avbpubkey": "", - "com.android.apogee.pem": "", - }, - Blueprint: ` -apex_key { - name: "com.android.apogee.key", - public_key: "com.android.apogee.avbpubkey", - private_key: "com.android.apogee.pem", -} -`, - ExpectedBazelTargets: []string{MakeBazelTargetNoRestrictions("apex_key", "com.android.apogee.key", AttrNameToString{ - "private_key": `"com.android.apogee.pem"`, - "public_key": `"com.android.apogee.avbpubkey"`, - }), - }}) -} - -func TestApexKeySimple_KeysAreSrcFilesNotInDir(t *testing.T) { - runApexKeyTestCase(t, Bp2buildTestCase{ - Description: "apex key - keys are not src or module, use key_name attributes", - ModuleTypeUnderTest: "apex_key", - ModuleTypeUnderTestFactory: apex.ApexKeyFactory, - Filesystem: map[string]string{ - // deliberately left empty - }, - Blueprint: ` -apex_key { - name: "com.android.apogee.key", - public_key: "com.android.apogee.avbpubkey", - private_key: "com.android.apogee.pem", -} -`, - ExpectedBazelTargets: []string{MakeBazelTargetNoRestrictions("apex_key", "com.android.apogee.key", AttrNameToString{ - "private_key_name": `"com.android.apogee.pem"`, - "public_key_name": `"com.android.apogee.avbpubkey"`, - }), - }}) -} - -func TestApexKey_KeysAreModules(t *testing.T) { - runApexKeyTestCase(t, Bp2buildTestCase{ - Description: "apex key - keys are modules, use key attributes", - ModuleTypeUnderTest: "apex_key", - ModuleTypeUnderTestFactory: apex.ApexKeyFactory, - Filesystem: map[string]string{}, - Blueprint: ` -apex_key { - name: "com.android.apogee.key", - public_key: ":com.android.apogee.avbpubkey", - private_key: ":com.android.apogee.pem", -} -` + simpleModuleDoNotConvertBp2build("filegroup", "com.android.apogee.avbpubkey") + - simpleModuleDoNotConvertBp2build("filegroup", "com.android.apogee.pem"), - ExpectedBazelTargets: []string{MakeBazelTargetNoRestrictions("apex_key", "com.android.apogee.key", AttrNameToString{ - "private_key": `":com.android.apogee.pem"`, - "public_key": `":com.android.apogee.avbpubkey"`, - }), - }}) -} diff --git a/bp2build/api_domain_conversion_test.go b/bp2build/api_domain_conversion_test.go deleted file mode 100644 index 224008fef1..0000000000 --- a/bp2build/api_domain_conversion_test.go +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2022 Google Inc. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package bp2build - -import ( - "testing" - - "android/soong/android" - "android/soong/cc" -) - -func registerApiDomainModuleTypes(ctx android.RegistrationContext) { - android.RegisterApiDomainBuildComponents(ctx) - cc.RegisterNdkModuleTypes(ctx) - cc.RegisterLibraryBuildComponents(ctx) -} - -func TestApiDomainContributionsTest(t *testing.T) { - bp := ` - api_domain { - name: "system", - cc_api_contributions: [ - "libfoo.ndk", - "libbar", - ], - } - ` - fs := map[string]string{ - "libfoo/Android.bp": ` - ndk_library { - name: "libfoo", - } - `, - "libbar/Android.bp": ` - cc_library { - name: "libbar", - } - `, - } - expectedBazelTarget := MakeBazelTargetNoRestrictions( - "api_domain", - "system", - AttrNameToString{ - "cc_api_contributions": `[ - "//libfoo:libfoo.ndk.contribution", - "//libbar:libbar.contribution", - ]`, - "target_compatible_with": `["//build/bazel/platforms/os:android"]`, - }, - ) - RunApiBp2BuildTestCase(t, registerApiDomainModuleTypes, Bp2buildTestCase{ - Blueprint: bp, - ExpectedBazelTargets: []string{expectedBazelTarget}, - Filesystem: fs, - }) -} diff --git a/bp2build/bp2build.go b/bp2build/bp2build.go deleted file mode 100644 index d1dfb9d36e..0000000000 --- a/bp2build/bp2build.go +++ /dev/null @@ -1,142 +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 bp2build - -import ( - "fmt" - "os" - "path/filepath" - "strings" - - "android/soong/android" - "android/soong/bazel" - "android/soong/shared" -) - -func deleteFilesExcept(ctx *CodegenContext, rootOutputPath android.OutputPath, except []BazelFile) { - // Delete files that should no longer be present. - bp2buildDirAbs := shared.JoinPath(ctx.topDir, rootOutputPath.String()) - - filesToDelete := make(map[string]struct{}) - err := filepath.Walk(bp2buildDirAbs, - func(path string, info os.FileInfo, err error) error { - if err != nil { - return err - } - if !info.IsDir() { - relPath, err := filepath.Rel(bp2buildDirAbs, path) - if err != nil { - return err - } - filesToDelete[relPath] = struct{}{} - } - return nil - }) - if err != nil { - fmt.Printf("ERROR reading %s: %s", bp2buildDirAbs, err) - os.Exit(1) - } - - for _, bazelFile := range except { - filePath := filepath.Join(bazelFile.Dir, bazelFile.Basename) - delete(filesToDelete, filePath) - } - for f, _ := range filesToDelete { - absPath := shared.JoinPath(bp2buildDirAbs, f) - if err := os.RemoveAll(absPath); err != nil { - fmt.Printf("ERROR deleting %s: %s", absPath, err) - os.Exit(1) - } - } -} - -// Codegen is the backend of bp2build. The code generator is responsible for -// writing .bzl files that are equivalent to Android.bp files that are capable -// of being built with Bazel. -func Codegen(ctx *CodegenContext) *CodegenMetrics { - // This directory stores BUILD files that could be eventually checked-in. - bp2buildDir := android.PathForOutput(ctx, "bp2build") - - res, errs := GenerateBazelTargets(ctx, true) - if len(errs) > 0 { - errMsgs := make([]string, len(errs)) - for i, err := range errs { - errMsgs[i] = fmt.Sprintf("%q", err) - } - fmt.Printf("ERROR: Encountered %d error(s): \nERROR: %s", len(errs), strings.Join(errMsgs, "\n")) - os.Exit(1) - } - bp2buildFiles := CreateBazelFiles(ctx.Config(), nil, res.buildFileToTargets, ctx.mode) - writeFiles(ctx, bp2buildDir, bp2buildFiles) - // Delete files under the bp2build root which weren't just written. An - // alternative would have been to delete the whole directory and write these - // files. However, this would regenerate files which were otherwise unchanged - // since the last bp2build run, which would have negative incremental - // performance implications. - deleteFilesExcept(ctx, bp2buildDir, bp2buildFiles) - - injectionFiles, err := CreateSoongInjectionDirFiles(ctx, res.metrics) - if err != nil { - fmt.Printf("%s\n", err.Error()) - os.Exit(1) - } - writeFiles(ctx, android.PathForOutput(ctx, bazel.SoongInjectionDirName), injectionFiles) - return &res.metrics -} - -// Wrapper function that will be responsible for all files in soong_injection directory -// This includes -// 1. config value(s) that are hardcoded in Soong -// 2. product_config variables -func CreateSoongInjectionDirFiles(ctx *CodegenContext, metrics CodegenMetrics) ([]BazelFile, error) { - var ret []BazelFile - - productConfigFiles, err := CreateProductConfigFiles(ctx) - if err != nil { - return nil, err - } - ret = append(ret, productConfigFiles...) - injectionFiles, err := soongInjectionFiles(ctx.Config(), metrics) - if err != nil { - return nil, err - } - ret = append(ret, injectionFiles...) - return ret, nil -} - -// Get the output directory and create it if it doesn't exist. -func getOrCreateOutputDir(outputDir android.OutputPath, ctx android.PathContext, dir string) android.OutputPath { - dirPath := outputDir.Join(ctx, dir) - if err := android.CreateOutputDirIfNonexistent(dirPath, os.ModePerm); err != nil { - fmt.Printf("ERROR: path %s: %s", dirPath, err.Error()) - } - return dirPath -} - -// writeFiles materializes a list of BazelFile rooted at outputDir. -func writeFiles(ctx android.PathContext, outputDir android.OutputPath, files []BazelFile) { - for _, f := range files { - p := getOrCreateOutputDir(outputDir, ctx, f.Dir).Join(ctx, f.Basename) - if err := writeFile(p, f.Contents); err != nil { - panic(fmt.Errorf("Failed to write %q (dir %q) due to %q", f.Basename, f.Dir, err)) - } - } -} - -func writeFile(pathToFile android.OutputPath, content string) error { - // These files are made editable to allow users to modify and iterate on them - // in the source tree. - return android.WriteFileToOutputDir(pathToFile, []byte(content), 0644) -} diff --git a/bp2build/bp2build_product_config.go b/bp2build/bp2build_product_config.go deleted file mode 100644 index 3abef9db18..0000000000 --- a/bp2build/bp2build_product_config.go +++ /dev/null @@ -1,119 +0,0 @@ -package bp2build - -import ( - "fmt" - "os" - "path/filepath" - "strings" -) - -func CreateProductConfigFiles( - ctx *CodegenContext) ([]BazelFile, error) { - cfg := &ctx.config - targetProduct := "unknown" - if cfg.HasDeviceProduct() { - targetProduct = cfg.DeviceProduct() - } - targetBuildVariant := "user" - if cfg.Eng() { - targetBuildVariant = "eng" - } else if cfg.Debuggable() { - targetBuildVariant = "userdebug" - } - - productVariablesFileName := cfg.ProductVariablesFileName - if !strings.HasPrefix(productVariablesFileName, "/") { - productVariablesFileName = filepath.Join(ctx.topDir, productVariablesFileName) - } - bytes, err := os.ReadFile(productVariablesFileName) - if err != nil { - return nil, err - } - - // TODO(b/249685973): the name is product_config_platforms because product_config - // was already used for other files. Deduplicate them. - currentProductFolder := fmt.Sprintf("product_config_platforms/products/%s-%s", targetProduct, targetBuildVariant) - - productReplacer := strings.NewReplacer( - "{PRODUCT}", targetProduct, - "{VARIANT}", targetBuildVariant, - "{PRODUCT_FOLDER}", currentProductFolder) - - result := []BazelFile{ - newFile( - currentProductFolder, - "soong.variables.bzl", - `variables = json.decode("""`+strings.ReplaceAll(string(bytes), "\\", "\\\\")+`""")`), - newFile( - currentProductFolder, - "BUILD", - productReplacer.Replace(` -package(default_visibility=[ - "@soong_injection//product_config_platforms:__subpackages__", - "@//build/bazel/product_config:__subpackages__", -]) -load(":soong.variables.bzl", _soong_variables = "variables") -load("@//build/bazel/product_config:android_product.bzl", "android_product") - -android_product( - name = "{PRODUCT}-{VARIANT}", - soong_variables = _soong_variables, -) -`)), - newFile( - "product_config_platforms", - "BUILD.bazel", - productReplacer.Replace(` -package(default_visibility = [ - "@//build/bazel/product_config:__subpackages__", - "@soong_injection//product_config_platforms:__subpackages__", -]) -`)), - newFile( - "product_config_platforms", - "product_labels.bzl", - productReplacer.Replace(` -# This file keeps a list of all the products in the android source tree, because they're -# discovered as part of a preprocessing step before bazel runs. -# TODO: When we start generating the platforms for more than just the -# currently lunched product, they should all be listed here -product_labels = [ - "@soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}" -] -`)), - newFile( - "product_config_platforms", - "common.bazelrc", - productReplacer.Replace(` -build --platforms @soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_linux_x86_64 - -build:android --platforms=@soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT} -build:linux_x86_64 --platforms=@soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_linux_x86_64 -build:linux_bionic_x86_64 --platforms=@soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_linux_bionic_x86_64 -build:linux_musl_x86 --platforms=@soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_linux_musl_x86 -build:linux_musl_x86_64 --platforms=@soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_linux_musl_x86_64 -`)), - newFile( - "product_config_platforms", - "linux.bazelrc", - productReplacer.Replace(` -build --host_platform @soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_linux_x86_64 -`)), - newFile( - "product_config_platforms", - "darwin.bazelrc", - productReplacer.Replace(` -build --host_platform @soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_darwin_x86_64 -`)), - newFile( - "product_config_platforms", - "platform_mappings", - productReplacer.Replace(` -flags: - --cpu=k8 - @soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT} -`)), - } - - return result, nil -} diff --git a/bp2build/bpf_conversion_test.go b/bp2build/bpf_conversion_test.go deleted file mode 100644 index 1259f9e356..0000000000 --- a/bp2build/bpf_conversion_test.go +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2022 Google Inc. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package bp2build - -import ( - "android/soong/android" - "android/soong/bpf" - - "testing" -) - -func runBpfTestCase(t *testing.T, tc Bp2buildTestCase) { - t.Helper() - (&tc).ModuleTypeUnderTest = "bpf" - (&tc).ModuleTypeUnderTestFactory = bpf.BpfFactory - RunBp2BuildTestCase(t, registerBpfModuleTypes, tc) -} - -func registerBpfModuleTypes(ctx android.RegistrationContext) {} - -func TestBpfSupportedAttrs(t *testing.T) { - runBpfTestCase(t, Bp2buildTestCase{ - Description: "Bpf module only converts supported attributes", - Filesystem: map[string]string{}, - Blueprint: ` -bpf { - name: "bpfTestOut.o", - srcs: ["bpfTestSrcOne.c", - "bpfTestSrcTwo.c"], - btf: true, - cflags: ["-bpfCflagOne", - "-bpfCflagTwo"], - include_dirs: ["ia/ib/ic"], - sub_dir: "sa/ab", -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("bpf", "bpfTestOut.o", AttrNameToString{ - "absolute_includes": `["ia/ib/ic"]`, - "btf": `True`, - "copts": `[ - "-bpfCflagOne", - "-bpfCflagTwo", - ]`, - "srcs": `[ - "bpfTestSrcOne.c", - "bpfTestSrcTwo.c", - ]`, - "target_compatible_with": `["//build/bazel/platforms/os:android"]`, - }), - }, - }) -} diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go index b7678a4697..af2f55019c 100644 --- a/bp2build/build_conversion.go +++ b/bp2build/build_conversion.go @@ -36,18 +36,24 @@ type BazelAttributes struct { Attrs map[string]string } -type BazelTarget struct { - name string - packageName string - content string - ruleClass string - bzlLoadLocation string +type BazelLoadSymbol struct { + // The name of the symbol in the file being loaded + symbol string + // The name the symbol wil have in this file. Can be left blank to use the same name as symbol. + alias string +} + +type BazelLoad struct { + file string + symbols []BazelLoadSymbol } -// IsLoadedFromStarlark determines if the BazelTarget's rule class is loaded from a .bzl file, -// as opposed to a native rule built into Bazel. -func (t BazelTarget) IsLoadedFromStarlark() bool { - return t.bzlLoadLocation != "" +type BazelTarget struct { + name string + packageName string + content string + ruleClass string + loads []BazelLoad } // Label is the fully qualified Bazel label constructed from the BazelTarget's @@ -92,45 +98,77 @@ func (targets BazelTargets) sort() { // statements (use LoadStatements for that), since the targets are usually not // adjacent to the load statements at the top of the BUILD file. func (targets BazelTargets) String() string { - var res string + var res strings.Builder for i, target := range targets { if target.ruleClass != "package" { - res += target.content + res.WriteString(target.content) } if i != len(targets)-1 { - res += "\n\n" + res.WriteString("\n\n") } } - return res + return res.String() } // LoadStatements return the string representation of the sorted and deduplicated // Starlark rule load statements needed by a group of BazelTargets. func (targets BazelTargets) LoadStatements() string { - bzlToLoadedSymbols := map[string][]string{} + // First, merge all the load statements from all the targets onto one list + bzlToLoadedSymbols := map[string][]BazelLoadSymbol{} for _, target := range targets { - if target.IsLoadedFromStarlark() { - bzlToLoadedSymbols[target.bzlLoadLocation] = - append(bzlToLoadedSymbols[target.bzlLoadLocation], target.ruleClass) + for _, load := range target.loads { + outer: + for _, symbol := range load.symbols { + alias := symbol.alias + if alias == "" { + alias = symbol.symbol + } + for _, otherSymbol := range bzlToLoadedSymbols[load.file] { + otherAlias := otherSymbol.alias + if otherAlias == "" { + otherAlias = otherSymbol.symbol + } + if symbol.symbol == otherSymbol.symbol && alias == otherAlias { + continue outer + } else if alias == otherAlias { + panic(fmt.Sprintf("Conflicting destination (%s) for loads of %s and %s", alias, symbol.symbol, otherSymbol.symbol)) + } + } + bzlToLoadedSymbols[load.file] = append(bzlToLoadedSymbols[load.file], symbol) + } } } - var loadStatements []string - for bzl, ruleClasses := range bzlToLoadedSymbols { - loadStatement := "load(\"" - loadStatement += bzl - loadStatement += "\", " - ruleClasses = android.SortedUniqueStrings(ruleClasses) - for i, ruleClass := range ruleClasses { - loadStatement += "\"" + ruleClass + "\"" - if i != len(ruleClasses)-1 { - loadStatement += ", " + var loadStatements strings.Builder + for i, bzl := range android.SortedKeys(bzlToLoadedSymbols) { + symbols := bzlToLoadedSymbols[bzl] + loadStatements.WriteString("load(\"") + loadStatements.WriteString(bzl) + loadStatements.WriteString("\", ") + sort.Slice(symbols, func(i, j int) bool { + if symbols[i].symbol < symbols[j].symbol { + return true + } + return symbols[i].alias < symbols[j].alias + }) + for j, symbol := range symbols { + if symbol.alias != "" && symbol.alias != symbol.symbol { + loadStatements.WriteString(symbol.alias) + loadStatements.WriteString(" = ") + } + loadStatements.WriteString("\"") + loadStatements.WriteString(symbol.symbol) + loadStatements.WriteString("\"") + if j != len(symbols)-1 { + loadStatements.WriteString(", ") } } - loadStatement += ")" - loadStatements = append(loadStatements, loadStatement) + loadStatements.WriteString(")") + if i != len(bzlToLoadedSymbols)-1 { + loadStatements.WriteString("\n") + } } - return strings.Join(android.SortedUniqueStrings(loadStatements), "\n") + return loadStatements.String() } type bpToBuildContext interface { @@ -160,21 +198,13 @@ func (ctx *CodegenContext) Mode() CodegenMode { type CodegenMode int const ( - // Bp2Build - generate BUILD files with targets buildable by Bazel directly. - // - // This mode is used for the Soong->Bazel build definition conversion. - Bp2Build CodegenMode = iota - // QueryView - generate BUILD files with targets representing fully mutated // Soong modules, representing the fully configured Soong module graph with // variants and dependency edges. // // This mode is used for discovering and introspecting the existing Soong // module graph. - QueryView - - // ApiBp2build - generate BUILD files for API contribution targets - ApiBp2build + QueryView CodegenMode = iota ) type unconvertedDepsMode int @@ -189,12 +219,8 @@ const ( func (mode CodegenMode) String() string { switch mode { - case Bp2Build: - return "Bp2Build" case QueryView: return "QueryView" - case ApiBp2build: - return "ApiBp2build" default: return fmt.Sprintf("%d", mode) } @@ -220,9 +246,6 @@ func (ctx *CodegenContext) Context() *android.Context { return ctx.context } // writing BUILD files in the output directory. func NewCodegenContext(config android.Config, context *android.Context, mode CodegenMode, topDir string) *CodegenContext { var unconvertedDeps unconvertedDepsMode - if config.IsEnvTrue("BP2BUILD_ERROR_UNCONVERTED") { - unconvertedDeps = errorModulesUnconvertedDeps - } return &CodegenContext{ context: context, config: config, @@ -243,8 +266,8 @@ func propsToAttributes(props map[string]string) string { } type conversionResults struct { - buildFileToTargets map[string]BazelTargets - metrics CodegenMetrics + buildFileToTargets map[string]BazelTargets + moduleNameToPartition map[string]string } func (r conversionResults) BuildDirToTargets() map[string]BazelTargets { @@ -252,82 +275,23 @@ func (r conversionResults) BuildDirToTargets() map[string]BazelTargets { } func GenerateBazelTargets(ctx *CodegenContext, generateFilegroups bool) (conversionResults, []error) { + ctx.Context().BeginEvent("GenerateBazelTargets") + defer ctx.Context().EndEvent("GenerateBazelTargets") buildFileToTargets := make(map[string]BazelTargets) - // Simple metrics tracking for bp2build - metrics := CreateCodegenMetrics() - dirs := make(map[string]bool) + moduleNameToPartition := make(map[string]string) var errs []error bpCtx := ctx.Context() bpCtx.VisitAllModules(func(m blueprint.Module) { dir := bpCtx.ModuleDir(m) - moduleType := bpCtx.ModuleType(m) dirs[dir] = true var targets []BazelTarget switch ctx.Mode() { - case Bp2Build: - // There are two main ways of converting a Soong module to Bazel: - // 1) Manually handcrafting a Bazel target and associating the module with its label - // 2) Automatically generating with bp2build converters - // - // bp2build converters are used for the majority of modules. - if b, ok := m.(android.Bazelable); ok && b.HasHandcraftedLabel() { - // Handle modules converted to handcrafted targets. - // - // Since these modules are associated with some handcrafted - // target in a BUILD file, we don't autoconvert them. - - // Log the module. - metrics.AddConvertedModule(m, moduleType, dir, Handcrafted) - } else if aModule, ok := m.(android.Module); ok && aModule.IsConvertedByBp2build() { - // Handle modules converted to generated targets. - - // Log the module. - metrics.AddConvertedModule(aModule, moduleType, dir, Generated) - - // Handle modules with unconverted deps. By default, emit a warning. - if unconvertedDeps := aModule.GetUnconvertedBp2buildDeps(); len(unconvertedDeps) > 0 { - msg := fmt.Sprintf("%s %s:%s depends on unconverted modules: %s", - moduleType, bpCtx.ModuleDir(m), m.Name(), strings.Join(unconvertedDeps, ", ")) - switch ctx.unconvertedDepMode { - case warnUnconvertedDeps: - metrics.moduleWithUnconvertedDepsMsgs = append(metrics.moduleWithUnconvertedDepsMsgs, msg) - case errorModulesUnconvertedDeps: - errs = append(errs, fmt.Errorf(msg)) - return - } - } - if unconvertedDeps := aModule.GetMissingBp2buildDeps(); len(unconvertedDeps) > 0 { - msg := fmt.Sprintf("%s %s:%s depends on missing modules: %s", - moduleType, bpCtx.ModuleDir(m), m.Name(), strings.Join(unconvertedDeps, ", ")) - switch ctx.unconvertedDepMode { - case warnUnconvertedDeps: - metrics.moduleWithMissingDepsMsgs = append(metrics.moduleWithMissingDepsMsgs, msg) - case errorModulesUnconvertedDeps: - errs = append(errs, fmt.Errorf(msg)) - return - } - } - var targetErrs []error - targets, targetErrs = generateBazelTargets(bpCtx, aModule) - errs = append(errs, targetErrs...) - for _, t := range targets { - // A module can potentially generate more than 1 Bazel - // target, each of a different rule class. - metrics.IncrementRuleClassCount(t.ruleClass) - } - } else if _, ok := ctx.Config().BazelModulesForceEnabledByFlag()[m.Name()]; ok && m.Name() != "" { - err := fmt.Errorf("Force Enabled Module %s not converted", m.Name()) - errs = append(errs, err) - } else { - metrics.AddUnconvertedModule(moduleType) - return - } case QueryView: // Blocklist certain module types from being generated. if canonicalizeModuleType(bpCtx.ModuleType(m)) == "package" { @@ -340,10 +304,6 @@ func GenerateBazelTargets(ctx *CodegenContext, generateFilegroups bool) (convers errs = append(errs, err) } targets = append(targets, t) - case ApiBp2build: - if aModule, ok := m.(android.Module); ok && aModule.IsConvertedByBp2build() { - targets, errs = generateBazelTargets(bpCtx, aModule) - } default: errs = append(errs, fmt.Errorf("Unknown code-generation mode: %s", ctx.Mode())) return @@ -374,73 +334,18 @@ func GenerateBazelTargets(ctx *CodegenContext, generateFilegroups bool) (convers for dir := range dirs { buildFileToTargets[dir] = append(buildFileToTargets[dir], BazelTarget{ name: "bp2build_all_srcs", - content: `filegroup(name = "bp2build_all_srcs", srcs = glob(["**/*"]))`, + content: `filegroup(name = "bp2build_all_srcs", srcs = glob(["**/*"]), tags = ["manual"])`, ruleClass: "filegroup", }) } } return conversionResults{ - buildFileToTargets: buildFileToTargets, - metrics: metrics, + buildFileToTargets: buildFileToTargets, + moduleNameToPartition: moduleNameToPartition, }, errs } -func generateBazelTargets(ctx bpToBuildContext, m android.Module) ([]BazelTarget, []error) { - var targets []BazelTarget - var errs []error - for _, m := range m.Bp2buildTargets() { - target, err := generateBazelTarget(ctx, m) - if err != nil { - errs = append(errs, err) - return targets, errs - } - targets = append(targets, target) - } - return targets, errs -} - -type bp2buildModule interface { - TargetName() string - TargetPackage() string - BazelRuleClass() string - BazelRuleLoadLocation() string - BazelAttributes() []interface{} -} - -func generateBazelTarget(ctx bpToBuildContext, m bp2buildModule) (BazelTarget, error) { - ruleClass := m.BazelRuleClass() - bzlLoadLocation := m.BazelRuleLoadLocation() - - // extract the bazel attributes from the module. - attrs := m.BazelAttributes() - props, err := extractModuleProperties(attrs, true) - if err != nil { - return BazelTarget{}, err - } - - // name is handled in a special manner - delete(props.Attrs, "name") - - // Return the Bazel target with rule class and attributes, ready to be - // code-generated. - attributes := propsToAttributes(props.Attrs) - var content string - targetName := m.TargetName() - if targetName != "" { - content = fmt.Sprintf(ruleTargetTemplate, ruleClass, targetName, attributes) - } else { - content = fmt.Sprintf(unnamedRuleTargetTemplate, ruleClass, attributes) - } - return BazelTarget{ - name: targetName, - packageName: m.TargetPackage(), - ruleClass: ruleClass, - bzlLoadLocation: bzlLoadLocation, - content: content, - }, nil -} - // 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, error) { @@ -600,6 +505,11 @@ func prettyPrint(propertyValue reflect.Value, indent int, emitZeroValues bool) ( // TODO(b/164227191): implement pretty print for interfaces. // Interfaces are used for for arch, multilib and target properties. return "", nil + case reflect.Map: + if v, ok := propertyValue.Interface().(bazel.StringMapAttribute); ok { + return starlark_fmt.PrintStringStringDict(v, indent), nil + } + return "", fmt.Errorf("bp2build expects map of type map[string]string for field: %s", propertyValue) default: return "", fmt.Errorf( "unexpected kind for property struct field: %s", propertyValue.Kind()) diff --git a/bp2build/build_conversion_test.go b/bp2build/build_conversion_test.go deleted file mode 100644 index 73ee26b607..0000000000 --- a/bp2build/build_conversion_test.go +++ /dev/null @@ -1,1900 +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 bp2build - -import ( - "fmt" - "strings" - "testing" - - "android/soong/android" - "android/soong/android/allowlists" - "android/soong/python" -) - -func TestGenerateSoongModuleTargets(t *testing.T) { - testCases := []struct { - description string - bp string - expectedBazelTarget string - }{ - { - description: "only name", - bp: `custom { name: "foo" } - `, - expectedBazelTarget: `soong_module( - name = "foo", - soong_module_name = "foo", - soong_module_type = "custom", - soong_module_variant = "", - soong_module_deps = [ - ], - bool_prop = False, - string_prop = "", -)`, - }, - { - description: "handles bool", - bp: `custom { - name: "foo", - bool_prop: true, -} - `, - expectedBazelTarget: `soong_module( - name = "foo", - soong_module_name = "foo", - soong_module_type = "custom", - soong_module_variant = "", - soong_module_deps = [ - ], - bool_prop = True, - string_prop = "", -)`, - }, - { - description: "string escaping", - bp: `custom { - name: "foo", - owner: "a_string_with\"quotes\"_and_\\backslashes\\\\", -} - `, - expectedBazelTarget: `soong_module( - name = "foo", - soong_module_name = "foo", - soong_module_type = "custom", - soong_module_variant = "", - soong_module_deps = [ - ], - bool_prop = False, - owner = "a_string_with\"quotes\"_and_\\backslashes\\\\", - string_prop = "", -)`, - }, - { - description: "single item string list", - bp: `custom { - name: "foo", - required: ["bar"], -} - `, - expectedBazelTarget: `soong_module( - name = "foo", - soong_module_name = "foo", - soong_module_type = "custom", - soong_module_variant = "", - soong_module_deps = [ - ], - bool_prop = False, - required = ["bar"], - string_prop = "", -)`, - }, - { - description: "list of strings", - bp: `custom { - name: "foo", - target_required: ["qux", "bazqux"], -} - `, - expectedBazelTarget: `soong_module( - name = "foo", - soong_module_name = "foo", - soong_module_type = "custom", - soong_module_variant = "", - soong_module_deps = [ - ], - bool_prop = False, - string_prop = "", - target_required = [ - "qux", - "bazqux", - ], -)`, - }, - { - description: "dist/dists", - bp: `custom { - name: "foo", - dist: { - targets: ["goal_foo"], - tag: ".foo", - }, - dists: [{ - targets: ["goal_bar"], - tag: ".bar", - }], -} - `, - expectedBazelTarget: `soong_module( - name = "foo", - soong_module_name = "foo", - soong_module_type = "custom", - soong_module_variant = "", - soong_module_deps = [ - ], - bool_prop = False, - dist = { - "tag": ".foo", - "targets": ["goal_foo"], - }, - dists = [{ - "tag": ".bar", - "targets": ["goal_bar"], - }], - string_prop = "", -)`, - }, - { - description: "put it together", - bp: `custom { - name: "foo", - required: ["bar"], - target_required: ["qux", "bazqux"], - bool_prop: true, - owner: "custom_owner", - dists: [ - { - tag: ".tag", - targets: ["my_goal"], - }, - ], -} - `, - expectedBazelTarget: `soong_module( - name = "foo", - soong_module_name = "foo", - soong_module_type = "custom", - soong_module_variant = "", - soong_module_deps = [ - ], - bool_prop = True, - dists = [{ - "tag": ".tag", - "targets": ["my_goal"], - }], - owner = "custom_owner", - required = ["bar"], - string_prop = "", - target_required = [ - "qux", - "bazqux", - ], -)`, - }, - } - - dir := "." - for _, testCase := range testCases { - t.Run(testCase.description, func(t *testing.T) { - config := android.TestConfig(buildDir, nil, testCase.bp, nil) - ctx := android.NewTestContext(config) - - ctx.RegisterModuleType("custom", customModuleFactoryHostAndDevice) - ctx.Register() - - _, errs := ctx.ParseFileList(dir, []string{"Android.bp"}) - android.FailIfErrored(t, errs) - _, errs = ctx.PrepareBuildActions(config) - android.FailIfErrored(t, errs) - - codegenCtx := NewCodegenContext(config, ctx.Context, QueryView, "") - bazelTargets, err := generateBazelTargetsForDir(codegenCtx, dir) - android.FailIfErrored(t, err) - if actualCount, expectedCount := len(bazelTargets), 1; actualCount != expectedCount { - t.Fatalf("Expected %d bazel target, got %d", expectedCount, actualCount) - } - - actualBazelTarget := bazelTargets[0] - if actualBazelTarget.content != testCase.expectedBazelTarget { - t.Errorf( - "Expected generated Bazel target to be '%s', got '%s'", - testCase.expectedBazelTarget, - actualBazelTarget.content, - ) - } - }) - } -} - -func TestGenerateBazelTargetModules(t *testing.T) { - testCases := []Bp2buildTestCase{ - { - Description: "string prop empty", - Blueprint: `custom { - name: "foo", - string_literal_prop: "", - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("custom", "foo", AttrNameToString{ - "string_literal_prop": `""`, - }), - }, - }, - { - Description: `string prop "PROP"`, - Blueprint: `custom { - name: "foo", - string_literal_prop: "PROP", - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("custom", "foo", AttrNameToString{ - "string_literal_prop": `"PROP"`, - }), - }, - }, - { - Description: `string prop arch variant`, - Blueprint: `custom { - name: "foo", - arch: { - arm: { string_literal_prop: "ARM" }, - arm64: { string_literal_prop: "ARM64" }, - }, - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("custom", "foo", AttrNameToString{ - "string_literal_prop": `select({ - "//build/bazel/platforms/arch:arm": "ARM", - "//build/bazel/platforms/arch:arm64": "ARM64", - "//conditions:default": None, - })`, - }), - }, - }, - { - Description: "string ptr props", - Blueprint: `custom { - name: "foo", - string_ptr_prop: "", - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("custom", "foo", AttrNameToString{ - "string_ptr_prop": `""`, - }), - }, - }, - { - Description: "string list props", - Blueprint: `custom { - name: "foo", - string_list_prop: ["a", "b"], - string_ptr_prop: "a", - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("custom", "foo", AttrNameToString{ - "string_list_prop": `[ - "a", - "b", - ]`, - "string_ptr_prop": `"a"`, - }), - }, - }, - { - Description: "control characters", - Blueprint: `custom { - name: "foo", - string_list_prop: ["\t", "\n"], - string_ptr_prop: "a\t\n\r", - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("custom", "foo", AttrNameToString{ - "string_list_prop": `[ - "\t", - "\n", - ]`, - "string_ptr_prop": `"a\t\n\r"`, - }), - }, - }, - { - Description: "handles dep", - Blueprint: `custom { - name: "has_dep", - arch_paths: [":dep"], - bazel_module: { bp2build_available: true }, -} - -custom { - name: "dep", - arch_paths: ["abc"], - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("custom", "dep", AttrNameToString{ - "arch_paths": `["abc"]`, - }), - MakeBazelTarget("custom", "has_dep", AttrNameToString{ - "arch_paths": `[":dep"]`, - }), - }, - }, - { - Description: "non-existent dep", - Blueprint: `custom { - name: "has_dep", - arch_paths: [":dep"], - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("custom", "has_dep", AttrNameToString{ - "arch_paths": `[":dep__BP2BUILD__MISSING__DEP"]`, - }), - }, - }, - { - Description: "arch-variant srcs", - Blueprint: `custom { - name: "arch_paths", - arch: { - x86: { arch_paths: ["x86.txt"] }, - x86_64: { arch_paths: ["x86_64.txt"] }, - arm: { arch_paths: ["arm.txt"] }, - arm64: { arch_paths: ["arm64.txt"] }, - riscv64: { arch_paths: ["riscv64.txt"] }, - }, - target: { - linux: { arch_paths: ["linux.txt"] }, - bionic: { arch_paths: ["bionic.txt"] }, - host: { arch_paths: ["host.txt"] }, - not_windows: { arch_paths: ["not_windows.txt"] }, - android: { arch_paths: ["android.txt"] }, - linux_musl: { arch_paths: ["linux_musl.txt"] }, - musl: { arch_paths: ["musl.txt"] }, - linux_glibc: { arch_paths: ["linux_glibc.txt"] }, - glibc: { arch_paths: ["glibc.txt"] }, - linux_bionic: { arch_paths: ["linux_bionic.txt"] }, - darwin: { arch_paths: ["darwin.txt"] }, - windows: { arch_paths: ["windows.txt"] }, - }, - multilib: { - lib32: { arch_paths: ["lib32.txt"] }, - lib64: { arch_paths: ["lib64.txt"] }, - }, - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("custom", "arch_paths", AttrNameToString{ - "arch_paths": `select({ - "//build/bazel/platforms/arch:arm": [ - "arm.txt", - "lib32.txt", - ], - "//build/bazel/platforms/arch:arm64": [ - "arm64.txt", - "lib64.txt", - ], - "//build/bazel/platforms/arch:riscv64": [ - "riscv64.txt", - "lib64.txt", - ], - "//build/bazel/platforms/arch:x86": [ - "x86.txt", - "lib32.txt", - ], - "//build/bazel/platforms/arch:x86_64": [ - "x86_64.txt", - "lib64.txt", - ], - "//conditions:default": [], - }) + select({ - "//build/bazel/platforms/os:android": [ - "linux.txt", - "bionic.txt", - "android.txt", - ], - "//build/bazel/platforms/os:darwin": [ - "host.txt", - "darwin.txt", - "not_windows.txt", - ], - "//build/bazel/platforms/os:linux_bionic": [ - "host.txt", - "linux.txt", - "bionic.txt", - "linux_bionic.txt", - "not_windows.txt", - ], - "//build/bazel/platforms/os:linux_glibc": [ - "host.txt", - "linux.txt", - "glibc.txt", - "linux_glibc.txt", - "not_windows.txt", - ], - "//build/bazel/platforms/os:linux_musl": [ - "host.txt", - "linux.txt", - "musl.txt", - "linux_musl.txt", - "not_windows.txt", - ], - "//build/bazel/platforms/os:windows": [ - "host.txt", - "windows.txt", - ], - "//conditions:default": [], - })`, - }), - }, - }, - { - Description: "arch-variant deps", - Blueprint: `custom { - name: "has_dep", - arch: { - x86: { - arch_paths: [":dep"], - }, - }, - bazel_module: { bp2build_available: true }, -} - -custom { - name: "dep", - arch_paths: ["abc"], - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("custom", "dep", AttrNameToString{ - "arch_paths": `["abc"]`, - }), - MakeBazelTarget("custom", "has_dep", AttrNameToString{ - "arch_paths": `select({ - "//build/bazel/platforms/arch:x86": [":dep"], - "//conditions:default": [], - })`, - }), - }, - }, - { - Description: "embedded props", - Blueprint: `custom { - name: "embedded_props", - embedded_prop: "abc", - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("custom", "embedded_props", AttrNameToString{ - "embedded_attr": `"abc"`, - }), - }, - }, - { - Description: "ptr to embedded props", - Blueprint: `custom { - name: "ptr_to_embedded_props", - other_embedded_prop: "abc", - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("custom", "ptr_to_embedded_props", AttrNameToString{ - "other_embedded_attr": `"abc"`, - }), - }, - }, - } - - dir := "." - for _, testCase := range testCases { - t.Run(testCase.Description, func(t *testing.T) { - config := android.TestConfig(buildDir, nil, testCase.Blueprint, nil) - ctx := android.NewTestContext(config) - - registerCustomModuleForBp2buildConversion(ctx) - - _, errs := ctx.ParseFileList(dir, []string{"Android.bp"}) - if errored(t, testCase, errs) { - return - } - _, errs = ctx.ResolveDependencies(config) - if errored(t, testCase, errs) { - return - } - - codegenCtx := NewCodegenContext(config, ctx.Context, Bp2Build, "") - bazelTargets, err := generateBazelTargetsForDir(codegenCtx, dir) - android.FailIfErrored(t, err) - - if actualCount, expectedCount := len(bazelTargets), len(testCase.ExpectedBazelTargets); actualCount != expectedCount { - t.Errorf("Expected %d bazel target (%s),\ngot %d (%s)", expectedCount, testCase.ExpectedBazelTargets, actualCount, bazelTargets) - } else { - for i, expectedBazelTarget := range testCase.ExpectedBazelTargets { - actualBazelTarget := bazelTargets[i] - if actualBazelTarget.content != expectedBazelTarget { - t.Errorf( - "Expected generated Bazel target to be '%s', got '%s'", - expectedBazelTarget, - actualBazelTarget.content, - ) - } - } - } - }) - } -} - -func TestBp2buildHostAndDevice(t *testing.T) { - testCases := []Bp2buildTestCase{ - { - Description: "host and device, device only", - ModuleTypeUnderTest: "custom", - ModuleTypeUnderTestFactory: customModuleFactoryHostAndDevice, - Blueprint: `custom { - name: "foo", - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - makeBazelTargetHostOrDevice("custom", "foo", AttrNameToString{}, android.DeviceSupported), - }, - }, - { - Description: "host and device, both", - ModuleTypeUnderTest: "custom", - ModuleTypeUnderTestFactory: customModuleFactoryHostAndDevice, - Blueprint: `custom { - name: "foo", - host_supported: true, - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTargetNoRestrictions("custom", "foo", AttrNameToString{}), - }, - }, - { - Description: "host and device, host explicitly disabled", - ModuleTypeUnderTest: "custom", - ModuleTypeUnderTestFactory: customModuleFactoryHostAndDevice, - Blueprint: `custom { - name: "foo", - host_supported: false, - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - makeBazelTargetHostOrDevice("custom", "foo", AttrNameToString{}, android.DeviceSupported), - }, - }, - { - Description: "host and device, neither", - ModuleTypeUnderTest: "custom", - ModuleTypeUnderTestFactory: customModuleFactoryHostAndDevice, - Blueprint: `custom { - name: "foo", - host_supported: false, - device_supported: false, - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTargetNoRestrictions("custom", "foo", AttrNameToString{ - "target_compatible_with": `["@platforms//:incompatible"]`, - }), - }, - }, - { - Description: "host and device, neither, cannot override with product_var", - ModuleTypeUnderTest: "custom", - ModuleTypeUnderTestFactory: customModuleFactoryHostAndDevice, - Blueprint: `custom { - name: "foo", - host_supported: false, - device_supported: false, - product_variables: { unbundled_build: { enabled: true } }, - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTargetNoRestrictions("custom", "foo", AttrNameToString{ - "target_compatible_with": `["@platforms//:incompatible"]`, - }), - }, - }, - { - Description: "host and device, both, disabled overrided with product_var", - ModuleTypeUnderTest: "custom", - ModuleTypeUnderTestFactory: customModuleFactoryHostAndDevice, - Blueprint: `custom { - name: "foo", - host_supported: true, - device_supported: true, - enabled: false, - product_variables: { unbundled_build: { enabled: true } }, - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTargetNoRestrictions("custom", "foo", AttrNameToString{ - "target_compatible_with": `["//build/bazel/product_variables:unbundled_build"]`, - }), - }, - }, - { - Description: "host and device, neither, cannot override with arch enabled", - ModuleTypeUnderTest: "custom", - ModuleTypeUnderTestFactory: customModuleFactoryHostAndDevice, - Blueprint: `custom { - name: "foo", - host_supported: false, - device_supported: false, - arch: { x86: { enabled: true } }, - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTargetNoRestrictions("custom", "foo", AttrNameToString{ - "target_compatible_with": `["@platforms//:incompatible"]`, - }), - }, - }, - { - Description: "host and device, host only", - ModuleTypeUnderTest: "custom", - ModuleTypeUnderTestFactory: customModuleFactoryHostAndDevice, - Blueprint: `custom { - name: "foo", - host_supported: true, - device_supported: false, - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - makeBazelTargetHostOrDevice("custom", "foo", AttrNameToString{}, android.HostSupported), - }, - }, - { - Description: "host only", - ModuleTypeUnderTest: "custom", - ModuleTypeUnderTestFactory: customModuleFactoryHostSupported, - Blueprint: `custom { - name: "foo", - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - makeBazelTargetHostOrDevice("custom", "foo", AttrNameToString{}, android.HostSupported), - }, - }, - { - Description: "device only", - ModuleTypeUnderTest: "custom", - ModuleTypeUnderTestFactory: customModuleFactoryDeviceSupported, - Blueprint: `custom { - name: "foo", - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - makeBazelTargetHostOrDevice("custom", "foo", AttrNameToString{}, android.DeviceSupported), - }, - }, - { - Description: "host and device default, default", - ModuleTypeUnderTest: "custom", - ModuleTypeUnderTestFactory: customModuleFactoryHostAndDeviceDefault, - Blueprint: `custom { - name: "foo", - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTargetNoRestrictions("custom", "foo", AttrNameToString{}), - }, - }, - { - Description: "host and device default, device only", - ModuleTypeUnderTest: "custom", - ModuleTypeUnderTestFactory: customModuleFactoryHostAndDeviceDefault, - Blueprint: `custom { - name: "foo", - host_supported: false, - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - makeBazelTargetHostOrDevice("custom", "foo", AttrNameToString{}, android.DeviceSupported), - }, - }, - { - Description: "host and device default, host only", - ModuleTypeUnderTest: "custom", - ModuleTypeUnderTestFactory: customModuleFactoryHostAndDeviceDefault, - Blueprint: `custom { - name: "foo", - device_supported: false, - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - makeBazelTargetHostOrDevice("custom", "foo", AttrNameToString{}, android.HostSupported), - }, - }, - { - Description: "host and device default, neither", - ModuleTypeUnderTest: "custom", - ModuleTypeUnderTestFactory: customModuleFactoryHostAndDeviceDefault, - Blueprint: `custom { - name: "foo", - host_supported: false, - device_supported: false, - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTargetNoRestrictions("custom", "foo", AttrNameToString{ - "target_compatible_with": `["@platforms//:incompatible"]`, - }), - }, - }, - } - - for _, tc := range testCases { - t.Run(tc.Description, func(t *testing.T) { - RunBp2BuildTestCaseSimple(t, tc) - }) - } -} - -func TestLoadStatements(t *testing.T) { - testCases := []struct { - bazelTargets BazelTargets - expectedLoadStatements string - }{ - { - bazelTargets: BazelTargets{ - BazelTarget{ - name: "foo", - ruleClass: "cc_library", - bzlLoadLocation: "//build/bazel/rules:cc.bzl", - }, - }, - expectedLoadStatements: `load("//build/bazel/rules:cc.bzl", "cc_library")`, - }, - { - bazelTargets: BazelTargets{ - BazelTarget{ - name: "foo", - ruleClass: "cc_library", - bzlLoadLocation: "//build/bazel/rules:cc.bzl", - }, - BazelTarget{ - name: "bar", - ruleClass: "cc_library", - bzlLoadLocation: "//build/bazel/rules:cc.bzl", - }, - }, - expectedLoadStatements: `load("//build/bazel/rules:cc.bzl", "cc_library")`, - }, - { - bazelTargets: BazelTargets{ - BazelTarget{ - name: "foo", - ruleClass: "cc_library", - bzlLoadLocation: "//build/bazel/rules:cc.bzl", - }, - BazelTarget{ - name: "bar", - ruleClass: "cc_binary", - bzlLoadLocation: "//build/bazel/rules:cc.bzl", - }, - }, - expectedLoadStatements: `load("//build/bazel/rules:cc.bzl", "cc_binary", "cc_library")`, - }, - { - bazelTargets: BazelTargets{ - BazelTarget{ - name: "foo", - ruleClass: "cc_library", - bzlLoadLocation: "//build/bazel/rules:cc.bzl", - }, - BazelTarget{ - name: "bar", - ruleClass: "cc_binary", - bzlLoadLocation: "//build/bazel/rules:cc.bzl", - }, - BazelTarget{ - name: "baz", - ruleClass: "java_binary", - bzlLoadLocation: "//build/bazel/rules:java.bzl", - }, - }, - expectedLoadStatements: `load("//build/bazel/rules:cc.bzl", "cc_binary", "cc_library") -load("//build/bazel/rules:java.bzl", "java_binary")`, - }, - { - bazelTargets: BazelTargets{ - BazelTarget{ - name: "foo", - ruleClass: "cc_binary", - bzlLoadLocation: "//build/bazel/rules:cc.bzl", - }, - BazelTarget{ - name: "bar", - ruleClass: "java_binary", - bzlLoadLocation: "//build/bazel/rules:java.bzl", - }, - BazelTarget{ - name: "baz", - ruleClass: "genrule", - // Note: no bzlLoadLocation for native rules - }, - }, - expectedLoadStatements: `load("//build/bazel/rules:cc.bzl", "cc_binary") -load("//build/bazel/rules:java.bzl", "java_binary")`, - }, - } - - for _, testCase := range testCases { - actual := testCase.bazelTargets.LoadStatements() - expected := testCase.expectedLoadStatements - if actual != expected { - t.Fatalf("Expected load statements to be %s, got %s", expected, actual) - } - } - -} - -func TestGenerateBazelTargetModules_OneToMany_LoadedFromStarlark(t *testing.T) { - testCases := []struct { - bp string - expectedBazelTarget string - expectedBazelTargetCount int - expectedLoadStatements string - }{ - { - bp: `custom { - name: "bar", - host_supported: true, - one_to_many_prop: true, - bazel_module: { bp2build_available: true }, -}`, - expectedBazelTarget: `my_library( - name = "bar", -) - -proto_library( - name = "bar_proto_library_deps", -) - -my_proto_library( - name = "bar_my_proto_library_deps", -)`, - expectedBazelTargetCount: 3, - expectedLoadStatements: `load("//build/bazel/rules:proto.bzl", "my_proto_library", "proto_library") -load("//build/bazel/rules:rules.bzl", "my_library")`, - }, - } - - dir := "." - for _, testCase := range testCases { - config := android.TestConfig(buildDir, nil, testCase.bp, nil) - ctx := android.NewTestContext(config) - ctx.RegisterModuleType("custom", customModuleFactoryHostAndDevice) - ctx.RegisterForBazelConversion() - - _, errs := ctx.ParseFileList(dir, []string{"Android.bp"}) - android.FailIfErrored(t, errs) - _, errs = ctx.ResolveDependencies(config) - android.FailIfErrored(t, errs) - - codegenCtx := NewCodegenContext(config, ctx.Context, Bp2Build, "") - bazelTargets, err := generateBazelTargetsForDir(codegenCtx, dir) - android.FailIfErrored(t, err) - if actualCount := len(bazelTargets); actualCount != testCase.expectedBazelTargetCount { - t.Fatalf("Expected %d bazel target, got %d", testCase.expectedBazelTargetCount, actualCount) - } - - actualBazelTargets := bazelTargets.String() - if actualBazelTargets != testCase.expectedBazelTarget { - t.Errorf( - "Expected generated Bazel target to be '%s', got '%s'", - testCase.expectedBazelTarget, - actualBazelTargets, - ) - } - - actualLoadStatements := bazelTargets.LoadStatements() - if actualLoadStatements != testCase.expectedLoadStatements { - t.Errorf( - "Expected generated load statements to be '%s', got '%s'", - testCase.expectedLoadStatements, - actualLoadStatements, - ) - } - } -} - -func TestModuleTypeBp2Build(t *testing.T) { - testCases := []Bp2buildTestCase{ - { - Description: "filegroup with does not specify srcs", - ModuleTypeUnderTest: "filegroup", - ModuleTypeUnderTestFactory: android.FileGroupFactory, - Blueprint: `filegroup { - name: "fg_foo", - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{}), - }, - }, - { - Description: "filegroup with no srcs", - ModuleTypeUnderTest: "filegroup", - ModuleTypeUnderTestFactory: android.FileGroupFactory, - Blueprint: `filegroup { - name: "fg_foo", - srcs: [], - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{}), - }, - }, - { - Description: "filegroup with srcs", - ModuleTypeUnderTest: "filegroup", - ModuleTypeUnderTestFactory: android.FileGroupFactory, - Blueprint: `filegroup { - name: "fg_foo", - srcs: ["a", "b"], - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{ - "srcs": `[ - "a", - "b", - ]`, - }), - }, - }, - { - Description: "filegroup with dot-slash-prefixed srcs", - ModuleTypeUnderTest: "filegroup", - ModuleTypeUnderTestFactory: android.FileGroupFactory, - Blueprint: `filegroup { - name: "fg_foo", - srcs: ["./a", "./b"], - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{ - "srcs": `[ - "a", - "b", - ]`, - }), - }, - }, - { - Description: "filegroup with excludes srcs", - ModuleTypeUnderTest: "filegroup", - ModuleTypeUnderTestFactory: android.FileGroupFactory, - Blueprint: `filegroup { - name: "fg_foo", - srcs: ["a", "b"], - exclude_srcs: ["a"], - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{ - "srcs": `["b"]`, - }), - }, - }, - { - Description: "depends_on_other_dir_module", - ModuleTypeUnderTest: "filegroup", - ModuleTypeUnderTestFactory: android.FileGroupFactory, - Blueprint: `filegroup { - name: "fg_foo", - srcs: [ - ":foo", - "c", - ], - bazel_module: { bp2build_available: true }, -}`, - Filesystem: map[string]string{ - "other/Android.bp": `filegroup { - name: "foo", - srcs: ["a", "b"], - bazel_module: { bp2build_available: true }, -}`, - }, - ExpectedBazelTargets: []string{ - MakeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{ - "srcs": `[ - "//other:foo", - "c", - ]`, - }), - }, - }, - { - Description: "depends_on_other_unconverted_module_error", - ModuleTypeUnderTest: "filegroup", - ModuleTypeUnderTestFactory: android.FileGroupFactory, - UnconvertedDepsMode: errorModulesUnconvertedDeps, - Blueprint: `filegroup { - name: "foobar", - srcs: [ - ":foo", - "c", - ], - bazel_module: { bp2build_available: true }, -}`, - ExpectedErr: fmt.Errorf(`filegroup .:foobar depends on unconverted modules: foo`), - Filesystem: map[string]string{ - "other/Android.bp": `filegroup { - name: "foo", - srcs: ["a", "b"], -}`, - }, - }, - { - Description: "depends_on_other_missing_module_error", - ModuleTypeUnderTest: "filegroup", - ModuleTypeUnderTestFactory: android.FileGroupFactory, - UnconvertedDepsMode: errorModulesUnconvertedDeps, - Blueprint: `filegroup { - name: "foobar", - srcs: [ - "c", - "//other:foo", - "//other:goo", - ], - bazel_module: { bp2build_available: true }, -}`, - ExpectedErr: fmt.Errorf(`filegroup .:foobar depends on missing modules: //other:goo`), - Filesystem: map[string]string{"other/Android.bp": `filegroup { - name: "foo", - srcs: ["a"], - bazel_module: { bp2build_available: true }, -} -`, - }, - }, - } - - for _, testCase := range testCases { - t.Run(testCase.Description, func(t *testing.T) { - RunBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, testCase) - }) - } -} - -func TestAllowlistingBp2buildTargetsExplicitly(t *testing.T) { - testCases := []struct { - moduleTypeUnderTest string - moduleTypeUnderTestFactory android.ModuleFactory - bp string - expectedCount int - description string - }{ - { - description: "explicitly unavailable", - moduleTypeUnderTest: "filegroup", - moduleTypeUnderTestFactory: android.FileGroupFactory, - bp: `filegroup { - name: "foo", - srcs: ["a", "b"], - bazel_module: { bp2build_available: false }, -}`, - expectedCount: 0, - }, - { - description: "implicitly unavailable", - moduleTypeUnderTest: "filegroup", - moduleTypeUnderTestFactory: android.FileGroupFactory, - bp: `filegroup { - name: "foo", - srcs: ["a", "b"], -}`, - expectedCount: 0, - }, - { - description: "explicitly available", - moduleTypeUnderTest: "filegroup", - moduleTypeUnderTestFactory: android.FileGroupFactory, - bp: `filegroup { - name: "foo", - srcs: ["a", "b"], - bazel_module: { bp2build_available: true }, -}`, - expectedCount: 1, - }, - { - description: "generates more than 1 target if needed", - moduleTypeUnderTest: "custom", - moduleTypeUnderTestFactory: customModuleFactoryHostAndDevice, - bp: `custom { - name: "foo", - one_to_many_prop: true, - bazel_module: { bp2build_available: true }, -}`, - expectedCount: 3, - }, - } - - dir := "." - for _, testCase := range testCases { - t.Run(testCase.description, func(t *testing.T) { - config := android.TestConfig(buildDir, nil, testCase.bp, nil) - ctx := android.NewTestContext(config) - ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory) - ctx.RegisterForBazelConversion() - - _, errs := ctx.ParseFileList(dir, []string{"Android.bp"}) - android.FailIfErrored(t, errs) - _, errs = ctx.ResolveDependencies(config) - android.FailIfErrored(t, errs) - - codegenCtx := NewCodegenContext(config, ctx.Context, Bp2Build, "") - bazelTargets, err := generateBazelTargetsForDir(codegenCtx, dir) - android.FailIfErrored(t, err) - if actualCount := len(bazelTargets); actualCount != testCase.expectedCount { - t.Fatalf("%s: Expected %d bazel target, got %d", testCase.description, testCase.expectedCount, actualCount) - } - }) - } -} - -func TestAllowlistingBp2buildTargetsWithConfig(t *testing.T) { - testCases := []struct { - moduleTypeUnderTest string - moduleTypeUnderTestFactory android.ModuleFactory - expectedCount map[string]int - description string - bp2buildConfig allowlists.Bp2BuildConfig - checkDir string - fs map[string]string - forceEnabledModules []string - expectedErrorMessages []string - }{ - { - description: "test bp2build config package and subpackages config", - moduleTypeUnderTest: "filegroup", - moduleTypeUnderTestFactory: android.FileGroupFactory, - expectedCount: map[string]int{ - "migrated": 1, - "migrated/but_not_really": 0, - "migrated/but_not_really/but_really": 1, - "not_migrated": 0, - "also_not_migrated": 0, - }, - bp2buildConfig: allowlists.Bp2BuildConfig{ - "migrated": allowlists.Bp2BuildDefaultTrueRecursively, - "migrated/but_not_really": allowlists.Bp2BuildDefaultFalse, - "not_migrated": allowlists.Bp2BuildDefaultFalse, - }, - fs: map[string]string{ - "migrated/Android.bp": `filegroup { name: "a" }`, - "migrated/but_not_really/Android.bp": `filegroup { name: "b" }`, - "migrated/but_not_really/but_really/Android.bp": `filegroup { name: "c" }`, - "not_migrated/Android.bp": `filegroup { name: "d" }`, - "also_not_migrated/Android.bp": `filegroup { name: "e" }`, - }, - }, - { - description: "test bp2build config opt-in and opt-out", - moduleTypeUnderTest: "filegroup", - moduleTypeUnderTestFactory: android.FileGroupFactory, - expectedCount: map[string]int{ - "package-opt-in": 2, - "package-opt-in/subpackage": 0, - "package-opt-out": 1, - "package-opt-out/subpackage": 0, - }, - bp2buildConfig: allowlists.Bp2BuildConfig{ - "package-opt-in": allowlists.Bp2BuildDefaultFalse, - "package-opt-out": allowlists.Bp2BuildDefaultTrueRecursively, - }, - fs: map[string]string{ - "package-opt-in/Android.bp": ` -filegroup { name: "opt-in-a" } -filegroup { name: "opt-in-b", bazel_module: { bp2build_available: true } } -filegroup { name: "opt-in-c", bazel_module: { bp2build_available: true } } -`, - - "package-opt-in/subpackage/Android.bp": ` -filegroup { name: "opt-in-d" } // parent package not configured to DefaultTrueRecursively -`, - - "package-opt-out/Android.bp": ` -filegroup { name: "opt-out-a" } -filegroup { name: "opt-out-b", bazel_module: { bp2build_available: false } } -filegroup { name: "opt-out-c", bazel_module: { bp2build_available: false } } -`, - - "package-opt-out/subpackage/Android.bp": ` -filegroup { name: "opt-out-g", bazel_module: { bp2build_available: false } } -filegroup { name: "opt-out-h", bazel_module: { bp2build_available: false } } -`, - }, - }, - { - description: "test force-enabled errors out", - moduleTypeUnderTest: "filegroup", - moduleTypeUnderTestFactory: android.FileGroupFactory, - expectedCount: map[string]int{ - "migrated": 0, - "not_migrated": 0, - }, - bp2buildConfig: allowlists.Bp2BuildConfig{ - "migrated/but_not_really": allowlists.Bp2BuildDefaultFalse, - "not_migrated": allowlists.Bp2BuildDefaultFalse, - }, - fs: map[string]string{ - "migrated/Android.bp": `filegroup { name: "a" }`, - }, - forceEnabledModules: []string{"a"}, - expectedErrorMessages: []string{"Force Enabled Module a not converted"}, - }, - } - - dir := "." - for _, testCase := range testCases { - fs := make(map[string][]byte) - toParse := []string{ - "Android.bp", - } - for f, content := range testCase.fs { - if strings.HasSuffix(f, "Android.bp") { - toParse = append(toParse, f) - } - fs[f] = []byte(content) - } - config := android.TestConfig(buildDir, nil, "", fs) - config.AddForceEnabledModules(testCase.forceEnabledModules) - ctx := android.NewTestContext(config) - ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory) - allowlist := android.NewBp2BuildAllowlist().SetDefaultConfig(testCase.bp2buildConfig) - ctx.RegisterBp2BuildConfig(allowlist) - ctx.RegisterForBazelConversion() - - _, errs := ctx.ParseFileList(dir, toParse) - android.FailIfErrored(t, errs) - _, errs = ctx.ResolveDependencies(config) - android.FailIfErrored(t, errs) - - codegenCtx := NewCodegenContext(config, ctx.Context, Bp2Build, "") - - // For each directory, test that the expected number of generated targets is correct. - for dir, expectedCount := range testCase.expectedCount { - bazelTargets, err := generateBazelTargetsForDir(codegenCtx, dir) - android.CheckErrorsAgainstExpectations(t, err, testCase.expectedErrorMessages) - if actualCount := len(bazelTargets); actualCount != expectedCount { - t.Fatalf( - "%s: Expected %d bazel target for %s package, got %d", - testCase.description, - expectedCount, - dir, - actualCount) - } - - } - } -} - -func TestCombineBuildFilesBp2buildTargets(t *testing.T) { - testCases := []Bp2buildTestCase{ - { - Description: "filegroup bazel_module.label", - ModuleTypeUnderTest: "filegroup", - ModuleTypeUnderTestFactory: android.FileGroupFactory, - Blueprint: `filegroup { - name: "fg_foo", - bazel_module: { label: "//other:fg_foo" }, -}`, - ExpectedBazelTargets: []string{}, - Filesystem: map[string]string{ - "other/BUILD.bazel": `// BUILD file`, - }, - }, - { - Description: "multiple bazel_module.label same BUILD", - ModuleTypeUnderTest: "filegroup", - ModuleTypeUnderTestFactory: android.FileGroupFactory, - Blueprint: `filegroup { - name: "fg_foo", - bazel_module: { label: "//other:fg_foo" }, - } - - filegroup { - name: "foo", - bazel_module: { label: "//other:foo" }, - }`, - ExpectedBazelTargets: []string{}, - Filesystem: map[string]string{ - "other/BUILD.bazel": `// BUILD file`, - }, - }, - { - Description: "filegroup bazel_module.label and bp2build in subdir", - ModuleTypeUnderTest: "filegroup", - ModuleTypeUnderTestFactory: android.FileGroupFactory, - Dir: "other", - Blueprint: ``, - Filesystem: map[string]string{ - "other/Android.bp": `filegroup { - name: "fg_foo", - bazel_module: { - bp2build_available: true, - }, - } - filegroup { - name: "fg_bar", - bazel_module: { - label: "//other:fg_bar" - }, - }`, - "other/BUILD.bazel": `// definition for fg_bar`, - }, - ExpectedBazelTargets: []string{ - MakeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{}), - }, - }, - { - Description: "filegroup bazel_module.label and filegroup bp2build", - ModuleTypeUnderTest: "filegroup", - ModuleTypeUnderTestFactory: android.FileGroupFactory, - - Filesystem: map[string]string{ - "other/BUILD.bazel": `// BUILD file`, - }, - Blueprint: `filegroup { - name: "fg_foo", - bazel_module: { - label: "//other:fg_foo", - }, - } - - filegroup { - name: "fg_bar", - bazel_module: { - bp2build_available: true, - }, - }`, - ExpectedBazelTargets: []string{ - MakeBazelTargetNoRestrictions("filegroup", "fg_bar", map[string]string{}), - }, - }, - } - - dir := "." - for _, testCase := range testCases { - t.Run(testCase.Description, func(t *testing.T) { - fs := make(map[string][]byte) - toParse := []string{ - "Android.bp", - } - for f, content := range testCase.Filesystem { - if strings.HasSuffix(f, "Android.bp") { - toParse = append(toParse, f) - } - fs[f] = []byte(content) - } - config := android.TestConfig(buildDir, nil, testCase.Blueprint, fs) - ctx := android.NewTestContext(config) - ctx.RegisterModuleType(testCase.ModuleTypeUnderTest, testCase.ModuleTypeUnderTestFactory) - ctx.RegisterForBazelConversion() - - _, errs := ctx.ParseFileList(dir, toParse) - if errored(t, testCase, errs) { - return - } - _, errs = ctx.ResolveDependencies(config) - if errored(t, testCase, errs) { - return - } - - checkDir := dir - if testCase.Dir != "" { - checkDir = testCase.Dir - } - codegenCtx := NewCodegenContext(config, ctx.Context, Bp2Build, "") - bazelTargets, err := generateBazelTargetsForDir(codegenCtx, checkDir) - android.FailIfErrored(t, err) - bazelTargets.sort() - actualCount := len(bazelTargets) - expectedCount := len(testCase.ExpectedBazelTargets) - if actualCount != expectedCount { - t.Errorf("Expected %d bazel target, got %d\n%s", expectedCount, actualCount, bazelTargets) - } - for i, target := range bazelTargets { - actualContent := target.content - expectedContent := testCase.ExpectedBazelTargets[i] - if expectedContent != actualContent { - t.Errorf( - "Expected generated Bazel target to be '%s', got '%s'", - expectedContent, - actualContent, - ) - } - } - }) - } -} - -func TestGlob(t *testing.T) { - testCases := []Bp2buildTestCase{ - { - Description: "filegroup with glob", - ModuleTypeUnderTest: "filegroup", - ModuleTypeUnderTestFactory: android.FileGroupFactory, - Blueprint: `filegroup { - name: "fg_foo", - srcs: ["**/*.txt"], - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{ - "srcs": `[ - "other/a.txt", - "other/b.txt", - "other/subdir/a.txt", - ]`, - }), - }, - Filesystem: map[string]string{ - "other/a.txt": "", - "other/b.txt": "", - "other/subdir/a.txt": "", - "other/file": "", - }, - }, - { - Description: "filegroup with glob in subdir", - ModuleTypeUnderTest: "filegroup", - ModuleTypeUnderTestFactory: android.FileGroupFactory, - Dir: "other", - Filesystem: map[string]string{ - "other/Android.bp": `filegroup { - name: "fg_foo", - srcs: ["**/*.txt"], - bazel_module: { bp2build_available: true }, -}`, - "other/a.txt": "", - "other/b.txt": "", - "other/subdir/a.txt": "", - "other/file": "", - }, - ExpectedBazelTargets: []string{ - MakeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{ - "srcs": `[ - "a.txt", - "b.txt", - "subdir/a.txt", - ]`, - }), - }, - }, - { - Description: "filegroup with glob with no kept BUILD files", - ModuleTypeUnderTest: "filegroup", - ModuleTypeUnderTestFactory: android.FileGroupFactory, - KeepBuildFileForDirs: []string{ - // empty - }, - Blueprint: `filegroup { - name: "fg_foo", - srcs: ["**/*.txt"], - bazel_module: { bp2build_available: true }, -}`, - Filesystem: map[string]string{ - "a.txt": "", - "b.txt": "", - "foo/BUILD": "", - "foo/a.txt": "", - "foo/bar/BUILD": "", - "foo/bar/b.txt": "", - }, - ExpectedBazelTargets: []string{ - MakeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{ - "srcs": `[ - "a.txt", - "b.txt", - "foo/a.txt", - "foo/bar/b.txt", - ]`, - }), - }, - }, - { - Description: "filegroup with glob with kept BUILD file", - ModuleTypeUnderTest: "filegroup", - ModuleTypeUnderTestFactory: android.FileGroupFactory, - KeepBuildFileForDirs: []string{ - "foo", - }, - Blueprint: `filegroup { - name: "fg_foo", - srcs: ["**/*.txt"], - bazel_module: { bp2build_available: true }, -}`, - Filesystem: map[string]string{ - "a.txt": "", - "b.txt": "", - "foo/BUILD": "", - "foo/a.txt": "", - "foo/bar/BUILD": "", - "foo/bar/b.txt": "", - }, - ExpectedBazelTargets: []string{ - MakeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{ - "srcs": `[ - "a.txt", - "b.txt", - "//foo:a.txt", - "//foo:bar/b.txt", - ]`, - }), - }, - }, - { - Description: "filegroup with glob with kept BUILD.bazel file", - ModuleTypeUnderTest: "filegroup", - ModuleTypeUnderTestFactory: android.FileGroupFactory, - KeepBuildFileForDirs: []string{ - "foo", - }, - Blueprint: `filegroup { - name: "fg_foo", - srcs: ["**/*.txt"], - bazel_module: { bp2build_available: true }, -}`, - Filesystem: map[string]string{ - "a.txt": "", - "b.txt": "", - "foo/BUILD.bazel": "", - "foo/a.txt": "", - "foo/bar/BUILD.bazel": "", - "foo/bar/b.txt": "", - }, - ExpectedBazelTargets: []string{ - MakeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{ - "srcs": `[ - "a.txt", - "b.txt", - "//foo:a.txt", - "//foo:bar/b.txt", - ]`, - }), - }, - }, - { - Description: "filegroup with glob with Android.bp file as boundary", - ModuleTypeUnderTest: "filegroup", - ModuleTypeUnderTestFactory: android.FileGroupFactory, - Blueprint: `filegroup { - name: "fg_foo", - srcs: ["**/*.txt"], - bazel_module: { bp2build_available: true }, -}`, - Filesystem: map[string]string{ - "a.txt": "", - "b.txt": "", - "foo/Android.bp": "", - "foo/a.txt": "", - "foo/bar/Android.bp": "", - "foo/bar/b.txt": "", - }, - ExpectedBazelTargets: []string{ - MakeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{ - "srcs": `[ - "a.txt", - "b.txt", - "//foo:a.txt", - "//foo/bar:b.txt", - ]`, - }), - }, - }, - { - Description: "filegroup with glob in subdir with kept BUILD and BUILD.bazel file", - ModuleTypeUnderTest: "filegroup", - ModuleTypeUnderTestFactory: android.FileGroupFactory, - Dir: "other", - KeepBuildFileForDirs: []string{ - "other/foo", - "other/foo/bar", - // deliberately not other/foo/baz/BUILD. - }, - Filesystem: map[string]string{ - "other/Android.bp": `filegroup { - name: "fg_foo", - srcs: ["**/*.txt"], - bazel_module: { bp2build_available: true }, -}`, - "other/a.txt": "", - "other/b.txt": "", - "other/foo/BUILD": "", - "other/foo/a.txt": "", - "other/foo/bar/BUILD.bazel": "", - "other/foo/bar/b.txt": "", - "other/foo/baz/BUILD": "", - "other/foo/baz/c.txt": "", - }, - ExpectedBazelTargets: []string{ - MakeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{ - "srcs": `[ - "a.txt", - "b.txt", - "//other/foo:a.txt", - "//other/foo/bar:b.txt", - "//other/foo:baz/c.txt", - ]`, - }), - }, - }, - } - - for _, testCase := range testCases { - t.Run(testCase.Description, func(t *testing.T) { - RunBp2BuildTestCaseSimple(t, testCase) - }) - } -} - -func TestGlobExcludeSrcs(t *testing.T) { - testCases := []Bp2buildTestCase{ - { - Description: "filegroup top level exclude_srcs", - ModuleTypeUnderTest: "filegroup", - ModuleTypeUnderTestFactory: android.FileGroupFactory, - Blueprint: `filegroup { - name: "fg_foo", - srcs: ["**/*.txt"], - exclude_srcs: ["c.txt"], - bazel_module: { bp2build_available: true }, -}`, - Filesystem: map[string]string{ - "a.txt": "", - "b.txt": "", - "c.txt": "", - "dir/Android.bp": "", - "dir/e.txt": "", - "dir/f.txt": "", - }, - ExpectedBazelTargets: []string{ - MakeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{ - "srcs": `[ - "a.txt", - "b.txt", - "//dir:e.txt", - "//dir:f.txt", - ]`, - }), - }, - }, - { - Description: "filegroup in subdir exclude_srcs", - ModuleTypeUnderTest: "filegroup", - ModuleTypeUnderTestFactory: android.FileGroupFactory, - Blueprint: "", - Dir: "dir", - Filesystem: map[string]string{ - "dir/Android.bp": `filegroup { - name: "fg_foo", - srcs: ["**/*.txt"], - exclude_srcs: ["b.txt"], - bazel_module: { bp2build_available: true }, -} -`, - "dir/a.txt": "", - "dir/b.txt": "", - "dir/subdir/Android.bp": "", - "dir/subdir/e.txt": "", - "dir/subdir/f.txt": "", - }, - ExpectedBazelTargets: []string{ - MakeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{ - "srcs": `[ - "a.txt", - "//dir/subdir:e.txt", - "//dir/subdir:f.txt", - ]`, - }), - }, - }, - } - - for _, testCase := range testCases { - t.Run(testCase.Description, func(t *testing.T) { - RunBp2BuildTestCaseSimple(t, testCase) - }) - } -} - -func TestCommonBp2BuildModuleAttrs(t *testing.T) { - testCases := []Bp2buildTestCase{ - { - Description: "Required into data test", - ModuleTypeUnderTest: "filegroup", - ModuleTypeUnderTestFactory: android.FileGroupFactory, - Blueprint: simpleModuleDoNotConvertBp2build("filegroup", "reqd") + ` -filegroup { - name: "fg_foo", - required: ["reqd"], - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{ - "data": `[":reqd"]`, - }), - }, - }, - { - Description: "Required into data test, cyclic self reference is filtered out", - ModuleTypeUnderTest: "filegroup", - ModuleTypeUnderTestFactory: android.FileGroupFactory, - Blueprint: simpleModuleDoNotConvertBp2build("filegroup", "reqd") + ` -filegroup { - name: "fg_foo", - required: ["reqd", "fg_foo"], - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{ - "data": `[":reqd"]`, - }), - }, - }, - { - Description: "Required via arch into data test", - ModuleTypeUnderTest: "python_library", - ModuleTypeUnderTestFactory: python.PythonLibraryFactory, - Blueprint: simpleModuleDoNotConvertBp2build("python_library", "reqdx86") + - simpleModuleDoNotConvertBp2build("python_library", "reqdarm") + ` -python_library { - name: "fg_foo", - arch: { - arm: { - required: ["reqdarm"], - }, - x86: { - required: ["reqdx86"], - }, - }, - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("py_library", "fg_foo", map[string]string{ - "data": `select({ - "//build/bazel/platforms/arch:arm": [":reqdarm"], - "//build/bazel/platforms/arch:x86": [":reqdx86"], - "//conditions:default": [], - })`, - "srcs_version": `"PY3"`, - "imports": `["."]`, - }), - }, - }, - { - Description: "Required appended to data test", - ModuleTypeUnderTest: "python_library", - ModuleTypeUnderTestFactory: python.PythonLibraryFactory, - Filesystem: map[string]string{ - "data.bin": "", - "src.py": "", - }, - Blueprint: simpleModuleDoNotConvertBp2build("python_library", "reqd") + ` -python_library { - name: "fg_foo", - data: ["data.bin"], - required: ["reqd"], - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("py_library", "fg_foo", map[string]string{ - "data": `[ - "data.bin", - ":reqd", - ]`, - "srcs_version": `"PY3"`, - "imports": `["."]`, - }), - }, - }, - { - Description: "All props-to-attrs at once together test", - ModuleTypeUnderTest: "filegroup", - ModuleTypeUnderTestFactory: android.FileGroupFactory, - Blueprint: simpleModuleDoNotConvertBp2build("filegroup", "reqd") + ` -filegroup { - name: "fg_foo", - required: ["reqd"], - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{ - "data": `[":reqd"]`, - }), - }, - }, - } - - for _, tc := range testCases { - t.Run(tc.Description, func(t *testing.T) { - RunBp2BuildTestCaseSimple(t, tc) - }) - } -} - -func TestLicensesAttrConversion(t *testing.T) { - RunBp2BuildTestCase(t, - func(ctx android.RegistrationContext) { - ctx.RegisterModuleType("license", android.LicenseFactory) - }, - Bp2buildTestCase{ - Description: "Test that licenses: attribute is converted", - ModuleTypeUnderTest: "filegroup", - ModuleTypeUnderTestFactory: android.FileGroupFactory, - Blueprint: ` -license { - name: "my_license", -} -filegroup { - name: "my_filegroup", - licenses: ["my_license"], -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTargetNoRestrictions("filegroup", "my_filegroup", AttrNameToString{ - "applicable_licenses": `[":my_license"]`, - }), - MakeBazelTargetNoRestrictions("android_license", "my_license", AttrNameToString{}), - }, - }) -} - -func TestGenerateApiBazelTargets(t *testing.T) { - bp := ` - custom { - name: "foo", - api: "foo.txt", - } - ` - expectedBazelTarget := MakeBazelTarget( - "custom_api_contribution", - "foo", - AttrNameToString{ - "api": `"foo.txt"`, - }, - ) - registerCustomModule := func(ctx android.RegistrationContext) { - ctx.RegisterModuleType("custom", customModuleFactoryHostAndDevice) - } - RunApiBp2BuildTestCase(t, registerCustomModule, Bp2buildTestCase{ - Blueprint: bp, - ExpectedBazelTargets: []string{expectedBazelTarget}, - Description: "Generating API contribution Bazel targets for custom module", - }) -} diff --git a/bp2build/bzl_conversion_test.go b/bp2build/bzl_conversion_test.go deleted file mode 100644 index a8e557deb8..0000000000 --- a/bp2build/bzl_conversion_test.go +++ /dev/null @@ -1,232 +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 bp2build - -import ( - "io/ioutil" - "os" - "strings" - "testing" - - "android/soong/android" -) - -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 = { - "soong_module_name": attr.string(mandatory = True), - "soong_module_variant": attr.string(), - "soong_module_deps": attr.label_list(providers = [SoongModuleInfo]), - "api": attr.string(), - "arch_paths": attr.string_list(), - "arch_paths_exclude": attr.string_list(), - # bazel_module start -# "label": attr.string(), -# "bp2build_available": attr.bool(), - # bazel_module end - "bool_prop": attr.bool(), - "bool_ptr_prop": attr.bool(), - "embedded_prop": attr.string(), - "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 - "one_to_many_prop": attr.bool(), - "other_embedded_prop": attr.string(), - "string_list_prop": attr.string_list(), - "string_literal_prop": attr.string(), - "string_prop": attr.string(), - "string_ptr_prop": attr.string(), - }, -) - -def _custom_defaults_impl(ctx): - return [SoongModuleInfo()] - -custom_defaults = rule( - implementation = _custom_defaults_impl, - attrs = { - "soong_module_name": attr.string(mandatory = True), - "soong_module_variant": attr.string(), - "soong_module_deps": attr.label_list(providers = [SoongModuleInfo]), - "api": attr.string(), - "arch_paths": attr.string_list(), - "arch_paths_exclude": attr.string_list(), - "bool_prop": attr.bool(), - "bool_ptr_prop": attr.bool(), - "embedded_prop": attr.string(), - "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 - "one_to_many_prop": attr.bool(), - "other_embedded_prop": attr.string(), - "string_list_prop": attr.string_list(), - "string_literal_prop": attr.string(), - "string_prop": attr.string(), - "string_ptr_prop": attr.string(), - }, -) - -def _custom_test__impl(ctx): - return [SoongModuleInfo()] - -custom_test_ = rule( - implementation = _custom_test__impl, - attrs = { - "soong_module_name": attr.string(mandatory = True), - "soong_module_variant": attr.string(), - "soong_module_deps": attr.label_list(providers = [SoongModuleInfo]), - "api": attr.string(), - "arch_paths": attr.string_list(), - "arch_paths_exclude": attr.string_list(), - "bool_prop": attr.bool(), - "bool_ptr_prop": attr.bool(), - "embedded_prop": attr.string(), - "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 - "one_to_many_prop": attr.bool(), - "other_embedded_prop": attr.string(), - "string_list_prop": attr.string_list(), - "string_literal_prop": attr.string(), - "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(android.NullConfig("out", "out/soong"), ruleShims, make(map[string]BazelTargets), QueryView) - - 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/cc_binary_conversion_test.go b/bp2build/cc_binary_conversion_test.go deleted file mode 100644 index 031573274d..0000000000 --- a/bp2build/cc_binary_conversion_test.go +++ /dev/null @@ -1,998 +0,0 @@ -// 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 bp2build - -import ( - "fmt" - "strings" - "testing" - - "android/soong/android" - "android/soong/cc" - "android/soong/genrule" -) - -const ( - ccBinaryTypePlaceHolder = "{rule_name}" -) - -type testBazelTarget struct { - typ string - name string - attrs AttrNameToString -} - -func generateBazelTargetsForTest(targets []testBazelTarget, hod android.HostOrDeviceSupported) []string { - ret := make([]string, 0, len(targets)) - for _, t := range targets { - attrs := t.attrs.clone() - ret = append(ret, makeBazelTargetHostOrDevice(t.typ, t.name, attrs, hod)) - } - return ret -} - -type ccBinaryBp2buildTestCase struct { - description string - filesystem map[string]string - blueprint string - targets []testBazelTarget -} - -func registerCcBinaryModuleTypes(ctx android.RegistrationContext) { - cc.RegisterCCBuildComponents(ctx) - ctx.RegisterModuleType("filegroup", android.FileGroupFactory) - ctx.RegisterModuleType("cc_library_static", cc.LibraryStaticFactory) - ctx.RegisterModuleType("cc_library", cc.LibraryFactory) - ctx.RegisterModuleType("genrule", genrule.GenRuleFactory) -} - -var binaryReplacer = strings.NewReplacer(ccBinaryTypePlaceHolder, "cc_binary") -var hostBinaryReplacer = strings.NewReplacer(ccBinaryTypePlaceHolder, "cc_binary_host") - -func runCcBinaryTests(t *testing.T, tc ccBinaryBp2buildTestCase) { - t.Helper() - runCcBinaryTestCase(t, tc) - runCcHostBinaryTestCase(t, tc) -} - -func runCcBinaryTestCase(t *testing.T, testCase ccBinaryBp2buildTestCase) { - t.Helper() - moduleTypeUnderTest := "cc_binary" - - description := fmt.Sprintf("%s %s", moduleTypeUnderTest, testCase.description) - t.Run(description, func(t *testing.T) { - t.Helper() - RunBp2BuildTestCase(t, registerCcBinaryModuleTypes, Bp2buildTestCase{ - ExpectedBazelTargets: generateBazelTargetsForTest(testCase.targets, android.DeviceSupported), - ModuleTypeUnderTest: moduleTypeUnderTest, - ModuleTypeUnderTestFactory: cc.BinaryFactory, - Description: description, - Blueprint: binaryReplacer.Replace(testCase.blueprint), - Filesystem: testCase.filesystem, - }) - }) -} - -func runCcHostBinaryTestCase(t *testing.T, testCase ccBinaryBp2buildTestCase) { - t.Helper() - moduleTypeUnderTest := "cc_binary_host" - description := fmt.Sprintf("%s %s", moduleTypeUnderTest, testCase.description) - t.Run(description, func(t *testing.T) { - RunBp2BuildTestCase(t, registerCcBinaryModuleTypes, Bp2buildTestCase{ - ExpectedBazelTargets: generateBazelTargetsForTest(testCase.targets, android.HostSupported), - ModuleTypeUnderTest: moduleTypeUnderTest, - ModuleTypeUnderTestFactory: cc.BinaryHostFactory, - Description: description, - Blueprint: hostBinaryReplacer.Replace(testCase.blueprint), - Filesystem: testCase.filesystem, - }) - }) -} - -func TestBasicCcBinary(t *testing.T) { - runCcBinaryTests(t, ccBinaryBp2buildTestCase{ - description: "basic -- properties -> attrs with little/no transformation", - filesystem: map[string]string{ - soongCcVersionLibBpPath: soongCcVersionLibBp, - }, - blueprint: ` -{rule_name} { - name: "foo", - srcs: ["a.cc"], - local_include_dirs: ["dir"], - include_dirs: ["absolute_dir"], - cflags: ["-Dcopt"], - cppflags: ["-Dcppflag"], - conlyflags: ["-Dconlyflag"], - asflags: ["-Dasflag"], - ldflags: ["ld-flag"], - rtti: true, - strip: { - all: true, - keep_symbols: true, - keep_symbols_and_debug_frame: true, - keep_symbols_list: ["symbol"], - none: true, - }, - sdk_version: "current", - min_sdk_version: "29", - use_version_lib: true, -} -`, - targets: []testBazelTarget{ - {"cc_binary", "foo", AttrNameToString{ - "absolute_includes": `["absolute_dir"]`, - "asflags": `["-Dasflag"]`, - "conlyflags": `["-Dconlyflag"]`, - "copts": `["-Dcopt"]`, - "cppflags": `["-Dcppflag"]`, - "linkopts": `["ld-flag"]`, - "local_includes": `[ - "dir", - ".", - ]`, - "rtti": `True`, - "srcs": `["a.cc"]`, - "strip": `{ - "all": True, - "keep_symbols": True, - "keep_symbols_and_debug_frame": True, - "keep_symbols_list": ["symbol"], - "none": True, - }`, - "sdk_version": `"current"`, - "min_sdk_version": `"29"`, - "use_version_lib": `True`, - "whole_archive_deps": `["//build/soong/cc/libbuildversion:libbuildversion"]`, - }, - }, - }, - }) -} - -func TestCcBinaryWithSharedLdflagDisableFeature(t *testing.T) { - runCcBinaryTests(t, ccBinaryBp2buildTestCase{ - description: `ldflag "-shared" disables static_flag feature`, - blueprint: ` -{rule_name} { - name: "foo", - ldflags: ["-shared"], - include_build_directory: false, -} -`, - targets: []testBazelTarget{ - {"cc_binary", "foo", AttrNameToString{ - "features": `["-static_flag"]`, - "linkopts": `["-shared"]`, - }, - }, - }, - }) -} - -func TestCcBinaryWithLinkStatic(t *testing.T) { - runCcBinaryTests(t, ccBinaryBp2buildTestCase{ - description: "link static", - blueprint: ` -{rule_name} { - name: "foo", - static_executable: true, - include_build_directory: false, -} -`, - targets: []testBazelTarget{ - {"cc_binary", "foo", AttrNameToString{ - "linkshared": `False`, - }, - }, - }, - }) -} - -func TestCcBinaryVersionScriptAndDynamicList(t *testing.T) { - runCcBinaryTests(t, ccBinaryBp2buildTestCase{ - description: `version script and dynamic list`, - blueprint: ` -{rule_name} { - name: "foo", - include_build_directory: false, - version_script: "vs", - dynamic_list: "dynamic.list", -} -`, - targets: []testBazelTarget{ - {"cc_binary", "foo", AttrNameToString{ - "additional_linker_inputs": `[ - "vs", - "dynamic.list", - ]`, - "linkopts": `[ - "-Wl,--version-script,$(location vs)", - "-Wl,--dynamic-list,$(location dynamic.list)", - ]`, - }, - }, - }, - }) -} - -func TestCcBinaryLdflagsSplitBySpaceExceptSoongAdded(t *testing.T) { - runCcBinaryTests(t, ccBinaryBp2buildTestCase{ - description: "ldflags are split by spaces except for the ones added by soong (version script and dynamic list)", - blueprint: ` -{rule_name} { - name: "foo", - ldflags: [ - "--nospace_flag", - "-z spaceflag", - ], - version_script: "version_script", - dynamic_list: "dynamic.list", - include_build_directory: false, -} -`, - targets: []testBazelTarget{ - {"cc_binary", "foo", AttrNameToString{ - "additional_linker_inputs": `[ - "version_script", - "dynamic.list", - ]`, - "linkopts": `[ - "--nospace_flag", - "-z", - "spaceflag", - "-Wl,--version-script,$(location version_script)", - "-Wl,--dynamic-list,$(location dynamic.list)", - ]`, - }}}, - }) -} - -func TestCcBinarySplitSrcsByLang(t *testing.T) { - runCcHostBinaryTestCase(t, ccBinaryBp2buildTestCase{ - description: "split srcs by lang", - blueprint: ` -{rule_name} { - name: "foo", - srcs: [ - "asonly.S", - "conly.c", - "cpponly.cpp", - ":fg_foo", - ], - include_build_directory: false, -} -` + simpleModuleDoNotConvertBp2build("filegroup", "fg_foo"), - targets: []testBazelTarget{ - {"cc_binary", "foo", AttrNameToString{ - "srcs": `[ - "cpponly.cpp", - ":fg_foo_cpp_srcs", - ]`, - "srcs_as": `[ - "asonly.S", - ":fg_foo_as_srcs", - ]`, - "srcs_c": `[ - "conly.c", - ":fg_foo_c_srcs", - ]`, - }, - }, - }, - }) -} - -func TestCcBinaryDoNotDistinguishBetweenDepsAndImplementationDeps(t *testing.T) { - runCcBinaryTestCase(t, ccBinaryBp2buildTestCase{ - description: "no implementation deps", - blueprint: ` -genrule { - name: "generated_hdr", - cmd: "nothing to see here", - bazel_module: { bp2build_available: false }, -} - -genrule { - name: "export_generated_hdr", - cmd: "nothing to see here", - bazel_module: { bp2build_available: false }, -} - -{rule_name} { - name: "foo", - srcs: ["foo.cpp"], - shared_libs: ["implementation_shared_dep", "shared_dep"], - export_shared_lib_headers: ["shared_dep"], - static_libs: ["implementation_static_dep", "static_dep"], - export_static_lib_headers: ["static_dep", "whole_static_dep"], - whole_static_libs: ["not_explicitly_exported_whole_static_dep", "whole_static_dep"], - include_build_directory: false, - generated_headers: ["generated_hdr", "export_generated_hdr"], - export_generated_headers: ["export_generated_hdr"], -} -` + - simpleModuleDoNotConvertBp2build("cc_library_static", "static_dep") + - simpleModuleDoNotConvertBp2build("cc_library_static", "implementation_static_dep") + - simpleModuleDoNotConvertBp2build("cc_library_static", "whole_static_dep") + - simpleModuleDoNotConvertBp2build("cc_library_static", "not_explicitly_exported_whole_static_dep") + - simpleModuleDoNotConvertBp2build("cc_library", "shared_dep") + - simpleModuleDoNotConvertBp2build("cc_library", "implementation_shared_dep"), - targets: []testBazelTarget{ - {"cc_binary", "foo", AttrNameToString{ - "deps": `[ - ":implementation_static_dep", - ":static_dep", - ]`, - "dynamic_deps": `[ - ":implementation_shared_dep", - ":shared_dep", - ]`, - "srcs": `[ - "foo.cpp", - ":generated_hdr", - ":export_generated_hdr", - ]`, - "whole_archive_deps": `[ - ":not_explicitly_exported_whole_static_dep", - ":whole_static_dep", - ]`, - "local_includes": `["."]`, - }, - }, - }, - }) -} - -func TestCcBinaryNocrtTests(t *testing.T) { - baseTestCases := []struct { - description string - soongProperty string - bazelAttr AttrNameToString - }{ - { - description: "nocrt: true", - soongProperty: `nocrt: true,`, - bazelAttr: AttrNameToString{"features": `["-link_crt"]`}, - }, - { - description: "nocrt: false", - soongProperty: `nocrt: false,`, - bazelAttr: AttrNameToString{}, - }, - { - description: "nocrt: not set", - bazelAttr: AttrNameToString{}, - }, - } - - baseBlueprint := `{rule_name} { - name: "foo",%s - include_build_directory: false, -} -` - - for _, btc := range baseTestCases { - prop := btc.soongProperty - if len(prop) > 0 { - prop = "\n" + prop - } - runCcBinaryTests(t, ccBinaryBp2buildTestCase{ - description: btc.description, - blueprint: fmt.Sprintf(baseBlueprint, prop), - targets: []testBazelTarget{ - {"cc_binary", "foo", btc.bazelAttr}, - }, - }) - } -} - -func TestCcBinaryNo_libcrtTests(t *testing.T) { - baseTestCases := []struct { - description string - soongProperty string - bazelAttr AttrNameToString - }{ - { - description: "no_libcrt: true", - soongProperty: `no_libcrt: true,`, - bazelAttr: AttrNameToString{"features": `["-use_libcrt"]`}, - }, - { - description: "no_libcrt: false", - soongProperty: `no_libcrt: false,`, - bazelAttr: AttrNameToString{}, - }, - { - description: "no_libcrt: not set", - bazelAttr: AttrNameToString{}, - }, - } - - baseBlueprint := `{rule_name} { - name: "foo",%s - include_build_directory: false, -} -` - - for _, btc := range baseTestCases { - prop := btc.soongProperty - if len(prop) > 0 { - prop = "\n" + prop - } - runCcBinaryTests(t, ccBinaryBp2buildTestCase{ - description: btc.description, - blueprint: fmt.Sprintf(baseBlueprint, prop), - targets: []testBazelTarget{ - {"cc_binary", "foo", btc.bazelAttr}, - }, - }) - } -} - -func TestCcBinaryPropertiesToFeatures(t *testing.T) { - baseTestCases := []struct { - description string - soongProperty string - bazelAttr AttrNameToString - }{ - { - description: "pack_relocation: true", - soongProperty: `pack_relocations: true,`, - bazelAttr: AttrNameToString{}, - }, - { - description: "pack_relocations: false", - soongProperty: `pack_relocations: false,`, - bazelAttr: AttrNameToString{"features": `["disable_pack_relocations"]`}, - }, - { - description: "pack_relocations: not set", - bazelAttr: AttrNameToString{}, - }, - { - description: "pack_relocation: true", - soongProperty: `allow_undefined_symbols: true,`, - bazelAttr: AttrNameToString{"features": `["-no_undefined_symbols"]`}, - }, - { - description: "allow_undefined_symbols: false", - soongProperty: `allow_undefined_symbols: false,`, - bazelAttr: AttrNameToString{}, - }, - { - description: "allow_undefined_symbols: not set", - bazelAttr: AttrNameToString{}, - }, - } - - baseBlueprint := `{rule_name} { - name: "foo",%s - include_build_directory: false, -} -` - for _, btc := range baseTestCases { - prop := btc.soongProperty - if len(prop) > 0 { - prop = "\n" + prop - } - runCcBinaryTests(t, ccBinaryBp2buildTestCase{ - description: btc.description, - blueprint: fmt.Sprintf(baseBlueprint, prop), - targets: []testBazelTarget{ - {"cc_binary", "foo", btc.bazelAttr}, - }, - }) - } -} - -func TestCcBinarySharedProto(t *testing.T) { - runCcBinaryTests(t, ccBinaryBp2buildTestCase{ - blueprint: soongCcProtoLibraries + `{rule_name} { - name: "foo", - srcs: ["foo.proto"], - proto: { - }, - include_build_directory: false, -}`, - targets: []testBazelTarget{ - {"proto_library", "foo_proto", AttrNameToString{ - "srcs": `["foo.proto"]`, - }}, {"cc_lite_proto_library", "foo_cc_proto_lite", AttrNameToString{ - "deps": `[":foo_proto"]`, - }}, {"cc_binary", "foo", AttrNameToString{ - "dynamic_deps": `[":libprotobuf-cpp-lite"]`, - "whole_archive_deps": `[":foo_cc_proto_lite"]`, - }}, - }, - }) -} - -func TestCcBinaryStaticProto(t *testing.T) { - runCcBinaryTests(t, ccBinaryBp2buildTestCase{ - blueprint: soongCcProtoLibraries + `{rule_name} { - name: "foo", - srcs: ["foo.proto"], - static_executable: true, - proto: { - }, - include_build_directory: false, -}`, - targets: []testBazelTarget{ - {"proto_library", "foo_proto", AttrNameToString{ - "srcs": `["foo.proto"]`, - }}, {"cc_lite_proto_library", "foo_cc_proto_lite", AttrNameToString{ - "deps": `[":foo_proto"]`, - }}, {"cc_binary", "foo", AttrNameToString{ - "deps": `[":libprotobuf-cpp-lite"]`, - "whole_archive_deps": `[":foo_cc_proto_lite"]`, - "linkshared": `False`, - }}, - }, - }) -} - -func TestCcBinaryConvertLex(t *testing.T) { - runCcBinaryTests(t, ccBinaryBp2buildTestCase{ - description: `.l and .ll sources converted to .c and .cc`, - blueprint: ` -{rule_name} { - name: "foo", - srcs: ["foo.c", "bar.cc", "foo1.l", "foo2.l", "bar1.ll", "bar2.ll"], - lex: { flags: ["--foo_opt", "--bar_opt"] }, - include_build_directory: false, -} -`, - targets: []testBazelTarget{ - {"genlex", "foo_genlex_l", AttrNameToString{ - "srcs": `[ - "foo1.l", - "foo2.l", - ]`, - "lexopts": `[ - "--foo_opt", - "--bar_opt", - ]`, - }}, - {"genlex", "foo_genlex_ll", AttrNameToString{ - "srcs": `[ - "bar1.ll", - "bar2.ll", - ]`, - "lexopts": `[ - "--foo_opt", - "--bar_opt", - ]`, - }}, - {"cc_binary", "foo", AttrNameToString{ - "srcs": `[ - "bar.cc", - ":foo_genlex_ll", - ]`, - "srcs_c": `[ - "foo.c", - ":foo_genlex_l", - ]`, - }}, - }, - }) -} - -func TestCcBinaryRuntimeLibs(t *testing.T) { - runCcBinaryTests(t, ccBinaryBp2buildTestCase{ - description: "cc_binary with runtime libs", - blueprint: ` -cc_library { - name: "bar", - srcs: ["b.cc"], -} - -{rule_name} { - name: "foo", - srcs: ["a.cc"], - runtime_libs: ["bar"], -} -`, - targets: []testBazelTarget{ - {"cc_library_static", "bar_bp2build_cc_library_static", AttrNameToString{ - "local_includes": `["."]`, - "srcs": `["b.cc"]`, - "target_compatible_with": `["//build/bazel/platforms/os:android"]`, - }, - }, - {"cc_library_shared", "bar", AttrNameToString{ - "local_includes": `["."]`, - "srcs": `["b.cc"]`, - "target_compatible_with": `["//build/bazel/platforms/os:android"]`, - }, - }, - {"cc_binary", "foo", AttrNameToString{ - "local_includes": `["."]`, - "srcs": `["a.cc"]`, - "runtime_deps": `[":bar"]`, - }, - }, - }, - }) -} - -func TestCcBinaryWithInstructionSet(t *testing.T) { - runCcBinaryTests(t, ccBinaryBp2buildTestCase{ - description: "instruction set", - blueprint: ` -{rule_name} { - name: "foo", - arch: { - arm: { - instruction_set: "arm", - } - } -} -`, - targets: []testBazelTarget{ - {"cc_binary", "foo", AttrNameToString{ - "features": `select({ - "//build/bazel/platforms/arch:arm": [ - "arm_isa_arm", - "-arm_isa_thumb", - ], - "//conditions:default": [], - })`, - "local_includes": `["."]`, - }}, - }, - }) -} - -func TestCcBinaryEmptySuffix(t *testing.T) { - runCcBinaryTests(t, ccBinaryBp2buildTestCase{ - description: "binary with empty suffix", - blueprint: ` -{rule_name} { - name: "foo", - suffix: "", -}`, - targets: []testBazelTarget{ - {"cc_binary", "foo", AttrNameToString{ - "local_includes": `["."]`, - "suffix": `""`, - }}, - }, - }) -} - -func TestCcBinarySuffix(t *testing.T) { - runCcBinaryTests(t, ccBinaryBp2buildTestCase{ - description: "binary with suffix", - blueprint: ` -{rule_name} { - name: "foo", - suffix: "-suf", -} -`, - targets: []testBazelTarget{ - {"cc_binary", "foo", AttrNameToString{ - "local_includes": `["."]`, - "suffix": `"-suf"`, - }}, - }, - }) -} - -func TestCcArchVariantBinarySuffix(t *testing.T) { - runCcBinaryTests(t, ccBinaryBp2buildTestCase{ - description: "binary with suffix", - blueprint: ` -{rule_name} { - name: "foo", - arch: { - arm64: { suffix: "-64" }, - arm: { suffix: "-32" }, - }, -} -`, - targets: []testBazelTarget{ - {"cc_binary", "foo", AttrNameToString{ - "local_includes": `["."]`, - "suffix": `select({ - "//build/bazel/platforms/arch:arm": "-32", - "//build/bazel/platforms/arch:arm64": "-64", - "//conditions:default": None, - })`, - }}, - }, - }) -} - -func TestCcBinaryWithSyspropSrcs(t *testing.T) { - runCcBinaryTestCase(t, ccBinaryBp2buildTestCase{ - description: "cc_binary with sysprop sources", - blueprint: ` -{rule_name} { - name: "foo", - srcs: [ - "bar.sysprop", - "baz.sysprop", - "blah.cpp", - ], - min_sdk_version: "5", -}`, - targets: []testBazelTarget{ - {"sysprop_library", "foo_sysprop_library", AttrNameToString{ - "srcs": `[ - "bar.sysprop", - "baz.sysprop", - ]`, - }}, - {"cc_sysprop_library_static", "foo_cc_sysprop_library_static", AttrNameToString{ - "dep": `":foo_sysprop_library"`, - "min_sdk_version": `"5"`, - }}, - {"cc_binary", "foo", AttrNameToString{ - "srcs": `["blah.cpp"]`, - "local_includes": `["."]`, - "min_sdk_version": `"5"`, - "whole_archive_deps": `[":foo_cc_sysprop_library_static"]`, - }}, - }, - }) -} - -func TestCcBinaryWithSyspropSrcsSomeConfigs(t *testing.T) { - runCcBinaryTestCase(t, ccBinaryBp2buildTestCase{ - description: "cc_binary with sysprop sources in some configs but not others", - blueprint: ` -{rule_name} { - name: "foo", - srcs: [ - "blah.cpp", - ], - target: { - android: { - srcs: ["bar.sysprop"], - }, - }, - min_sdk_version: "5", -}`, - targets: []testBazelTarget{ - {"sysprop_library", "foo_sysprop_library", AttrNameToString{ - "srcs": `select({ - "//build/bazel/platforms/os:android": ["bar.sysprop"], - "//conditions:default": [], - })`, - }}, - {"cc_sysprop_library_static", "foo_cc_sysprop_library_static", AttrNameToString{ - "dep": `":foo_sysprop_library"`, - "min_sdk_version": `"5"`, - }}, - {"cc_binary", "foo", AttrNameToString{ - "srcs": `["blah.cpp"]`, - "local_includes": `["."]`, - "min_sdk_version": `"5"`, - "whole_archive_deps": `select({ - "//build/bazel/platforms/os:android": [":foo_cc_sysprop_library_static"], - "//conditions:default": [], - })`, - }}, - }, - }) -} - -func TestCcBinaryWithIntegerOverflowProperty(t *testing.T) { - runCcBinaryTestCase(t, ccBinaryBp2buildTestCase{ - description: "cc_binary with integer overflow property specified", - blueprint: ` -{rule_name} { - name: "foo", - sanitize: { - integer_overflow: true, - }, -}`, - targets: []testBazelTarget{ - {"cc_binary", "foo", AttrNameToString{ - "local_includes": `["."]`, - "features": `["ubsan_integer_overflow"]`, - }}, - }, - }) -} - -func TestCcBinaryWithMiscUndefinedProperty(t *testing.T) { - runCcBinaryTestCase(t, ccBinaryBp2buildTestCase{ - description: "cc_binary with miscellaneous properties specified", - blueprint: ` -{rule_name} { - name: "foo", - sanitize: { - misc_undefined: ["undefined", "nullability"], - }, -}`, - targets: []testBazelTarget{ - {"cc_binary", "foo", AttrNameToString{ - "local_includes": `["."]`, - "features": `[ - "ubsan_undefined", - "ubsan_nullability", - ]`, - }}, - }, - }) -} - -func TestCcBinaryWithUBSanPropertiesArchSpecific(t *testing.T) { - runCcBinaryTestCase(t, ccBinaryBp2buildTestCase{ - description: "cc_binary has correct feature select when UBSan props are specified in arch specific blocks", - blueprint: ` -{rule_name} { - name: "foo", - sanitize: { - misc_undefined: ["undefined", "nullability"], - }, - target: { - android: { - sanitize: { - misc_undefined: ["alignment"], - }, - }, - linux_glibc: { - sanitize: { - integer_overflow: true, - }, - }, - }, -}`, - targets: []testBazelTarget{ - {"cc_binary", "foo", AttrNameToString{ - "local_includes": `["."]`, - "features": `[ - "ubsan_undefined", - "ubsan_nullability", - ] + select({ - "//build/bazel/platforms/os:android": ["ubsan_alignment"], - "//build/bazel/platforms/os:linux_glibc": ["ubsan_integer_overflow"], - "//conditions:default": [], - })`, - }}, - }, - }) -} - -func TestCcBinaryWithThinLto(t *testing.T) { - runCcBinaryTestCase(t, ccBinaryBp2buildTestCase{ - description: "cc_binary has correct features when thin LTO is enabled", - blueprint: ` -{rule_name} { - name: "foo", - lto: { - thin: true, - }, -}`, - targets: []testBazelTarget{ - {"cc_binary", "foo", AttrNameToString{ - "local_includes": `["."]`, - "features": `["android_thin_lto"]`, - }}, - }, - }) -} - -func TestCcBinaryWithLtoNever(t *testing.T) { - runCcBinaryTestCase(t, ccBinaryBp2buildTestCase{ - description: "cc_binary has correct features when LTO is explicitly disabled", - blueprint: ` -{rule_name} { - name: "foo", - lto: { - never: true, - }, -}`, - targets: []testBazelTarget{ - {"cc_binary", "foo", AttrNameToString{ - "local_includes": `["."]`, - "features": `["-android_thin_lto"]`, - }}, - }, - }) -} - -func TestCcBinaryWithThinLtoArchSpecific(t *testing.T) { - runCcBinaryTestCase(t, ccBinaryBp2buildTestCase{ - description: "cc_binary has correct features when LTO differs across arch and os variants", - blueprint: ` -{rule_name} { - name: "foo", - target: { - android: { - lto: { - thin: true, - }, - }, - }, - arch: { - riscv64: { - lto: { - thin: false, - }, - }, - }, -}`, - targets: []testBazelTarget{ - {"cc_binary", "foo", AttrNameToString{ - "local_includes": `["."]`, - "features": `select({ - "//build/bazel/platforms/os_arch:android_arm": ["android_thin_lto"], - "//build/bazel/platforms/os_arch:android_arm64": ["android_thin_lto"], - "//build/bazel/platforms/os_arch:android_riscv64": ["-android_thin_lto"], - "//build/bazel/platforms/os_arch:android_x86": ["android_thin_lto"], - "//build/bazel/platforms/os_arch:android_x86_64": ["android_thin_lto"], - "//conditions:default": [], - })`, - }}, - }, - }) -} - -func TestCcBinaryWithThinLtoDisabledDefaultEnabledVariant(t *testing.T) { - runCcBinaryTestCase(t, ccBinaryBp2buildTestCase{ - description: "cc_binary has correct features when LTO disabled by default but enabled on a particular variant", - blueprint: ` -{rule_name} { - name: "foo", - lto: { - never: true, - }, - target: { - android: { - lto: { - thin: true, - never: false, - }, - }, - }, -}`, - targets: []testBazelTarget{ - {"cc_binary", "foo", AttrNameToString{ - "local_includes": `["."]`, - "features": `select({ - "//build/bazel/platforms/os:android": ["android_thin_lto"], - "//conditions:default": ["-android_thin_lto"], - })`, - }}, - }, - }) -} - -func TestCcBinaryWithThinLtoAndWholeProgramVtables(t *testing.T) { - runCcBinaryTestCase(t, ccBinaryBp2buildTestCase{ - description: "cc_binary has correct features when thin LTO is enabled with whole_program_vtables", - blueprint: ` -{rule_name} { - name: "foo", - lto: { - thin: true, - }, - whole_program_vtables: true, -}`, - targets: []testBazelTarget{ - {"cc_binary", "foo", AttrNameToString{ - "local_includes": `["."]`, - "features": `[ - "android_thin_lto", - "android_thin_lto_whole_program_vtables", - ]`, - }}, - }, - }) -} diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go deleted file mode 100644 index 7165ac45b5..0000000000 --- a/bp2build/cc_library_conversion_test.go +++ /dev/null @@ -1,4392 +0,0 @@ -// 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 bp2build - -import ( - "fmt" - "testing" - - "android/soong/android" - "android/soong/cc" -) - -const ( - // See cc/testing.go for more context - soongCcLibraryPreamble = ` -cc_defaults { - name: "linux_bionic_supported", -} -` - - soongCcVersionLibBpPath = "build/soong/cc/libbuildversion/Android.bp" - soongCcVersionLibBp = ` -cc_library_static { - name: "libbuildversion", - bazel_module: { bp2build_available: false }, -} -` - - soongCcProtoLibraries = ` -cc_library { - name: "libprotobuf-cpp-lite", - bazel_module: { bp2build_available: false }, -} - -cc_library { - name: "libprotobuf-cpp-full", - bazel_module: { bp2build_available: false }, -}` - - soongCcProtoPreamble = soongCcLibraryPreamble + soongCcProtoLibraries -) - -func runCcLibraryTestCase(t *testing.T, tc Bp2buildTestCase) { - t.Helper() - RunBp2BuildTestCase(t, registerCcLibraryModuleTypes, tc) -} - -func registerCcLibraryModuleTypes(ctx android.RegistrationContext) { - cc.RegisterCCBuildComponents(ctx) - ctx.RegisterModuleType("filegroup", android.FileGroupFactory) - ctx.RegisterModuleType("cc_library_static", cc.LibraryStaticFactory) - ctx.RegisterModuleType("cc_prebuilt_library_static", cc.PrebuiltStaticLibraryFactory) - ctx.RegisterModuleType("cc_library_headers", cc.LibraryHeaderFactory) -} - -func TestCcLibrarySimple(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library - simple example", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Filesystem: map[string]string{ - soongCcVersionLibBpPath: soongCcVersionLibBp, - "android.cpp": "", - "bionic.cpp": "", - "darwin.cpp": "", - // Refer to cc.headerExts for the supported header extensions in Soong. - "header.h": "", - "header.hh": "", - "header.hpp": "", - "header.hxx": "", - "header.h++": "", - "header.inl": "", - "header.inc": "", - "header.ipp": "", - "header.h.generic": "", - "impl.cpp": "", - "linux.cpp": "", - "x86.cpp": "", - "x86_64.cpp": "", - "foo-dir/a.h": "", - }, - Blueprint: soongCcLibraryPreamble + - simpleModuleDoNotConvertBp2build("cc_library_headers", "some-headers") + ` -cc_library { - name: "foo-lib", - srcs: ["impl.cpp"], - cflags: ["-Wall"], - header_libs: ["some-headers"], - export_include_dirs: ["foo-dir"], - ldflags: ["-Wl,--exclude-libs=bar.a"], - arch: { - x86: { - ldflags: ["-Wl,--exclude-libs=baz.a"], - srcs: ["x86.cpp"], - }, - x86_64: { - ldflags: ["-Wl,--exclude-libs=qux.a"], - srcs: ["x86_64.cpp"], - }, - }, - target: { - android: { - srcs: ["android.cpp"], - }, - linux_glibc: { - srcs: ["linux.cpp"], - }, - darwin: { - srcs: ["darwin.cpp"], - }, - bionic: { - srcs: ["bionic.cpp"] - }, - }, - include_build_directory: false, - sdk_version: "current", - min_sdk_version: "29", - use_version_lib: true, -} -`, - ExpectedBazelTargets: makeCcLibraryTargets("foo-lib", AttrNameToString{ - "copts": `["-Wall"]`, - "export_includes": `["foo-dir"]`, - "implementation_deps": `[":some-headers"]`, - "linkopts": `["-Wl,--exclude-libs=bar.a"] + select({ - "//build/bazel/platforms/arch:x86": ["-Wl,--exclude-libs=baz.a"], - "//build/bazel/platforms/arch:x86_64": ["-Wl,--exclude-libs=qux.a"], - "//conditions:default": [], - })`, - "srcs": `["impl.cpp"] + select({ - "//build/bazel/platforms/arch:x86": ["x86.cpp"], - "//build/bazel/platforms/arch:x86_64": ["x86_64.cpp"], - "//conditions:default": [], - }) + select({ - "//build/bazel/platforms/os:android": [ - "bionic.cpp", - "android.cpp", - ], - "//build/bazel/platforms/os:darwin": ["darwin.cpp"], - "//build/bazel/platforms/os:linux_bionic": ["bionic.cpp"], - "//build/bazel/platforms/os:linux_glibc": ["linux.cpp"], - "//conditions:default": [], - })`, - "sdk_version": `"current"`, - "min_sdk_version": `"29"`, - "use_version_lib": `True`, - "implementation_whole_archive_deps": `["//build/soong/cc/libbuildversion:libbuildversion"]`, - }), - }) -} - -func TestCcLibraryTrimmedLdAndroid(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library - trimmed example of //bionic/linker:ld-android", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Filesystem: map[string]string{ - "ld-android.cpp": "", - "linked_list.h": "", - "linker.h": "", - "linker_block_allocator.h": "", - "linker_cfi.h": "", - }, - Blueprint: soongCcLibraryPreamble + - simpleModuleDoNotConvertBp2build("cc_library_headers", "libc_headers") + ` -cc_library { - name: "fake-ld-android", - srcs: ["ld_android.cpp"], - cflags: [ - "-Wall", - "-Wextra", - "-Wunused", - "-Werror", - ], - header_libs: ["libc_headers"], - ldflags: [ - "-Wl,--exclude-libs=libgcc.a", - "-Wl,--exclude-libs=libgcc_stripped.a", - "-Wl,--exclude-libs=libclang_rt.builtins-arm-android.a", - "-Wl,--exclude-libs=libclang_rt.builtins-aarch64-android.a", - "-Wl,--exclude-libs=libclang_rt.builtins-i686-android.a", - "-Wl,--exclude-libs=libclang_rt.builtins-x86_64-android.a", - ], - arch: { - x86: { - ldflags: ["-Wl,--exclude-libs=libgcc_eh.a"], - }, - x86_64: { - ldflags: ["-Wl,--exclude-libs=libgcc_eh.a"], - }, - }, - include_build_directory: false, -} -`, - ExpectedBazelTargets: makeCcLibraryTargets("fake-ld-android", AttrNameToString{ - "srcs": `["ld_android.cpp"]`, - "copts": `[ - "-Wall", - "-Wextra", - "-Wunused", - "-Werror", - ]`, - "implementation_deps": `[":libc_headers"]`, - "linkopts": `[ - "-Wl,--exclude-libs=libgcc.a", - "-Wl,--exclude-libs=libgcc_stripped.a", - "-Wl,--exclude-libs=libclang_rt.builtins-arm-android.a", - "-Wl,--exclude-libs=libclang_rt.builtins-aarch64-android.a", - "-Wl,--exclude-libs=libclang_rt.builtins-i686-android.a", - "-Wl,--exclude-libs=libclang_rt.builtins-x86_64-android.a", - ] + select({ - "//build/bazel/platforms/arch:x86": ["-Wl,--exclude-libs=libgcc_eh.a"], - "//build/bazel/platforms/arch:x86_64": ["-Wl,--exclude-libs=libgcc_eh.a"], - "//conditions:default": [], - })`, - }), - }) -} - -func TestCcLibraryExcludeSrcs(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library exclude_srcs - trimmed example of //external/arm-optimized-routines:libarm-optimized-routines-math", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Dir: "external", - Filesystem: map[string]string{ - "external/math/cosf.c": "", - "external/math/erf.c": "", - "external/math/erf_data.c": "", - "external/math/erff.c": "", - "external/math/erff_data.c": "", - "external/Android.bp": ` -cc_library { - name: "fake-libarm-optimized-routines-math", - exclude_srcs: [ - // Provided by: - // bionic/libm/upstream-freebsd/lib/msun/src/s_erf.c - // bionic/libm/upstream-freebsd/lib/msun/src/s_erff.c - "math/erf.c", - "math/erf_data.c", - "math/erff.c", - "math/erff_data.c", - ], - srcs: [ - "math/*.c", - ], - // arch-specific settings - arch: { - arm64: { - cflags: [ - "-DHAVE_FAST_FMA=1", - ], - }, - }, - bazel_module: { bp2build_available: true }, -} -`, - }, - Blueprint: soongCcLibraryPreamble, - ExpectedBazelTargets: makeCcLibraryTargets("fake-libarm-optimized-routines-math", AttrNameToString{ - "copts": `select({ - "//build/bazel/platforms/arch:arm64": ["-DHAVE_FAST_FMA=1"], - "//conditions:default": [], - })`, - "local_includes": `["."]`, - "srcs_c": `["math/cosf.c"]`, - }), - }) -} - -func TestCcLibrarySharedStaticProps(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library shared/static props", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Filesystem: map[string]string{ - "both.cpp": "", - "sharedonly.cpp": "", - "staticonly.cpp": "", - }, - Blueprint: soongCcLibraryPreamble + ` -cc_library { - name: "a", - srcs: ["both.cpp"], - cflags: ["bothflag"], - shared_libs: ["shared_dep_for_both"], - static_libs: ["static_dep_for_both", "whole_and_static_lib_for_both"], - whole_static_libs: ["whole_static_lib_for_both", "whole_and_static_lib_for_both"], - static: { - srcs: ["staticonly.cpp"], - cflags: ["staticflag"], - shared_libs: ["shared_dep_for_static"], - static_libs: ["static_dep_for_static"], - whole_static_libs: ["whole_static_lib_for_static"], - }, - shared: { - srcs: ["sharedonly.cpp"], - cflags: ["sharedflag"], - shared_libs: ["shared_dep_for_shared"], - static_libs: ["static_dep_for_shared"], - whole_static_libs: ["whole_static_lib_for_shared"], - }, - include_build_directory: false, -} - -cc_library_static { - name: "static_dep_for_shared", - bazel_module: { bp2build_available: false }, -} - -cc_library_static { - name: "static_dep_for_static", - bazel_module: { bp2build_available: false }, -} - -cc_library_static { - name: "static_dep_for_both", - bazel_module: { bp2build_available: false }, -} - -cc_library_static { - name: "whole_static_lib_for_shared", - bazel_module: { bp2build_available: false }, -} - -cc_library_static { - name: "whole_static_lib_for_static", - bazel_module: { bp2build_available: false }, -} - -cc_library_static { - name: "whole_static_lib_for_both", - bazel_module: { bp2build_available: false }, -} - -cc_library_static { - name: "whole_and_static_lib_for_both", - bazel_module: { bp2build_available: false }, -} - -cc_library { - name: "shared_dep_for_shared", - bazel_module: { bp2build_available: false }, -} - -cc_library { - name: "shared_dep_for_static", - bazel_module: { bp2build_available: false }, -} - -cc_library { - name: "shared_dep_for_both", - bazel_module: { bp2build_available: false }, -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "a_bp2build_cc_library_static", AttrNameToString{ - "copts": `[ - "bothflag", - "staticflag", - ]`, - "implementation_deps": `[ - ":static_dep_for_both", - ":static_dep_for_static", - ]`, - "implementation_dynamic_deps": `[ - ":shared_dep_for_both", - ":shared_dep_for_static", - ]`, - "srcs": `[ - "both.cpp", - "staticonly.cpp", - ]`, - "whole_archive_deps": `[ - ":whole_static_lib_for_both", - ":whole_and_static_lib_for_both", - ":whole_static_lib_for_static", - ]`}), - MakeBazelTarget("cc_library_shared", "a", AttrNameToString{ - "copts": `[ - "bothflag", - "sharedflag", - ]`, - "implementation_deps": `[ - ":static_dep_for_both", - ":static_dep_for_shared", - ]`, - "implementation_dynamic_deps": `[ - ":shared_dep_for_both", - ":shared_dep_for_shared", - ]`, - "srcs": `[ - "both.cpp", - "sharedonly.cpp", - ]`, - "whole_archive_deps": `[ - ":whole_static_lib_for_both", - ":whole_and_static_lib_for_both", - ":whole_static_lib_for_shared", - ]`, - }), - }, - }) -} - -func TestCcLibraryDeps(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library shared/static props", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Filesystem: map[string]string{ - "both.cpp": "", - "sharedonly.cpp": "", - "staticonly.cpp": "", - }, - Blueprint: soongCcLibraryPreamble + ` -cc_library { - name: "a", - srcs: ["both.cpp"], - cflags: ["bothflag"], - shared_libs: ["implementation_shared_dep_for_both", "shared_dep_for_both"], - export_shared_lib_headers: ["shared_dep_for_both"], - static_libs: ["implementation_static_dep_for_both", "static_dep_for_both"], - export_static_lib_headers: ["static_dep_for_both", "whole_static_dep_for_both"], - whole_static_libs: ["not_explicitly_exported_whole_static_dep_for_both", "whole_static_dep_for_both"], - static: { - srcs: ["staticonly.cpp"], - cflags: ["staticflag"], - shared_libs: ["implementation_shared_dep_for_static", "shared_dep_for_static"], - export_shared_lib_headers: ["shared_dep_for_static"], - static_libs: ["implementation_static_dep_for_static", "static_dep_for_static"], - export_static_lib_headers: ["static_dep_for_static", "whole_static_dep_for_static"], - whole_static_libs: ["not_explicitly_exported_whole_static_dep_for_static", "whole_static_dep_for_static"], - }, - shared: { - srcs: ["sharedonly.cpp"], - cflags: ["sharedflag"], - shared_libs: ["implementation_shared_dep_for_shared", "shared_dep_for_shared"], - export_shared_lib_headers: ["shared_dep_for_shared"], - static_libs: ["implementation_static_dep_for_shared", "static_dep_for_shared"], - export_static_lib_headers: ["static_dep_for_shared", "whole_static_dep_for_shared"], - whole_static_libs: ["not_explicitly_exported_whole_static_dep_for_shared", "whole_static_dep_for_shared"], - }, - include_build_directory: false, -} -` + simpleModuleDoNotConvertBp2build("cc_library_static", "static_dep_for_shared") + - simpleModuleDoNotConvertBp2build("cc_library_static", "implementation_static_dep_for_shared") + - simpleModuleDoNotConvertBp2build("cc_library_static", "static_dep_for_static") + - simpleModuleDoNotConvertBp2build("cc_library_static", "implementation_static_dep_for_static") + - simpleModuleDoNotConvertBp2build("cc_library_static", "static_dep_for_both") + - simpleModuleDoNotConvertBp2build("cc_library_static", "implementation_static_dep_for_both") + - simpleModuleDoNotConvertBp2build("cc_library_static", "whole_static_dep_for_shared") + - simpleModuleDoNotConvertBp2build("cc_library_static", "not_explicitly_exported_whole_static_dep_for_shared") + - simpleModuleDoNotConvertBp2build("cc_library_static", "whole_static_dep_for_static") + - simpleModuleDoNotConvertBp2build("cc_library_static", "not_explicitly_exported_whole_static_dep_for_static") + - simpleModuleDoNotConvertBp2build("cc_library_static", "whole_static_dep_for_both") + - simpleModuleDoNotConvertBp2build("cc_library_static", "not_explicitly_exported_whole_static_dep_for_both") + - simpleModuleDoNotConvertBp2build("cc_library", "shared_dep_for_shared") + - simpleModuleDoNotConvertBp2build("cc_library", "implementation_shared_dep_for_shared") + - simpleModuleDoNotConvertBp2build("cc_library", "shared_dep_for_static") + - simpleModuleDoNotConvertBp2build("cc_library", "implementation_shared_dep_for_static") + - simpleModuleDoNotConvertBp2build("cc_library", "shared_dep_for_both") + - simpleModuleDoNotConvertBp2build("cc_library", "implementation_shared_dep_for_both"), - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "a_bp2build_cc_library_static", AttrNameToString{ - "copts": `[ - "bothflag", - "staticflag", - ]`, - "deps": `[ - ":static_dep_for_both", - ":static_dep_for_static", - ]`, - "dynamic_deps": `[ - ":shared_dep_for_both", - ":shared_dep_for_static", - ]`, - "implementation_deps": `[ - ":implementation_static_dep_for_both", - ":implementation_static_dep_for_static", - ]`, - "implementation_dynamic_deps": `[ - ":implementation_shared_dep_for_both", - ":implementation_shared_dep_for_static", - ]`, - "srcs": `[ - "both.cpp", - "staticonly.cpp", - ]`, - "whole_archive_deps": `[ - ":not_explicitly_exported_whole_static_dep_for_both", - ":whole_static_dep_for_both", - ":not_explicitly_exported_whole_static_dep_for_static", - ":whole_static_dep_for_static", - ]`, - }), - MakeBazelTarget("cc_library_shared", "a", AttrNameToString{ - "copts": `[ - "bothflag", - "sharedflag", - ]`, - "deps": `[ - ":static_dep_for_both", - ":static_dep_for_shared", - ]`, - "dynamic_deps": `[ - ":shared_dep_for_both", - ":shared_dep_for_shared", - ]`, - "implementation_deps": `[ - ":implementation_static_dep_for_both", - ":implementation_static_dep_for_shared", - ]`, - "implementation_dynamic_deps": `[ - ":implementation_shared_dep_for_both", - ":implementation_shared_dep_for_shared", - ]`, - "srcs": `[ - "both.cpp", - "sharedonly.cpp", - ]`, - "whole_archive_deps": `[ - ":not_explicitly_exported_whole_static_dep_for_both", - ":whole_static_dep_for_both", - ":not_explicitly_exported_whole_static_dep_for_shared", - ":whole_static_dep_for_shared", - ]`, - })}, - }, - ) -} - -func TestCcLibraryWholeStaticLibsAlwaysLink(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Dir: "foo/bar", - Filesystem: map[string]string{ - "foo/bar/Android.bp": ` -cc_library { - name: "a", - whole_static_libs: ["whole_static_lib_for_both"], - static: { - whole_static_libs: ["whole_static_lib_for_static"], - }, - shared: { - whole_static_libs: ["whole_static_lib_for_shared"], - }, - bazel_module: { bp2build_available: true }, - include_build_directory: false, -} - -cc_prebuilt_library_static { name: "whole_static_lib_for_shared" } - -cc_prebuilt_library_static { name: "whole_static_lib_for_static" } - -cc_prebuilt_library_static { name: "whole_static_lib_for_both" } -`, - }, - Blueprint: soongCcLibraryPreamble, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "a_bp2build_cc_library_static", AttrNameToString{ - "whole_archive_deps": `[ - ":whole_static_lib_for_both_alwayslink", - ":whole_static_lib_for_static_alwayslink", - ]`, - }), - MakeBazelTarget("cc_library_shared", "a", AttrNameToString{ - "whole_archive_deps": `[ - ":whole_static_lib_for_both_alwayslink", - ":whole_static_lib_for_shared_alwayslink", - ]`, - }), - }, - }, - ) -} - -func TestCcLibrarySharedStaticPropsInArch(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library shared/static props in arch", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Dir: "foo/bar", - Filesystem: map[string]string{ - "foo/bar/arm.cpp": "", - "foo/bar/x86.cpp": "", - "foo/bar/sharedonly.cpp": "", - "foo/bar/staticonly.cpp": "", - "foo/bar/Android.bp": ` -cc_library { - name: "a", - arch: { - arm: { - shared: { - srcs: ["arm_shared.cpp"], - cflags: ["-DARM_SHARED"], - static_libs: ["arm_static_dep_for_shared"], - whole_static_libs: ["arm_whole_static_dep_for_shared"], - shared_libs: ["arm_shared_dep_for_shared"], - }, - }, - x86: { - static: { - srcs: ["x86_static.cpp"], - cflags: ["-DX86_STATIC"], - static_libs: ["x86_dep_for_static"], - }, - }, - }, - target: { - android: { - shared: { - srcs: ["android_shared.cpp"], - cflags: ["-DANDROID_SHARED"], - static_libs: ["android_dep_for_shared"], - }, - }, - android_arm: { - shared: { - cflags: ["-DANDROID_ARM_SHARED"], - }, - }, - }, - srcs: ["both.cpp"], - cflags: ["bothflag"], - static_libs: ["static_dep_for_both"], - static: { - srcs: ["staticonly.cpp"], - cflags: ["staticflag"], - static_libs: ["static_dep_for_static"], - }, - shared: { - srcs: ["sharedonly.cpp"], - cflags: ["sharedflag"], - static_libs: ["static_dep_for_shared"], - }, - bazel_module: { bp2build_available: true }, -} - -cc_library_static { name: "static_dep_for_shared" } -cc_library_static { name: "static_dep_for_static" } -cc_library_static { name: "static_dep_for_both" } - -cc_library_static { name: "arm_static_dep_for_shared" } -cc_library_static { name: "arm_whole_static_dep_for_shared" } -cc_library_static { name: "arm_shared_dep_for_shared" } - -cc_library_static { name: "x86_dep_for_static" } - -cc_library_static { name: "android_dep_for_shared" } -`, - }, - Blueprint: soongCcLibraryPreamble, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "a_bp2build_cc_library_static", AttrNameToString{ - "copts": `[ - "bothflag", - "staticflag", - ] + select({ - "//build/bazel/platforms/arch:x86": ["-DX86_STATIC"], - "//conditions:default": [], - })`, - "implementation_deps": `[ - ":static_dep_for_both", - ":static_dep_for_static", - ] + select({ - "//build/bazel/platforms/arch:x86": [":x86_dep_for_static"], - "//conditions:default": [], - })`, - "local_includes": `["."]`, - "srcs": `[ - "both.cpp", - "staticonly.cpp", - ] + select({ - "//build/bazel/platforms/arch:x86": ["x86_static.cpp"], - "//conditions:default": [], - })`, - }), - MakeBazelTarget("cc_library_shared", "a", AttrNameToString{ - "copts": `[ - "bothflag", - "sharedflag", - ] + select({ - "//build/bazel/platforms/arch:arm": ["-DARM_SHARED"], - "//conditions:default": [], - }) + select({ - "//build/bazel/platforms/os:android": ["-DANDROID_SHARED"], - "//conditions:default": [], - }) + select({ - "//build/bazel/platforms/os_arch:android_arm": ["-DANDROID_ARM_SHARED"], - "//conditions:default": [], - })`, - "implementation_deps": `[ - ":static_dep_for_both", - ":static_dep_for_shared", - ] + select({ - "//build/bazel/platforms/arch:arm": [":arm_static_dep_for_shared"], - "//conditions:default": [], - }) + select({ - "//build/bazel/platforms/os:android": [":android_dep_for_shared"], - "//conditions:default": [], - })`, - "implementation_dynamic_deps": `select({ - "//build/bazel/platforms/arch:arm": [":arm_shared_dep_for_shared"], - "//conditions:default": [], - })`, - "local_includes": `["."]`, - "srcs": `[ - "both.cpp", - "sharedonly.cpp", - ] + select({ - "//build/bazel/platforms/arch:arm": ["arm_shared.cpp"], - "//conditions:default": [], - }) + select({ - "//build/bazel/platforms/os:android": ["android_shared.cpp"], - "//conditions:default": [], - })`, - "whole_archive_deps": `select({ - "//build/bazel/platforms/arch:arm": [":arm_whole_static_dep_for_shared"], - "//conditions:default": [], - })`, - }), - }, - }, - ) -} - -func TestCcLibrarySharedStaticPropsWithMixedSources(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library shared/static props with c/cpp/s mixed sources", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Dir: "foo/bar", - Filesystem: map[string]string{ - "foo/bar/both_source.cpp": "", - "foo/bar/both_source.cc": "", - "foo/bar/both_source.c": "", - "foo/bar/both_source.s": "", - "foo/bar/both_source.S": "", - "foo/bar/shared_source.cpp": "", - "foo/bar/shared_source.cc": "", - "foo/bar/shared_source.c": "", - "foo/bar/shared_source.s": "", - "foo/bar/shared_source.S": "", - "foo/bar/static_source.cpp": "", - "foo/bar/static_source.cc": "", - "foo/bar/static_source.c": "", - "foo/bar/static_source.s": "", - "foo/bar/static_source.S": "", - "foo/bar/Android.bp": ` -cc_library { - name: "a", - srcs: [ - "both_source.cpp", - "both_source.cc", - "both_source.c", - "both_source.s", - "both_source.S", - ":both_filegroup", - ], - static: { - srcs: [ - "static_source.cpp", - "static_source.cc", - "static_source.c", - "static_source.s", - "static_source.S", - ":static_filegroup", - ], - }, - shared: { - srcs: [ - "shared_source.cpp", - "shared_source.cc", - "shared_source.c", - "shared_source.s", - "shared_source.S", - ":shared_filegroup", - ], - }, - bazel_module: { bp2build_available: true }, -} - -filegroup { - name: "both_filegroup", - srcs: [ - // Not relevant, handled by filegroup macro - ], -} - -filegroup { - name: "shared_filegroup", - srcs: [ - // Not relevant, handled by filegroup macro - ], -} - -filegroup { - name: "static_filegroup", - srcs: [ - // Not relevant, handled by filegroup macro - ], -} -`, - }, - Blueprint: soongCcLibraryPreamble, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "a_bp2build_cc_library_static", AttrNameToString{ - "local_includes": `["."]`, - "srcs": `[ - "both_source.cpp", - "both_source.cc", - ":both_filegroup_cpp_srcs", - "static_source.cpp", - "static_source.cc", - ":static_filegroup_cpp_srcs", - ]`, - "srcs_as": `[ - "both_source.s", - "both_source.S", - ":both_filegroup_as_srcs", - "static_source.s", - "static_source.S", - ":static_filegroup_as_srcs", - ]`, - "srcs_c": `[ - "both_source.c", - ":both_filegroup_c_srcs", - "static_source.c", - ":static_filegroup_c_srcs", - ]`, - }), - MakeBazelTarget("cc_library_shared", "a", AttrNameToString{ - "local_includes": `["."]`, - "srcs": `[ - "both_source.cpp", - "both_source.cc", - ":both_filegroup_cpp_srcs", - "shared_source.cpp", - "shared_source.cc", - ":shared_filegroup_cpp_srcs", - ]`, - "srcs_as": `[ - "both_source.s", - "both_source.S", - ":both_filegroup_as_srcs", - "shared_source.s", - "shared_source.S", - ":shared_filegroup_as_srcs", - ]`, - "srcs_c": `[ - "both_source.c", - ":both_filegroup_c_srcs", - "shared_source.c", - ":shared_filegroup_c_srcs", - ]`, - })}}) -} - -func TestCcLibraryNonConfiguredVersionScriptAndDynamicList(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library non-configured version script and dynamic list", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Dir: "foo/bar", - Filesystem: map[string]string{ - "foo/bar/Android.bp": ` -cc_library { - name: "a", - srcs: ["a.cpp"], - version_script: "v.map", - dynamic_list: "dynamic.list", - bazel_module: { bp2build_available: true }, - include_build_directory: false, -} -`, - }, - Blueprint: soongCcLibraryPreamble, - ExpectedBazelTargets: makeCcLibraryTargets("a", AttrNameToString{ - "additional_linker_inputs": `[ - "v.map", - "dynamic.list", - ]`, - "linkopts": `[ - "-Wl,--version-script,$(location v.map)", - "-Wl,--dynamic-list,$(location dynamic.list)", - ]`, - "srcs": `["a.cpp"]`, - }), - }, - ) -} - -func TestCcLibraryConfiguredVersionScriptAndDynamicList(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library configured version script and dynamic list", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Dir: "foo/bar", - Filesystem: map[string]string{ - "foo/bar/Android.bp": ` -cc_library { - name: "a", - srcs: ["a.cpp"], - arch: { - arm: { - version_script: "arm.map", - dynamic_list: "dynamic_arm.list", - }, - arm64: { - version_script: "arm64.map", - dynamic_list: "dynamic_arm64.list", - }, - }, - - bazel_module: { bp2build_available: true }, - include_build_directory: false, -} - `, - }, - Blueprint: soongCcLibraryPreamble, - ExpectedBazelTargets: makeCcLibraryTargets("a", AttrNameToString{ - "additional_linker_inputs": `select({ - "//build/bazel/platforms/arch:arm": [ - "arm.map", - "dynamic_arm.list", - ], - "//build/bazel/platforms/arch:arm64": [ - "arm64.map", - "dynamic_arm64.list", - ], - "//conditions:default": [], - })`, - "linkopts": `select({ - "//build/bazel/platforms/arch:arm": [ - "-Wl,--version-script,$(location arm.map)", - "-Wl,--dynamic-list,$(location dynamic_arm.list)", - ], - "//build/bazel/platforms/arch:arm64": [ - "-Wl,--version-script,$(location arm64.map)", - "-Wl,--dynamic-list,$(location dynamic_arm64.list)", - ], - "//conditions:default": [], - })`, - "srcs": `["a.cpp"]`, - }), - }, - ) -} - -func TestCcLibraryLdflagsSplitBySpaceExceptSoongAdded(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "ldflags are split by spaces except for the ones added by soong (version script and dynamic list)", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Filesystem: map[string]string{ - "version_script": "", - "dynamic.list": "", - }, - Blueprint: ` -cc_library { - name: "foo", - ldflags: [ - "--nospace_flag", - "-z spaceflag", - ], - version_script: "version_script", - dynamic_list: "dynamic.list", - include_build_directory: false, -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{}), - MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ - "additional_linker_inputs": `[ - "version_script", - "dynamic.list", - ]`, - "linkopts": `[ - "--nospace_flag", - "-z", - "spaceflag", - "-Wl,--version-script,$(location version_script)", - "-Wl,--dynamic-list,$(location dynamic.list)", - ]`, - }), - }, - }) -} - -func TestCcLibrarySharedLibs(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library shared_libs", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: soongCcLibraryPreamble + ` -cc_library { - name: "mylib", - bazel_module: { bp2build_available: false }, -} - -cc_library { - name: "a", - shared_libs: ["mylib",], - include_build_directory: false, -} -`, - ExpectedBazelTargets: makeCcLibraryTargets("a", AttrNameToString{ - "implementation_dynamic_deps": `[":mylib"]`, - }), - }, - ) -} - -func TestCcLibraryFeatures(t *testing.T) { - expected_targets := []string{} - expected_targets = append(expected_targets, makeCcLibraryTargets("a", AttrNameToString{ - "features": `[ - "disable_pack_relocations", - "-no_undefined_symbols", - ]`, - "native_coverage": `False`, - "srcs": `["a.cpp"]`, - })...) - expected_targets = append(expected_targets, makeCcLibraryTargets("b", AttrNameToString{ - "features": `select({ - "//build/bazel/platforms/arch:x86_64": [ - "disable_pack_relocations", - "-no_undefined_symbols", - ], - "//conditions:default": [], - })`, - "native_coverage": `False`, - "srcs": `["b.cpp"]`, - })...) - expected_targets = append(expected_targets, makeCcLibraryTargets("c", AttrNameToString{ - "features": `select({ - "//build/bazel/platforms/os:darwin": [ - "disable_pack_relocations", - "-no_undefined_symbols", - ], - "//conditions:default": [], - })`, - "srcs": `["c.cpp"]`, - })...) - - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library pack_relocations test", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: soongCcLibraryPreamble + ` -cc_library { - name: "a", - srcs: ["a.cpp"], - pack_relocations: false, - allow_undefined_symbols: true, - include_build_directory: false, - native_coverage: false, -} - -cc_library { - name: "b", - srcs: ["b.cpp"], - arch: { - x86_64: { - pack_relocations: false, - allow_undefined_symbols: true, - }, - }, - include_build_directory: false, - native_coverage: false, -} - -cc_library { - name: "c", - srcs: ["c.cpp"], - target: { - darwin: { - pack_relocations: false, - allow_undefined_symbols: true, - }, - }, - include_build_directory: false, -}`, - ExpectedBazelTargets: expected_targets, - }) -} - -func TestCcLibrarySpacesInCopts(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library spaces in copts", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: soongCcLibraryPreamble + ` -cc_library { - name: "a", - cflags: ["-include header.h",], - include_build_directory: false, -} -`, - ExpectedBazelTargets: makeCcLibraryTargets("a", AttrNameToString{ - "copts": `[ - "-include", - "header.h", - ]`, - }), - }, - ) -} - -func TestCcLibraryCppFlagsGoesIntoCopts(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library cppflags usage", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: soongCcLibraryPreamble + `cc_library { - name: "a", - srcs: ["a.cpp"], - cflags: ["-Wall"], - cppflags: [ - "-fsigned-char", - "-pedantic", - ], - arch: { - arm64: { - cppflags: ["-DARM64=1"], - }, - }, - target: { - android: { - cppflags: ["-DANDROID=1"], - }, - }, - include_build_directory: false, -} -`, - ExpectedBazelTargets: makeCcLibraryTargets("a", AttrNameToString{ - "copts": `["-Wall"]`, - "cppflags": `[ - "-fsigned-char", - "-pedantic", - ] + select({ - "//build/bazel/platforms/arch:arm64": ["-DARM64=1"], - "//conditions:default": [], - }) + select({ - "//build/bazel/platforms/os:android": ["-DANDROID=1"], - "//conditions:default": [], - })`, - "srcs": `["a.cpp"]`, - }), - }, - ) -} - -func TestCcLibraryExcludeLibs(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Filesystem: map[string]string{}, - Blueprint: soongCcLibraryStaticPreamble + ` -cc_library { - name: "foo_static", - srcs: ["common.c"], - whole_static_libs: [ - "arm_whole_static_lib_excludes", - "malloc_not_svelte_whole_static_lib_excludes" - ], - static_libs: [ - "arm_static_lib_excludes", - "malloc_not_svelte_static_lib_excludes" - ], - shared_libs: [ - "arm_shared_lib_excludes", - ], - arch: { - arm: { - exclude_shared_libs: [ - "arm_shared_lib_excludes", - ], - exclude_static_libs: [ - "arm_static_lib_excludes", - "arm_whole_static_lib_excludes", - ], - }, - }, - product_variables: { - malloc_not_svelte: { - shared_libs: ["malloc_not_svelte_shared_lib"], - whole_static_libs: ["malloc_not_svelte_whole_static_lib"], - exclude_static_libs: [ - "malloc_not_svelte_static_lib_excludes", - "malloc_not_svelte_whole_static_lib_excludes", - ], - }, - }, - include_build_directory: false, -} - -cc_library { - name: "arm_whole_static_lib_excludes", - bazel_module: { bp2build_available: false }, -} - -cc_library { - name: "malloc_not_svelte_whole_static_lib", - bazel_module: { bp2build_available: false }, -} - -cc_library { - name: "malloc_not_svelte_whole_static_lib_excludes", - bazel_module: { bp2build_available: false }, -} - -cc_library { - name: "arm_static_lib_excludes", - bazel_module: { bp2build_available: false }, -} - -cc_library { - name: "malloc_not_svelte_static_lib_excludes", - bazel_module: { bp2build_available: false }, -} - -cc_library { - name: "arm_shared_lib_excludes", - bazel_module: { bp2build_available: false }, -} - -cc_library { - name: "malloc_not_svelte_shared_lib", - bazel_module: { bp2build_available: false }, -} -`, - ExpectedBazelTargets: makeCcLibraryTargets("foo_static", AttrNameToString{ - "implementation_deps": `select({ - "//build/bazel/platforms/arch:arm": [], - "//conditions:default": [":arm_static_lib_excludes_bp2build_cc_library_static"], - }) + select({ - "//build/bazel/product_variables:malloc_not_svelte": [], - "//conditions:default": [":malloc_not_svelte_static_lib_excludes_bp2build_cc_library_static"], - })`, - "implementation_dynamic_deps": `select({ - "//build/bazel/platforms/arch:arm": [], - "//conditions:default": [":arm_shared_lib_excludes"], - }) + select({ - "//build/bazel/product_variables:malloc_not_svelte": [":malloc_not_svelte_shared_lib"], - "//conditions:default": [], - })`, - "srcs_c": `["common.c"]`, - "whole_archive_deps": `select({ - "//build/bazel/platforms/arch:arm": [], - "//conditions:default": [":arm_whole_static_lib_excludes_bp2build_cc_library_static"], - }) + select({ - "//build/bazel/product_variables:malloc_not_svelte": [":malloc_not_svelte_whole_static_lib_bp2build_cc_library_static"], - "//conditions:default": [":malloc_not_svelte_whole_static_lib_excludes_bp2build_cc_library_static"], - })`, - }), - }, - ) -} - -func TestCcLibraryProductVariablesHeaderLibs(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Filesystem: map[string]string{}, - Blueprint: soongCcLibraryStaticPreamble + ` -cc_library { - name: "foo_static", - srcs: ["common.c"], - product_variables: { - malloc_not_svelte: { - header_libs: ["malloc_not_svelte_header_lib"], - }, - }, - include_build_directory: false, -} - -cc_library { - name: "malloc_not_svelte_header_lib", - bazel_module: { bp2build_available: false }, -} -`, - ExpectedBazelTargets: makeCcLibraryTargets("foo_static", AttrNameToString{ - "implementation_deps": `select({ - "//build/bazel/product_variables:malloc_not_svelte": [":malloc_not_svelte_header_lib"], - "//conditions:default": [], - })`, - "srcs_c": `["common.c"]`, - "target_compatible_with": `["//build/bazel/platforms/os:android"]`, - }), - }, - ) -} - -func TestCCLibraryNoCrtTrue(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library - nocrt: true disables feature", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Filesystem: map[string]string{ - "impl.cpp": "", - }, - Blueprint: soongCcLibraryPreamble + ` -cc_library { - name: "foo-lib", - srcs: ["impl.cpp"], - nocrt: true, - include_build_directory: false, -} -`, - ExpectedBazelTargets: makeCcLibraryTargets("foo-lib", AttrNameToString{ - "features": `["-link_crt"]`, - "srcs": `["impl.cpp"]`, - }), - }, - ) -} - -func TestCCLibraryNoCrtFalse(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library - nocrt: false - does not emit attribute", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Filesystem: map[string]string{ - "impl.cpp": "", - }, - Blueprint: soongCcLibraryPreamble + ` -cc_library { - name: "foo-lib", - srcs: ["impl.cpp"], - nocrt: false, - include_build_directory: false, -} -`, - ExpectedBazelTargets: makeCcLibraryTargets("foo-lib", AttrNameToString{ - "srcs": `["impl.cpp"]`, - }), - }) -} - -func TestCCLibraryNoCrtArchVariant(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library - nocrt in select", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Filesystem: map[string]string{ - "impl.cpp": "", - }, - Blueprint: soongCcLibraryPreamble + ` -cc_library { - name: "foo-lib", - srcs: ["impl.cpp"], - arch: { - arm: { - nocrt: true, - }, - x86: { - nocrt: false, - }, - }, - include_build_directory: false, -} -`, - ExpectedBazelTargets: makeCcLibraryTargets("foo-lib", AttrNameToString{ - "features": `select({ - "//build/bazel/platforms/arch:arm": ["-link_crt"], - "//conditions:default": [], - })`, - "srcs": `["impl.cpp"]`, - }), - }) -} - -func TestCCLibraryNoLibCrtTrue(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Filesystem: map[string]string{ - "impl.cpp": "", - }, - Blueprint: soongCcLibraryPreamble + ` -cc_library { - name: "foo-lib", - srcs: ["impl.cpp"], - no_libcrt: true, - include_build_directory: false, -} -`, - ExpectedBazelTargets: makeCcLibraryTargets("foo-lib", AttrNameToString{ - "features": `["-use_libcrt"]`, - "srcs": `["impl.cpp"]`, - }), - }) -} - -func makeCcLibraryTargets(name string, attrs AttrNameToString) []string { - STATIC_ONLY_ATTRS := map[string]bool{} - SHARED_ONLY_ATTRS := map[string]bool{ - "link_crt": true, - "additional_linker_inputs": true, - "linkopts": true, - "strip": true, - "inject_bssl_hash": true, - "stubs_symbol_file": true, - "use_version_lib": true, - } - - sharedAttrs := AttrNameToString{} - staticAttrs := AttrNameToString{} - for key, val := range attrs { - if _, staticOnly := STATIC_ONLY_ATTRS[key]; !staticOnly { - sharedAttrs[key] = val - } - if _, sharedOnly := SHARED_ONLY_ATTRS[key]; !sharedOnly { - staticAttrs[key] = val - } - } - sharedTarget := MakeBazelTarget("cc_library_shared", name, sharedAttrs) - staticTarget := MakeBazelTarget("cc_library_static", name+"_bp2build_cc_library_static", staticAttrs) - - return []string{staticTarget, sharedTarget} -} - -func TestCCLibraryNoLibCrtFalse(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Filesystem: map[string]string{ - "impl.cpp": "", - }, - Blueprint: soongCcLibraryPreamble + ` -cc_library { - name: "foo-lib", - srcs: ["impl.cpp"], - no_libcrt: false, - include_build_directory: false, -} -`, - ExpectedBazelTargets: makeCcLibraryTargets("foo-lib", AttrNameToString{ - "srcs": `["impl.cpp"]`, - }), - }) -} - -func TestCCLibraryNoLibCrtArchVariant(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Filesystem: map[string]string{ - "impl.cpp": "", - }, - Blueprint: soongCcLibraryPreamble + ` -cc_library { - name: "foo-lib", - srcs: ["impl.cpp"], - arch: { - arm: { - no_libcrt: true, - }, - x86: { - no_libcrt: true, - }, - }, - include_build_directory: false, -} -`, - ExpectedBazelTargets: makeCcLibraryTargets("foo-lib", AttrNameToString{ - "srcs": `["impl.cpp"]`, - "features": `select({ - "//build/bazel/platforms/arch:arm": ["-use_libcrt"], - "//build/bazel/platforms/arch:x86": ["-use_libcrt"], - "//conditions:default": [], - })`, - }), - }) -} - -func TestCCLibraryNoLibCrtArchAndTargetVariant(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Filesystem: map[string]string{ - "impl.cpp": "", - }, - Blueprint: soongCcLibraryPreamble + ` -cc_library { - name: "foo-lib", - srcs: ["impl.cpp"], - arch: { - arm: { - no_libcrt: true, - }, - x86: { - no_libcrt: true, - }, - }, - target: { - darwin: { - no_libcrt: true, - } - }, - include_build_directory: false, -} -`, - ExpectedBazelTargets: makeCcLibraryTargets("foo-lib", AttrNameToString{ - "features": `select({ - "//build/bazel/platforms/arch:arm": ["-use_libcrt"], - "//build/bazel/platforms/arch:x86": ["-use_libcrt"], - "//conditions:default": [], - }) + select({ - "//build/bazel/platforms/os:darwin": ["-use_libcrt"], - "//conditions:default": [], - })`, - "srcs": `["impl.cpp"]`, - }), - }) -} - -func TestCCLibraryNoLibCrtArchAndTargetVariantConflict(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Filesystem: map[string]string{ - "impl.cpp": "", - }, - Blueprint: soongCcLibraryPreamble + ` -cc_library { - name: "foo-lib", - srcs: ["impl.cpp"], - arch: { - arm: { - no_libcrt: true, - }, - // This is expected to override the value for darwin_x86_64. - x86_64: { - no_libcrt: true, - }, - }, - target: { - darwin: { - no_libcrt: false, - } - }, - include_build_directory: false, -} -`, - ExpectedBazelTargets: makeCcLibraryTargets("foo-lib", AttrNameToString{ - "srcs": `["impl.cpp"]`, - "features": `select({ - "//build/bazel/platforms/arch:arm": ["-use_libcrt"], - "//build/bazel/platforms/arch:x86_64": ["-use_libcrt"], - "//conditions:default": [], - })`, - }), - }) -} - -func TestCcLibraryStrip(t *testing.T) { - expectedTargets := []string{} - expectedTargets = append(expectedTargets, makeCcLibraryTargets("all", AttrNameToString{ - "strip": `{ - "all": True, - }`, - })...) - expectedTargets = append(expectedTargets, makeCcLibraryTargets("keep_symbols", AttrNameToString{ - "strip": `{ - "keep_symbols": True, - }`, - })...) - expectedTargets = append(expectedTargets, makeCcLibraryTargets("keep_symbols_and_debug_frame", AttrNameToString{ - "strip": `{ - "keep_symbols_and_debug_frame": True, - }`, - })...) - expectedTargets = append(expectedTargets, makeCcLibraryTargets("keep_symbols_list", AttrNameToString{ - "strip": `{ - "keep_symbols_list": ["symbol"], - }`, - })...) - expectedTargets = append(expectedTargets, makeCcLibraryTargets("none", AttrNameToString{ - "strip": `{ - "none": True, - }`, - })...) - expectedTargets = append(expectedTargets, makeCcLibraryTargets("nothing", AttrNameToString{})...) - - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library strip args", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: soongCcLibraryPreamble + ` -cc_library { - name: "nothing", - include_build_directory: false, -} -cc_library { - name: "keep_symbols", - strip: { - keep_symbols: true, - }, - include_build_directory: false, -} -cc_library { - name: "keep_symbols_and_debug_frame", - strip: { - keep_symbols_and_debug_frame: true, - }, - include_build_directory: false, -} -cc_library { - name: "none", - strip: { - none: true, - }, - include_build_directory: false, -} -cc_library { - name: "keep_symbols_list", - strip: { - keep_symbols_list: ["symbol"], - }, - include_build_directory: false, -} -cc_library { - name: "all", - strip: { - all: true, - }, - include_build_directory: false, -} -`, - ExpectedBazelTargets: expectedTargets, - }) -} - -func TestCcLibraryStripWithArch(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library strip args", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: soongCcLibraryPreamble + ` -cc_library { - name: "multi-arch", - target: { - darwin: { - strip: { - keep_symbols_list: ["foo", "bar"] - } - }, - }, - arch: { - arm: { - strip: { - keep_symbols_and_debug_frame: true, - }, - }, - arm64: { - strip: { - keep_symbols: true, - }, - }, - }, - include_build_directory: false, -} -`, - ExpectedBazelTargets: makeCcLibraryTargets("multi-arch", AttrNameToString{ - "strip": `{ - "keep_symbols": select({ - "//build/bazel/platforms/arch:arm64": True, - "//conditions:default": None, - }), - "keep_symbols_and_debug_frame": select({ - "//build/bazel/platforms/arch:arm": True, - "//conditions:default": None, - }), - "keep_symbols_list": select({ - "//build/bazel/platforms/os:darwin": [ - "foo", - "bar", - ], - "//conditions:default": [], - }), - }`, - }), - }, - ) -} - -func TestCcLibrary_SystemSharedLibsRootEmpty(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library system_shared_libs empty at root", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: soongCcLibraryPreamble + ` -cc_library { - name: "root_empty", - system_shared_libs: [], - include_build_directory: false, -} -`, - ExpectedBazelTargets: makeCcLibraryTargets("root_empty", AttrNameToString{ - "system_dynamic_deps": `[]`, - }), - }, - ) -} - -func TestCcLibrary_SystemSharedLibsStaticEmpty(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library system_shared_libs empty for static variant", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: soongCcLibraryPreamble + ` -cc_library { - name: "static_empty", - static: { - system_shared_libs: [], - }, - include_build_directory: false, -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "static_empty_bp2build_cc_library_static", AttrNameToString{ - "system_dynamic_deps": "[]", - }), - MakeBazelTarget("cc_library_shared", "static_empty", AttrNameToString{}), - }, - }) -} - -func TestCcLibrary_SystemSharedLibsSharedEmpty(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library system_shared_libs empty for shared variant", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: soongCcLibraryPreamble + ` -cc_library { - name: "shared_empty", - shared: { - system_shared_libs: [], - }, - include_build_directory: false, -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "shared_empty_bp2build_cc_library_static", AttrNameToString{}), - MakeBazelTarget("cc_library_shared", "shared_empty", AttrNameToString{ - "system_dynamic_deps": "[]", - }), - }, - }) -} - -func TestCcLibrary_SystemSharedLibsSharedBionicEmpty(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library system_shared_libs empty for shared, bionic variant", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: soongCcLibraryPreamble + ` -cc_library { - name: "shared_empty", - target: { - bionic: { - shared: { - system_shared_libs: [], - } - } - }, - include_build_directory: false, -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "shared_empty_bp2build_cc_library_static", AttrNameToString{}), - MakeBazelTarget("cc_library_shared", "shared_empty", AttrNameToString{ - "system_dynamic_deps": "[]", - }), - }, - }) -} - -func TestCcLibrary_SystemSharedLibsLinuxBionicEmpty(t *testing.T) { - // Note that this behavior is technically incorrect (it's a simplification). - // The correct behavior would be if bp2build wrote `system_dynamic_deps = []` - // only for linux_bionic, but `android` had `["libc", "libdl", "libm"]. - // b/195791252 tracks the fix. - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library system_shared_libs empty for linux_bionic variant", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: soongCcLibraryPreamble + ` -cc_library { - name: "libc_musl", - bazel_module: { bp2build_available: false }, -} - -cc_library { - name: "target_linux_bionic_empty", - target: { - linux_bionic: { - system_shared_libs: [], - }, - }, - include_build_directory: false, -} -`, - ExpectedBazelTargets: makeCcLibraryTargets("target_linux_bionic_empty", AttrNameToString{ - "system_dynamic_deps": `select({ - "//build/bazel/platforms/os:linux_musl": [":libc_musl"], - "//conditions:default": [], - })`, - }), - }, - ) -} - -func TestCcLibrary_SystemSharedLibsBionicEmpty(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library system_shared_libs empty for bionic variant", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: soongCcLibraryPreamble + ` -cc_library { - name: "libc_musl", - bazel_module: { bp2build_available: false }, -} - -cc_library { - name: "target_bionic_empty", - target: { - bionic: { - system_shared_libs: [], - }, - }, - include_build_directory: false, -} -`, - ExpectedBazelTargets: makeCcLibraryTargets("target_bionic_empty", AttrNameToString{ - "system_dynamic_deps": `select({ - "//build/bazel/platforms/os:linux_musl": [":libc_musl"], - "//conditions:default": [], - })`, - }), - }, - ) -} - -func TestCcLibrary_SystemSharedLibsMuslEmpty(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library system_shared_lib empty for musl variant", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: soongCcLibraryPreamble + ` -cc_library { - name: "libc_musl", - bazel_module: { bp2build_available: false }, -} - -cc_library { - name: "target_musl_empty", - target: { - musl: { - system_shared_libs: [], - }, - }, - include_build_directory: false, -} -`, - ExpectedBazelTargets: makeCcLibraryTargets("target_musl_empty", AttrNameToString{ - "system_dynamic_deps": `[]`, - }), - }) -} - -func TestCcLibrary_SystemSharedLibsLinuxMuslEmpty(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library system_shared_lib empty for linux_musl variant", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: soongCcLibraryPreamble + ` -cc_library { - name: "libc_musl", - bazel_module: { bp2build_available: false }, -} - -cc_library { - name: "target_linux_musl_empty", - target: { - linux_musl: { - system_shared_libs: [], - }, - }, - include_build_directory: false, -} -`, - ExpectedBazelTargets: makeCcLibraryTargets("target_linux_musl_empty", AttrNameToString{ - "system_dynamic_deps": `[]`, - }), - }) -} -func TestCcLibrary_SystemSharedLibsSharedAndRoot(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library system_shared_libs set for shared and root", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: soongCcLibraryPreamble + ` -cc_library { - name: "libc", - bazel_module: { bp2build_available: false }, -} -cc_library { - name: "libm", - bazel_module: { bp2build_available: false }, -} - -cc_library { - name: "foo", - system_shared_libs: ["libc"], - shared: { - system_shared_libs: ["libm"], - }, - include_build_directory: false, -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{ - "system_dynamic_deps": `[":libc"]`, - }), - MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ - "system_dynamic_deps": `[ - ":libc", - ":libm", - ]`, - }), - }, - }) -} - -func TestCcLibraryOsSelects(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library - selects for all os targets", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Filesystem: map[string]string{}, - Blueprint: soongCcLibraryPreamble + ` -cc_library { - name: "foo-lib", - srcs: ["base.cpp"], - target: { - android: { - srcs: ["android.cpp"], - }, - linux: { - srcs: ["linux.cpp"], - }, - linux_glibc: { - srcs: ["linux_glibc.cpp"], - }, - darwin: { - srcs: ["darwin.cpp"], - }, - bionic: { - srcs: ["bionic.cpp"], - }, - linux_musl: { - srcs: ["linux_musl.cpp"], - }, - windows: { - srcs: ["windows.cpp"], - }, - }, - include_build_directory: false, -} -`, - ExpectedBazelTargets: makeCcLibraryTargets("foo-lib", AttrNameToString{ - "srcs": `["base.cpp"] + select({ - "//build/bazel/platforms/os:android": [ - "linux.cpp", - "bionic.cpp", - "android.cpp", - ], - "//build/bazel/platforms/os:darwin": ["darwin.cpp"], - "//build/bazel/platforms/os:linux_bionic": [ - "linux.cpp", - "bionic.cpp", - ], - "//build/bazel/platforms/os:linux_glibc": [ - "linux.cpp", - "linux_glibc.cpp", - ], - "//build/bazel/platforms/os:linux_musl": [ - "linux.cpp", - "linux_musl.cpp", - ], - "//build/bazel/platforms/os:windows": ["windows.cpp"], - "//conditions:default": [], - })`, - }), - }, - ) -} - -func TestLibcryptoHashInjection(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library - libcrypto hash injection", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Filesystem: map[string]string{}, - Blueprint: soongCcLibraryPreamble + ` -cc_library { - name: "libcrypto", - target: { - android: { - inject_bssl_hash: true, - }, - }, - include_build_directory: false, -} -`, - ExpectedBazelTargets: makeCcLibraryTargets("libcrypto", AttrNameToString{ - "inject_bssl_hash": `select({ - "//build/bazel/platforms/os:android": True, - "//conditions:default": None, - })`, - }), - }, - ) -} - -func TestCcLibraryCppStdWithGnuExtensions_ConvertsToFeatureAttr(t *testing.T) { - type testCase struct { - cpp_std string - c_std string - gnu_extensions string - bazel_cpp_std string - bazel_c_std string - } - - testCases := []testCase{ - // Existing usages of cpp_std in AOSP are: - // experimental, c++11, c++17, c++2a, c++98, gnu++11, gnu++17 - // - // not set, only emit if gnu_extensions is disabled. the default (gnu+17 - // is set in the toolchain.) - {cpp_std: "", gnu_extensions: "", bazel_cpp_std: ""}, - {cpp_std: "", gnu_extensions: "false", bazel_cpp_std: "cpp_std_default_no_gnu", bazel_c_std: "c_std_default_no_gnu"}, - {cpp_std: "", gnu_extensions: "true", bazel_cpp_std: ""}, - // experimental defaults to gnu++2a - {cpp_std: "experimental", gnu_extensions: "", bazel_cpp_std: "cpp_std_experimental"}, - {cpp_std: "experimental", gnu_extensions: "false", bazel_cpp_std: "cpp_std_experimental_no_gnu", bazel_c_std: "c_std_default_no_gnu"}, - {cpp_std: "experimental", gnu_extensions: "true", bazel_cpp_std: "cpp_std_experimental"}, - // Explicitly setting a c++ std does not use replace gnu++ std even if - // gnu_extensions is true. - // "c++11", - {cpp_std: "c++11", gnu_extensions: "", bazel_cpp_std: "c++11"}, - {cpp_std: "c++11", gnu_extensions: "false", bazel_cpp_std: "c++11", bazel_c_std: "c_std_default_no_gnu"}, - {cpp_std: "c++11", gnu_extensions: "true", bazel_cpp_std: "c++11"}, - // "c++17", - {cpp_std: "c++17", gnu_extensions: "", bazel_cpp_std: "c++17"}, - {cpp_std: "c++17", gnu_extensions: "false", bazel_cpp_std: "c++17", bazel_c_std: "c_std_default_no_gnu"}, - {cpp_std: "c++17", gnu_extensions: "true", bazel_cpp_std: "c++17"}, - // "c++2a", - {cpp_std: "c++2a", gnu_extensions: "", bazel_cpp_std: "c++2a"}, - {cpp_std: "c++2a", gnu_extensions: "false", bazel_cpp_std: "c++2a", bazel_c_std: "c_std_default_no_gnu"}, - {cpp_std: "c++2a", gnu_extensions: "true", bazel_cpp_std: "c++2a"}, - // "c++98", - {cpp_std: "c++98", gnu_extensions: "", bazel_cpp_std: "c++98"}, - {cpp_std: "c++98", gnu_extensions: "false", bazel_cpp_std: "c++98", bazel_c_std: "c_std_default_no_gnu"}, - {cpp_std: "c++98", gnu_extensions: "true", bazel_cpp_std: "c++98"}, - // gnu++ is replaced with c++ if gnu_extensions is explicitly false. - // "gnu++11", - {cpp_std: "gnu++11", gnu_extensions: "", bazel_cpp_std: "gnu++11"}, - {cpp_std: "gnu++11", gnu_extensions: "false", bazel_cpp_std: "c++11", bazel_c_std: "c_std_default_no_gnu"}, - {cpp_std: "gnu++11", gnu_extensions: "true", bazel_cpp_std: "gnu++11"}, - // "gnu++17", - {cpp_std: "gnu++17", gnu_extensions: "", bazel_cpp_std: "gnu++17"}, - {cpp_std: "gnu++17", gnu_extensions: "false", bazel_cpp_std: "c++17", bazel_c_std: "c_std_default_no_gnu"}, - {cpp_std: "gnu++17", gnu_extensions: "true", bazel_cpp_std: "gnu++17"}, - - // some c_std test cases - {c_std: "experimental", gnu_extensions: "", bazel_c_std: "c_std_experimental"}, - {c_std: "experimental", gnu_extensions: "false", bazel_cpp_std: "cpp_std_default_no_gnu", bazel_c_std: "c_std_experimental_no_gnu"}, - {c_std: "experimental", gnu_extensions: "true", bazel_c_std: "c_std_experimental"}, - {c_std: "gnu11", cpp_std: "gnu++17", gnu_extensions: "", bazel_cpp_std: "gnu++17", bazel_c_std: "gnu11"}, - {c_std: "gnu11", cpp_std: "gnu++17", gnu_extensions: "false", bazel_cpp_std: "c++17", bazel_c_std: "c11"}, - {c_std: "gnu11", cpp_std: "gnu++17", gnu_extensions: "true", bazel_cpp_std: "gnu++17", bazel_c_std: "gnu11"}, - } - for i, tc := range testCases { - name := fmt.Sprintf("cpp std: %q, c std: %q, gnu_extensions: %q", tc.cpp_std, tc.c_std, tc.gnu_extensions) - t.Run(name, func(t *testing.T) { - name_prefix := fmt.Sprintf("a_%v", i) - cppStdProp := "" - if tc.cpp_std != "" { - cppStdProp = fmt.Sprintf(" cpp_std: \"%s\",", tc.cpp_std) - } - cStdProp := "" - if tc.c_std != "" { - cStdProp = fmt.Sprintf(" c_std: \"%s\",", tc.c_std) - } - gnuExtensionsProp := "" - if tc.gnu_extensions != "" { - gnuExtensionsProp = fmt.Sprintf(" gnu_extensions: %s,", tc.gnu_extensions) - } - attrs := AttrNameToString{} - if tc.bazel_cpp_std != "" { - attrs["cpp_std"] = fmt.Sprintf(`"%s"`, tc.bazel_cpp_std) - } - if tc.bazel_c_std != "" { - attrs["c_std"] = fmt.Sprintf(`"%s"`, tc.bazel_c_std) - } - - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: fmt.Sprintf( - "cc_library with cpp_std: %s and gnu_extensions: %s", tc.cpp_std, tc.gnu_extensions), - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: soongCcLibraryPreamble + fmt.Sprintf(` -cc_library { - name: "%s_full", -%s // cpp_std: *string -%s // c_std: *string -%s // gnu_extensions: *bool - include_build_directory: false, -} -`, name_prefix, cppStdProp, cStdProp, gnuExtensionsProp), - ExpectedBazelTargets: makeCcLibraryTargets(name_prefix+"_full", attrs), - }) - - runCcLibraryStaticTestCase(t, Bp2buildTestCase{ - Description: fmt.Sprintf( - "cc_library_static with cpp_std: %s and gnu_extensions: %s", tc.cpp_std, tc.gnu_extensions), - ModuleTypeUnderTest: "cc_library_static", - ModuleTypeUnderTestFactory: cc.LibraryStaticFactory, - Blueprint: soongCcLibraryPreamble + fmt.Sprintf(` -cc_library_static { - name: "%s_static", -%s // cpp_std: *string -%s // c_std: *string -%s // gnu_extensions: *bool - include_build_directory: false, -} -`, name_prefix, cppStdProp, cStdProp, gnuExtensionsProp), - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", name_prefix+"_static", attrs), - }, - }) - - runCcLibrarySharedTestCase(t, Bp2buildTestCase{ - Description: fmt.Sprintf( - "cc_library_shared with cpp_std: %s and gnu_extensions: %s", tc.cpp_std, tc.gnu_extensions), - ModuleTypeUnderTest: "cc_library_shared", - ModuleTypeUnderTestFactory: cc.LibrarySharedFactory, - Blueprint: soongCcLibraryPreamble + fmt.Sprintf(` -cc_library_shared { - name: "%s_shared", -%s // cpp_std: *string -%s // c_std: *string -%s // gnu_extensions: *bool - include_build_directory: false, -} -`, name_prefix, cppStdProp, cStdProp, gnuExtensionsProp), - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_shared", name_prefix+"_shared", attrs), - }, - }) - }) - } -} - -func TestCcLibraryProtoSimple(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: soongCcProtoPreamble + `cc_library { - name: "foo", - srcs: ["foo.proto"], - include_build_directory: false, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("proto_library", "foo_proto", AttrNameToString{ - "srcs": `["foo.proto"]`, - }), MakeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", AttrNameToString{ - "deps": `[":foo_proto"]`, - }), MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{ - "implementation_whole_archive_deps": `[":foo_cc_proto_lite"]`, - "deps": `[":libprotobuf-cpp-lite"]`, - }), MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ - "dynamic_deps": `[":libprotobuf-cpp-lite"]`, - "implementation_whole_archive_deps": `[":foo_cc_proto_lite"]`, - }), - }, - }) -} - -func TestCcLibraryProtoNoCanonicalPathFromRoot(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: soongCcProtoPreamble + `cc_library { - name: "foo", - srcs: ["foo.proto"], - proto: { canonical_path_from_root: false}, - include_build_directory: false, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("proto_library", "foo_proto", AttrNameToString{ - "srcs": `["foo.proto"]`, - "strip_import_prefix": `""`, - }), MakeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", AttrNameToString{ - "deps": `[":foo_proto"]`, - }), MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{ - "implementation_whole_archive_deps": `[":foo_cc_proto_lite"]`, - "deps": `[":libprotobuf-cpp-lite"]`, - }), MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ - "dynamic_deps": `[":libprotobuf-cpp-lite"]`, - "implementation_whole_archive_deps": `[":foo_cc_proto_lite"]`, - }), - }, - }) -} - -func TestCcLibraryProtoExplicitCanonicalPathFromRoot(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: soongCcProtoPreamble + `cc_library { - name: "foo", - srcs: ["foo.proto"], - proto: { canonical_path_from_root: true}, - include_build_directory: false, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("proto_library", "foo_proto", AttrNameToString{ - "srcs": `["foo.proto"]`, - }), MakeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", AttrNameToString{ - "deps": `[":foo_proto"]`, - }), MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{ - "implementation_whole_archive_deps": `[":foo_cc_proto_lite"]`, - "deps": `[":libprotobuf-cpp-lite"]`, - }), MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ - "dynamic_deps": `[":libprotobuf-cpp-lite"]`, - "implementation_whole_archive_deps": `[":foo_cc_proto_lite"]`, - }), - }, - }) -} - -func TestCcLibraryProtoFull(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: soongCcProtoPreamble + `cc_library { - name: "foo", - srcs: ["foo.proto"], - proto: { - type: "full", - }, - include_build_directory: false, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("proto_library", "foo_proto", AttrNameToString{ - "srcs": `["foo.proto"]`, - }), MakeBazelTarget("cc_proto_library", "foo_cc_proto", AttrNameToString{ - "deps": `[":foo_proto"]`, - }), MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{ - "implementation_whole_archive_deps": `[":foo_cc_proto"]`, - "deps": `[":libprotobuf-cpp-full"]`, - }), MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ - "dynamic_deps": `[":libprotobuf-cpp-full"]`, - "implementation_whole_archive_deps": `[":foo_cc_proto"]`, - }), - }, - }) -} - -func TestCcLibraryProtoLite(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: soongCcProtoPreamble + `cc_library { - name: "foo", - srcs: ["foo.proto"], - proto: { - type: "lite", - }, - include_build_directory: false, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("proto_library", "foo_proto", AttrNameToString{ - "srcs": `["foo.proto"]`, - }), MakeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", AttrNameToString{ - "deps": `[":foo_proto"]`, - }), MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{ - "implementation_whole_archive_deps": `[":foo_cc_proto_lite"]`, - "deps": `[":libprotobuf-cpp-lite"]`, - }), MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ - "dynamic_deps": `[":libprotobuf-cpp-lite"]`, - "implementation_whole_archive_deps": `[":foo_cc_proto_lite"]`, - }), - }, - }) -} - -func TestCcLibraryProtoExportHeaders(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: soongCcProtoPreamble + `cc_library { - name: "foo", - srcs: ["foo.proto"], - proto: { - export_proto_headers: true, - }, - include_build_directory: false, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("proto_library", "foo_proto", AttrNameToString{ - "srcs": `["foo.proto"]`, - }), MakeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", AttrNameToString{ - "deps": `[":foo_proto"]`, - }), MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{ - "deps": `[":libprotobuf-cpp-lite"]`, - "whole_archive_deps": `[":foo_cc_proto_lite"]`, - }), MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ - "dynamic_deps": `[":libprotobuf-cpp-lite"]`, - "whole_archive_deps": `[":foo_cc_proto_lite"]`, - }), - }, - }) -} - -func TestCcLibraryProtoIncludeDirs(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: soongCcProtoPreamble + `cc_library { - name: "foo", - srcs: ["foo.proto"], - proto: { - include_dirs: ["external/protobuf/src"], - }, - include_build_directory: false, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("proto_library", "foo_proto", AttrNameToString{ - "srcs": `["foo.proto"]`, - "deps": `["//external/protobuf:libprotobuf-proto"]`, - }), MakeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", AttrNameToString{ - "deps": `[":foo_proto"]`, - }), MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{ - "deps": `[":libprotobuf-cpp-lite"]`, - "implementation_whole_archive_deps": `[":foo_cc_proto_lite"]`, - }), MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ - "dynamic_deps": `[":libprotobuf-cpp-lite"]`, - "implementation_whole_archive_deps": `[":foo_cc_proto_lite"]`, - }), - }, - }) -} - -func TestCcLibraryProtoIncludeDirsUnknown(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: soongCcProtoPreamble + `cc_library { - name: "foo", - srcs: ["foo.proto"], - proto: { - include_dirs: ["external/protobuf/abc"], - }, - include_build_directory: false, -}`, - ExpectedErr: fmt.Errorf("module \"foo\": Could not find the proto_library target for include dir: external/protobuf/abc"), - }) -} - -func TestCcLibraryConvertedProtoFilegroups(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: soongCcProtoPreamble + ` -filegroup { - name: "a_fg_proto", - srcs: ["a_fg.proto"], -} - -cc_library { - name: "a", - srcs: [ - ":a_fg_proto", - "a.proto", - ], - proto: { - export_proto_headers: true, - }, - include_build_directory: false, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("proto_library", "a_proto", AttrNameToString{ - "deps": `[":a_fg_proto_bp2build_converted"]`, - "srcs": `["a.proto"]`, - }), MakeBazelTarget("cc_lite_proto_library", "a_cc_proto_lite", AttrNameToString{ - "deps": `[ - ":a_fg_proto_bp2build_converted", - ":a_proto", - ]`, - }), MakeBazelTarget("cc_library_static", "a_bp2build_cc_library_static", AttrNameToString{ - "deps": `[":libprotobuf-cpp-lite"]`, - "whole_archive_deps": `[":a_cc_proto_lite"]`, - }), MakeBazelTarget("cc_library_shared", "a", AttrNameToString{ - "dynamic_deps": `[":libprotobuf-cpp-lite"]`, - "whole_archive_deps": `[":a_cc_proto_lite"]`, - }), MakeBazelTargetNoRestrictions("proto_library", "a_fg_proto_bp2build_converted", AttrNameToString{ - "srcs": `["a_fg.proto"]`, - "tags": `[ - "apex_available=//apex_available:anyapex", - "manual", - ]`, - }), MakeBazelTargetNoRestrictions("filegroup", "a_fg_proto", AttrNameToString{ - "srcs": `["a_fg.proto"]`, - }), - }, - }) -} - -func TestCcLibraryConvertedProtoFilegroupsNoProtoFiles(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: soongCcProtoPreamble + ` -filegroup { - name: "a_fg_proto", - srcs: ["a_fg.proto"], -} - -cc_library { - name: "a", - srcs: [ - ":a_fg_proto", - ], - proto: { - export_proto_headers: true, - }, - include_build_directory: false, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_lite_proto_library", "a_cc_proto_lite", AttrNameToString{ - "deps": `[":a_fg_proto_bp2build_converted"]`, - }), MakeBazelTarget("cc_library_static", "a_bp2build_cc_library_static", AttrNameToString{ - "deps": `[":libprotobuf-cpp-lite"]`, - "whole_archive_deps": `[":a_cc_proto_lite"]`, - }), MakeBazelTarget("cc_library_shared", "a", AttrNameToString{ - "dynamic_deps": `[":libprotobuf-cpp-lite"]`, - "whole_archive_deps": `[":a_cc_proto_lite"]`, - }), MakeBazelTargetNoRestrictions("proto_library", "a_fg_proto_bp2build_converted", AttrNameToString{ - "srcs": `["a_fg.proto"]`, - "tags": `[ - "apex_available=//apex_available:anyapex", - "manual", - ]`, - }), MakeBazelTargetNoRestrictions("filegroup", "a_fg_proto", AttrNameToString{ - "srcs": `["a_fg.proto"]`, - }), - }, - }) -} - -func TestCcLibraryExternalConvertedProtoFilegroups(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Filesystem: map[string]string{ - "path/to/A/Android.bp": ` -filegroup { - name: "a_fg_proto", - srcs: ["a_fg.proto"], -}`, - }, - Blueprint: soongCcProtoPreamble + ` -cc_library { - name: "a", - srcs: [ - ":a_fg_proto", - "a.proto", - ], - proto: { - export_proto_headers: true, - }, - include_build_directory: false, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("proto_library", "a_proto", AttrNameToString{ - "deps": `["//path/to/A:a_fg_proto_bp2build_converted"]`, - "srcs": `["a.proto"]`, - }), MakeBazelTarget("cc_lite_proto_library", "a_cc_proto_lite", AttrNameToString{ - "deps": `[ - "//path/to/A:a_fg_proto_bp2build_converted", - ":a_proto", - ]`, - }), MakeBazelTarget("cc_library_static", "a_bp2build_cc_library_static", AttrNameToString{ - "deps": `[":libprotobuf-cpp-lite"]`, - "whole_archive_deps": `[":a_cc_proto_lite"]`, - }), MakeBazelTarget("cc_library_shared", "a", AttrNameToString{ - "dynamic_deps": `[":libprotobuf-cpp-lite"]`, - "whole_archive_deps": `[":a_cc_proto_lite"]`, - }), - }, - }) -} - -func TestCcLibraryProtoFilegroups(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: soongCcProtoPreamble + - simpleModuleDoNotConvertBp2build("filegroup", "a_fg_proto") + - simpleModuleDoNotConvertBp2build("filegroup", "b_protos") + - simpleModuleDoNotConvertBp2build("filegroup", "c-proto-srcs") + - simpleModuleDoNotConvertBp2build("filegroup", "proto-srcs-d") + ` -cc_library { - name: "a", - srcs: [":a_fg_proto"], - proto: { - export_proto_headers: true, - }, - include_build_directory: false, -} - -cc_library { - name: "b", - srcs: [":b_protos"], - proto: { - export_proto_headers: true, - }, - include_build_directory: false, -} - -cc_library { - name: "c", - srcs: [":c-proto-srcs"], - proto: { - export_proto_headers: true, - }, - include_build_directory: false, -} - -cc_library { - name: "d", - srcs: [":proto-srcs-d"], - proto: { - export_proto_headers: true, - }, - include_build_directory: false, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("proto_library", "a_proto", AttrNameToString{ - "srcs": `[":a_fg_proto"]`, - }), MakeBazelTarget("cc_lite_proto_library", "a_cc_proto_lite", AttrNameToString{ - "deps": `[":a_proto"]`, - }), MakeBazelTarget("cc_library_static", "a_bp2build_cc_library_static", AttrNameToString{ - "deps": `[":libprotobuf-cpp-lite"]`, - "whole_archive_deps": `[":a_cc_proto_lite"]`, - "srcs": `[":a_fg_proto_cpp_srcs"]`, - "srcs_as": `[":a_fg_proto_as_srcs"]`, - "srcs_c": `[":a_fg_proto_c_srcs"]`, - }), MakeBazelTarget("cc_library_shared", "a", AttrNameToString{ - "dynamic_deps": `[":libprotobuf-cpp-lite"]`, - "whole_archive_deps": `[":a_cc_proto_lite"]`, - "srcs": `[":a_fg_proto_cpp_srcs"]`, - "srcs_as": `[":a_fg_proto_as_srcs"]`, - "srcs_c": `[":a_fg_proto_c_srcs"]`, - }), MakeBazelTarget("proto_library", "b_proto", AttrNameToString{ - "srcs": `[":b_protos"]`, - }), MakeBazelTarget("cc_lite_proto_library", "b_cc_proto_lite", AttrNameToString{ - "deps": `[":b_proto"]`, - }), MakeBazelTarget("cc_library_static", "b_bp2build_cc_library_static", AttrNameToString{ - "deps": `[":libprotobuf-cpp-lite"]`, - "whole_archive_deps": `[":b_cc_proto_lite"]`, - "srcs": `[":b_protos_cpp_srcs"]`, - "srcs_as": `[":b_protos_as_srcs"]`, - "srcs_c": `[":b_protos_c_srcs"]`, - }), MakeBazelTarget("cc_library_shared", "b", AttrNameToString{ - "dynamic_deps": `[":libprotobuf-cpp-lite"]`, - "whole_archive_deps": `[":b_cc_proto_lite"]`, - "srcs": `[":b_protos_cpp_srcs"]`, - "srcs_as": `[":b_protos_as_srcs"]`, - "srcs_c": `[":b_protos_c_srcs"]`, - }), MakeBazelTarget("proto_library", "c_proto", AttrNameToString{ - "srcs": `[":c-proto-srcs"]`, - }), MakeBazelTarget("cc_lite_proto_library", "c_cc_proto_lite", AttrNameToString{ - "deps": `[":c_proto"]`, - }), MakeBazelTarget("cc_library_static", "c_bp2build_cc_library_static", AttrNameToString{ - "deps": `[":libprotobuf-cpp-lite"]`, - "whole_archive_deps": `[":c_cc_proto_lite"]`, - "srcs": `[":c-proto-srcs_cpp_srcs"]`, - "srcs_as": `[":c-proto-srcs_as_srcs"]`, - "srcs_c": `[":c-proto-srcs_c_srcs"]`, - }), MakeBazelTarget("cc_library_shared", "c", AttrNameToString{ - "dynamic_deps": `[":libprotobuf-cpp-lite"]`, - "whole_archive_deps": `[":c_cc_proto_lite"]`, - "srcs": `[":c-proto-srcs_cpp_srcs"]`, - "srcs_as": `[":c-proto-srcs_as_srcs"]`, - "srcs_c": `[":c-proto-srcs_c_srcs"]`, - }), MakeBazelTarget("proto_library", "d_proto", AttrNameToString{ - "srcs": `[":proto-srcs-d"]`, - }), MakeBazelTarget("cc_lite_proto_library", "d_cc_proto_lite", AttrNameToString{ - "deps": `[":d_proto"]`, - }), MakeBazelTarget("cc_library_static", "d_bp2build_cc_library_static", AttrNameToString{ - "deps": `[":libprotobuf-cpp-lite"]`, - "whole_archive_deps": `[":d_cc_proto_lite"]`, - "srcs": `[":proto-srcs-d_cpp_srcs"]`, - "srcs_as": `[":proto-srcs-d_as_srcs"]`, - "srcs_c": `[":proto-srcs-d_c_srcs"]`, - }), MakeBazelTarget("cc_library_shared", "d", AttrNameToString{ - "dynamic_deps": `[":libprotobuf-cpp-lite"]`, - "whole_archive_deps": `[":d_cc_proto_lite"]`, - "srcs": `[":proto-srcs-d_cpp_srcs"]`, - "srcs_as": `[":proto-srcs-d_as_srcs"]`, - "srcs_c": `[":proto-srcs-d_c_srcs"]`, - }), - }, - }) -} - -func TestCcLibraryDisabledArchAndTarget(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: soongCcProtoPreamble + `cc_library { - name: "foo", - srcs: ["foo.cpp"], - host_supported: true, - target: { - darwin: { - enabled: false, - }, - windows: { - enabled: false, - }, - linux_glibc_x86: { - enabled: false, - }, - }, - include_build_directory: false, -}`, - ExpectedBazelTargets: makeCcLibraryTargets("foo", AttrNameToString{ - "srcs": `["foo.cpp"]`, - "target_compatible_with": `select({ - "//build/bazel/platforms/os_arch:darwin_arm64": ["@platforms//:incompatible"], - "//build/bazel/platforms/os_arch:darwin_x86_64": ["@platforms//:incompatible"], - "//build/bazel/platforms/os_arch:linux_glibc_x86": ["@platforms//:incompatible"], - "//build/bazel/platforms/os_arch:windows_x86": ["@platforms//:incompatible"], - "//build/bazel/platforms/os_arch:windows_x86_64": ["@platforms//:incompatible"], - "//conditions:default": [], - })`, - }), - }) -} - -func TestCcLibraryDisabledArchAndTargetWithDefault(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: soongCcProtoPreamble + `cc_library { - name: "foo", - srcs: ["foo.cpp"], - enabled: false, - host_supported: true, - target: { - darwin: { - enabled: true, - }, - windows: { - enabled: false, - }, - linux_glibc_x86: { - enabled: false, - }, - }, - include_build_directory: false, -}`, - ExpectedBazelTargets: makeCcLibraryTargets("foo", AttrNameToString{ - "srcs": `["foo.cpp"]`, - "target_compatible_with": `select({ - "//build/bazel/platforms/os_arch:darwin_arm64": [], - "//build/bazel/platforms/os_arch:darwin_x86_64": [], - "//conditions:default": ["@platforms//:incompatible"], - })`, - }), - }) -} - -func TestCcLibrarySharedDisabled(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: soongCcProtoPreamble + `cc_library { - name: "foo", - srcs: ["foo.cpp"], - enabled: false, - shared: { - enabled: true, - }, - target: { - android: { - shared: { - enabled: false, - }, - } - }, - include_build_directory: false, -}`, - ExpectedBazelTargets: []string{MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{ - "srcs": `["foo.cpp"]`, - "target_compatible_with": `["@platforms//:incompatible"]`, - }), MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ - "srcs": `["foo.cpp"]`, - "target_compatible_with": `select({ - "//build/bazel/platforms/os:android": ["@platforms//:incompatible"], - "//conditions:default": [], - })`, - }), - }, - }) -} - -func TestCcLibraryStaticDisabledForSomeArch(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: soongCcProtoPreamble + `cc_library { - name: "foo", - host_supported: true, - srcs: ["foo.cpp"], - shared: { - enabled: false - }, - target: { - darwin: { - enabled: true, - }, - windows: { - enabled: false, - }, - linux_glibc_x86: { - shared: { - enabled: true, - }, - }, - }, - include_build_directory: false, -}`, - ExpectedBazelTargets: []string{MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{ - "srcs": `["foo.cpp"]`, - "target_compatible_with": `select({ - "//build/bazel/platforms/os:windows": ["@platforms//:incompatible"], - "//conditions:default": [], - })`, - }), MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ - "srcs": `["foo.cpp"]`, - "target_compatible_with": `select({ - "//build/bazel/platforms/os_arch:darwin_arm64": [], - "//build/bazel/platforms/os_arch:darwin_x86_64": [], - "//build/bazel/platforms/os_arch:linux_glibc_x86": [], - "//conditions:default": ["@platforms//:incompatible"], - })`, - }), - }}) -} - -func TestCcLibraryStubs(t *testing.T) { - expectedBazelTargets := makeCcLibraryTargets("a", AttrNameToString{ - "stubs_symbol_file": `"a.map.txt"`, - }) - expectedBazelTargets = append(expectedBazelTargets, makeCcStubSuiteTargets("a", AttrNameToString{ - "soname": `"a.so"`, - "source_library_label": `"//foo/bar:a"`, - "stubs_symbol_file": `"a.map.txt"`, - "stubs_versions": `[ - "28", - "29", - "current", - ]`, - })) - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library stubs", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Dir: "foo/bar", - Filesystem: map[string]string{ - "foo/bar/Android.bp": ` -cc_library { - name: "a", - stubs: { symbol_file: "a.map.txt", versions: ["28", "29", "current"] }, - bazel_module: { bp2build_available: true }, - include_build_directory: false, -} -`, - }, - Blueprint: soongCcLibraryPreamble, - ExpectedBazelTargets: expectedBazelTargets, - }, - ) -} - -func TestCcApiContributionsWithHdrs(t *testing.T) { - bp := ` - cc_library { - name: "libfoo", - stubs: { symbol_file: "libfoo.map.txt", versions: ["28", "29", "current"] }, - llndk: { symbol_file: "libfoo.map.txt", override_export_include_dirs: ["dir2"]}, - export_include_dirs: ["dir1"], - } - ` - expectedBazelTargets := []string{ - MakeBazelTarget( - "cc_api_library_headers", - "libfoo.module-libapi.headers", - AttrNameToString{ - "export_includes": `["dir1"]`, - }), - MakeBazelTarget( - "cc_api_library_headers", - "libfoo.vendorapi.headers", - AttrNameToString{ - "export_includes": `["dir2"]`, - }), - MakeBazelTarget( - "cc_api_contribution", - "libfoo.contribution", - AttrNameToString{ - "api": `"libfoo.map.txt"`, - "library_name": `"libfoo"`, - "api_surfaces": `[ - "module-libapi", - "vendorapi", - ]`, - "hdrs": `[ - ":libfoo.module-libapi.headers", - ":libfoo.vendorapi.headers", - ]`, - }), - } - RunApiBp2BuildTestCase(t, cc.RegisterLibraryBuildComponents, Bp2buildTestCase{ - Blueprint: bp, - Description: "cc API contributions to module-libapi and vendorapi", - ExpectedBazelTargets: expectedBazelTargets, - }) -} - -func TestCcApiSurfaceCombinations(t *testing.T) { - testCases := []struct { - bp string - expectedApi string - expectedApiSurfaces string - description string - }{ - { - bp: ` - cc_library { - name: "a", - stubs: {symbol_file: "a.map.txt"}, - }`, - expectedApi: `"a.map.txt"`, - expectedApiSurfaces: `["module-libapi"]`, - description: "Library that contributes to module-libapi", - }, - { - bp: ` - cc_library { - name: "a", - llndk: {symbol_file: "a.map.txt"}, - }`, - expectedApi: `"a.map.txt"`, - expectedApiSurfaces: `["vendorapi"]`, - description: "Library that contributes to vendorapi", - }, - { - bp: ` - cc_library { - name: "a", - llndk: {symbol_file: "a.map.txt"}, - stubs: {symbol_file: "a.map.txt"}, - }`, - expectedApi: `"a.map.txt"`, - expectedApiSurfaces: `[ - "module-libapi", - "vendorapi", - ]`, - description: "Library that contributes to module-libapi and vendorapi", - }, - } - for _, testCase := range testCases { - expectedBazelTargets := []string{ - MakeBazelTarget( - "cc_api_contribution", - "a.contribution", - AttrNameToString{ - "library_name": `"a"`, - "hdrs": `[]`, - "api": testCase.expectedApi, - "api_surfaces": testCase.expectedApiSurfaces, - }, - ), - } - RunApiBp2BuildTestCase(t, cc.RegisterLibraryBuildComponents, Bp2buildTestCase{ - Blueprint: testCase.bp, - Description: testCase.description, - ExpectedBazelTargets: expectedBazelTargets, - }) - } -} - -// llndk struct property in Soong provides users with several options to configure the exported include dirs -// Test the generated bazel targets for the different configurations -func TestCcVendorApiHeaders(t *testing.T) { - testCases := []struct { - bp string - expectedIncludes string - expectedSystemIncludes string - description string - }{ - { - bp: ` - cc_library { - name: "a", - export_include_dirs: ["include"], - export_system_include_dirs: ["base_system_include"], - llndk: { - symbol_file: "a.map.txt", - export_headers_as_system: true, - }, - } - `, - expectedIncludes: "", - expectedSystemIncludes: `[ - "base_system_include", - "include", - ]`, - description: "Headers are exported as system to API surface", - }, - { - bp: ` - cc_library { - name: "a", - export_include_dirs: ["include"], - export_system_include_dirs: ["base_system_include"], - llndk: { - symbol_file: "a.map.txt", - override_export_include_dirs: ["llndk_include"], - }, - } - `, - expectedIncludes: `["llndk_include"]`, - expectedSystemIncludes: `["base_system_include"]`, - description: "Non-system Headers are ovverriden before export to API surface", - }, - { - bp: ` - cc_library { - name: "a", - export_include_dirs: ["include"], - export_system_include_dirs: ["base_system_include"], - llndk: { - symbol_file: "a.map.txt", - override_export_include_dirs: ["llndk_include"], - export_headers_as_system: true, - }, - } - `, - expectedIncludes: "", // includes are set to nil - expectedSystemIncludes: `[ - "base_system_include", - "llndk_include", - ]`, - description: "System Headers are extended before export to API surface", - }, - } - for _, testCase := range testCases { - attrs := AttrNameToString{} - if testCase.expectedIncludes != "" { - attrs["export_includes"] = testCase.expectedIncludes - } - if testCase.expectedSystemIncludes != "" { - attrs["export_system_includes"] = testCase.expectedSystemIncludes - } - - expectedBazelTargets := []string{ - MakeBazelTarget("cc_api_library_headers", "a.vendorapi.headers", attrs), - // Create a target for cc_api_contribution target - MakeBazelTarget("cc_api_contribution", "a.contribution", AttrNameToString{ - "api": `"a.map.txt"`, - "api_surfaces": `["vendorapi"]`, - "hdrs": `[":a.vendorapi.headers"]`, - "library_name": `"a"`, - }), - } - RunApiBp2BuildTestCase(t, cc.RegisterLibraryBuildComponents, Bp2buildTestCase{ - Blueprint: testCase.bp, - ExpectedBazelTargets: expectedBazelTargets, - }) - } -} - -func TestCcLibraryStubsAcrossConfigsDuplicatesRemoved(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "stub target generation of the same lib across configs should not result in duplicates", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Filesystem: map[string]string{ - "bar.map.txt": "", - }, - Blueprint: ` -cc_library { - name: "barlib", - stubs: { symbol_file: "bar.map.txt", versions: ["28", "29", "current"] }, - bazel_module: { bp2build_available: false }, -} -cc_library { - name: "foolib", - shared_libs: ["barlib"], - target: { - android: { - shared_libs: ["barlib"], - }, - }, - bazel_module: { bp2build_available: true }, - apex_available: ["foo"], -}`, - ExpectedBazelTargets: makeCcLibraryTargets("foolib", AttrNameToString{ - "implementation_dynamic_deps": `select({ - "//build/bazel/rules/apex:android-in_apex": ["@api_surfaces//module-libapi/current:barlib"], - "//conditions:default": [":barlib"], - })`, - "local_includes": `["."]`, - "tags": `["apex_available=foo"]`, - }), - }) -} - -func TestCcLibraryExcludesLibsHost(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Filesystem: map[string]string{ - "bar.map.txt": "", - }, - Blueprint: simpleModuleDoNotConvertBp2build("cc_library", "bazlib") + ` -cc_library { - name: "quxlib", - stubs: { symbol_file: "bar.map.txt", versions: ["current"] }, - bazel_module: { bp2build_available: false }, -} -cc_library { - name: "barlib", - stubs: { symbol_file: "bar.map.txt", versions: ["28", "29", "current"] }, - bazel_module: { bp2build_available: false }, -} -cc_library { - name: "foolib", - shared_libs: ["barlib", "quxlib"], - target: { - host: { - shared_libs: ["bazlib"], - exclude_shared_libs: ["barlib"], - }, - }, - include_build_directory: false, - bazel_module: { bp2build_available: true }, - apex_available: ["foo"], -}`, - ExpectedBazelTargets: makeCcLibraryTargets("foolib", AttrNameToString{ - "implementation_dynamic_deps": `select({ - "//build/bazel/platforms/os:darwin": [":bazlib"], - "//build/bazel/platforms/os:linux_bionic": [":bazlib"], - "//build/bazel/platforms/os:linux_glibc": [":bazlib"], - "//build/bazel/platforms/os:linux_musl": [":bazlib"], - "//build/bazel/platforms/os:windows": [":bazlib"], - "//conditions:default": [], - }) + select({ - "//build/bazel/platforms/os:darwin": [":quxlib"], - "//build/bazel/platforms/os:linux_bionic": [":quxlib"], - "//build/bazel/platforms/os:linux_glibc": [":quxlib"], - "//build/bazel/platforms/os:linux_musl": [":quxlib"], - "//build/bazel/platforms/os:windows": [":quxlib"], - "//build/bazel/rules/apex:android-in_apex": [ - "@api_surfaces//module-libapi/current:barlib", - "@api_surfaces//module-libapi/current:quxlib", - ], - "//conditions:default": [ - ":barlib", - ":quxlib", - ], - })`, - "tags": `["apex_available=foo"]`, - }), - }) -} - -func TestCcLibraryEscapeLdflags(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: soongCcProtoPreamble + `cc_library { - name: "foo", - ldflags: ["-Wl,--rpath,${ORIGIN}"], - include_build_directory: false, -}`, - ExpectedBazelTargets: makeCcLibraryTargets("foo", AttrNameToString{ - "linkopts": `["-Wl,--rpath,$${ORIGIN}"]`, - }), - }) -} - -func TestCcLibraryConvertLex(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Filesystem: map[string]string{ - "foo.c": "", - "bar.cc": "", - "foo1.l": "", - "bar1.ll": "", - "foo2.l": "", - "bar2.ll": "", - }, - Blueprint: `cc_library { - name: "foo_lib", - srcs: ["foo.c", "bar.cc", "foo1.l", "foo2.l", "bar1.ll", "bar2.ll"], - lex: { flags: ["--foo_flags"] }, - include_build_directory: false, - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: append([]string{ - MakeBazelTarget("genlex", "foo_lib_genlex_l", AttrNameToString{ - "srcs": `[ - "foo1.l", - "foo2.l", - ]`, - "lexopts": `["--foo_flags"]`, - }), - MakeBazelTarget("genlex", "foo_lib_genlex_ll", AttrNameToString{ - "srcs": `[ - "bar1.ll", - "bar2.ll", - ]`, - "lexopts": `["--foo_flags"]`, - }), - }, - makeCcLibraryTargets("foo_lib", AttrNameToString{ - "srcs": `[ - "bar.cc", - ":foo_lib_genlex_ll", - ]`, - "srcs_c": `[ - "foo.c", - ":foo_lib_genlex_l", - ]`, - })...), - }) -} - -func TestCCLibraryRuntimeDeps(t *testing.T) { - runCcLibrarySharedTestCase(t, Bp2buildTestCase{ - Blueprint: `cc_library_shared { - name: "bar", -} - -cc_library { - name: "foo", - runtime_libs: ["foo"], -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_shared", "bar", AttrNameToString{ - "local_includes": `["."]`, - }), - MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{ - "runtime_deps": `[":foo"]`, - "local_includes": `["."]`, - }), - MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ - "runtime_deps": `[":foo"]`, - "local_includes": `["."]`, - }), - }, - }) -} - -func TestCcLibraryWithInstructionSet(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: `cc_library { - name: "foo", - arch: { - arm: { - instruction_set: "arm", - } - } -} -`, - ExpectedBazelTargets: makeCcLibraryTargets("foo", AttrNameToString{ - "features": `select({ - "//build/bazel/platforms/arch:arm": [ - "arm_isa_arm", - "-arm_isa_thumb", - ], - "//conditions:default": [], - })`, - "local_includes": `["."]`, - }), - }) -} - -func TestCcLibraryEmptySuffix(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library with empty suffix", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Filesystem: map[string]string{ - "foo.c": "", - }, - Blueprint: `cc_library { - name: "foo", - suffix: "", - srcs: ["foo.c"], - include_build_directory: false, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{ - "srcs_c": `["foo.c"]`, - }), - MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ - "srcs_c": `["foo.c"]`, - "suffix": `""`, - }), - }, - }) -} - -func TestCcLibrarySuffix(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library with suffix", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Filesystem: map[string]string{ - "foo.c": "", - }, - Blueprint: `cc_library { - name: "foo", - suffix: "-suf", - srcs: ["foo.c"], - include_build_directory: false, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{ - "srcs_c": `["foo.c"]`, - }), - MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ - "srcs_c": `["foo.c"]`, - "suffix": `"-suf"`, - }), - }, - }) -} - -func TestCcLibraryArchVariantSuffix(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library with arch-variant suffix", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Filesystem: map[string]string{ - "foo.c": "", - }, - Blueprint: `cc_library { - name: "foo", - arch: { - arm64: { suffix: "-64" }, - arm: { suffix: "-32" }, - }, - srcs: ["foo.c"], - include_build_directory: false, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{ - "srcs_c": `["foo.c"]`, - }), - MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ - "srcs_c": `["foo.c"]`, - "suffix": `select({ - "//build/bazel/platforms/arch:arm": "-32", - "//build/bazel/platforms/arch:arm64": "-64", - "//conditions:default": None, - })`, - }), - }, - }) -} - -func TestCcLibraryWithAidlSrcs(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library with aidl srcs", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: ` -filegroup { - name: "A_aidl", - srcs: ["aidl/A.aidl"], - path: "aidl", -} -cc_library { - name: "foo", - srcs: [ - ":A_aidl", - "B.aidl", - ], -}`, - ExpectedBazelTargets: []string{ - MakeBazelTargetNoRestrictions("aidl_library", "A_aidl", AttrNameToString{ - "srcs": `["aidl/A.aidl"]`, - "strip_import_prefix": `"aidl"`, - "tags": `["apex_available=//apex_available:anyapex"]`, - }), - MakeBazelTarget("aidl_library", "foo_aidl_library", AttrNameToString{ - "srcs": `["B.aidl"]`, - }), - MakeBazelTarget("cc_aidl_library", "foo_cc_aidl_library", AttrNameToString{ - "deps": `[ - ":A_aidl", - ":foo_aidl_library", - ]`, - }), - MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{ - "implementation_whole_archive_deps": `[":foo_cc_aidl_library"]`, - "local_includes": `["."]`, - }), - MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ - "implementation_whole_archive_deps": `[":foo_cc_aidl_library"]`, - "local_includes": `["."]`, - }), - }, - }) -} - -func TestCcLibraryWithNonAdjacentAidlFilegroup(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library with non aidl filegroup", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Filesystem: map[string]string{ - "path/to/A/Android.bp": ` -filegroup { - name: "A_aidl", - srcs: ["aidl/A.aidl"], - path: "aidl", -}`, - }, - Blueprint: ` -cc_library { - name: "foo", - srcs: [ - ":A_aidl", - ], -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_aidl_library", "foo_cc_aidl_library", AttrNameToString{ - "deps": `["//path/to/A:A_aidl"]`, - }), - MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{ - "implementation_whole_archive_deps": `[":foo_cc_aidl_library"]`, - "local_includes": `["."]`, - }), - MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ - "local_includes": `["."]`, - "implementation_whole_archive_deps": `[":foo_cc_aidl_library"]`, - }), - }, - }) -} - -func TestCcLibraryWithExportAidlHeaders(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library with export aidl headers", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: ` -cc_library { - name: "foo", - srcs: [ - "Foo.aidl", - ], - aidl: { - export_aidl_headers: true, - } -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("aidl_library", "foo_aidl_library", AttrNameToString{ - "srcs": `["Foo.aidl"]`, - }), - MakeBazelTarget("cc_aidl_library", "foo_cc_aidl_library", AttrNameToString{ - "deps": `[":foo_aidl_library"]`, - }), - MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{ - "whole_archive_deps": `[":foo_cc_aidl_library"]`, - "local_includes": `["."]`, - }), - MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ - "whole_archive_deps": `[":foo_cc_aidl_library"]`, - "local_includes": `["."]`, - }), - }, - }) -} - -func TestCcLibraryWithTargetApex(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library with target.apex", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: ` -cc_library { - name: "foo", - shared_libs: ["bar", "baz"], - static_libs: ["baz", "buh"], - target: { - apex: { - exclude_shared_libs: ["bar"], - exclude_static_libs: ["buh"], - } - } -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{ - "implementation_deps": `[":baz__BP2BUILD__MISSING__DEP"] + select({ - "//build/bazel/rules/apex:in_apex": [], - "//conditions:default": [":buh__BP2BUILD__MISSING__DEP"], - })`, - "implementation_dynamic_deps": `[":baz__BP2BUILD__MISSING__DEP"] + select({ - "//build/bazel/rules/apex:in_apex": [], - "//conditions:default": [":bar__BP2BUILD__MISSING__DEP"], - })`, - "local_includes": `["."]`, - }), - MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ - "implementation_deps": `[":baz__BP2BUILD__MISSING__DEP"] + select({ - "//build/bazel/rules/apex:in_apex": [], - "//conditions:default": [":buh__BP2BUILD__MISSING__DEP"], - })`, - "implementation_dynamic_deps": `[":baz__BP2BUILD__MISSING__DEP"] + select({ - "//build/bazel/rules/apex:in_apex": [], - "//conditions:default": [":bar__BP2BUILD__MISSING__DEP"], - })`, - "local_includes": `["."]`, - }), - }, - }) -} - -func TestCcLibraryWithTargetApexAndExportLibHeaders(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library with target.apex and export_shared|static_lib_headers", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: ` -cc_library_static { - name: "foo", - shared_libs: ["bar", "baz"], - static_libs: ["abc"], - export_shared_lib_headers: ["baz"], - export_static_lib_headers: ["abc"], - target: { - apex: { - exclude_shared_libs: ["baz", "bar"], - exclude_static_libs: ["abc"], - } - } -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo", AttrNameToString{ - "implementation_dynamic_deps": `select({ - "//build/bazel/rules/apex:in_apex": [], - "//conditions:default": [":bar__BP2BUILD__MISSING__DEP"], - })`, - "dynamic_deps": `select({ - "//build/bazel/rules/apex:in_apex": [], - "//conditions:default": [":baz__BP2BUILD__MISSING__DEP"], - })`, - "deps": `select({ - "//build/bazel/rules/apex:in_apex": [], - "//conditions:default": [":abc__BP2BUILD__MISSING__DEP"], - })`, - "local_includes": `["."]`, - }), - }, - }) -} - -func TestCcLibraryWithSyspropSrcs(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library with sysprop sources", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: ` -cc_library { - name: "foo", - srcs: [ - "bar.sysprop", - "baz.sysprop", - "blah.cpp", - ], - min_sdk_version: "5", -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("sysprop_library", "foo_sysprop_library", AttrNameToString{ - "srcs": `[ - "bar.sysprop", - "baz.sysprop", - ]`, - }), - MakeBazelTarget("cc_sysprop_library_static", "foo_cc_sysprop_library_static", AttrNameToString{ - "dep": `":foo_sysprop_library"`, - "min_sdk_version": `"5"`, - }), - MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{ - "srcs": `["blah.cpp"]`, - "local_includes": `["."]`, - "min_sdk_version": `"5"`, - "whole_archive_deps": `[":foo_cc_sysprop_library_static"]`, - }), - MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ - "srcs": `["blah.cpp"]`, - "local_includes": `["."]`, - "min_sdk_version": `"5"`, - "whole_archive_deps": `[":foo_cc_sysprop_library_static"]`, - }), - }, - }) -} - -func TestCcLibraryWithSyspropSrcsSomeConfigs(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library with sysprop sources in some configs but not others", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: ` -cc_library { - name: "foo", - host_supported: true, - srcs: [ - "blah.cpp", - ], - target: { - android: { - srcs: ["bar.sysprop"], - }, - }, - min_sdk_version: "5", -}`, - ExpectedBazelTargets: []string{ - MakeBazelTargetNoRestrictions("sysprop_library", "foo_sysprop_library", AttrNameToString{ - "srcs": `select({ - "//build/bazel/platforms/os:android": ["bar.sysprop"], - "//conditions:default": [], - })`, - }), - MakeBazelTargetNoRestrictions("cc_sysprop_library_static", "foo_cc_sysprop_library_static", AttrNameToString{ - "dep": `":foo_sysprop_library"`, - "min_sdk_version": `"5"`, - }), - MakeBazelTargetNoRestrictions("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{ - "srcs": `["blah.cpp"]`, - "local_includes": `["."]`, - "min_sdk_version": `"5"`, - "whole_archive_deps": `select({ - "//build/bazel/platforms/os:android": [":foo_cc_sysprop_library_static"], - "//conditions:default": [], - })`, - }), - MakeBazelTargetNoRestrictions("cc_library_shared", "foo", AttrNameToString{ - "srcs": `["blah.cpp"]`, - "local_includes": `["."]`, - "min_sdk_version": `"5"`, - "whole_archive_deps": `select({ - "//build/bazel/platforms/os:android": [":foo_cc_sysprop_library_static"], - "//conditions:default": [], - })`, - }), - }, - }) -} - -func TestCcLibraryWithAidlAndLibs(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_aidl_library depends on libs from parent cc_library_static", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: ` -cc_library_static { - name: "foo", - srcs: [ - "Foo.aidl", - ], - static_libs: [ - "bar-static", - "baz-static", - ], - shared_libs: [ - "bar-shared", - "baz-shared", - ], - export_static_lib_headers: [ - "baz-static", - ], - export_shared_lib_headers: [ - "baz-shared", - ], -}` + - simpleModuleDoNotConvertBp2build("cc_library_static", "bar-static") + - simpleModuleDoNotConvertBp2build("cc_library_static", "baz-static") + - simpleModuleDoNotConvertBp2build("cc_library", "bar-shared") + - simpleModuleDoNotConvertBp2build("cc_library", "baz-shared"), - ExpectedBazelTargets: []string{ - MakeBazelTarget("aidl_library", "foo_aidl_library", AttrNameToString{ - "srcs": `["Foo.aidl"]`, - }), - MakeBazelTarget("cc_aidl_library", "foo_cc_aidl_library", AttrNameToString{ - "deps": `[":foo_aidl_library"]`, - "implementation_deps": `[ - ":baz-static", - ":bar-static", - ]`, - "implementation_dynamic_deps": `[ - ":baz-shared", - ":bar-shared", - ]`, - }), - MakeBazelTarget("cc_library_static", "foo", AttrNameToString{ - "implementation_whole_archive_deps": `[":foo_cc_aidl_library"]`, - "deps": `[":baz-static"]`, - "implementation_deps": `[":bar-static"]`, - "dynamic_deps": `[":baz-shared"]`, - "implementation_dynamic_deps": `[":bar-shared"]`, - "local_includes": `["."]`, - }), - }, - }) -} - -func TestCcLibraryWithTidy(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library uses tidy properties", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: ` -cc_library_static { - name: "foo", - srcs: ["foo.cpp"], -} -cc_library_static { - name: "foo-no-tidy", - srcs: ["foo.cpp"], - tidy: false, -} -cc_library_static { - name: "foo-tidy", - srcs: ["foo.cpp"], - tidy: true, - tidy_checks: ["check1", "check2"], - tidy_checks_as_errors: ["check1error", "check2error"], - tidy_disabled_srcs: ["bar.cpp"], - tidy_timeout_srcs: ["baz.cpp"], -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo", AttrNameToString{ - "local_includes": `["."]`, - "srcs": `["foo.cpp"]`, - }), - MakeBazelTarget("cc_library_static", "foo-no-tidy", AttrNameToString{ - "local_includes": `["."]`, - "srcs": `["foo.cpp"]`, - "tidy": `"never"`, - }), - MakeBazelTarget("cc_library_static", "foo-tidy", AttrNameToString{ - "local_includes": `["."]`, - "srcs": `["foo.cpp"]`, - "tidy": `"local"`, - "tidy_checks": `[ - "check1", - "check2", - ]`, - "tidy_checks_as_errors": `[ - "check1error", - "check2error", - ]`, - "tidy_disabled_srcs": `["bar.cpp"]`, - "tidy_timeout_srcs": `["baz.cpp"]`, - }), - }, - }) -} - -func TestCcLibraryWithAfdoEnabled(t *testing.T) { - bp := ` -cc_library { - name: "foo", - afdo: true, - include_build_directory: false, -}` - - // TODO(b/260714900): Add test case for arch-specific afdo profile - testCases := []struct { - description string - filesystem map[string]string - expectedBazelTargets []string - }{ - { - description: "cc_library with afdo enabled and existing profile", - filesystem: map[string]string{ - "vendor/google_data/pgo_profile/sampling/BUILD": "", - "vendor/google_data/pgo_profile/sampling/foo.afdo": "", - }, - expectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{}), - MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ - "fdo_profile": `"//vendor/google_data/pgo_profile/sampling:foo"`, - }), - }, - }, - { - description: "cc_library with afdo enabled and existing profile in AOSP", - filesystem: map[string]string{ - "toolchain/pgo-profiles/sampling/BUILD": "", - "toolchain/pgo-profiles/sampling/foo.afdo": "", - }, - expectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{}), - MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ - "fdo_profile": `"//toolchain/pgo-profiles/sampling:foo"`, - }), - }, - }, - { - description: "cc_library with afdo enabled but profile filename doesn't match with module name", - filesystem: map[string]string{ - "toolchain/pgo-profiles/sampling/BUILD": "", - "toolchain/pgo-profiles/sampling/bar.afdo": "", - }, - expectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{}), - MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{}), - }, - }, - { - description: "cc_library with afdo enabled but profile doesn't exist", - expectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{}), - MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{}), - }, - }, - { - description: "cc_library with afdo enabled and existing profile but BUILD file doesn't exist", - filesystem: map[string]string{ - "vendor/google_data/pgo_profile/sampling/foo.afdo": "", - }, - expectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{}), - MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{}), - }, - }, - } - for _, testCase := range testCases { - t.Run(testCase.description, func(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - ExpectedBazelTargets: testCase.expectedBazelTargets, - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Description: testCase.description, - Blueprint: binaryReplacer.Replace(bp), - Filesystem: testCase.filesystem, - }) - }) - } -} - -func TestCcLibraryHeaderAbiChecker(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library with header abi checker", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: `cc_library { - name: "foo", - header_abi_checker: { - enabled: true, - symbol_file: "a.map.txt", - exclude_symbol_versions: [ - "29", - "30", - ], - exclude_symbol_tags: [ - "tag1", - "tag2", - ], - check_all_apis: true, - diff_flags: ["-allow-adding-removing-weak-symbols"], - }, - include_build_directory: false, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{}), - MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ - "abi_checker_enabled": `True`, - "abi_checker_symbol_file": `"a.map.txt"`, - "abi_checker_exclude_symbol_versions": `[ - "29", - "30", - ]`, - "abi_checker_exclude_symbol_tags": `[ - "tag1", - "tag2", - ]`, - "abi_checker_check_all_apis": `True`, - "abi_checker_diff_flags": `["-allow-adding-removing-weak-symbols"]`, - }), - }, - }) -} - -func TestCcLibraryApexAvailable(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library apex_available converted to tags", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: soongCcLibraryPreamble + ` -cc_library { - name: "a", - srcs: ["a.cpp"], - apex_available: ["com.android.foo"], -} -`, - ExpectedBazelTargets: makeCcLibraryTargets("a", AttrNameToString{ - "tags": `["apex_available=com.android.foo"]`, - "srcs": `["a.cpp"]`, - "local_includes": `["."]`, - }), - }, - ) -} - -func TestCcLibraryApexAvailableMultiple(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library apex_available converted to multiple tags", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: soongCcLibraryPreamble + ` -cc_library { - name: "a", - srcs: ["a.cpp"], - apex_available: ["com.android.foo", "//apex_available:platform", "com.android.bar"], -} -`, - ExpectedBazelTargets: makeCcLibraryTargets("a", AttrNameToString{ - "tags": `[ - "apex_available=com.android.foo", - "apex_available=//apex_available:platform", - "apex_available=com.android.bar", - ]`, - "srcs": `["a.cpp"]`, - "local_includes": `["."]`, - }), - }, - ) -} - -// Export_include_dirs and Export_system_include_dirs have "variant_prepend" tag. -// In bp2build output, variant info(select) should go before general info. -// Internal order of the property should be unchanged. (e.g. ["eid1", "eid2"]) -func TestCcLibraryVariantPrependPropOrder(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library variant prepend properties order", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: soongCcLibraryPreamble + ` -cc_library { - name: "a", - srcs: ["a.cpp"], - export_include_dirs: ["eid1", "eid2"], - export_system_include_dirs: ["esid1", "esid2"], - target: { - android: { - export_include_dirs: ["android_eid1", "android_eid2"], - export_system_include_dirs: ["android_esid1", "android_esid2"], - }, - android_arm: { - export_include_dirs: ["android_arm_eid1", "android_arm_eid2"], - export_system_include_dirs: ["android_arm_esid1", "android_arm_esid2"], - }, - linux: { - export_include_dirs: ["linux_eid1", "linux_eid2"], - export_system_include_dirs: ["linux_esid1", "linux_esid2"], - }, - }, - multilib: { - lib32: { - export_include_dirs: ["lib32_eid1", "lib32_eid2"], - export_system_include_dirs: ["lib32_esid1", "lib32_esid2"], - }, - }, - arch: { - arm: { - export_include_dirs: ["arm_eid1", "arm_eid2"], - export_system_include_dirs: ["arm_esid1", "arm_esid2"], - }, - } -} -`, - ExpectedBazelTargets: makeCcLibraryTargets("a", AttrNameToString{ - "export_includes": `select({ - "//build/bazel/platforms/os_arch:android_arm": [ - "android_arm_eid1", - "android_arm_eid2", - ], - "//conditions:default": [], - }) + select({ - "//build/bazel/platforms/os:android": [ - "android_eid1", - "android_eid2", - "linux_eid1", - "linux_eid2", - ], - "//build/bazel/platforms/os:linux_bionic": [ - "linux_eid1", - "linux_eid2", - ], - "//build/bazel/platforms/os:linux_glibc": [ - "linux_eid1", - "linux_eid2", - ], - "//build/bazel/platforms/os:linux_musl": [ - "linux_eid1", - "linux_eid2", - ], - "//conditions:default": [], - }) + select({ - "//build/bazel/platforms/arch:arm": [ - "lib32_eid1", - "lib32_eid2", - "arm_eid1", - "arm_eid2", - ], - "//build/bazel/platforms/arch:x86": [ - "lib32_eid1", - "lib32_eid2", - ], - "//conditions:default": [], - }) + [ - "eid1", - "eid2", - ]`, - "export_system_includes": `select({ - "//build/bazel/platforms/os_arch:android_arm": [ - "android_arm_esid1", - "android_arm_esid2", - ], - "//conditions:default": [], - }) + select({ - "//build/bazel/platforms/os:android": [ - "android_esid1", - "android_esid2", - "linux_esid1", - "linux_esid2", - ], - "//build/bazel/platforms/os:linux_bionic": [ - "linux_esid1", - "linux_esid2", - ], - "//build/bazel/platforms/os:linux_glibc": [ - "linux_esid1", - "linux_esid2", - ], - "//build/bazel/platforms/os:linux_musl": [ - "linux_esid1", - "linux_esid2", - ], - "//conditions:default": [], - }) + select({ - "//build/bazel/platforms/arch:arm": [ - "lib32_esid1", - "lib32_esid2", - "arm_esid1", - "arm_esid2", - ], - "//build/bazel/platforms/arch:x86": [ - "lib32_esid1", - "lib32_esid2", - ], - "//conditions:default": [], - }) + [ - "esid1", - "esid2", - ]`, - "srcs": `["a.cpp"]`, - "local_includes": `["."]`, - "target_compatible_with": `["//build/bazel/platforms/os:android"]`, - }), - }, - ) -} - -func TestCcLibraryWithIntegerOverflowProperty(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library has correct features when integer_overflow property is provided", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: ` -cc_library { - name: "foo", - sanitize: { - integer_overflow: true, - }, -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{ - "features": `["ubsan_integer_overflow"]`, - "local_includes": `["."]`, - }), - MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ - "features": `["ubsan_integer_overflow"]`, - "local_includes": `["."]`, - }), - }, - }) -} - -func TestCcLibraryWithMiscUndefinedProperty(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library has correct features when misc_undefined property is provided", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: ` -cc_library { - name: "foo", - sanitize: { - misc_undefined: ["undefined", "nullability"], - }, -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{ - "features": `[ - "ubsan_undefined", - "ubsan_nullability", - ]`, - "local_includes": `["."]`, - }), - MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ - "features": `[ - "ubsan_undefined", - "ubsan_nullability", - ]`, - "local_includes": `["."]`, - }), - }, - }) -} - -func TestCcLibraryWithUBSanPropertiesArchSpecific(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library has correct feature select when UBSan props are specified in arch specific blocks", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: ` -cc_library { - name: "foo", - sanitize: { - misc_undefined: ["undefined", "nullability"], - }, - target: { - android: { - sanitize: { - misc_undefined: ["alignment"], - }, - }, - linux_glibc: { - sanitize: { - integer_overflow: true, - }, - }, - }, -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{ - "features": `[ - "ubsan_undefined", - "ubsan_nullability", - ] + select({ - "//build/bazel/platforms/os:android": ["ubsan_alignment"], - "//build/bazel/platforms/os:linux_glibc": ["ubsan_integer_overflow"], - "//conditions:default": [], - })`, - "local_includes": `["."]`, - }), - MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ - "features": `[ - "ubsan_undefined", - "ubsan_nullability", - ] + select({ - "//build/bazel/platforms/os:android": ["ubsan_alignment"], - "//build/bazel/platforms/os:linux_glibc": ["ubsan_integer_overflow"], - "//conditions:default": [], - })`, - "local_includes": `["."]`, - }), - }, - }) -} - -func TestCcLibraryInApexWithStubSharedLibs(t *testing.T) { - runCcLibrarySharedTestCase(t, Bp2buildTestCase{ - Description: "cc_library with in apex with stub shared_libs and export_shared_lib_headers", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: ` -cc_library { - name: "barlib", - stubs: { symbol_file: "bar.map.txt", versions: ["28", "29", "current"] }, - bazel_module: { bp2build_available: false }, -} -cc_library { - name: "bazlib", - stubs: { symbol_file: "bar.map.txt", versions: ["28", "29", "current"] }, - bazel_module: { bp2build_available: false }, -} -cc_library { - name: "foo", - shared_libs: ["barlib", "bazlib"], - export_shared_lib_headers: ["bazlib"], - apex_available: [ - "apex_available:platform", - ], -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{ - "implementation_dynamic_deps": `select({ - "//build/bazel/rules/apex:android-in_apex": ["@api_surfaces//module-libapi/current:barlib"], - "//conditions:default": [":barlib"], - })`, - "dynamic_deps": `select({ - "//build/bazel/rules/apex:android-in_apex": ["@api_surfaces//module-libapi/current:bazlib"], - "//conditions:default": [":bazlib"], - })`, - "local_includes": `["."]`, - "tags": `["apex_available=apex_available:platform"]`, - }), - MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ - "implementation_dynamic_deps": `select({ - "//build/bazel/rules/apex:android-in_apex": ["@api_surfaces//module-libapi/current:barlib"], - "//conditions:default": [":barlib"], - })`, - "dynamic_deps": `select({ - "//build/bazel/rules/apex:android-in_apex": ["@api_surfaces//module-libapi/current:bazlib"], - "//conditions:default": [":bazlib"], - })`, - "local_includes": `["."]`, - "tags": `["apex_available=apex_available:platform"]`, - }), - }, - }) -} - -func TestCcLibraryWithThinLto(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library has correct features when thin LTO is enabled", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: ` -cc_library { - name: "foo", - lto: { - thin: true, - }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{ - "features": `["android_thin_lto"]`, - "local_includes": `["."]`, - }), - MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ - "features": `["android_thin_lto"]`, - "local_includes": `["."]`, - }), - }, - }) -} - -func TestCcLibraryWithLtoNever(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library has correct features when LTO is explicitly disabled", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: ` -cc_library { - name: "foo", - lto: { - never: true, - }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{ - "features": `["-android_thin_lto"]`, - "local_includes": `["."]`, - }), - MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ - "features": `["-android_thin_lto"]`, - "local_includes": `["."]`, - }), - }, - }) -} - -func TestCcLibraryWithThinLtoArchSpecific(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library has correct features when LTO differs across arch and os variants", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: ` -cc_library { - name: "foo", - target: { - android: { - lto: { - thin: true, - }, - }, - }, - arch: { - riscv64: { - lto: { - thin: false, - }, - }, - }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{ - "local_includes": `["."]`, - "features": `select({ - "//build/bazel/platforms/os_arch:android_arm": ["android_thin_lto"], - "//build/bazel/platforms/os_arch:android_arm64": ["android_thin_lto"], - "//build/bazel/platforms/os_arch:android_riscv64": ["-android_thin_lto"], - "//build/bazel/platforms/os_arch:android_x86": ["android_thin_lto"], - "//build/bazel/platforms/os_arch:android_x86_64": ["android_thin_lto"], - "//conditions:default": [], - })`}), - MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ - "local_includes": `["."]`, - "features": `select({ - "//build/bazel/platforms/os_arch:android_arm": ["android_thin_lto"], - "//build/bazel/platforms/os_arch:android_arm64": ["android_thin_lto"], - "//build/bazel/platforms/os_arch:android_riscv64": ["-android_thin_lto"], - "//build/bazel/platforms/os_arch:android_x86": ["android_thin_lto"], - "//build/bazel/platforms/os_arch:android_x86_64": ["android_thin_lto"], - "//conditions:default": [], - })`}), - }, - }) -} - -func TestCcLibraryWithThinLtoDisabledDefaultEnabledVariant(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library has correct features when LTO disabled by default but enabled on a particular variant", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: ` -cc_library { - name: "foo", - lto: { - never: true, - }, - target: { - android: { - lto: { - thin: true, - never: false, - }, - }, - }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{ - "local_includes": `["."]`, - "features": `select({ - "//build/bazel/platforms/os:android": ["android_thin_lto"], - "//conditions:default": ["-android_thin_lto"], - })`, - }), - MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ - "local_includes": `["."]`, - "features": `select({ - "//build/bazel/platforms/os:android": ["android_thin_lto"], - "//conditions:default": ["-android_thin_lto"], - })`, - }), - }, - }) -} - -func TestCcLibraryWithThinLtoWholeProgramVtables(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library has correct features when thin LTO is enabled with whole_program_vtables", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: ` -cc_library { - name: "foo", - lto: { - thin: true, - }, - whole_program_vtables: true, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{ - "features": `[ - "android_thin_lto", - "android_thin_lto_whole_program_vtables", - ]`, - "local_includes": `["."]`, - }), - MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ - "features": `[ - "android_thin_lto", - "android_thin_lto_whole_program_vtables", - ]`, - "local_includes": `["."]`, - }), - }, - }) -} - -func TestCcLibraryCppFlagsInProductVariables(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library cppflags in product variables", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: soongCcLibraryPreamble + `cc_library { - name: "a", - srcs: ["a.cpp"], - cppflags: [ - "-Wextra", - "-DDEBUG_ONLY_CODE=0", - ], - product_variables: { - eng: { - cppflags: [ - "-UDEBUG_ONLY_CODE", - "-DDEBUG_ONLY_CODE=1", - ], - }, - }, - include_build_directory: false, -} -`, - ExpectedBazelTargets: makeCcLibraryTargets("a", AttrNameToString{ - "cppflags": `[ - "-Wextra", - "-DDEBUG_ONLY_CODE=0", - ] + select({ - "//build/bazel/product_variables:eng": [ - "-UDEBUG_ONLY_CODE", - "-DDEBUG_ONLY_CODE=1", - ], - "//conditions:default": [], - })`, - "srcs": `["a.cpp"]`, - }), - }, - ) -} diff --git a/bp2build/cc_library_headers_conversion_test.go b/bp2build/cc_library_headers_conversion_test.go deleted file mode 100644 index 072f5b3b48..0000000000 --- a/bp2build/cc_library_headers_conversion_test.go +++ /dev/null @@ -1,478 +0,0 @@ -// 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 bp2build - -import ( - "testing" - - "android/soong/android" - "android/soong/cc" -) - -const ( - // See cc/testing.go for more context - soongCcLibraryHeadersPreamble = ` -cc_defaults { - name: "linux_bionic_supported", -}` -) - -func TestCcLibraryHeadersLoadStatement(t *testing.T) { - testCases := []struct { - bazelTargets BazelTargets - expectedLoadStatements string - }{ - { - bazelTargets: BazelTargets{ - BazelTarget{ - name: "cc_library_headers_target", - ruleClass: "cc_library_headers", - // Note: no bzlLoadLocation for native rules - }, - }, - expectedLoadStatements: ``, - }, - } - - for _, testCase := range testCases { - actual := testCase.bazelTargets.LoadStatements() - expected := testCase.expectedLoadStatements - if actual != expected { - t.Fatalf("Expected load statements to be %s, got %s", expected, actual) - } - } -} - -func registerCcLibraryHeadersModuleTypes(ctx android.RegistrationContext) { - cc.RegisterCCBuildComponents(ctx) -} - -func runCcLibraryHeadersTestCase(t *testing.T, tc Bp2buildTestCase) { - t.Helper() - RunBp2BuildTestCase(t, registerCcLibraryHeadersModuleTypes, tc) -} - -func TestCcLibraryHeadersSimple(t *testing.T) { - runCcLibraryHeadersTestCase(t, Bp2buildTestCase{ - Description: "cc_library_headers test", - ModuleTypeUnderTest: "cc_library_headers", - ModuleTypeUnderTestFactory: cc.LibraryHeaderFactory, - Filesystem: map[string]string{ - "lib-1/lib1a.h": "", - "lib-1/lib1b.h": "", - "lib-2/lib2a.h": "", - "lib-2/lib2b.h": "", - "dir-1/dir1a.h": "", - "dir-1/dir1b.h": "", - "dir-2/dir2a.h": "", - "dir-2/dir2b.h": "", - "arch_arm64_exported_include_dir/a.h": "", - "arch_x86_exported_include_dir/b.h": "", - "arch_x86_64_exported_include_dir/c.h": "", - }, - Blueprint: soongCcLibraryHeadersPreamble + ` -cc_library_headers { - name: "foo_headers", - export_include_dirs: ["dir-1", "dir-2"], - header_libs: ["lib-1", "lib-2"], - - arch: { - arm64: { - // We expect dir-1 headers to be dropped, because dir-1 is already in export_include_dirs - export_include_dirs: ["arch_arm64_exported_include_dir", "dir-1"], - }, - x86: { - export_include_dirs: ["arch_x86_exported_include_dir"], - }, - x86_64: { - export_include_dirs: ["arch_x86_64_exported_include_dir"], - }, - }, - sdk_version: "current", - min_sdk_version: "29", - - // TODO: Also support export_header_lib_headers -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_headers", "foo_headers", AttrNameToString{ - "export_includes": `select({ - "//build/bazel/platforms/arch:arm64": ["arch_arm64_exported_include_dir"], - "//build/bazel/platforms/arch:x86": ["arch_x86_exported_include_dir"], - "//build/bazel/platforms/arch:x86_64": ["arch_x86_64_exported_include_dir"], - "//conditions:default": [], - }) + [ - "dir-1", - "dir-2", - ]`, - "sdk_version": `"current"`, - "min_sdk_version": `"29"`, - }), - }, - }) -} - -func TestCcApiHeaders(t *testing.T) { - fs := map[string]string{ - "bar/Android.bp": `cc_library_headers { name: "bar_headers", }`, - } - bp := ` - cc_library_headers { - name: "foo_headers", - export_include_dirs: ["dir1", "dir2"], - export_header_lib_headers: ["bar_headers"], - - arch: { - arm: { - export_include_dirs: ["dir_arm"], - }, - x86: { - export_include_dirs: ["dir_x86"], - }, - }, - - target: { - android: { - export_include_dirs: ["dir1", "dir_android"], - }, - windows: { - export_include_dirs: ["dir_windows"], - }, - } - } - ` - expectedBazelTargets := []string{ - MakeBazelTarget("cc_api_library_headers", "foo_headers.contribution.arm", AttrNameToString{ - "export_includes": `["dir_arm"]`, - "arch": `"arm"`, - }), - MakeBazelTarget("cc_api_library_headers", "foo_headers.contribution.x86", AttrNameToString{ - "export_includes": `["dir_x86"]`, - "arch": `"x86"`, - }), - MakeBazelTarget("cc_api_library_headers", "foo_headers.contribution.androidos", AttrNameToString{ - "export_includes": `["dir_android"]`, // common includes are deduped - }), - // Windows headers are not exported - MakeBazelTarget("cc_api_library_headers", "foo_headers.contribution", AttrNameToString{ - "export_includes": `[ - "dir1", - "dir2", - ]`, - "deps": `[ - "//bar:bar_headers.contribution", - ":foo_headers.contribution.arm", - ":foo_headers.contribution.x86", - ":foo_headers.contribution.androidos", - ]`, - }), - } - RunApiBp2BuildTestCase(t, cc.RegisterLibraryHeadersBuildComponents, Bp2buildTestCase{ - Blueprint: bp, - Description: "Header library contributions to API surfaces", - ExpectedBazelTargets: expectedBazelTargets, - Filesystem: fs, - }) -} - -// header_libs has "variant_prepend" tag. In bp2build output, -// variant info(select) should go before general info. -func TestCcLibraryHeadersOsSpecificHeader(t *testing.T) { - runCcLibraryHeadersTestCase(t, Bp2buildTestCase{ - Description: "cc_library_headers test with os-specific header_libs props", - ModuleTypeUnderTest: "cc_library_headers", - ModuleTypeUnderTestFactory: cc.LibraryHeaderFactory, - Filesystem: map[string]string{}, - Blueprint: soongCcLibraryPreamble + ` -cc_library_headers { - name: "android-lib", - bazel_module: { bp2build_available: false }, -} -cc_library_headers { - name: "base-lib", - bazel_module: { bp2build_available: false }, -} -cc_library_headers { - name: "darwin-lib", - bazel_module: { bp2build_available: false }, -} -cc_library_headers { - name: "linux-lib", - bazel_module: { bp2build_available: false }, -} -cc_library_headers { - name: "linux_bionic-lib", - bazel_module: { bp2build_available: false }, -} -cc_library_headers { - name: "windows-lib", - bazel_module: { bp2build_available: false }, -} -cc_library_headers { - name: "foo_headers", - header_libs: ["base-lib"], - export_header_lib_headers: ["base-lib"], - target: { - android: { - header_libs: ["android-lib"], - export_header_lib_headers: ["android-lib"], - }, - darwin: { - header_libs: ["darwin-lib"], - export_header_lib_headers: ["darwin-lib"], - }, - linux_bionic: { - header_libs: ["linux_bionic-lib"], - export_header_lib_headers: ["linux_bionic-lib"], - }, - linux_glibc: { - header_libs: ["linux-lib"], - export_header_lib_headers: ["linux-lib"], - }, - windows: { - header_libs: ["windows-lib"], - export_header_lib_headers: ["windows-lib"], - }, - }, - include_build_directory: false, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_headers", "foo_headers", AttrNameToString{ - "deps": `select({ - "//build/bazel/platforms/os:android": [":android-lib"], - "//build/bazel/platforms/os:darwin": [":darwin-lib"], - "//build/bazel/platforms/os:linux_bionic": [":linux_bionic-lib"], - "//build/bazel/platforms/os:linux_glibc": [":linux-lib"], - "//build/bazel/platforms/os:windows": [":windows-lib"], - "//conditions:default": [], - }) + [":base-lib"]`, - }), - }, - }) -} - -func TestCcLibraryHeadersOsSpecficHeaderLibsExportHeaderLibHeaders(t *testing.T) { - runCcLibraryHeadersTestCase(t, Bp2buildTestCase{ - Description: "cc_library_headers test with os-specific header_libs and export_header_lib_headers props", - ModuleTypeUnderTest: "cc_library_headers", - ModuleTypeUnderTestFactory: cc.LibraryHeaderFactory, - Filesystem: map[string]string{}, - Blueprint: soongCcLibraryPreamble + ` -cc_library_headers { - name: "android-lib", - bazel_module: { bp2build_available: false }, - } -cc_library_headers { - name: "exported-lib", - bazel_module: { bp2build_available: false }, -} -cc_library_headers { - name: "foo_headers", - target: { - android: { - header_libs: ["android-lib", "exported-lib"], - export_header_lib_headers: ["exported-lib"] - }, - }, - include_build_directory: false, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_headers", "foo_headers", AttrNameToString{ - "deps": `select({ - "//build/bazel/platforms/os:android": [":exported-lib"], - "//conditions:default": [], - })`, - }), - }, - }) -} - -func TestCcLibraryHeadersArchAndTargetExportSystemIncludes(t *testing.T) { - runCcLibraryHeadersTestCase(t, Bp2buildTestCase{ - Description: "cc_library_headers test with arch-specific and target-specific export_system_include_dirs props", - ModuleTypeUnderTest: "cc_library_headers", - ModuleTypeUnderTestFactory: cc.LibraryHeaderFactory, - Filesystem: map[string]string{}, - Blueprint: soongCcLibraryPreamble + `cc_library_headers { - name: "foo_headers", - export_system_include_dirs: [ - "shared_include_dir", - ], - target: { - android: { - export_system_include_dirs: [ - "android_include_dir", - ], - }, - linux_glibc: { - export_system_include_dirs: [ - "linux_include_dir", - ], - }, - darwin: { - export_system_include_dirs: [ - "darwin_include_dir", - ], - }, - }, - arch: { - arm: { - export_system_include_dirs: [ - "arm_include_dir", - ], - }, - x86_64: { - export_system_include_dirs: [ - "x86_64_include_dir", - ], - }, - }, - include_build_directory: false, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_headers", "foo_headers", AttrNameToString{ - "export_system_includes": `select({ - "//build/bazel/platforms/os:android": ["android_include_dir"], - "//build/bazel/platforms/os:darwin": ["darwin_include_dir"], - "//build/bazel/platforms/os:linux_glibc": ["linux_include_dir"], - "//conditions:default": [], - }) + select({ - "//build/bazel/platforms/arch:arm": ["arm_include_dir"], - "//build/bazel/platforms/arch:x86_64": ["x86_64_include_dir"], - "//conditions:default": [], - }) + ["shared_include_dir"]`, - }), - }, - }) -} - -func TestCcLibraryHeadersNoCrtIgnored(t *testing.T) { - runCcLibraryHeadersTestCase(t, Bp2buildTestCase{ - Description: "cc_library_headers test", - ModuleTypeUnderTest: "cc_library_headers", - ModuleTypeUnderTestFactory: cc.LibraryHeaderFactory, - Filesystem: map[string]string{ - "lib-1/lib1a.h": "", - "lib-1/lib1b.h": "", - "lib-2/lib2a.h": "", - "lib-2/lib2b.h": "", - "dir-1/dir1a.h": "", - "dir-1/dir1b.h": "", - "dir-2/dir2a.h": "", - "dir-2/dir2b.h": "", - "arch_arm64_exported_include_dir/a.h": "", - "arch_x86_exported_include_dir/b.h": "", - "arch_x86_64_exported_include_dir/c.h": "", - }, - Blueprint: soongCcLibraryHeadersPreamble + ` -cc_library_headers { - name: "lib-1", - export_include_dirs: ["lib-1"], - no_libcrt: true, - include_build_directory: false, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_headers", "lib-1", AttrNameToString{ - "export_includes": `["lib-1"]`, - }), - }, - }) -} - -func TestCcLibraryHeadersExportedStaticLibHeadersReexported(t *testing.T) { - runCcLibraryHeadersTestCase(t, Bp2buildTestCase{ - Description: "cc_library_headers exported_static_lib_headers is reexported", - ModuleTypeUnderTest: "cc_library_headers", - ModuleTypeUnderTestFactory: cc.LibraryHeaderFactory, - Filesystem: map[string]string{}, - Blueprint: soongCcLibraryHeadersPreamble + ` -cc_library_headers { - name: "foo_headers", - export_static_lib_headers: ["foo_export"], - static_libs: ["foo_export", "foo_no_reexport"], - bazel_module: { bp2build_available: true }, -} -` + simpleModuleDoNotConvertBp2build("cc_library_headers", "foo_export"), - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_headers", "foo_headers", AttrNameToString{ - "deps": `[":foo_export"]`, - }), - }, - }) -} - -func TestCcLibraryHeadersExportedSharedLibHeadersReexported(t *testing.T) { - runCcLibraryHeadersTestCase(t, Bp2buildTestCase{ - Description: "cc_library_headers exported_shared_lib_headers is reexported", - ModuleTypeUnderTest: "cc_library_headers", - ModuleTypeUnderTestFactory: cc.LibraryHeaderFactory, - Filesystem: map[string]string{}, - Blueprint: soongCcLibraryHeadersPreamble + ` -cc_library_headers { - name: "foo_headers", - export_shared_lib_headers: ["foo_export"], - shared_libs: ["foo_export", "foo_no_reexport"], - bazel_module: { bp2build_available: true }, -} -` + simpleModuleDoNotConvertBp2build("cc_library_headers", "foo_export"), - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_headers", "foo_headers", AttrNameToString{ - "deps": `[":foo_export"]`, - }), - }, - }) -} - -func TestCcLibraryHeadersExportedHeaderLibHeadersReexported(t *testing.T) { - runCcLibraryHeadersTestCase(t, Bp2buildTestCase{ - Description: "cc_library_headers exported_header_lib_headers is reexported", - ModuleTypeUnderTest: "cc_library_headers", - ModuleTypeUnderTestFactory: cc.LibraryHeaderFactory, - Filesystem: map[string]string{}, - Blueprint: soongCcLibraryHeadersPreamble + ` -cc_library_headers { - name: "foo_headers", - export_header_lib_headers: ["foo_export"], - header_libs: ["foo_export", "foo_no_reexport"], - bazel_module: { bp2build_available: true }, -} -` + simpleModuleDoNotConvertBp2build("cc_library_headers", "foo_export"), - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_headers", "foo_headers", AttrNameToString{ - "deps": `[":foo_export"]`, - }), - }, - }) -} - -func TestCcLibraryHeadersWholeStaticLibsReexported(t *testing.T) { - runCcLibraryHeadersTestCase(t, Bp2buildTestCase{ - Description: "cc_library_headers whole_static_libs is reexported", - ModuleTypeUnderTest: "cc_library_headers", - ModuleTypeUnderTestFactory: cc.LibraryHeaderFactory, - Filesystem: map[string]string{}, - Blueprint: soongCcLibraryHeadersPreamble + ` -cc_library_headers { - name: "foo_headers", - whole_static_libs: ["foo_export"], - bazel_module: { bp2build_available: true }, -} -` + simpleModuleDoNotConvertBp2build("cc_library_headers", "foo_export"), - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_headers", "foo_headers", AttrNameToString{ - "deps": `[":foo_export"]`, - }), - }, - }) -} diff --git a/bp2build/cc_library_shared_conversion_test.go b/bp2build/cc_library_shared_conversion_test.go deleted file mode 100644 index 9ba933717d..0000000000 --- a/bp2build/cc_library_shared_conversion_test.go +++ /dev/null @@ -1,1315 +0,0 @@ -// 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 bp2build - -import ( - "testing" - - "android/soong/android" - "android/soong/cc" -) - -const ( - // See cc/testing.go for more context - // TODO(alexmarquez): Split out the preamble into common code? - soongCcLibrarySharedPreamble = soongCcLibraryStaticPreamble -) - -func registerCcLibrarySharedModuleTypes(ctx android.RegistrationContext) { - cc.RegisterCCBuildComponents(ctx) - ctx.RegisterModuleType("cc_library_headers", cc.LibraryHeaderFactory) - ctx.RegisterModuleType("cc_library_static", cc.LibraryStaticFactory) - ctx.RegisterModuleType("cc_library", cc.LibraryFactory) -} - -func runCcLibrarySharedTestCase(t *testing.T, tc Bp2buildTestCase) { - t.Helper() - t.Parallel() - (&tc).ModuleTypeUnderTest = "cc_library_shared" - (&tc).ModuleTypeUnderTestFactory = cc.LibrarySharedFactory - RunBp2BuildTestCase(t, registerCcLibrarySharedModuleTypes, tc) -} - -func TestCcLibrarySharedSimple(t *testing.T) { - runCcLibrarySharedTestCase(t, Bp2buildTestCase{ - Description: "cc_library_shared simple overall test", - Filesystem: map[string]string{ - // NOTE: include_dir headers *should not* appear in Bazel hdrs later (?) - "include_dir_1/include_dir_1_a.h": "", - "include_dir_1/include_dir_1_b.h": "", - "include_dir_2/include_dir_2_a.h": "", - "include_dir_2/include_dir_2_b.h": "", - // NOTE: local_include_dir headers *should not* appear in Bazel hdrs later (?) - "local_include_dir_1/local_include_dir_1_a.h": "", - "local_include_dir_1/local_include_dir_1_b.h": "", - "local_include_dir_2/local_include_dir_2_a.h": "", - "local_include_dir_2/local_include_dir_2_b.h": "", - // NOTE: export_include_dir headers *should* appear in Bazel hdrs later - "export_include_dir_1/export_include_dir_1_a.h": "", - "export_include_dir_1/export_include_dir_1_b.h": "", - "export_include_dir_2/export_include_dir_2_a.h": "", - "export_include_dir_2/export_include_dir_2_b.h": "", - // NOTE: Soong implicitly includes headers in the current directory - "implicit_include_1.h": "", - "implicit_include_2.h": "", - }, - Blueprint: soongCcLibrarySharedPreamble + ` -cc_library_headers { - name: "header_lib_1", - export_include_dirs: ["header_lib_1"], - bazel_module: { bp2build_available: false }, -} - -cc_library_headers { - name: "header_lib_2", - export_include_dirs: ["header_lib_2"], - bazel_module: { bp2build_available: false }, -} - -cc_library_shared { - name: "shared_lib_1", - srcs: ["shared_lib_1.cc"], - bazel_module: { bp2build_available: false }, -} - -cc_library_shared { - name: "shared_lib_2", - srcs: ["shared_lib_2.cc"], - bazel_module: { bp2build_available: false }, -} - -cc_library_static { - name: "whole_static_lib_1", - srcs: ["whole_static_lib_1.cc"], - bazel_module: { bp2build_available: false }, -} - -cc_library_static { - name: "whole_static_lib_2", - srcs: ["whole_static_lib_2.cc"], - bazel_module: { bp2build_available: false }, -} - -cc_library_shared { - name: "foo_shared", - srcs: [ - "foo_shared1.cc", - "foo_shared2.cc", - ], - cflags: [ - "-Dflag1", - "-Dflag2" - ], - shared_libs: [ - "shared_lib_1", - "shared_lib_2" - ], - whole_static_libs: [ - "whole_static_lib_1", - "whole_static_lib_2" - ], - include_dirs: [ - "include_dir_1", - "include_dir_2", - ], - local_include_dirs: [ - "local_include_dir_1", - "local_include_dir_2", - ], - export_include_dirs: [ - "export_include_dir_1", - "export_include_dir_2" - ], - header_libs: [ - "header_lib_1", - "header_lib_2" - ], - sdk_version: "current", - min_sdk_version: "29", - - // TODO: Also support export_header_lib_headers -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_shared", "foo_shared", AttrNameToString{ - "absolute_includes": `[ - "include_dir_1", - "include_dir_2", - ]`, - "copts": `[ - "-Dflag1", - "-Dflag2", - ]`, - "export_includes": `[ - "export_include_dir_1", - "export_include_dir_2", - ]`, - "implementation_deps": `[ - ":header_lib_1", - ":header_lib_2", - ]`, - "implementation_dynamic_deps": `[ - ":shared_lib_1", - ":shared_lib_2", - ]`, - "local_includes": `[ - "local_include_dir_1", - "local_include_dir_2", - ".", - ]`, - "srcs": `[ - "foo_shared1.cc", - "foo_shared2.cc", - ]`, - "whole_archive_deps": `[ - ":whole_static_lib_1", - ":whole_static_lib_2", - ]`, - "sdk_version": `"current"`, - "min_sdk_version": `"29"`, - }), - }, - }) -} - -func TestCcLibrarySharedArchSpecificSharedLib(t *testing.T) { - runCcLibrarySharedTestCase(t, Bp2buildTestCase{ - Description: "cc_library_shared arch-specific shared_libs with whole_static_libs", - Filesystem: map[string]string{}, - Blueprint: soongCcLibrarySharedPreamble + ` -cc_library_static { - name: "static_dep", - bazel_module: { bp2build_available: false }, -} -cc_library_shared { - name: "shared_dep", - bazel_module: { bp2build_available: false }, -} -cc_library_shared { - name: "foo_shared", - arch: { arm64: { shared_libs: ["shared_dep"], whole_static_libs: ["static_dep"] } }, - include_build_directory: false, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_shared", "foo_shared", AttrNameToString{ - "implementation_dynamic_deps": `select({ - "//build/bazel/platforms/arch:arm64": [":shared_dep"], - "//conditions:default": [], - })`, - "whole_archive_deps": `select({ - "//build/bazel/platforms/arch:arm64": [":static_dep"], - "//conditions:default": [], - })`, - }), - }, - }) -} - -func TestCcLibrarySharedOsSpecificSharedLib(t *testing.T) { - runCcLibrarySharedTestCase(t, Bp2buildTestCase{ - Description: "cc_library_shared os-specific shared_libs", - Filesystem: map[string]string{}, - Blueprint: soongCcLibrarySharedPreamble + ` -cc_library_shared { - name: "shared_dep", - bazel_module: { bp2build_available: false }, -} -cc_library_shared { - name: "foo_shared", - target: { android: { shared_libs: ["shared_dep"], } }, - include_build_directory: false, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_shared", "foo_shared", AttrNameToString{ - "implementation_dynamic_deps": `select({ - "//build/bazel/platforms/os:android": [":shared_dep"], - "//conditions:default": [], - })`, - }), - }, - }) -} - -func TestCcLibrarySharedBaseArchOsSpecificSharedLib(t *testing.T) { - runCcLibrarySharedTestCase(t, Bp2buildTestCase{ - Description: "cc_library_shared base, arch, and os-specific shared_libs", - Filesystem: map[string]string{}, - Blueprint: soongCcLibrarySharedPreamble + ` -cc_library_shared { - name: "shared_dep", - bazel_module: { bp2build_available: false }, -} -cc_library_shared { - name: "shared_dep2", - bazel_module: { bp2build_available: false }, -} -cc_library_shared { - name: "shared_dep3", - bazel_module: { bp2build_available: false }, -} -cc_library_shared { - name: "foo_shared", - shared_libs: ["shared_dep"], - target: { android: { shared_libs: ["shared_dep2"] } }, - arch: { arm64: { shared_libs: ["shared_dep3"] } }, - include_build_directory: false, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_shared", "foo_shared", AttrNameToString{ - "implementation_dynamic_deps": `[":shared_dep"] + select({ - "//build/bazel/platforms/arch:arm64": [":shared_dep3"], - "//conditions:default": [], - }) + select({ - "//build/bazel/platforms/os:android": [":shared_dep2"], - "//conditions:default": [], - })`, - }), - }, - }) -} - -func TestCcLibrarySharedSimpleExcludeSrcs(t *testing.T) { - runCcLibrarySharedTestCase(t, Bp2buildTestCase{ - Description: "cc_library_shared simple exclude_srcs", - Filesystem: map[string]string{ - "common.c": "", - "foo-a.c": "", - "foo-excluded.c": "", - }, - Blueprint: soongCcLibrarySharedPreamble + ` -cc_library_shared { - name: "foo_shared", - srcs: ["common.c", "foo-*.c"], - exclude_srcs: ["foo-excluded.c"], - include_build_directory: false, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_shared", "foo_shared", AttrNameToString{ - "srcs_c": `[ - "common.c", - "foo-a.c", - ]`, - }), - }, - }) -} - -func TestCcLibrarySharedStrip(t *testing.T) { - runCcLibrarySharedTestCase(t, Bp2buildTestCase{ - Description: "cc_library_shared stripping", - Filesystem: map[string]string{}, - Blueprint: soongCcLibrarySharedPreamble + ` -cc_library_shared { - name: "foo_shared", - strip: { - keep_symbols: false, - keep_symbols_and_debug_frame: true, - keep_symbols_list: ["sym", "sym2"], - all: true, - none: false, - }, - include_build_directory: false, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_shared", "foo_shared", AttrNameToString{ - "strip": `{ - "all": True, - "keep_symbols": False, - "keep_symbols_and_debug_frame": True, - "keep_symbols_list": [ - "sym", - "sym2", - ], - "none": False, - }`, - }), - }, - }) -} - -func TestCcLibrarySharedVersionScriptAndDynamicList(t *testing.T) { - runCcLibrarySharedTestCase(t, Bp2buildTestCase{ - Description: "cc_library_shared version script and dynamic list", - Filesystem: map[string]string{ - "version_script": "", - "dynamic.list": "", - }, - Blueprint: soongCcLibrarySharedPreamble + ` -cc_library_shared { - name: "foo_shared", - version_script: "version_script", - dynamic_list: "dynamic.list", - include_build_directory: false, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_shared", "foo_shared", AttrNameToString{ - "additional_linker_inputs": `[ - "version_script", - "dynamic.list", - ]`, - "linkopts": `[ - "-Wl,--version-script,$(location version_script)", - "-Wl,--dynamic-list,$(location dynamic.list)", - ]`, - }), - }, - }) -} - -func TestCcLibraryLdflagsSplitBySpaceSoongAdded(t *testing.T) { - runCcLibrarySharedTestCase(t, Bp2buildTestCase{ - Description: "ldflags are split by spaces except for the ones added by soong (version script and dynamic list)", - Filesystem: map[string]string{ - "version_script": "", - "dynamic.list": "", - }, - Blueprint: ` -cc_library_shared { - name: "foo", - ldflags: [ - "--nospace_flag", - "-z spaceflag", - ], - version_script: "version_script", - dynamic_list: "dynamic.list", - include_build_directory: false, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ - "additional_linker_inputs": `[ - "version_script", - "dynamic.list", - ]`, - "linkopts": `[ - "--nospace_flag", - "-z", - "spaceflag", - "-Wl,--version-script,$(location version_script)", - "-Wl,--dynamic-list,$(location dynamic.list)", - ]`, - }), - }, - }) -} - -func TestCcLibrarySharedNoCrtTrue(t *testing.T) { - runCcLibrarySharedTestCase(t, Bp2buildTestCase{ - Description: "cc_library_shared - nocrt: true disables feature", - Filesystem: map[string]string{ - "impl.cpp": "", - }, - Blueprint: soongCcLibraryPreamble + ` -cc_library_shared { - name: "foo_shared", - srcs: ["impl.cpp"], - nocrt: true, - include_build_directory: false, -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_shared", "foo_shared", AttrNameToString{ - "features": `["-link_crt"]`, - "srcs": `["impl.cpp"]`, - }), - }, - }) -} - -func TestCcLibrarySharedNoCrtFalse(t *testing.T) { - runCcLibrarySharedTestCase(t, Bp2buildTestCase{ - Description: "cc_library_shared - nocrt: false doesn't disable feature", - Filesystem: map[string]string{ - "impl.cpp": "", - }, - Blueprint: soongCcLibraryPreamble + ` -cc_library_shared { - name: "foo_shared", - srcs: ["impl.cpp"], - nocrt: false, - include_build_directory: false, -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_shared", "foo_shared", AttrNameToString{ - "srcs": `["impl.cpp"]`, - }), - }, - }) -} - -func TestCcLibrarySharedNoCrtArchVariant(t *testing.T) { - runCcLibrarySharedTestCase(t, Bp2buildTestCase{ - Description: "cc_library_shared - nocrt in select", - Filesystem: map[string]string{ - "impl.cpp": "", - }, - Blueprint: soongCcLibraryPreamble + ` -cc_library_shared { - name: "foo_shared", - srcs: ["impl.cpp"], - arch: { - arm: { - nocrt: true, - }, - x86: { - nocrt: false, - }, - }, - include_build_directory: false, -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_shared", "foo_shared", AttrNameToString{ - "features": `select({ - "//build/bazel/platforms/arch:arm": ["-link_crt"], - "//conditions:default": [], - })`, - "srcs": `["impl.cpp"]`, - }), - }, - }) -} - -func TestCcLibrarySharedProto(t *testing.T) { - runCcLibrarySharedTestCase(t, Bp2buildTestCase{ - Blueprint: soongCcProtoPreamble + `cc_library_shared { - name: "foo", - srcs: ["foo.proto"], - proto: { - export_proto_headers: true, - }, - include_build_directory: false, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("proto_library", "foo_proto", AttrNameToString{ - "srcs": `["foo.proto"]`, - }), MakeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", AttrNameToString{ - "deps": `[":foo_proto"]`, - }), MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ - "dynamic_deps": `[":libprotobuf-cpp-lite"]`, - "whole_archive_deps": `[":foo_cc_proto_lite"]`, - }), - }, - }) -} - -func TestCcLibrarySharedUseVersionLib(t *testing.T) { - runCcLibrarySharedTestCase(t, Bp2buildTestCase{ - Filesystem: map[string]string{ - soongCcVersionLibBpPath: soongCcVersionLibBp, - }, - Blueprint: soongCcProtoPreamble + `cc_library_shared { - name: "foo", - use_version_lib: true, - include_build_directory: false, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ - "use_version_lib": "True", - "implementation_whole_archive_deps": `["//build/soong/cc/libbuildversion:libbuildversion"]`, - }), - }, - }) -} - -func TestCcLibrarySharedStubs(t *testing.T) { - runCcLibrarySharedTestCase(t, Bp2buildTestCase{ - Description: "cc_library_shared stubs", - ModuleTypeUnderTest: "cc_library_shared", - ModuleTypeUnderTestFactory: cc.LibrarySharedFactory, - Dir: "foo/bar", - Filesystem: map[string]string{ - "foo/bar/Android.bp": ` -cc_library_shared { - name: "a", - stubs: { symbol_file: "a.map.txt", versions: ["28", "29", "current"] }, - bazel_module: { bp2build_available: true }, - include_build_directory: false, -} -`, - }, - Blueprint: soongCcLibraryPreamble, - ExpectedBazelTargets: []string{makeCcStubSuiteTargets("a", AttrNameToString{ - "soname": `"a.so"`, - "source_library_label": `"//foo/bar:a"`, - "stubs_symbol_file": `"a.map.txt"`, - "stubs_versions": `[ - "28", - "29", - "current", - ]`, - }), - MakeBazelTarget("cc_library_shared", "a", AttrNameToString{ - "stubs_symbol_file": `"a.map.txt"`, - }), - }, - }) -} - -func TestCcLibrarySharedStubs_UseImplementationInSameApex(t *testing.T) { - runCcLibrarySharedTestCase(t, Bp2buildTestCase{ - Description: "cc_library_shared stubs", - ModuleTypeUnderTest: "cc_library_shared", - ModuleTypeUnderTestFactory: cc.LibrarySharedFactory, - Blueprint: soongCcLibrarySharedPreamble + ` -cc_library_shared { - name: "a", - stubs: { symbol_file: "a.map.txt", versions: ["28", "29", "current"] }, - bazel_module: { bp2build_available: false }, - include_build_directory: false, - apex_available: ["made_up_apex"], -} -cc_library_shared { - name: "b", - shared_libs: [":a"], - include_build_directory: false, - apex_available: ["made_up_apex"], -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_shared", "b", AttrNameToString{ - "implementation_dynamic_deps": `[":a"]`, - "tags": `["apex_available=made_up_apex"]`, - }), - }, - }) -} - -func TestCcLibrarySharedStubs_UseStubsInDifferentApex(t *testing.T) { - runCcLibrarySharedTestCase(t, Bp2buildTestCase{ - Description: "cc_library_shared stubs", - ModuleTypeUnderTest: "cc_library_shared", - ModuleTypeUnderTestFactory: cc.LibrarySharedFactory, - Blueprint: soongCcLibrarySharedPreamble + ` -cc_library_shared { - name: "a", - stubs: { symbol_file: "a.map.txt", versions: ["28", "29", "current"] }, - bazel_module: { bp2build_available: false }, - include_build_directory: false, - apex_available: ["apex_a"], -} -cc_library_shared { - name: "b", - shared_libs: [":a"], - include_build_directory: false, - apex_available: ["apex_b"], -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_shared", "b", AttrNameToString{ - "implementation_dynamic_deps": `select({ - "//build/bazel/rules/apex:android-in_apex": ["@api_surfaces//module-libapi/current:a"], - "//conditions:default": [":a"], - })`, - "tags": `["apex_available=apex_b"]`, - }), - }, - }) -} - -func TestCcLibrarySharedStubs_IgnorePlatformAvailable(t *testing.T) { - runCcLibrarySharedTestCase(t, Bp2buildTestCase{ - Description: "cc_library_shared stubs", - ModuleTypeUnderTest: "cc_library_shared", - ModuleTypeUnderTestFactory: cc.LibrarySharedFactory, - Blueprint: soongCcLibrarySharedPreamble + ` -cc_library_shared { - name: "a", - stubs: { symbol_file: "a.map.txt", versions: ["28", "29", "current"] }, - bazel_module: { bp2build_available: false }, - include_build_directory: false, - apex_available: ["//apex_available:platform", "apex_a"], -} -cc_library_shared { - name: "b", - shared_libs: [":a"], - include_build_directory: false, - apex_available: ["//apex_available:platform", "apex_b"], -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_shared", "b", AttrNameToString{ - "implementation_dynamic_deps": `select({ - "//build/bazel/rules/apex:android-in_apex": ["@api_surfaces//module-libapi/current:a"], - "//conditions:default": [":a"], - })`, - "tags": `[ - "apex_available=//apex_available:platform", - "apex_available=apex_b", - ]`, - }), - }, - }) -} - -func TestCcLibrarySharedStubs_MultipleApexAvailable(t *testing.T) { - runCcLibrarySharedTestCase(t, Bp2buildTestCase{ - ModuleTypeUnderTest: "cc_library_shared", - ModuleTypeUnderTestFactory: cc.LibrarySharedFactory, - Blueprint: soongCcLibrarySharedPreamble + ` -cc_library_shared { - name: "a", - stubs: { symbol_file: "a.map.txt", versions: ["28", "29", "current"] }, - bazel_module: { bp2build_available: false }, - include_build_directory: false, - apex_available: ["//apex_available:platform", "apex_a", "apex_b"], -} -cc_library_shared { - name: "b", - shared_libs: [":a"], - include_build_directory: false, - apex_available: ["//apex_available:platform", "apex_b"], -} - -cc_library_shared { - name: "c", - shared_libs: [":a"], - include_build_directory: false, - apex_available: ["//apex_available:platform", "apex_a", "apex_b"], -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_shared", "b", AttrNameToString{ - "implementation_dynamic_deps": `select({ - "//build/bazel/rules/apex:android-in_apex": ["@api_surfaces//module-libapi/current:a"], - "//conditions:default": [":a"], - })`, - "tags": `[ - "apex_available=//apex_available:platform", - "apex_available=apex_b", - ]`, - }), - MakeBazelTarget("cc_library_shared", "c", AttrNameToString{ - "implementation_dynamic_deps": `[":a"]`, - "tags": `[ - "apex_available=//apex_available:platform", - "apex_available=apex_a", - "apex_available=apex_b", - ]`, - }), - }, - }) -} - -func TestCcLibrarySharedSystemSharedLibsSharedEmpty(t *testing.T) { - runCcLibrarySharedTestCase(t, Bp2buildTestCase{ - Description: "cc_library_shared system_shared_libs empty shared default", - ModuleTypeUnderTest: "cc_library_shared", - ModuleTypeUnderTestFactory: cc.LibrarySharedFactory, - Blueprint: soongCcLibrarySharedPreamble + ` -cc_defaults { - name: "empty_defaults", - shared: { - system_shared_libs: [], - }, - include_build_directory: false, -} -cc_library_shared { - name: "empty", - defaults: ["empty_defaults"], -} -`, - ExpectedBazelTargets: []string{MakeBazelTarget("cc_library_shared", "empty", AttrNameToString{ - "system_dynamic_deps": "[]", - })}, - }) -} - -func TestCcLibrarySharedConvertLex(t *testing.T) { - runCcLibrarySharedTestCase(t, Bp2buildTestCase{ - Description: "cc_library_shared with lex files", - ModuleTypeUnderTest: "cc_library_shared", - ModuleTypeUnderTestFactory: cc.LibrarySharedFactory, - Filesystem: map[string]string{ - "foo.c": "", - "bar.cc": "", - "foo1.l": "", - "bar1.ll": "", - "foo2.l": "", - "bar2.ll": "", - }, - Blueprint: `cc_library_shared { - name: "foo_lib", - srcs: ["foo.c", "bar.cc", "foo1.l", "foo2.l", "bar1.ll", "bar2.ll"], - lex: { flags: ["--foo_flags"] }, - include_build_directory: false, - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("genlex", "foo_lib_genlex_l", AttrNameToString{ - "srcs": `[ - "foo1.l", - "foo2.l", - ]`, - "lexopts": `["--foo_flags"]`, - }), - MakeBazelTarget("genlex", "foo_lib_genlex_ll", AttrNameToString{ - "srcs": `[ - "bar1.ll", - "bar2.ll", - ]`, - "lexopts": `["--foo_flags"]`, - }), - MakeBazelTarget("cc_library_shared", "foo_lib", AttrNameToString{ - "srcs": `[ - "bar.cc", - ":foo_lib_genlex_ll", - ]`, - "srcs_c": `[ - "foo.c", - ":foo_lib_genlex_l", - ]`, - }), - }, - }) -} - -func TestCcLibrarySharedClangUnknownFlags(t *testing.T) { - runCcLibrarySharedTestCase(t, Bp2buildTestCase{ - Blueprint: soongCcProtoPreamble + `cc_library_shared { - name: "foo", - conlyflags: ["-a", "-finline-functions"], - cflags: ["-b","-finline-functions"], - cppflags: ["-c", "-finline-functions"], - ldflags: ["-d","-finline-functions", "-e"], - include_build_directory: false, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ - "conlyflags": `["-a"]`, - "copts": `["-b"]`, - "cppflags": `["-c"]`, - "linkopts": `[ - "-d", - "-e", - ]`, - }), - }, - }) -} - -func TestCCLibraryFlagSpaceSplitting(t *testing.T) { - runCcLibrarySharedTestCase(t, Bp2buildTestCase{ - Blueprint: soongCcProtoPreamble + `cc_library_shared { - name: "foo", - conlyflags: [ "-include header.h"], - cflags: ["-include header.h"], - cppflags: ["-include header.h"], - version_script: "version_script", - include_build_directory: false, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ - "additional_linker_inputs": `["version_script"]`, - "conlyflags": `[ - "-include", - "header.h", - ]`, - "copts": `[ - "-include", - "header.h", - ]`, - "cppflags": `[ - "-include", - "header.h", - ]`, - "linkopts": `["-Wl,--version-script,$(location version_script)"]`, - }), - }, - }) -} - -func TestCCLibrarySharedRuntimeDeps(t *testing.T) { - runCcLibrarySharedTestCase(t, Bp2buildTestCase{ - Blueprint: `cc_library_shared { - name: "bar", -} - -cc_library_shared { - name: "foo", - runtime_libs: ["foo"], -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_shared", "bar", AttrNameToString{ - "local_includes": `["."]`, - }), - MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ - "runtime_deps": `[":foo"]`, - "local_includes": `["."]`, - }), - }, - }) -} - -func TestCcLibrarySharedEmptySuffix(t *testing.T) { - runCcLibrarySharedTestCase(t, Bp2buildTestCase{ - Description: "cc_library_shared with empty suffix", - Filesystem: map[string]string{ - "foo.c": "", - }, - Blueprint: soongCcLibrarySharedPreamble + ` -cc_library_shared { - name: "foo_shared", - suffix: "", - srcs: ["foo.c"], - include_build_directory: false, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_shared", "foo_shared", AttrNameToString{ - "srcs_c": `["foo.c"]`, - "suffix": `""`, - }), - }, - }) -} - -func TestCcLibrarySharedSuffix(t *testing.T) { - runCcLibrarySharedTestCase(t, Bp2buildTestCase{ - Description: "cc_library_shared with suffix", - Filesystem: map[string]string{ - "foo.c": "", - }, - Blueprint: soongCcLibrarySharedPreamble + ` -cc_library_shared { - name: "foo_shared", - suffix: "-suf", - srcs: ["foo.c"], - include_build_directory: false, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_shared", "foo_shared", AttrNameToString{ - "srcs_c": `["foo.c"]`, - "suffix": `"-suf"`, - }), - }, - }) -} - -func TestCcLibrarySharedArchVariantSuffix(t *testing.T) { - runCcLibrarySharedTestCase(t, Bp2buildTestCase{ - Description: "cc_library_shared with arch-variant suffix", - Filesystem: map[string]string{ - "foo.c": "", - }, - Blueprint: soongCcLibrarySharedPreamble + ` -cc_library_shared { - name: "foo_shared", - arch: { - arm64: { suffix: "-64" }, - arm: { suffix: "-32" }, - }, - srcs: ["foo.c"], - include_build_directory: false, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_shared", "foo_shared", AttrNameToString{ - "srcs_c": `["foo.c"]`, - "suffix": `select({ - "//build/bazel/platforms/arch:arm": "-32", - "//build/bazel/platforms/arch:arm64": "-64", - "//conditions:default": None, - })`, - }), - }, - }) -} - -func TestCcLibrarySharedWithSyspropSrcs(t *testing.T) { - runCcLibrarySharedTestCase(t, Bp2buildTestCase{ - Description: "cc_library_shared with sysprop sources", - Blueprint: ` -cc_library_shared { - name: "foo", - srcs: [ - "bar.sysprop", - "baz.sysprop", - "blah.cpp", - ], - min_sdk_version: "5", -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("sysprop_library", "foo_sysprop_library", AttrNameToString{ - "srcs": `[ - "bar.sysprop", - "baz.sysprop", - ]`, - }), - MakeBazelTarget("cc_sysprop_library_static", "foo_cc_sysprop_library_static", AttrNameToString{ - "dep": `":foo_sysprop_library"`, - "min_sdk_version": `"5"`, - }), - MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ - "srcs": `["blah.cpp"]`, - "local_includes": `["."]`, - "min_sdk_version": `"5"`, - "whole_archive_deps": `[":foo_cc_sysprop_library_static"]`, - }), - }, - }) -} - -func TestCcLibrarySharedWithSyspropSrcsSomeConfigs(t *testing.T) { - runCcLibrarySharedTestCase(t, Bp2buildTestCase{ - Description: "cc_library_shared with sysprop sources in some configs but not others", - Blueprint: ` -cc_library_shared { - name: "foo", - srcs: [ - "blah.cpp", - ], - target: { - android: { - srcs: ["bar.sysprop"], - }, - }, - min_sdk_version: "5", -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("sysprop_library", "foo_sysprop_library", AttrNameToString{ - "srcs": `select({ - "//build/bazel/platforms/os:android": ["bar.sysprop"], - "//conditions:default": [], - })`, - }), - MakeBazelTarget("cc_sysprop_library_static", "foo_cc_sysprop_library_static", AttrNameToString{ - "dep": `":foo_sysprop_library"`, - "min_sdk_version": `"5"`, - }), - MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ - "srcs": `["blah.cpp"]`, - "local_includes": `["."]`, - "min_sdk_version": `"5"`, - "whole_archive_deps": `select({ - "//build/bazel/platforms/os:android": [":foo_cc_sysprop_library_static"], - "//conditions:default": [], - })`, - }), - }, - }) -} - -func TestCcLibrarySharedHeaderAbiChecker(t *testing.T) { - runCcLibrarySharedTestCase(t, Bp2buildTestCase{ - Description: "cc_library_shared with header abi checker", - Blueprint: `cc_library_shared { - name: "foo", - header_abi_checker: { - enabled: true, - symbol_file: "a.map.txt", - exclude_symbol_versions: [ - "29", - "30", - ], - exclude_symbol_tags: [ - "tag1", - "tag2", - ], - check_all_apis: true, - diff_flags: ["-allow-adding-removing-weak-symbols"], - }, - include_build_directory: false, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ - "abi_checker_enabled": `True`, - "abi_checker_symbol_file": `"a.map.txt"`, - "abi_checker_exclude_symbol_versions": `[ - "29", - "30", - ]`, - "abi_checker_exclude_symbol_tags": `[ - "tag1", - "tag2", - ]`, - "abi_checker_check_all_apis": `True`, - "abi_checker_diff_flags": `["-allow-adding-removing-weak-symbols"]`, - }), - }, - }) -} - -func TestCcLibrarySharedWithIntegerOverflowProperty(t *testing.T) { - runCcLibrarySharedTestCase(t, Bp2buildTestCase{ - Description: "cc_library_shared has correct features when integer_overflow property is provided", - Blueprint: ` -cc_library_shared { - name: "foo", - sanitize: { - integer_overflow: true, - }, -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ - "features": `["ubsan_integer_overflow"]`, - "local_includes": `["."]`, - }), - }, - }) -} - -func TestCcLibrarySharedWithMiscUndefinedProperty(t *testing.T) { - runCcLibrarySharedTestCase(t, Bp2buildTestCase{ - Description: "cc_library_shared has correct features when misc_undefined property is provided", - Blueprint: ` -cc_library_shared { - name: "foo", - sanitize: { - misc_undefined: ["undefined", "nullability"], - }, -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ - "features": `[ - "ubsan_undefined", - "ubsan_nullability", - ]`, - "local_includes": `["."]`, - }), - }, - }) -} - -func TestCcLibrarySharedWithUBSanPropertiesArchSpecific(t *testing.T) { - runCcLibrarySharedTestCase(t, Bp2buildTestCase{ - Description: "cc_library_shared has correct feature select when UBSan props are specified in arch specific blocks", - Blueprint: ` -cc_library_shared { - name: "foo", - sanitize: { - misc_undefined: ["undefined", "nullability"], - }, - target: { - android: { - sanitize: { - misc_undefined: ["alignment"], - }, - }, - linux_glibc: { - sanitize: { - integer_overflow: true, - }, - }, - }, -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ - "features": `[ - "ubsan_undefined", - "ubsan_nullability", - ] + select({ - "//build/bazel/platforms/os:android": ["ubsan_alignment"], - "//build/bazel/platforms/os:linux_glibc": ["ubsan_integer_overflow"], - "//conditions:default": [], - })`, - "local_includes": `["."]`, - }), - }, - }) -} - -func TestCcLibrarySharedWithThinLto(t *testing.T) { - runCcLibrarySharedTestCase(t, Bp2buildTestCase{ - Description: "cc_library_shared has correct features when thin lto is enabled", - Blueprint: ` -cc_library_shared { - name: "foo", - lto: { - thin: true, - }, -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ - "features": `["android_thin_lto"]`, - "local_includes": `["."]`, - }), - }, - }) -} - -func TestCcLibrarySharedWithLtoNever(t *testing.T) { - runCcLibrarySharedTestCase(t, Bp2buildTestCase{ - Description: "cc_library_shared has correct features when thin lto is enabled", - Blueprint: ` -cc_library_shared { - name: "foo", - lto: { - never: true, - }, -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ - "features": `["-android_thin_lto"]`, - "local_includes": `["."]`, - }), - }, - }) -} - -func TestCcLibrarySharedWithThinLtoArchSpecific(t *testing.T) { - runCcLibrarySharedTestCase(t, Bp2buildTestCase{ - Description: "cc_library_shared has correct features when LTO differs across arch and os variants", - Blueprint: ` -cc_library_shared { - name: "foo", - target: { - android: { - lto: { - thin: true, - }, - }, - }, - arch: { - riscv64: { - lto: { - thin: false, - }, - }, - }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ - "local_includes": `["."]`, - "features": `select({ - "//build/bazel/platforms/os_arch:android_arm": ["android_thin_lto"], - "//build/bazel/platforms/os_arch:android_arm64": ["android_thin_lto"], - "//build/bazel/platforms/os_arch:android_riscv64": ["-android_thin_lto"], - "//build/bazel/platforms/os_arch:android_x86": ["android_thin_lto"], - "//build/bazel/platforms/os_arch:android_x86_64": ["android_thin_lto"], - "//conditions:default": [], - })`}), - }, - }) -} - -func TestCcLibrarySharedWithThinLtoDisabledDefaultEnabledVariant(t *testing.T) { - runCcLibrarySharedTestCase(t, Bp2buildTestCase{ - Description: "cc_library_shared with thin lto disabled by default but enabled on a particular variant", - Blueprint: ` -cc_library_shared { - name: "foo", - lto: { - never: true, - }, - target: { - android: { - lto: { - thin: true, - never: false, - }, - }, - }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ - "local_includes": `["."]`, - "features": `select({ - "//build/bazel/platforms/os:android": ["android_thin_lto"], - "//conditions:default": ["-android_thin_lto"], - })`, - }), - }, - }) -} - -func TestCcLibrarySharedWithThinLtoAndWholeProgramVtables(t *testing.T) { - runCcLibrarySharedTestCase(t, Bp2buildTestCase{ - Description: "cc_library_shared has correct features when thin LTO is enabled with whole_program_vtables", - Blueprint: ` -cc_library_shared { - name: "foo", - lto: { - thin: true, - }, - whole_program_vtables: true, -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ - "features": `[ - "android_thin_lto", - "android_thin_lto_whole_program_vtables", - ]`, - "local_includes": `["."]`, - }), - }, - }) -} - -func TestCcLibrarySharedStubsDessertVersionConversion(t *testing.T) { - runCcLibrarySharedTestCase(t, Bp2buildTestCase{ - Description: "cc_library_shared converts dessert codename versions to numerical versions", - Blueprint: ` -cc_library_shared { - name: "a", - include_build_directory: false, - stubs: { - symbol_file: "a.map.txt", - versions: [ - "Q", - "R", - "31", - ], - }, -} -cc_library_shared { - name: "b", - include_build_directory: false, - stubs: { - symbol_file: "b.map.txt", - versions: [ - "Q", - "R", - "31", - "current", - ], - }, -} -`, - ExpectedBazelTargets: []string{ - makeCcStubSuiteTargets("a", AttrNameToString{ - "soname": `"a.so"`, - "source_library_label": `"//:a"`, - "stubs_symbol_file": `"a.map.txt"`, - "stubs_versions": `[ - "29", - "30", - "31", - "current", - ]`, - }), - MakeBazelTarget("cc_library_shared", "a", AttrNameToString{ - "stubs_symbol_file": `"a.map.txt"`, - }), - makeCcStubSuiteTargets("b", AttrNameToString{ - "soname": `"b.so"`, - "source_library_label": `"//:b"`, - "stubs_symbol_file": `"b.map.txt"`, - "stubs_versions": `[ - "29", - "30", - "31", - "current", - ]`, - }), - MakeBazelTarget("cc_library_shared", "b", AttrNameToString{ - "stubs_symbol_file": `"b.map.txt"`, - }), - }, - }) -} diff --git a/bp2build/cc_library_static_conversion_test.go b/bp2build/cc_library_static_conversion_test.go deleted file mode 100644 index cd4cf51a17..0000000000 --- a/bp2build/cc_library_static_conversion_test.go +++ /dev/null @@ -1,2034 +0,0 @@ -// 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 bp2build - -import ( - "fmt" - "testing" - - "android/soong/android" - "android/soong/cc" - "android/soong/genrule" -) - -const ( - // See cc/testing.go for more context - soongCcLibraryStaticPreamble = ` -cc_defaults { - name: "linux_bionic_supported", -}` -) - -func TestCcLibraryStaticLoadStatement(t *testing.T) { - testCases := []struct { - bazelTargets BazelTargets - expectedLoadStatements string - }{ - { - bazelTargets: BazelTargets{ - BazelTarget{ - name: "cc_library_static_target", - ruleClass: "cc_library_static", - // NOTE: No bzlLoadLocation for native rules - }, - }, - expectedLoadStatements: ``, - }, - } - - for _, testCase := range testCases { - actual := testCase.bazelTargets.LoadStatements() - expected := testCase.expectedLoadStatements - if actual != expected { - t.Fatalf("Expected load statements to be %s, got %s", expected, actual) - } - } -} - -func registerCcLibraryStaticModuleTypes(ctx android.RegistrationContext) { - cc.RegisterCCBuildComponents(ctx) - ctx.RegisterModuleType("cc_library_headers", cc.LibraryHeaderFactory) - ctx.RegisterModuleType("genrule", genrule.GenRuleFactory) - // Required for system_shared_libs dependencies. - ctx.RegisterModuleType("cc_library", cc.LibraryFactory) -} - -func runCcLibraryStaticTestCase(t *testing.T, tc Bp2buildTestCase) { - t.Helper() - - (&tc).ModuleTypeUnderTest = "cc_library_static" - (&tc).ModuleTypeUnderTestFactory = cc.LibraryStaticFactory - RunBp2BuildTestCase(t, registerCcLibraryStaticModuleTypes, tc) -} - -func TestCcLibraryStaticSimple(t *testing.T) { - runCcLibraryStaticTestCase(t, Bp2buildTestCase{ - Description: "cc_library_static test", - Filesystem: map[string]string{ - // NOTE: include_dir headers *should not* appear in Bazel hdrs later (?) - "include_dir_1/include_dir_1_a.h": "", - "include_dir_1/include_dir_1_b.h": "", - "include_dir_2/include_dir_2_a.h": "", - "include_dir_2/include_dir_2_b.h": "", - // NOTE: local_include_dir headers *should not* appear in Bazel hdrs later (?) - "local_include_dir_1/local_include_dir_1_a.h": "", - "local_include_dir_1/local_include_dir_1_b.h": "", - "local_include_dir_2/local_include_dir_2_a.h": "", - "local_include_dir_2/local_include_dir_2_b.h": "", - // NOTE: export_include_dir headers *should* appear in Bazel hdrs later - "export_include_dir_1/export_include_dir_1_a.h": "", - "export_include_dir_1/export_include_dir_1_b.h": "", - "export_include_dir_2/export_include_dir_2_a.h": "", - "export_include_dir_2/export_include_dir_2_b.h": "", - // NOTE: Soong implicitly includes headers in the current directory - "implicit_include_1.h": "", - "implicit_include_2.h": "", - }, - Blueprint: soongCcLibraryStaticPreamble + ` -cc_library_headers { - name: "header_lib_1", - export_include_dirs: ["header_lib_1"], - bazel_module: { bp2build_available: false }, -} - -cc_library_headers { - name: "header_lib_2", - export_include_dirs: ["header_lib_2"], - bazel_module: { bp2build_available: false }, -} - -cc_library_static { - name: "static_lib_1", - srcs: ["static_lib_1.cc"], - bazel_module: { bp2build_available: false }, -} - -cc_library_static { - name: "static_lib_2", - srcs: ["static_lib_2.cc"], - bazel_module: { bp2build_available: false }, -} - -cc_library_static { - name: "whole_static_lib_1", - srcs: ["whole_static_lib_1.cc"], - bazel_module: { bp2build_available: false }, -} - -cc_library_static { - name: "whole_static_lib_2", - srcs: ["whole_static_lib_2.cc"], - bazel_module: { bp2build_available: false }, -} - -cc_library_static { - name: "foo_static", - srcs: [ - "foo_static1.cc", - "foo_static2.cc", - ], - cflags: [ - "-Dflag1", - "-Dflag2" - ], - static_libs: [ - "static_lib_1", - "static_lib_2" - ], - whole_static_libs: [ - "whole_static_lib_1", - "whole_static_lib_2" - ], - include_dirs: [ - "include_dir_1", - "include_dir_2", - ], - local_include_dirs: [ - "local_include_dir_1", - "local_include_dir_2", - ], - export_include_dirs: [ - "export_include_dir_1", - "export_include_dir_2" - ], - header_libs: [ - "header_lib_1", - "header_lib_2" - ], - sdk_version: "current", - min_sdk_version: "29", - - // TODO: Also support export_header_lib_headers -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{ - "absolute_includes": `[ - "include_dir_1", - "include_dir_2", - ]`, - "copts": `[ - "-Dflag1", - "-Dflag2", - ]`, - "export_includes": `[ - "export_include_dir_1", - "export_include_dir_2", - ]`, - "implementation_deps": `[ - ":header_lib_1", - ":header_lib_2", - ":static_lib_1", - ":static_lib_2", - ]`, - "local_includes": `[ - "local_include_dir_1", - "local_include_dir_2", - ".", - ]`, - "srcs": `[ - "foo_static1.cc", - "foo_static2.cc", - ]`, - "whole_archive_deps": `[ - ":whole_static_lib_1", - ":whole_static_lib_2", - ]`, - "sdk_version": `"current"`, - "min_sdk_version": `"29"`, - }), - }, - }) -} - -func TestCcLibraryStaticSubpackage(t *testing.T) { - runCcLibraryStaticTestCase(t, Bp2buildTestCase{ - Description: "cc_library_static subpackage test", - Filesystem: map[string]string{ - // subpackage with subdirectory - "subpackage/Android.bp": "", - "subpackage/subpackage_header.h": "", - "subpackage/subdirectory/subdirectory_header.h": "", - // subsubpackage with subdirectory - "subpackage/subsubpackage/Android.bp": "", - "subpackage/subsubpackage/subsubpackage_header.h": "", - "subpackage/subsubpackage/subdirectory/subdirectory_header.h": "", - // subsubsubpackage with subdirectory - "subpackage/subsubpackage/subsubsubpackage/Android.bp": "", - "subpackage/subsubpackage/subsubsubpackage/subsubsubpackage_header.h": "", - "subpackage/subsubpackage/subsubsubpackage/subdirectory/subdirectory_header.h": "", - }, - Blueprint: soongCcLibraryStaticPreamble + ` -cc_library_static { - name: "foo_static", - srcs: [], - include_dirs: [ - "subpackage", - ], -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{ - "absolute_includes": `["subpackage"]`, - "local_includes": `["."]`, - }), - }, - }) -} - -func TestCcLibraryStaticExportIncludeDir(t *testing.T) { - runCcLibraryStaticTestCase(t, Bp2buildTestCase{ - Description: "cc_library_static export include dir", - Filesystem: map[string]string{ - // subpackage with subdirectory - "subpackage/Android.bp": "", - "subpackage/subpackage_header.h": "", - "subpackage/subdirectory/subdirectory_header.h": "", - }, - Blueprint: soongCcLibraryStaticPreamble + ` -cc_library_static { - name: "foo_static", - export_include_dirs: ["subpackage"], - include_build_directory: false, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{ - "export_includes": `["subpackage"]`, - }), - }, - }) -} - -func TestCcLibraryStaticExportSystemIncludeDir(t *testing.T) { - runCcLibraryStaticTestCase(t, Bp2buildTestCase{ - Description: "cc_library_static export system include dir", - Filesystem: map[string]string{ - // subpackage with subdirectory - "subpackage/Android.bp": "", - "subpackage/subpackage_header.h": "", - "subpackage/subdirectory/subdirectory_header.h": "", - }, - Blueprint: soongCcLibraryStaticPreamble + ` -cc_library_static { - name: "foo_static", - export_system_include_dirs: ["subpackage"], - include_build_directory: false, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{ - "export_system_includes": `["subpackage"]`, - }), - }, - }) -} - -func TestCcLibraryStaticManyIncludeDirs(t *testing.T) { - runCcLibraryStaticTestCase(t, Bp2buildTestCase{ - Description: "cc_library_static include_dirs, local_include_dirs, export_include_dirs (b/183742505)", - Dir: "subpackage", - Filesystem: map[string]string{ - // subpackage with subdirectory - "subpackage/Android.bp": ` -cc_library_static { - name: "foo_static", - // include_dirs are workspace/root relative - include_dirs: [ - "subpackage/subsubpackage", - "subpackage2", - "subpackage3/subsubpackage" - ], - local_include_dirs: ["subsubpackage2"], // module dir relative - export_include_dirs: ["./exported_subsubpackage"], // module dir relative - include_build_directory: true, - bazel_module: { bp2build_available: true }, -}`, - "subpackage/subsubpackage/header.h": "", - "subpackage/subsubpackage2/header.h": "", - "subpackage/exported_subsubpackage/header.h": "", - "subpackage2/header.h": "", - "subpackage3/subsubpackage/header.h": "", - }, - Blueprint: soongCcLibraryStaticPreamble, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{ - "absolute_includes": `[ - "subpackage/subsubpackage", - "subpackage2", - "subpackage3/subsubpackage", - ]`, - "export_includes": `["./exported_subsubpackage"]`, - "local_includes": `[ - "subsubpackage2", - ".", - ]`, - })}, - }) -} - -func TestCcLibraryStaticIncludeBuildDirectoryDisabled(t *testing.T) { - runCcLibraryStaticTestCase(t, Bp2buildTestCase{ - Description: "cc_library_static include_build_directory disabled", - Filesystem: map[string]string{ - // subpackage with subdirectory - "subpackage/Android.bp": "", - "subpackage/subpackage_header.h": "", - "subpackage/subdirectory/subdirectory_header.h": "", - }, - Blueprint: soongCcLibraryStaticPreamble + ` -cc_library_static { - name: "foo_static", - include_dirs: ["subpackage"], // still used, but local_include_dirs is recommended - local_include_dirs: ["subpackage2"], - include_build_directory: false, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{ - "absolute_includes": `["subpackage"]`, - "local_includes": `["subpackage2"]`, - }), - }, - }) -} - -func TestCcLibraryStaticIncludeBuildDirectoryEnabled(t *testing.T) { - runCcLibraryStaticTestCase(t, Bp2buildTestCase{ - Description: "cc_library_static include_build_directory enabled", - Filesystem: map[string]string{ - // subpackage with subdirectory - "subpackage/Android.bp": "", - "subpackage/subpackage_header.h": "", - "subpackage2/Android.bp": "", - "subpackage2/subpackage2_header.h": "", - "subpackage/subdirectory/subdirectory_header.h": "", - }, - Blueprint: soongCcLibraryStaticPreamble + ` -cc_library_static { - name: "foo_static", - include_dirs: ["subpackage"], // still used, but local_include_dirs is recommended - local_include_dirs: ["subpackage2"], - include_build_directory: true, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{ - "absolute_includes": `["subpackage"]`, - "local_includes": `[ - "subpackage2", - ".", - ]`, - }), - }, - }) -} - -func TestCcLibraryStaticArchSpecificStaticLib(t *testing.T) { - runCcLibraryStaticTestCase(t, Bp2buildTestCase{ - Description: "cc_library_static arch-specific static_libs", - Filesystem: map[string]string{}, - Blueprint: soongCcLibraryStaticPreamble + ` -cc_library_static { - name: "static_dep", - bazel_module: { bp2build_available: false }, -} -cc_library_static { - name: "static_dep2", - bazel_module: { bp2build_available: false }, -} -cc_library_static { - name: "foo_static", - arch: { arm64: { static_libs: ["static_dep"], whole_static_libs: ["static_dep2"] } }, - include_build_directory: false, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{ - "implementation_deps": `select({ - "//build/bazel/platforms/arch:arm64": [":static_dep"], - "//conditions:default": [], - })`, - "whole_archive_deps": `select({ - "//build/bazel/platforms/arch:arm64": [":static_dep2"], - "//conditions:default": [], - })`, - }), - }, - }) -} - -func TestCcLibraryStaticOsSpecificStaticLib(t *testing.T) { - runCcLibraryStaticTestCase(t, Bp2buildTestCase{ - Description: "cc_library_static os-specific static_libs", - Filesystem: map[string]string{}, - Blueprint: soongCcLibraryStaticPreamble + ` -cc_library_static { - name: "static_dep", - bazel_module: { bp2build_available: false }, -} -cc_library_static { - name: "static_dep2", - bazel_module: { bp2build_available: false }, -} -cc_library_static { - name: "foo_static", - target: { android: { static_libs: ["static_dep"], whole_static_libs: ["static_dep2"] } }, - include_build_directory: false, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{ - "implementation_deps": `select({ - "//build/bazel/platforms/os:android": [":static_dep"], - "//conditions:default": [], - })`, - "whole_archive_deps": `select({ - "//build/bazel/platforms/os:android": [":static_dep2"], - "//conditions:default": [], - })`, - }), - }, - }) -} - -func TestCcLibraryStaticBaseArchOsSpecificStaticLib(t *testing.T) { - runCcLibraryStaticTestCase(t, Bp2buildTestCase{ - Description: "cc_library_static base, arch and os-specific static_libs", - Filesystem: map[string]string{}, - Blueprint: soongCcLibraryStaticPreamble + ` -cc_library_static { - name: "static_dep", - bazel_module: { bp2build_available: false }, -} -cc_library_static { - name: "static_dep2", - bazel_module: { bp2build_available: false }, -} -cc_library_static { - name: "static_dep3", - bazel_module: { bp2build_available: false }, -} -cc_library_static { - name: "static_dep4", - bazel_module: { bp2build_available: false }, -} -cc_library_static { - name: "foo_static", - static_libs: ["static_dep"], - whole_static_libs: ["static_dep2"], - target: { android: { static_libs: ["static_dep3"] } }, - arch: { arm64: { static_libs: ["static_dep4"] } }, - include_build_directory: false, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{ - "implementation_deps": `[":static_dep"] + select({ - "//build/bazel/platforms/arch:arm64": [":static_dep4"], - "//conditions:default": [], - }) + select({ - "//build/bazel/platforms/os:android": [":static_dep3"], - "//conditions:default": [], - })`, - "whole_archive_deps": `[":static_dep2"]`, - }), - }, - }) -} - -func TestCcLibraryStaticSimpleExcludeSrcs(t *testing.T) { - runCcLibraryStaticTestCase(t, Bp2buildTestCase{ - Description: "cc_library_static simple exclude_srcs", - Filesystem: map[string]string{ - "common.c": "", - "foo-a.c": "", - "foo-excluded.c": "", - }, - Blueprint: soongCcLibraryStaticPreamble + ` -cc_library_static { - name: "foo_static", - srcs: ["common.c", "foo-*.c"], - exclude_srcs: ["foo-excluded.c"], - include_build_directory: false, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{ - "srcs_c": `[ - "common.c", - "foo-a.c", - ]`, - }), - }, - }) -} - -func TestCcLibraryStaticOneArchSrcs(t *testing.T) { - runCcLibraryStaticTestCase(t, Bp2buildTestCase{ - Description: "cc_library_static one arch specific srcs", - Filesystem: map[string]string{ - "common.c": "", - "foo-arm.c": "", - }, - Blueprint: soongCcLibraryStaticPreamble + ` -cc_library_static { - name: "foo_static", - srcs: ["common.c"], - arch: { arm: { srcs: ["foo-arm.c"] } }, - include_build_directory: false, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{ - "srcs_c": `["common.c"] + select({ - "//build/bazel/platforms/arch:arm": ["foo-arm.c"], - "//conditions:default": [], - })`, - }), - }, - }) -} - -func TestCcLibraryStaticOneArchSrcsExcludeSrcs(t *testing.T) { - runCcLibraryStaticTestCase(t, Bp2buildTestCase{ - Description: "cc_library_static one arch specific srcs and exclude_srcs", - Filesystem: map[string]string{ - "common.c": "", - "for-arm.c": "", - "not-for-arm.c": "", - "not-for-anything.c": "", - }, - Blueprint: soongCcLibraryStaticPreamble + ` -cc_library_static { - name: "foo_static", - srcs: ["common.c", "not-for-*.c"], - exclude_srcs: ["not-for-anything.c"], - arch: { - arm: { srcs: ["for-arm.c"], exclude_srcs: ["not-for-arm.c"] }, - }, - include_build_directory: false, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{ - "srcs_c": `["common.c"] + select({ - "//build/bazel/platforms/arch:arm": ["for-arm.c"], - "//conditions:default": ["not-for-arm.c"], - })`, - }), - }, - }) -} - -func TestCcLibraryStaticTwoArchExcludeSrcs(t *testing.T) { - runCcLibraryStaticTestCase(t, Bp2buildTestCase{ - Description: "cc_library_static arch specific exclude_srcs for 2 architectures", - Filesystem: map[string]string{ - "common.c": "", - "for-arm.c": "", - "for-x86.c": "", - "not-for-arm.c": "", - "not-for-x86.c": "", - }, - Blueprint: soongCcLibraryStaticPreamble + ` -cc_library_static { - name: "foo_static", - srcs: ["common.c", "not-for-*.c"], - exclude_srcs: ["not-for-everything.c"], - arch: { - arm: { srcs: ["for-arm.c"], exclude_srcs: ["not-for-arm.c"] }, - x86: { srcs: ["for-x86.c"], exclude_srcs: ["not-for-x86.c"] }, - }, - include_build_directory: false, -} `, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{ - "srcs_c": `["common.c"] + select({ - "//build/bazel/platforms/arch:arm": [ - "not-for-x86.c", - "for-arm.c", - ], - "//build/bazel/platforms/arch:x86": [ - "not-for-arm.c", - "for-x86.c", - ], - "//conditions:default": [ - "not-for-arm.c", - "not-for-x86.c", - ], - })`, - }), - }, - }) -} - -func TestCcLibraryStaticFourArchExcludeSrcs(t *testing.T) { - runCcLibraryStaticTestCase(t, Bp2buildTestCase{ - Description: "cc_library_static arch specific exclude_srcs for 4 architectures", - Filesystem: map[string]string{ - "common.c": "", - "for-arm.c": "", - "for-arm64.c": "", - "for-x86.c": "", - "for-x86_64.c": "", - "not-for-arm.c": "", - "not-for-arm64.c": "", - "not-for-x86.c": "", - "not-for-x86_64.c": "", - "not-for-everything.c": "", - }, - Blueprint: soongCcLibraryStaticPreamble + ` -cc_library_static { - name: "foo_static", - srcs: ["common.c", "not-for-*.c"], - exclude_srcs: ["not-for-everything.c"], - arch: { - arm: { srcs: ["for-arm.c"], exclude_srcs: ["not-for-arm.c"] }, - arm64: { srcs: ["for-arm64.c"], exclude_srcs: ["not-for-arm64.c"] }, - x86: { srcs: ["for-x86.c"], exclude_srcs: ["not-for-x86.c"] }, - x86_64: { srcs: ["for-x86_64.c"], exclude_srcs: ["not-for-x86_64.c"] }, - }, - include_build_directory: false, -} `, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{ - "srcs_c": `["common.c"] + select({ - "//build/bazel/platforms/arch:arm": [ - "not-for-arm64.c", - "not-for-x86.c", - "not-for-x86_64.c", - "for-arm.c", - ], - "//build/bazel/platforms/arch:arm64": [ - "not-for-arm.c", - "not-for-x86.c", - "not-for-x86_64.c", - "for-arm64.c", - ], - "//build/bazel/platforms/arch:x86": [ - "not-for-arm.c", - "not-for-arm64.c", - "not-for-x86_64.c", - "for-x86.c", - ], - "//build/bazel/platforms/arch:x86_64": [ - "not-for-arm.c", - "not-for-arm64.c", - "not-for-x86.c", - "for-x86_64.c", - ], - "//conditions:default": [ - "not-for-arm.c", - "not-for-arm64.c", - "not-for-x86.c", - "not-for-x86_64.c", - ], - })`, - }), - }, - }) -} - -func TestCcLibraryStaticOneArchEmpty(t *testing.T) { - runCcLibraryStaticTestCase(t, Bp2buildTestCase{ - Description: "cc_library_static one arch empty", - Filesystem: map[string]string{ - "common.cc": "", - "foo-no-arm.cc": "", - "foo-excluded.cc": "", - }, - Blueprint: soongCcLibraryStaticPreamble + ` -cc_library_static { - name: "foo_static", - srcs: ["common.cc", "foo-*.cc"], - exclude_srcs: ["foo-excluded.cc"], - arch: { - arm: { exclude_srcs: ["foo-no-arm.cc"] }, - }, - include_build_directory: false, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{ - "srcs": `["common.cc"] + select({ - "//build/bazel/platforms/arch:arm": [], - "//conditions:default": ["foo-no-arm.cc"], - })`, - }), - }, - }) -} - -func TestCcLibraryStaticOneArchEmptyOtherSet(t *testing.T) { - runCcLibraryStaticTestCase(t, Bp2buildTestCase{ - Description: "cc_library_static one arch empty other set", - Filesystem: map[string]string{ - "common.cc": "", - "foo-no-arm.cc": "", - "x86-only.cc": "", - "foo-excluded.cc": "", - }, - Blueprint: soongCcLibraryStaticPreamble + ` -cc_library_static { - name: "foo_static", - srcs: ["common.cc", "foo-*.cc"], - exclude_srcs: ["foo-excluded.cc"], - arch: { - arm: { exclude_srcs: ["foo-no-arm.cc"] }, - x86: { srcs: ["x86-only.cc"] }, - }, - include_build_directory: false, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{ - "srcs": `["common.cc"] + select({ - "//build/bazel/platforms/arch:arm": [], - "//build/bazel/platforms/arch:x86": [ - "foo-no-arm.cc", - "x86-only.cc", - ], - "//conditions:default": ["foo-no-arm.cc"], - })`, - }), - }, - }) -} - -func TestCcLibraryStaticMultipleDepSameName(t *testing.T) { - runCcLibraryStaticTestCase(t, Bp2buildTestCase{ - Description: "cc_library_static multiple dep same name panic", - Filesystem: map[string]string{}, - Blueprint: soongCcLibraryStaticPreamble + ` -cc_library_static { - name: "static_dep", - bazel_module: { bp2build_available: false }, -} -cc_library_static { - name: "foo_static", - static_libs: ["static_dep", "static_dep"], - include_build_directory: false, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{ - "implementation_deps": `[":static_dep"]`, - }), - }, - }) -} - -func TestCcLibraryStaticOneMultilibSrcsExcludeSrcs(t *testing.T) { - runCcLibraryStaticTestCase(t, Bp2buildTestCase{ - Description: "cc_library_static 1 multilib srcs and exclude_srcs", - Filesystem: map[string]string{ - "common.c": "", - "for-lib32.c": "", - "not-for-lib32.c": "", - }, - Blueprint: soongCcLibraryStaticPreamble + ` -cc_library_static { - name: "foo_static", - srcs: ["common.c", "not-for-*.c"], - multilib: { - lib32: { srcs: ["for-lib32.c"], exclude_srcs: ["not-for-lib32.c"] }, - }, - include_build_directory: false, -} `, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{ - "srcs_c": `["common.c"] + select({ - "//build/bazel/platforms/arch:arm": ["for-lib32.c"], - "//build/bazel/platforms/arch:x86": ["for-lib32.c"], - "//conditions:default": ["not-for-lib32.c"], - })`, - }), - }, - }) -} - -func TestCcLibraryStaticTwoMultilibSrcsExcludeSrcs(t *testing.T) { - runCcLibraryStaticTestCase(t, Bp2buildTestCase{ - Description: "cc_library_static 2 multilib srcs and exclude_srcs", - Filesystem: map[string]string{ - "common.c": "", - "for-lib32.c": "", - "for-lib64.c": "", - "not-for-lib32.c": "", - "not-for-lib64.c": "", - }, - Blueprint: soongCcLibraryStaticPreamble + ` -cc_library_static { - name: "foo_static", - srcs: ["common.c", "not-for-*.c"], - multilib: { - lib32: { srcs: ["for-lib32.c"], exclude_srcs: ["not-for-lib32.c"] }, - lib64: { srcs: ["for-lib64.c"], exclude_srcs: ["not-for-lib64.c"] }, - }, - include_build_directory: false, -} `, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{ - "srcs_c": `["common.c"] + select({ - "//build/bazel/platforms/arch:arm": [ - "not-for-lib64.c", - "for-lib32.c", - ], - "//build/bazel/platforms/arch:arm64": [ - "not-for-lib32.c", - "for-lib64.c", - ], - "//build/bazel/platforms/arch:riscv64": [ - "not-for-lib32.c", - "for-lib64.c", - ], - "//build/bazel/platforms/arch:x86": [ - "not-for-lib64.c", - "for-lib32.c", - ], - "//build/bazel/platforms/arch:x86_64": [ - "not-for-lib32.c", - "for-lib64.c", - ], - "//conditions:default": [ - "not-for-lib32.c", - "not-for-lib64.c", - ], - })`, - }), - }, - }) -} - -func TestCcLibrarySTaticArchMultilibSrcsExcludeSrcs(t *testing.T) { - runCcLibraryStaticTestCase(t, Bp2buildTestCase{ - Description: "cc_library_static arch and multilib srcs and exclude_srcs", - Filesystem: map[string]string{ - "common.c": "", - "for-arm.c": "", - "for-arm64.c": "", - "for-x86.c": "", - "for-x86_64.c": "", - "for-lib32.c": "", - "for-lib64.c": "", - "not-for-arm.c": "", - "not-for-arm64.c": "", - "not-for-riscv64.c": "", - "not-for-x86.c": "", - "not-for-x86_64.c": "", - "not-for-lib32.c": "", - "not-for-lib64.c": "", - "not-for-everything.c": "", - }, - Blueprint: soongCcLibraryStaticPreamble + ` -cc_library_static { - name: "foo_static", - srcs: ["common.c", "not-for-*.c"], - exclude_srcs: ["not-for-everything.c"], - arch: { - arm: { srcs: ["for-arm.c"], exclude_srcs: ["not-for-arm.c"] }, - arm64: { srcs: ["for-arm64.c"], exclude_srcs: ["not-for-arm64.c"] }, - riscv64: { srcs: ["for-riscv64.c"], exclude_srcs: ["not-for-riscv64.c"] }, - x86: { srcs: ["for-x86.c"], exclude_srcs: ["not-for-x86.c"] }, - x86_64: { srcs: ["for-x86_64.c"], exclude_srcs: ["not-for-x86_64.c"] }, - }, - multilib: { - lib32: { srcs: ["for-lib32.c"], exclude_srcs: ["not-for-lib32.c"] }, - lib64: { srcs: ["for-lib64.c"], exclude_srcs: ["not-for-lib64.c"] }, - }, - include_build_directory: false, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{ - "srcs_c": `["common.c"] + select({ - "//build/bazel/platforms/arch:arm": [ - "not-for-arm64.c", - "not-for-lib64.c", - "not-for-riscv64.c", - "not-for-x86.c", - "not-for-x86_64.c", - "for-arm.c", - "for-lib32.c", - ], - "//build/bazel/platforms/arch:arm64": [ - "not-for-arm.c", - "not-for-lib32.c", - "not-for-riscv64.c", - "not-for-x86.c", - "not-for-x86_64.c", - "for-arm64.c", - "for-lib64.c", - ], - "//build/bazel/platforms/arch:riscv64": [ - "not-for-arm.c", - "not-for-arm64.c", - "not-for-lib32.c", - "not-for-x86.c", - "not-for-x86_64.c", - "for-riscv64.c", - "for-lib64.c", - ], - "//build/bazel/platforms/arch:x86": [ - "not-for-arm.c", - "not-for-arm64.c", - "not-for-lib64.c", - "not-for-riscv64.c", - "not-for-x86_64.c", - "for-x86.c", - "for-lib32.c", - ], - "//build/bazel/platforms/arch:x86_64": [ - "not-for-arm.c", - "not-for-arm64.c", - "not-for-lib32.c", - "not-for-riscv64.c", - "not-for-x86.c", - "for-x86_64.c", - "for-lib64.c", - ], - "//conditions:default": [ - "not-for-arm.c", - "not-for-arm64.c", - "not-for-lib32.c", - "not-for-lib64.c", - "not-for-riscv64.c", - "not-for-x86.c", - "not-for-x86_64.c", - ], - })`, - }), - }, - }) -} - -func TestCcLibraryStaticGeneratedHeadersAllPartitions(t *testing.T) { - runCcLibraryStaticTestCase(t, Bp2buildTestCase{ - Blueprint: soongCcLibraryStaticPreamble + ` -genrule { - name: "generated_hdr", - cmd: "nothing to see here", - bazel_module: { bp2build_available: false }, -} - -genrule { - name: "export_generated_hdr", - cmd: "nothing to see here", - bazel_module: { bp2build_available: false }, -} - -cc_library_static { - name: "foo_static", - srcs: ["cpp_src.cpp", "as_src.S", "c_src.c"], - generated_headers: ["generated_hdr", "export_generated_hdr"], - export_generated_headers: ["export_generated_hdr"], - include_build_directory: false, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{ - "export_includes": `["."]`, - "local_includes": `["."]`, - "hdrs": `[":export_generated_hdr"]`, - "srcs": `[ - "cpp_src.cpp", - ":generated_hdr", - ]`, - "srcs_as": `[ - "as_src.S", - ":generated_hdr", - ]`, - "srcs_c": `[ - "c_src.c", - ":generated_hdr", - ]`, - }), - }, - }) -} - -// generated_headers has "variant_prepend" tag. In bp2build output, -// variant info(select) should go before general info. -func TestCcLibraryStaticArchSrcsExcludeSrcsGeneratedFiles(t *testing.T) { - runCcLibraryStaticTestCase(t, Bp2buildTestCase{ - Description: "cc_library_static arch srcs/exclude_srcs with generated files", - Filesystem: map[string]string{ - "common.cpp": "", - "for-x86.cpp": "", - "not-for-x86.cpp": "", - "not-for-everything.cpp": "", - "dep/Android.bp": simpleModuleDoNotConvertBp2build("genrule", "generated_src_other_pkg") + - simpleModuleDoNotConvertBp2build("genrule", "generated_hdr_other_pkg") + - simpleModuleDoNotConvertBp2build("genrule", "generated_src_other_pkg_x86") + - simpleModuleDoNotConvertBp2build("genrule", "generated_hdr_other_pkg_x86") + - simpleModuleDoNotConvertBp2build("genrule", "generated_hdr_other_pkg_android"), - }, - Blueprint: soongCcLibraryStaticPreamble + - simpleModuleDoNotConvertBp2build("genrule", "generated_src") + - simpleModuleDoNotConvertBp2build("genrule", "generated_src_not_x86") + - simpleModuleDoNotConvertBp2build("genrule", "generated_src_android") + - simpleModuleDoNotConvertBp2build("genrule", "generated_hdr") + ` -cc_library_static { - name: "foo_static", - srcs: ["common.cpp", "not-for-*.cpp"], - exclude_srcs: ["not-for-everything.cpp"], - generated_sources: ["generated_src", "generated_src_other_pkg", "generated_src_not_x86"], - generated_headers: ["generated_hdr", "generated_hdr_other_pkg"], - export_generated_headers: ["generated_hdr_other_pkg"], - arch: { - x86: { - srcs: ["for-x86.cpp"], - exclude_srcs: ["not-for-x86.cpp"], - generated_headers: ["generated_hdr_other_pkg_x86"], - exclude_generated_sources: ["generated_src_not_x86"], - export_generated_headers: ["generated_hdr_other_pkg_x86"], - }, - }, - target: { - android: { - generated_sources: ["generated_src_android"], - generated_headers: ["generated_hdr_other_pkg_android"], - export_generated_headers: ["generated_hdr_other_pkg_android"], - }, - }, - - include_build_directory: false, -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{ - "srcs": `[ - "common.cpp", - ":generated_src", - "//dep:generated_src_other_pkg", - ":generated_hdr", - ] + select({ - "//build/bazel/platforms/arch:x86": ["for-x86.cpp"], - "//conditions:default": [ - "not-for-x86.cpp", - ":generated_src_not_x86", - ], - }) + select({ - "//build/bazel/platforms/os:android": [":generated_src_android"], - "//conditions:default": [], - })`, - "hdrs": `select({ - "//build/bazel/platforms/os:android": ["//dep:generated_hdr_other_pkg_android"], - "//conditions:default": [], - }) + select({ - "//build/bazel/platforms/arch:x86": ["//dep:generated_hdr_other_pkg_x86"], - "//conditions:default": [], - }) + ["//dep:generated_hdr_other_pkg"]`, - "local_includes": `["."]`, - "export_absolute_includes": `["dep"]`, - }), - }, - }) -} - -func TestCcLibraryStaticGetTargetProperties(t *testing.T) { - runCcLibraryStaticTestCase(t, Bp2buildTestCase{ - - Description: "cc_library_static complex GetTargetProperties", - Blueprint: soongCcLibraryStaticPreamble + ` -cc_library_static { - name: "foo_static", - target: { - android: { - srcs: ["android_src.c"], - }, - android_arm: { - srcs: ["android_arm_src.c"], - }, - android_arm64: { - srcs: ["android_arm64_src.c"], - }, - android_x86: { - srcs: ["android_x86_src.c"], - }, - android_x86_64: { - srcs: ["android_x86_64_src.c"], - }, - linux_bionic_arm64: { - srcs: ["linux_bionic_arm64_src.c"], - }, - linux_bionic_x86_64: { - srcs: ["linux_bionic_x86_64_src.c"], - }, - }, - include_build_directory: false, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{ - "srcs_c": `select({ - "//build/bazel/platforms/os:android": ["android_src.c"], - "//conditions:default": [], - }) + select({ - "//build/bazel/platforms/os_arch:android_arm": ["android_arm_src.c"], - "//build/bazel/platforms/os_arch:android_arm64": ["android_arm64_src.c"], - "//build/bazel/platforms/os_arch:android_x86": ["android_x86_src.c"], - "//build/bazel/platforms/os_arch:android_x86_64": ["android_x86_64_src.c"], - "//build/bazel/platforms/os_arch:linux_bionic_arm64": ["linux_bionic_arm64_src.c"], - "//build/bazel/platforms/os_arch:linux_bionic_x86_64": ["linux_bionic_x86_64_src.c"], - "//conditions:default": [], - })`, - }), - }, - }) -} - -func TestCcLibraryStaticProductVariableSelects(t *testing.T) { - runCcLibraryStaticTestCase(t, Bp2buildTestCase{ - Description: "cc_library_static product variable selects", - Blueprint: soongCcLibraryStaticPreamble + ` -cc_library_static { - name: "foo_static", - srcs: ["common.c"], - product_variables: { - malloc_not_svelte: { - cflags: ["-Wmalloc_not_svelte"], - }, - malloc_zero_contents: { - cflags: ["-Wmalloc_zero_contents"], - }, - binder32bit: { - cflags: ["-Wbinder32bit"], - }, - }, - include_build_directory: false, -} `, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{ - "copts": `select({ - "//build/bazel/product_variables:binder32bit": ["-Wbinder32bit"], - "//conditions:default": [], - }) + select({ - "//build/bazel/product_variables:malloc_not_svelte": ["-Wmalloc_not_svelte"], - "//conditions:default": [], - }) + select({ - "//build/bazel/product_variables:malloc_zero_contents": ["-Wmalloc_zero_contents"], - "//conditions:default": [], - })`, - "srcs_c": `["common.c"]`, - }), - }, - }) -} - -func TestCcLibraryStaticProductVariableArchSpecificSelects(t *testing.T) { - runCcLibraryStaticTestCase(t, Bp2buildTestCase{ - Description: "cc_library_static arch-specific product variable selects", - Filesystem: map[string]string{}, - Blueprint: soongCcLibraryStaticPreamble + ` -cc_library_static { - name: "foo_static", - srcs: ["common.c"], - product_variables: { - malloc_not_svelte: { - cflags: ["-Wmalloc_not_svelte"], - }, - }, - arch: { - arm64: { - product_variables: { - malloc_not_svelte: { - cflags: ["-Warm64_malloc_not_svelte"], - }, - }, - }, - }, - multilib: { - lib32: { - product_variables: { - malloc_not_svelte: { - cflags: ["-Wlib32_malloc_not_svelte"], - }, - }, - }, - }, - target: { - android: { - product_variables: { - malloc_not_svelte: { - cflags: ["-Wandroid_malloc_not_svelte"], - }, - }, - } - }, - include_build_directory: false, -} `, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{ - "copts": `select({ - "//build/bazel/product_variables:malloc_not_svelte": ["-Wmalloc_not_svelte"], - "//conditions:default": [], - }) + select({ - "//build/bazel/product_variables:malloc_not_svelte-android": ["-Wandroid_malloc_not_svelte"], - "//conditions:default": [], - }) + select({ - "//build/bazel/product_variables:malloc_not_svelte-arm": ["-Wlib32_malloc_not_svelte"], - "//conditions:default": [], - }) + select({ - "//build/bazel/product_variables:malloc_not_svelte-arm64": ["-Warm64_malloc_not_svelte"], - "//conditions:default": [], - }) + select({ - "//build/bazel/product_variables:malloc_not_svelte-x86": ["-Wlib32_malloc_not_svelte"], - "//conditions:default": [], - })`, - "srcs_c": `["common.c"]`, - }), - }, - }) -} - -func TestCcLibraryStaticProductVariableStringReplacement(t *testing.T) { - runCcLibraryStaticTestCase(t, Bp2buildTestCase{ - Description: "cc_library_static product variable string replacement", - Filesystem: map[string]string{}, - Blueprint: soongCcLibraryStaticPreamble + ` -cc_library_static { - name: "foo_static", - srcs: ["common.S"], - product_variables: { - platform_sdk_version: { - asflags: ["-DPLATFORM_SDK_VERSION=%d"], - }, - }, - include_build_directory: false, -} `, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{ - "asflags": `select({ - "//build/bazel/product_variables:platform_sdk_version": ["-DPLATFORM_SDK_VERSION=$(Platform_sdk_version)"], - "//conditions:default": [], - })`, - "srcs_as": `["common.S"]`, - }), - }, - }) -} - -func TestStaticLibrary_SystemSharedLibsRootEmpty(t *testing.T) { - runCcLibraryStaticTestCase(t, Bp2buildTestCase{ - Description: "cc_library_static system_shared_lib empty root", - Blueprint: soongCcLibraryStaticPreamble + ` -cc_library_static { - name: "root_empty", - system_shared_libs: [], - include_build_directory: false, -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "root_empty", AttrNameToString{ - "system_dynamic_deps": `[]`, - }), - }, - }) -} - -func TestStaticLibrary_SystemSharedLibsStaticEmpty(t *testing.T) { - runCcLibraryStaticTestCase(t, Bp2buildTestCase{ - Description: "cc_library_static system_shared_lib empty static default", - Blueprint: soongCcLibraryStaticPreamble + ` -cc_defaults { - name: "static_empty_defaults", - static: { - system_shared_libs: [], - }, - include_build_directory: false, -} -cc_library_static { - name: "static_empty", - defaults: ["static_empty_defaults"], -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "static_empty", AttrNameToString{ - "system_dynamic_deps": `[]`, - }), - }, - }) -} - -func TestStaticLibrary_SystemSharedLibsBionicEmpty(t *testing.T) { - runCcLibraryStaticTestCase(t, Bp2buildTestCase{ - Description: "cc_library_static system_shared_lib empty for bionic variant", - Blueprint: soongCcLibraryStaticPreamble + ` -cc_library { - name: "libc_musl", - bazel_module: { bp2build_available: false }, -} - -cc_library_static { - name: "target_bionic_empty", - target: { - bionic: { - system_shared_libs: [], - }, - }, - include_build_directory: false, -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "target_bionic_empty", AttrNameToString{ - "system_dynamic_deps": `select({ - "//build/bazel/platforms/os:linux_musl": [":libc_musl"], - "//conditions:default": [], - })`, - }), - }, - }) -} - -func TestStaticLibrary_SystemSharedLibsLinuxBionicEmpty(t *testing.T) { - // Note that this behavior is technically incorrect (it's a simplification). - // The correct behavior would be if bp2build wrote `system_dynamic_deps = []` - // only for linux_bionic, but `android` had `["libc", "libdl", "libm"]. - // b/195791252 tracks the fix. - runCcLibraryStaticTestCase(t, Bp2buildTestCase{ - Description: "cc_library_static system_shared_lib empty for linux_bionic variant", - Blueprint: soongCcLibraryStaticPreamble + ` -cc_library { - name: "libc_musl", - bazel_module: { bp2build_available: false }, -} - -cc_library_static { - name: "target_linux_bionic_empty", - target: { - linux_bionic: { - system_shared_libs: [], - }, - }, - include_build_directory: false, -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "target_linux_bionic_empty", AttrNameToString{ - "system_dynamic_deps": `select({ - "//build/bazel/platforms/os:linux_musl": [":libc_musl"], - "//conditions:default": [], - })`, - }), - }, - }) -} - -func TestStaticLibrary_SystemSharedLibsMuslEmpty(t *testing.T) { - runCcLibraryStaticTestCase(t, Bp2buildTestCase{ - Description: "cc_library_static system_shared_lib empty for musl variant", - Blueprint: soongCcLibraryStaticPreamble + ` -cc_library { - name: "libc_musl", - bazel_module: { bp2build_available: false }, -} - -cc_library_static { - name: "target_musl_empty", - target: { - musl: { - system_shared_libs: [], - }, - }, - include_build_directory: false, -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "target_musl_empty", AttrNameToString{ - "system_dynamic_deps": `[]`, - }), - }, - }) -} - -func TestStaticLibrary_SystemSharedLibsLinuxMuslEmpty(t *testing.T) { - runCcLibraryStaticTestCase(t, Bp2buildTestCase{ - Description: "cc_library_static system_shared_lib empty for linux_musl variant", - Blueprint: soongCcLibraryStaticPreamble + ` -cc_library { - name: "libc_musl", - bazel_module: { bp2build_available: false }, -} - -cc_library_static { - name: "target_linux_musl_empty", - target: { - linux_musl: { - system_shared_libs: [], - }, - }, - include_build_directory: false, -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "target_linux_musl_empty", AttrNameToString{ - "system_dynamic_deps": `[]`, - }), - }, - }) -} - -func TestStaticLibrary_SystemSharedLibsBionic(t *testing.T) { - runCcLibraryStaticTestCase(t, Bp2buildTestCase{ - Description: "cc_library_static system_shared_libs set for bionic variant", - Blueprint: soongCcLibraryStaticPreamble + - simpleModuleDoNotConvertBp2build("cc_library", "libc") + ` -cc_library { - name: "libc_musl", - bazel_module: { bp2build_available: false }, -} - -cc_library_static { - name: "target_bionic", - target: { - bionic: { - system_shared_libs: ["libc"], - }, - }, - include_build_directory: false, -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "target_bionic", AttrNameToString{ - "system_dynamic_deps": `select({ - "//build/bazel/platforms/os:android": [":libc"], - "//build/bazel/platforms/os:linux_bionic": [":libc"], - "//build/bazel/platforms/os:linux_musl": [":libc_musl"], - "//conditions:default": [], - })`, - }), - }, - }) -} - -func TestStaticLibrary_SystemSharedLibsLinuxRootAndLinuxBionic(t *testing.T) { - runCcLibraryStaticTestCase(t, Bp2buildTestCase{ - Description: "cc_library_static system_shared_libs set for root and linux_bionic variant", - Blueprint: soongCcLibraryStaticPreamble + - simpleModuleDoNotConvertBp2build("cc_library", "libc") + - simpleModuleDoNotConvertBp2build("cc_library", "libm") + ` -cc_library { - name: "libc_musl", - bazel_module: { bp2build_available: false }, -} - -cc_library_static { - name: "target_linux_bionic", - system_shared_libs: ["libc"], - target: { - linux_bionic: { - system_shared_libs: ["libm"], - }, - }, - include_build_directory: false, -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "target_linux_bionic", AttrNameToString{ - "system_dynamic_deps": `[":libc"] + select({ - "//build/bazel/platforms/os:linux_bionic": [":libm"], - "//build/bazel/platforms/os:linux_musl": [":libc_musl"], - "//conditions:default": [], - })`, - }), - }, - }) -} - -func TestCcLibrarystatic_SystemSharedLibUsedAsDep(t *testing.T) { - runCcLibraryStaticTestCase(t, Bp2buildTestCase{ - Description: "cc_library_static system_shared_lib empty for linux_bionic variant", - Blueprint: soongCcLibraryStaticPreamble + - simpleModuleDoNotConvertBp2build("cc_library", "libc") + ` - -cc_library { - name: "libm", - stubs: { - symbol_file: "libm.map.txt", - versions: ["current"], - }, - bazel_module: { bp2build_available: false }, -} - -cc_library_static { - name: "used_in_bionic_oses", - target: { - android: { - shared_libs: ["libc"], - }, - linux_bionic: { - shared_libs: ["libc"], - }, - }, - include_build_directory: false, - apex_available: ["foo"], -} - -cc_library_static { - name: "all", - shared_libs: ["libc"], - include_build_directory: false, - apex_available: ["foo"], -} - -cc_library_static { - name: "keep_for_empty_system_shared_libs", - shared_libs: ["libc"], - system_shared_libs: [], - include_build_directory: false, - apex_available: ["foo"], -} - -cc_library_static { - name: "used_with_stubs", - shared_libs: ["libm"], - include_build_directory: false, - apex_available: ["foo"], -} - -cc_library_static { - name: "keep_with_stubs", - shared_libs: ["libm"], - system_shared_libs: [], - include_build_directory: false, - apex_available: ["foo"], -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "all", AttrNameToString{ - "tags": `["apex_available=foo"]`, - }), - MakeBazelTarget("cc_library_static", "keep_for_empty_system_shared_libs", AttrNameToString{ - "implementation_dynamic_deps": `[":libc"]`, - "system_dynamic_deps": `[]`, - "tags": `["apex_available=foo"]`, - }), - MakeBazelTarget("cc_library_static", "keep_with_stubs", AttrNameToString{ - "implementation_dynamic_deps": `select({ - "//build/bazel/rules/apex:android-in_apex": ["@api_surfaces//module-libapi/current:libm"], - "//conditions:default": [":libm"], - })`, - "system_dynamic_deps": `[]`, - "tags": `["apex_available=foo"]`, - }), - MakeBazelTarget("cc_library_static", "used_in_bionic_oses", AttrNameToString{ - "tags": `["apex_available=foo"]`, - }), - MakeBazelTarget("cc_library_static", "used_with_stubs", AttrNameToString{ - "tags": `["apex_available=foo"]`, - }), - }, - }) -} - -func TestCcLibraryStaticProto(t *testing.T) { - runCcLibraryStaticTestCase(t, Bp2buildTestCase{ - Blueprint: soongCcProtoPreamble + `cc_library_static { - name: "foo", - srcs: ["foo.proto"], - proto: { - export_proto_headers: true, - }, - include_build_directory: false, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("proto_library", "foo_proto", AttrNameToString{ - "srcs": `["foo.proto"]`, - }), MakeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", AttrNameToString{ - "deps": `[":foo_proto"]`, - }), MakeBazelTarget("cc_library_static", "foo", AttrNameToString{ - "deps": `[":libprotobuf-cpp-lite"]`, - "whole_archive_deps": `[":foo_cc_proto_lite"]`, - }), - }, - }) -} - -func TestCcLibraryStaticUseVersionLib(t *testing.T) { - runCcLibraryStaticTestCase(t, Bp2buildTestCase{ - Filesystem: map[string]string{ - soongCcVersionLibBpPath: soongCcVersionLibBp, - }, - Blueprint: soongCcProtoPreamble + `cc_library_static { - name: "foo", - use_version_lib: true, - static_libs: ["libbuildversion"], - include_build_directory: false, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo", AttrNameToString{ - "implementation_whole_archive_deps": `["//build/soong/cc/libbuildversion:libbuildversion"]`, - }), - }, - }) -} - -func TestCcLibraryStaticUseVersionLibHasDep(t *testing.T) { - runCcLibraryStaticTestCase(t, Bp2buildTestCase{ - Filesystem: map[string]string{ - soongCcVersionLibBpPath: soongCcVersionLibBp, - }, - Blueprint: soongCcProtoPreamble + `cc_library_static { - name: "foo", - use_version_lib: true, - whole_static_libs: ["libbuildversion"], - include_build_directory: false, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo", AttrNameToString{ - "whole_archive_deps": `["//build/soong/cc/libbuildversion:libbuildversion"]`, - }), - }, - }) -} - -func TestCcLibraryStaticStdInFlags(t *testing.T) { - runCcLibraryStaticTestCase(t, Bp2buildTestCase{ - Blueprint: soongCcProtoPreamble + `cc_library_static { - name: "foo", - cflags: ["-std=candcpp"], - conlyflags: ["-std=conly"], - cppflags: ["-std=cpp"], - include_build_directory: false, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo", AttrNameToString{ - "conlyflags": `["-std=conly"]`, - "cppflags": `["-std=cpp"]`, - }), - }, - }) -} - -func TestCcLibraryStaticStl(t *testing.T) { - testCases := []struct { - desc string - prop string - attr AttrNameToString - }{ - { - desc: "c++_shared deduped to libc++", - prop: `stl: "c++_shared",`, - attr: AttrNameToString{ - "stl": `"libc++"`, - }, - }, - { - desc: "libc++ to libc++", - prop: `stl: "libc++",`, - attr: AttrNameToString{ - "stl": `"libc++"`, - }, - }, - { - desc: "c++_static to libc++_static", - prop: `stl: "c++_static",`, - attr: AttrNameToString{ - "stl": `"libc++_static"`, - }, - }, - { - desc: "libc++_static to libc++_static", - prop: `stl: "libc++_static",`, - attr: AttrNameToString{ - "stl": `"libc++_static"`, - }, - }, - { - desc: "system to system", - prop: `stl: "system",`, - attr: AttrNameToString{ - "stl": `"system"`, - }, - }, - { - desc: "none to none", - prop: `stl: "none",`, - attr: AttrNameToString{ - "stl": `"none"`, - }, - }, - { - desc: "empty to empty", - attr: AttrNameToString{}, - }, - } - for _, tc := range testCases { - t.Run(tc.desc, func(*testing.T) { - runCcLibraryStaticTestCase(t, Bp2buildTestCase{ - Blueprint: fmt.Sprintf(`cc_library_static { - name: "foo", - include_build_directory: false, - %s -}`, tc.prop), - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo", tc.attr), - }, - }) - }) - } -} - -func TestCCLibraryStaticRuntimeDeps(t *testing.T) { - runCcLibrarySharedTestCase(t, Bp2buildTestCase{ - Blueprint: `cc_library_shared { - name: "bar", -} - -cc_library_static { - name: "foo", - runtime_libs: ["foo"], -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_shared", "bar", AttrNameToString{ - "local_includes": `["."]`, - }), - MakeBazelTarget("cc_library_static", "foo", AttrNameToString{ - "runtime_deps": `[":foo"]`, - "local_includes": `["."]`, - }), - }, - }) -} - -func TestCcLibraryStaticWithSyspropSrcs(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library_static with sysprop sources", - Blueprint: ` -cc_library_static { - name: "foo", - srcs: [ - "bar.sysprop", - "baz.sysprop", - "blah.cpp", - ], - min_sdk_version: "5", -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("sysprop_library", "foo_sysprop_library", AttrNameToString{ - "srcs": `[ - "bar.sysprop", - "baz.sysprop", - ]`, - }), - MakeBazelTarget("cc_sysprop_library_static", "foo_cc_sysprop_library_static", AttrNameToString{ - "dep": `":foo_sysprop_library"`, - "min_sdk_version": `"5"`, - }), - MakeBazelTarget("cc_library_static", "foo", AttrNameToString{ - "srcs": `["blah.cpp"]`, - "local_includes": `["."]`, - "min_sdk_version": `"5"`, - "whole_archive_deps": `[":foo_cc_sysprop_library_static"]`, - }), - }, - }) -} - -func TestCcLibraryStaticWithSyspropSrcsSomeConfigs(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library_static with sysprop sources in some configs but not others", - Blueprint: ` -cc_library_static { - name: "foo", - srcs: [ - "blah.cpp", - ], - target: { - android: { - srcs: ["bar.sysprop"], - }, - }, - min_sdk_version: "5", -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("sysprop_library", "foo_sysprop_library", AttrNameToString{ - "srcs": `select({ - "//build/bazel/platforms/os:android": ["bar.sysprop"], - "//conditions:default": [], - })`, - }), - MakeBazelTarget("cc_sysprop_library_static", "foo_cc_sysprop_library_static", AttrNameToString{ - "dep": `":foo_sysprop_library"`, - "min_sdk_version": `"5"`, - }), - MakeBazelTarget("cc_library_static", "foo", AttrNameToString{ - "srcs": `["blah.cpp"]`, - "local_includes": `["."]`, - "min_sdk_version": `"5"`, - "whole_archive_deps": `select({ - "//build/bazel/platforms/os:android": [":foo_cc_sysprop_library_static"], - "//conditions:default": [], - })`, - }), - }, - }) -} - -func TestCcLibraryStaticWithIntegerOverflowProperty(t *testing.T) { - runCcLibraryStaticTestCase(t, Bp2buildTestCase{ - Description: "cc_library_static has correct features when integer_overflow property is provided", - Blueprint: ` -cc_library_static { - name: "foo", - sanitize: { - integer_overflow: true, - }, -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo", AttrNameToString{ - "features": `["ubsan_integer_overflow"]`, - "local_includes": `["."]`, - }), - }, - }) -} - -func TestCcLibraryStaticWithMiscUndefinedProperty(t *testing.T) { - runCcLibraryStaticTestCase(t, Bp2buildTestCase{ - Description: "cc_library_static has correct features when misc_undefined property is provided", - Blueprint: ` -cc_library_static { - name: "foo", - sanitize: { - misc_undefined: ["undefined", "nullability"], - }, -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo", AttrNameToString{ - "features": `[ - "ubsan_undefined", - "ubsan_nullability", - ]`, - "local_includes": `["."]`, - }), - }, - }) -} - -func TestCcLibraryStaticWithUBSanPropertiesArchSpecific(t *testing.T) { - runCcLibraryStaticTestCase(t, Bp2buildTestCase{ - Description: "cc_library_static has correct feature select when UBSan props are specified in arch specific blocks", - Blueprint: ` -cc_library_static { - name: "foo", - sanitize: { - misc_undefined: ["undefined", "nullability"], - }, - target: { - android: { - sanitize: { - misc_undefined: ["alignment"], - }, - }, - linux_glibc: { - sanitize: { - integer_overflow: true, - }, - }, - }, -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo", AttrNameToString{ - "features": `[ - "ubsan_undefined", - "ubsan_nullability", - ] + select({ - "//build/bazel/platforms/os:android": ["ubsan_alignment"], - "//build/bazel/platforms/os:linux_glibc": ["ubsan_integer_overflow"], - "//conditions:default": [], - })`, - "local_includes": `["."]`, - }), - }, - }) -} - -func TestCcLibraryStaticWithThinLto(t *testing.T) { - runCcLibraryStaticTestCase(t, Bp2buildTestCase{ - Description: "cc_library_static has correct features when thin lto is enabled", - Blueprint: ` -cc_library_static { - name: "foo", - lto: { - thin: true, - }, -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo", AttrNameToString{ - "features": `["android_thin_lto"]`, - "local_includes": `["."]`, - }), - }, - }) -} - -func TestCcLibraryStaticWithLtoNever(t *testing.T) { - runCcLibraryStaticTestCase(t, Bp2buildTestCase{ - Description: "cc_library_static has correct features when thin lto is enabled", - Blueprint: ` -cc_library_static { - name: "foo", - lto: { - never: true, - }, -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo", AttrNameToString{ - "features": `["-android_thin_lto"]`, - "local_includes": `["."]`, - }), - }, - }) -} - -func TestCcLibraryStaticWithThinLtoArchSpecific(t *testing.T) { - runCcLibraryStaticTestCase(t, Bp2buildTestCase{ - Description: "cc_library_static has correct features when LTO differs across arch and os variants", - Blueprint: ` -cc_library_static { - name: "foo", - target: { - android: { - lto: { - thin: true, - }, - }, - }, - arch: { - riscv64: { - lto: { - thin: false, - }, - }, - }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo", AttrNameToString{ - "local_includes": `["."]`, - "features": `select({ - "//build/bazel/platforms/os_arch:android_arm": ["android_thin_lto"], - "//build/bazel/platforms/os_arch:android_arm64": ["android_thin_lto"], - "//build/bazel/platforms/os_arch:android_riscv64": ["-android_thin_lto"], - "//build/bazel/platforms/os_arch:android_x86": ["android_thin_lto"], - "//build/bazel/platforms/os_arch:android_x86_64": ["android_thin_lto"], - "//conditions:default": [], - })`}), - }, - }) -} - -func TestCcLibraryStaticWithThinLtoDisabledDefaultEnabledVariant(t *testing.T) { - runCcLibraryStaticTestCase(t, Bp2buildTestCase{ - Description: "cc_library_static has correct features when LTO disabled by default but enabled on a particular variant", - Blueprint: ` -cc_library_static { - name: "foo", - lto: { - never: true, - }, - target: { - android: { - lto: { - thin: true, - never: false, - }, - }, - }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo", AttrNameToString{ - "local_includes": `["."]`, - "features": `select({ - "//build/bazel/platforms/os:android": ["android_thin_lto"], - "//conditions:default": ["-android_thin_lto"], - })`, - }), - }, - }) -} - -func TestCcLibraryStaticWithThinLtoAndWholeProgramVtables(t *testing.T) { - runCcLibraryStaticTestCase(t, Bp2buildTestCase{ - Description: "cc_library_static has correct features when thin lto is enabled with whole_program_vtables", - Blueprint: ` -cc_library_static { - name: "foo", - lto: { - thin: true, - }, - whole_program_vtables: true, -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo", AttrNameToString{ - "features": `[ - "android_thin_lto", - "android_thin_lto_whole_program_vtables", - ]`, - "local_includes": `["."]`, - }), - }, - }) -} diff --git a/bp2build/cc_object_conversion_test.go b/bp2build/cc_object_conversion_test.go deleted file mode 100644 index eab84e1521..0000000000 --- a/bp2build/cc_object_conversion_test.go +++ /dev/null @@ -1,480 +0,0 @@ -// 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 bp2build - -import ( - "testing" - - "android/soong/android" - "android/soong/cc" -) - -func registerCcObjectModuleTypes(ctx android.RegistrationContext) { - // Always register cc_defaults module factory - ctx.RegisterModuleType("cc_defaults", func() android.Module { return cc.DefaultsFactory() }) - ctx.RegisterModuleType("cc_library_headers", cc.LibraryHeaderFactory) -} - -func runCcObjectTestCase(t *testing.T, tc Bp2buildTestCase) { - t.Helper() - (&tc).ModuleTypeUnderTest = "cc_object" - (&tc).ModuleTypeUnderTestFactory = cc.ObjectFactory - RunBp2BuildTestCase(t, registerCcObjectModuleTypes, tc) -} - -func TestCcObjectSimple(t *testing.T) { - runCcObjectTestCase(t, Bp2buildTestCase{ - Description: "simple cc_object generates cc_object with include header dep", - Filesystem: map[string]string{ - "a/b/foo.h": "", - "a/b/bar.h": "", - "a/b/exclude.c": "", - "a/b/c.c": "", - }, - Blueprint: `cc_object { - name: "foo", - local_include_dirs: ["include"], - system_shared_libs: [], - cflags: [ - "-Wno-gcc-compat", - "-Wall", - "-Werror", - ], - srcs: [ - "a/b/*.c" - ], - exclude_srcs: ["a/b/exclude.c"], - sdk_version: "current", - min_sdk_version: "29", - crt: true, -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_object", "foo", AttrNameToString{ - "copts": `[ - "-fno-addrsig", - "-Wno-gcc-compat", - "-Wall", - "-Werror", - ]`, - "local_includes": `[ - "include", - ".", - ]`, - "srcs": `["a/b/c.c"]`, - "system_dynamic_deps": `[]`, - "sdk_version": `"current"`, - "min_sdk_version": `"29"`, - "crt": "True", - }), - }, - }) -} - -func TestCcObjectDefaults(t *testing.T) { - runCcObjectTestCase(t, Bp2buildTestCase{ - Blueprint: `cc_object { - name: "foo", - system_shared_libs: [], - srcs: [ - "a/b/*.h", - "a/b/c.c" - ], - - defaults: ["foo_defaults"], -} - -cc_defaults { - name: "foo_defaults", - defaults: ["foo_bar_defaults"], -} - -cc_defaults { - name: "foo_bar_defaults", - cflags: [ - "-Werror", - ], -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_object", "foo", AttrNameToString{ - "copts": `[ - "-Werror", - "-fno-addrsig", - ]`, - "local_includes": `["."]`, - "srcs": `["a/b/c.c"]`, - "system_dynamic_deps": `[]`, - }), - }}) -} - -func TestCcObjectCcObjetDepsInObjs(t *testing.T) { - runCcObjectTestCase(t, Bp2buildTestCase{ - Description: "cc_object with cc_object deps in objs props", - Filesystem: map[string]string{ - "a/b/c.c": "", - "x/y/z.c": "", - }, - Blueprint: `cc_object { - name: "foo", - system_shared_libs: [], - srcs: ["a/b/c.c"], - objs: ["bar"], - include_build_directory: false, -} - -cc_object { - name: "bar", - system_shared_libs: [], - srcs: ["x/y/z.c"], - include_build_directory: false, -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_object", "bar", AttrNameToString{ - "copts": `["-fno-addrsig"]`, - "srcs": `["x/y/z.c"]`, - "system_dynamic_deps": `[]`, - }), MakeBazelTarget("cc_object", "foo", AttrNameToString{ - "copts": `["-fno-addrsig"]`, - "objs": `[":bar"]`, - "srcs": `["a/b/c.c"]`, - "system_dynamic_deps": `[]`, - }), - }, - }) -} - -func TestCcObjectIncludeBuildDirFalse(t *testing.T) { - runCcObjectTestCase(t, Bp2buildTestCase{ - Description: "cc_object with include_build_dir: false", - Filesystem: map[string]string{ - "a/b/c.c": "", - "x/y/z.c": "", - }, - Blueprint: `cc_object { - name: "foo", - system_shared_libs: [], - srcs: ["a/b/c.c"], - include_build_directory: false, -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_object", "foo", AttrNameToString{ - "copts": `["-fno-addrsig"]`, - "srcs": `["a/b/c.c"]`, - "system_dynamic_deps": `[]`, - }), - }, - }) -} - -func TestCcObjectProductVariable(t *testing.T) { - runCcObjectTestCase(t, Bp2buildTestCase{ - Description: "cc_object with product variable", - Blueprint: `cc_object { - name: "foo", - system_shared_libs: [], - include_build_directory: false, - product_variables: { - platform_sdk_version: { - asflags: ["-DPLATFORM_SDK_VERSION=%d"], - }, - }, - srcs: ["src.S"], -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_object", "foo", AttrNameToString{ - "asflags": `select({ - "//build/bazel/product_variables:platform_sdk_version": ["-DPLATFORM_SDK_VERSION=$(Platform_sdk_version)"], - "//conditions:default": [], - })`, - "copts": `["-fno-addrsig"]`, - "srcs_as": `["src.S"]`, - "system_dynamic_deps": `[]`, - }), - }, - }) -} - -func TestCcObjectCflagsOneArch(t *testing.T) { - runCcObjectTestCase(t, Bp2buildTestCase{ - Description: "cc_object setting cflags for one arch", - Blueprint: `cc_object { - name: "foo", - system_shared_libs: [], - srcs: ["a.cpp"], - arch: { - x86: { - cflags: ["-fPIC"], // string list - }, - arm: { - srcs: ["arch/arm/file.cpp"], // label list - }, - }, - include_build_directory: false, -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_object", "foo", AttrNameToString{ - "copts": `["-fno-addrsig"] + select({ - "//build/bazel/platforms/arch:x86": ["-fPIC"], - "//conditions:default": [], - })`, - "srcs": `["a.cpp"] + select({ - "//build/bazel/platforms/arch:arm": ["arch/arm/file.cpp"], - "//conditions:default": [], - })`, - "system_dynamic_deps": `[]`, - }), - }, - }) -} - -func TestCcObjectCflagsFourArch(t *testing.T) { - runCcObjectTestCase(t, Bp2buildTestCase{ - Description: "cc_object setting cflags for 4 architectures", - Blueprint: `cc_object { - name: "foo", - system_shared_libs: [], - srcs: ["base.cpp"], - arch: { - x86: { - srcs: ["x86.cpp"], - cflags: ["-fPIC"], - }, - x86_64: { - srcs: ["x86_64.cpp"], - cflags: ["-fPIC"], - }, - arm: { - srcs: ["arm.cpp"], - cflags: ["-Wall"], - }, - arm64: { - srcs: ["arm64.cpp"], - cflags: ["-Wall"], - }, - }, - include_build_directory: false, -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_object", "foo", AttrNameToString{ - "copts": `["-fno-addrsig"] + select({ - "//build/bazel/platforms/arch:arm": ["-Wall"], - "//build/bazel/platforms/arch:arm64": ["-Wall"], - "//build/bazel/platforms/arch:x86": ["-fPIC"], - "//build/bazel/platforms/arch:x86_64": ["-fPIC"], - "//conditions:default": [], - })`, - "srcs": `["base.cpp"] + select({ - "//build/bazel/platforms/arch:arm": ["arm.cpp"], - "//build/bazel/platforms/arch:arm64": ["arm64.cpp"], - "//build/bazel/platforms/arch:x86": ["x86.cpp"], - "//build/bazel/platforms/arch:x86_64": ["x86_64.cpp"], - "//conditions:default": [], - })`, - "system_dynamic_deps": `[]`, - }), - }, - }) -} - -func TestCcObjectLinkerScript(t *testing.T) { - runCcObjectTestCase(t, Bp2buildTestCase{ - Description: "cc_object setting linker_script", - Blueprint: `cc_object { - name: "foo", - srcs: ["base.cpp"], - linker_script: "bunny.lds", - include_build_directory: false, -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_object", "foo", AttrNameToString{ - "copts": `["-fno-addrsig"]`, - "linker_script": `"bunny.lds"`, - "srcs": `["base.cpp"]`, - }), - }, - }) -} - -func TestCcObjectDepsAndLinkerScriptSelects(t *testing.T) { - runCcObjectTestCase(t, Bp2buildTestCase{ - Description: "cc_object setting deps and linker_script across archs", - Blueprint: `cc_object { - name: "foo", - srcs: ["base.cpp"], - arch: { - x86: { - objs: ["x86_obj"], - linker_script: "x86.lds", - }, - x86_64: { - objs: ["x86_64_obj"], - linker_script: "x86_64.lds", - }, - arm: { - objs: ["arm_obj"], - linker_script: "arm.lds", - }, - }, - include_build_directory: false, -} - -cc_object { - name: "x86_obj", - system_shared_libs: [], - srcs: ["x86.cpp"], - include_build_directory: false, - bazel_module: { bp2build_available: false }, -} - -cc_object { - name: "x86_64_obj", - system_shared_libs: [], - srcs: ["x86_64.cpp"], - include_build_directory: false, - bazel_module: { bp2build_available: false }, -} - -cc_object { - name: "arm_obj", - system_shared_libs: [], - srcs: ["arm.cpp"], - include_build_directory: false, - bazel_module: { bp2build_available: false }, -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_object", "foo", AttrNameToString{ - "copts": `["-fno-addrsig"]`, - "objs": `select({ - "//build/bazel/platforms/arch:arm": [":arm_obj"], - "//build/bazel/platforms/arch:x86": [":x86_obj"], - "//build/bazel/platforms/arch:x86_64": [":x86_64_obj"], - "//conditions:default": [], - })`, - "linker_script": `select({ - "//build/bazel/platforms/arch:arm": "arm.lds", - "//build/bazel/platforms/arch:x86": "x86.lds", - "//build/bazel/platforms/arch:x86_64": "x86_64.lds", - "//conditions:default": None, - })`, - "srcs": `["base.cpp"]`, - }), - }, - }) -} - -func TestCcObjectSelectOnLinuxAndBionicArchs(t *testing.T) { - runCcObjectTestCase(t, Bp2buildTestCase{ - Description: "cc_object setting srcs based on linux and bionic archs", - Blueprint: `cc_object { - name: "foo", - srcs: ["base.cpp"], - target: { - linux_arm64: { - srcs: ["linux_arm64.cpp",] - }, - linux_x86: { - srcs: ["linux_x86.cpp",] - }, - bionic_arm64: { - srcs: ["bionic_arm64.cpp",] - }, - }, - include_build_directory: false, -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_object", "foo", AttrNameToString{ - "copts": `["-fno-addrsig"]`, - "srcs": `["base.cpp"] + select({ - "//build/bazel/platforms/os_arch:android_arm64": [ - "linux_arm64.cpp", - "bionic_arm64.cpp", - ], - "//build/bazel/platforms/os_arch:android_x86": ["linux_x86.cpp"], - "//build/bazel/platforms/os_arch:linux_bionic_arm64": [ - "linux_arm64.cpp", - "bionic_arm64.cpp", - ], - "//build/bazel/platforms/os_arch:linux_glibc_x86": ["linux_x86.cpp"], - "//build/bazel/platforms/os_arch:linux_musl_arm64": ["linux_arm64.cpp"], - "//build/bazel/platforms/os_arch:linux_musl_x86": ["linux_x86.cpp"], - "//conditions:default": [], - })`, - }), - }, - }) -} - -func TestCcObjectHeaderLib(t *testing.T) { - runCcObjectTestCase(t, Bp2buildTestCase{ - Description: "simple cc_object generates cc_object with include header dep", - Filesystem: map[string]string{ - "a/b/foo.h": "", - "a/b/bar.h": "", - "a/b/exclude.c": "", - "a/b/c.c": "", - }, - Blueprint: `cc_object { - name: "foo", - header_libs: ["libheaders"], - system_shared_libs: [], - cflags: [ - "-Wno-gcc-compat", - "-Wall", - "-Werror", - ], - srcs: [ - "a/b/*.c" - ], - exclude_srcs: ["a/b/exclude.c"], - sdk_version: "current", - min_sdk_version: "29", -} - -cc_library_headers { - name: "libheaders", - export_include_dirs: ["include"], -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_object", "foo", AttrNameToString{ - "copts": `[ - "-fno-addrsig", - "-Wno-gcc-compat", - "-Wall", - "-Werror", - ]`, - "deps": `[":libheaders"]`, - "local_includes": `["."]`, - "srcs": `["a/b/c.c"]`, - "system_dynamic_deps": `[]`, - "sdk_version": `"current"`, - "min_sdk_version": `"29"`, - }), - MakeBazelTarget("cc_library_headers", "libheaders", AttrNameToString{ - "export_includes": `["include"]`, - }), - }, - }) -} diff --git a/bp2build/cc_prebuilt_binary_conversion_test.go b/bp2build/cc_prebuilt_binary_conversion_test.go deleted file mode 100644 index 0e8048c272..0000000000 --- a/bp2build/cc_prebuilt_binary_conversion_test.go +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright 2022 Google Inc. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -package bp2build - -import ( - "fmt" - "testing" - - "android/soong/cc" -) - -func runCcPrebuiltBinaryTestCase(t *testing.T, testCase Bp2buildTestCase) { - t.Helper() - description := fmt.Sprintf("cc_prebuilt_binary: %s", testCase.Description) - testCase.ModuleTypeUnderTest = "cc_prebuilt_binary" - testCase.ModuleTypeUnderTestFactory = cc.PrebuiltBinaryFactory - testCase.Description = description - t.Run(description, func(t *testing.T) { - t.Helper() - RunBp2BuildTestCaseSimple(t, testCase) - }) -} - -func TestPrebuiltBinary(t *testing.T) { - runCcPrebuiltBinaryTestCase(t, - Bp2buildTestCase{ - Description: "simple", - Filesystem: map[string]string{ - "bin": "", - }, - Blueprint: ` -cc_prebuilt_binary { - name: "bintest", - srcs: ["bin"], - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_prebuilt_binary", "bintest", AttrNameToString{ - "src": `"bin"`, - })}, - }) -} - -func TestPrebuiltBinaryWithStrip(t *testing.T) { - runCcPrebuiltBinaryTestCase(t, - Bp2buildTestCase{ - Description: "with strip", - Filesystem: map[string]string{ - "bin": "", - }, - Blueprint: ` -cc_prebuilt_binary { - name: "bintest", - srcs: ["bin"], - strip: { all: true }, - bazel_module: { bp2build_available: true }, -}`, ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_prebuilt_binary", "bintest", AttrNameToString{ - "src": `"bin"`, - "strip": `{ - "all": True, - }`, - }), - }, - }) -} - -func TestPrebuiltBinaryWithArchVariance(t *testing.T) { - runCcPrebuiltBinaryTestCase(t, - Bp2buildTestCase{ - Description: "with arch variance", - Filesystem: map[string]string{ - "bina": "", - "binb": "", - }, - Blueprint: ` -cc_prebuilt_binary { - name: "bintest", - arch: { - arm64: { srcs: ["bina"], }, - arm: { srcs: ["binb"], }, - }, - bazel_module: { bp2build_available: true }, -}`, ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_prebuilt_binary", "bintest", AttrNameToString{ - "src": `select({ - "//build/bazel/platforms/arch:arm": "binb", - "//build/bazel/platforms/arch:arm64": "bina", - "//conditions:default": None, - })`, - }), - }, - }) -} - -func TestPrebuiltBinaryMultipleSrcsFails(t *testing.T) { - runCcPrebuiltBinaryTestCase(t, - Bp2buildTestCase{ - Description: "fails because multiple sources", - Filesystem: map[string]string{ - "bina": "", - "binb": "", - }, - Blueprint: ` -cc_prebuilt_binary { - name: "bintest", - srcs: ["bina", "binb"], - bazel_module: { bp2build_available: true }, -}`, - ExpectedErr: fmt.Errorf("Expected at most one source file"), - }) -} - -// TODO: nosrcs test diff --git a/bp2build/cc_prebuilt_library_conversion_test.go b/bp2build/cc_prebuilt_library_conversion_test.go deleted file mode 100644 index b88960e0e1..0000000000 --- a/bp2build/cc_prebuilt_library_conversion_test.go +++ /dev/null @@ -1,362 +0,0 @@ -// Copyright 2022 Google Inc. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -package bp2build - -import ( - "fmt" - "testing" - - "android/soong/cc" -) - -func runCcPrebuiltLibraryTestCase(t *testing.T, tc Bp2buildTestCase) { - t.Helper() - (&tc).ModuleTypeUnderTest = "cc_prebuilt_library" - (&tc).ModuleTypeUnderTestFactory = cc.PrebuiltLibraryFactory - RunBp2BuildTestCaseSimple(t, tc) -} - -func TestPrebuiltLibraryStaticAndSharedSimple(t *testing.T) { - runCcPrebuiltLibraryTestCase(t, - Bp2buildTestCase{ - Description: "prebuilt library static and shared simple", - Filesystem: map[string]string{ - "libf.so": "", - }, - Blueprint: ` -cc_prebuilt_library { - name: "libtest", - srcs: ["libf.so"], - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_prebuilt_library_static", "libtest_bp2build_cc_library_static", AttrNameToString{ - "static_library": `"libf.so"`, - }), - MakeBazelTarget("cc_prebuilt_library_static", "libtest_bp2build_cc_library_static_alwayslink", AttrNameToString{ - "static_library": `"libf.so"`, - "alwayslink": "True", - }), - MakeBazelTarget("cc_prebuilt_library_shared", "libtest", AttrNameToString{ - "shared_library": `"libf.so"`, - }), - }, - }) -} - -func TestPrebuiltLibraryWithArchVariance(t *testing.T) { - runCcPrebuiltLibraryTestCase(t, - Bp2buildTestCase{ - Description: "prebuilt library with arch variance", - Filesystem: map[string]string{ - "libf.so": "", - "libg.so": "", - }, - Blueprint: ` -cc_prebuilt_library { - name: "libtest", - arch: { - arm64: { srcs: ["libf.so"], }, - arm: { srcs: ["libg.so"], }, - }, - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_prebuilt_library_static", "libtest_bp2build_cc_library_static", AttrNameToString{ - "static_library": `select({ - "//build/bazel/platforms/arch:arm": "libg.so", - "//build/bazel/platforms/arch:arm64": "libf.so", - "//conditions:default": None, - })`}), - MakeBazelTarget("cc_prebuilt_library_static", "libtest_bp2build_cc_library_static_alwayslink", AttrNameToString{ - "alwayslink": "True", - "static_library": `select({ - "//build/bazel/platforms/arch:arm": "libg.so", - "//build/bazel/platforms/arch:arm64": "libf.so", - "//conditions:default": None, - })`}), - MakeBazelTarget("cc_prebuilt_library_shared", "libtest", AttrNameToString{ - "shared_library": `select({ - "//build/bazel/platforms/arch:arm": "libg.so", - "//build/bazel/platforms/arch:arm64": "libf.so", - "//conditions:default": None, - })`, - }), - }, - }) -} - -func TestPrebuiltLibraryAdditionalAttrs(t *testing.T) { - runCcPrebuiltLibraryTestCase(t, - Bp2buildTestCase{ - Description: "prebuilt library additional attributes", - Filesystem: map[string]string{ - "libf.so": "", - "testdir/1/include.h": "", - "testdir/2/other.h": "", - }, - Blueprint: ` -cc_prebuilt_library { - name: "libtest", - srcs: ["libf.so"], - export_include_dirs: ["testdir/1/"], - export_system_include_dirs: ["testdir/2/"], - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_prebuilt_library_static", "libtest_bp2build_cc_library_static", AttrNameToString{ - "static_library": `"libf.so"`, - "export_includes": `["testdir/1/"]`, - "export_system_includes": `["testdir/2/"]`, - }), - MakeBazelTarget("cc_prebuilt_library_static", "libtest_bp2build_cc_library_static_alwayslink", AttrNameToString{ - "static_library": `"libf.so"`, - "export_includes": `["testdir/1/"]`, - "export_system_includes": `["testdir/2/"]`, - "alwayslink": "True", - }), - MakeBazelTarget("cc_prebuilt_library_shared", "libtest", AttrNameToString{ - "shared_library": `"libf.so"`, - "export_includes": `["testdir/1/"]`, - "export_system_includes": `["testdir/2/"]`, - }), - }, - }) -} - -func TestPrebuiltLibrarySharedStanzaFails(t *testing.T) { - runCcPrebuiltLibraryTestCase(t, - Bp2buildTestCase{ - Description: "prebuilt library with shared stanza fails because multiple sources", - Filesystem: map[string]string{ - "libf.so": "", - "libg.so": "", - }, - Blueprint: ` -cc_prebuilt_library { - name: "libtest", - srcs: ["libf.so"], - shared: { - srcs: ["libg.so"], - }, - bazel_module: { bp2build_available: true }, -}`, - ExpectedErr: fmt.Errorf("Expected at most one source file"), - }) -} - -func TestPrebuiltLibraryStaticStanzaFails(t *testing.T) { - RunBp2BuildTestCaseSimple(t, - Bp2buildTestCase{ - Description: "prebuilt library with static stanza fails because multiple sources", - ModuleTypeUnderTest: "cc_prebuilt_library", - ModuleTypeUnderTestFactory: cc.PrebuiltLibraryFactory, - Filesystem: map[string]string{ - "libf.so": "", - "libg.so": "", - }, - Blueprint: ` -cc_prebuilt_library { - name: "libtest", - srcs: ["libf.so"], - static: { - srcs: ["libg.so"], - }, - bazel_module: { bp2build_available: true }, -}`, - ExpectedErr: fmt.Errorf("Expected at most one source file"), - }) -} - -func TestPrebuiltLibrarySharedAndStaticStanzas(t *testing.T) { - runCcPrebuiltLibraryTestCase(t, - Bp2buildTestCase{ - Description: "prebuilt library with both shared and static stanzas", - Filesystem: map[string]string{ - "libf.so": "", - "libg.so": "", - }, - Blueprint: ` -cc_prebuilt_library { - name: "libtest", - static: { - srcs: ["libf.so"], - }, - shared: { - srcs: ["libg.so"], - }, - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_prebuilt_library_static", "libtest_bp2build_cc_library_static", AttrNameToString{ - "static_library": `"libf.so"`, - }), - MakeBazelTarget("cc_prebuilt_library_static", "libtest_bp2build_cc_library_static_alwayslink", AttrNameToString{ - "static_library": `"libf.so"`, - "alwayslink": "True", - }), - MakeBazelTarget("cc_prebuilt_library_shared", "libtest", AttrNameToString{ - "shared_library": `"libg.so"`, - }), - }, - }) -} - -// TODO(b/228623543): When this bug is fixed, enable this test -//func TestPrebuiltLibraryOnlyShared(t *testing.T) { -// runCcPrebuiltLibraryTestCase(t, -// bp2buildTestCase{ -// description: "prebuilt library shared only", -// filesystem: map[string]string{ -// "libf.so": "", -// }, -// blueprint: ` -//cc_prebuilt_library { -// name: "libtest", -// srcs: ["libf.so"], -// static: { -// enabled: false, -// }, -// bazel_module: { bp2build_available: true }, -//}`, -// expectedBazelTargets: []string{ -// makeBazelTarget("cc_prebuilt_library_shared", "libtest", attrNameToString{ -// "shared_library": `"libf.so"`, -// }), -// }, -// }) -//} - -// TODO(b/228623543): When this bug is fixed, enable this test -//func TestPrebuiltLibraryOnlyStatic(t *testing.T) { -// runCcPrebuiltLibraryTestCase(t, -// bp2buildTestCase{ -// description: "prebuilt library static only", -// filesystem: map[string]string{ -// "libf.so": "", -// }, -// blueprint: ` -//cc_prebuilt_library { -// name: "libtest", -// srcs: ["libf.so"], -// shared: { -// enabled: false, -// }, -// bazel_module: { bp2build_available: true }, -//}`, -// expectedBazelTargets: []string{ -// makeBazelTarget("cc_prebuilt_library_static", "libtest_bp2build_cc_library_static", attrNameToString{ -// "static_library": `"libf.so"`, -// }), -// makeBazelTarget("cc_prebuilt_library_static", "libtest_bp2build_cc_library_static_always", attrNameToString{ -// "static_library": `"libf.so"`, -// "alwayslink": "True", -// }), -// }, -// }) -//} - -func TestPrebuiltLibraryWithExportIncludesArchVariant(t *testing.T) { - runCcPrebuiltLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_prebuilt_library correctly translates export_includes with arch variance", - Filesystem: map[string]string{ - "libf.so": "", - "libg.so": "", - }, - Blueprint: ` -cc_prebuilt_library { - name: "libtest", - srcs: ["libf.so"], - arch: { - arm: { export_include_dirs: ["testdir/1/"], }, - arm64: { export_include_dirs: ["testdir/2/"], }, - }, - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_prebuilt_library_shared", "libtest", AttrNameToString{ - "shared_library": `"libf.so"`, - "export_includes": `select({ - "//build/bazel/platforms/arch:arm": ["testdir/1/"], - "//build/bazel/platforms/arch:arm64": ["testdir/2/"], - "//conditions:default": [], - })`, - }), - MakeBazelTarget("cc_prebuilt_library_static", "libtest_bp2build_cc_library_static", AttrNameToString{ - "static_library": `"libf.so"`, - "export_includes": `select({ - "//build/bazel/platforms/arch:arm": ["testdir/1/"], - "//build/bazel/platforms/arch:arm64": ["testdir/2/"], - "//conditions:default": [], - })`, - }), - MakeBazelTarget("cc_prebuilt_library_static", "libtest_bp2build_cc_library_static_alwayslink", AttrNameToString{ - "alwayslink": "True", - "static_library": `"libf.so"`, - "export_includes": `select({ - "//build/bazel/platforms/arch:arm": ["testdir/1/"], - "//build/bazel/platforms/arch:arm64": ["testdir/2/"], - "//conditions:default": [], - })`, - }), - }, - }) -} - -func TestPrebuiltLibraryWithExportSystemIncludesArchVariant(t *testing.T) { - runCcPrebuiltLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_prebuilt_ibrary correctly translates export_system_includes with arch variance", - Filesystem: map[string]string{ - "libf.so": "", - "libg.so": "", - }, - Blueprint: ` -cc_prebuilt_library { - name: "libtest", - srcs: ["libf.so"], - arch: { - arm: { export_system_include_dirs: ["testdir/1/"], }, - arm64: { export_system_include_dirs: ["testdir/2/"], }, - }, - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_prebuilt_library_shared", "libtest", AttrNameToString{ - "shared_library": `"libf.so"`, - "export_system_includes": `select({ - "//build/bazel/platforms/arch:arm": ["testdir/1/"], - "//build/bazel/platforms/arch:arm64": ["testdir/2/"], - "//conditions:default": [], - })`, - }), - MakeBazelTarget("cc_prebuilt_library_static", "libtest_bp2build_cc_library_static", AttrNameToString{ - "static_library": `"libf.so"`, - "export_system_includes": `select({ - "//build/bazel/platforms/arch:arm": ["testdir/1/"], - "//build/bazel/platforms/arch:arm64": ["testdir/2/"], - "//conditions:default": [], - })`, - }), - MakeBazelTarget("cc_prebuilt_library_static", "libtest_bp2build_cc_library_static_alwayslink", AttrNameToString{ - "alwayslink": "True", - "static_library": `"libf.so"`, - "export_system_includes": `select({ - "//build/bazel/platforms/arch:arm": ["testdir/1/"], - "//build/bazel/platforms/arch:arm64": ["testdir/2/"], - "//conditions:default": [], - })`, - }), - }, - }) -} diff --git a/bp2build/cc_prebuilt_library_shared_conversion_test.go b/bp2build/cc_prebuilt_library_shared_conversion_test.go deleted file mode 100644 index 9e975aea36..0000000000 --- a/bp2build/cc_prebuilt_library_shared_conversion_test.go +++ /dev/null @@ -1,165 +0,0 @@ -// Copyright 2022 Google Inc. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -package bp2build - -import ( - "testing" - - "android/soong/cc" -) - -func runCcPrebuiltLibrarySharedTestCase(t *testing.T, tc Bp2buildTestCase) { - t.Parallel() - t.Helper() - (&tc).ModuleTypeUnderTest = "cc_prebuilt_library_shared" - (&tc).ModuleTypeUnderTestFactory = cc.PrebuiltSharedLibraryFactory - RunBp2BuildTestCaseSimple(t, tc) -} - -func TestPrebuiltLibrarySharedSimple(t *testing.T) { - runCcPrebuiltLibrarySharedTestCase(t, - Bp2buildTestCase{ - Description: "prebuilt library shared simple", - Filesystem: map[string]string{ - "libf.so": "", - }, - Blueprint: ` -cc_prebuilt_library_shared { - name: "libtest", - srcs: ["libf.so"], - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_prebuilt_library_shared", "libtest", AttrNameToString{ - "shared_library": `"libf.so"`, - }), - }, - }) -} - -func TestPrebuiltLibrarySharedWithArchVariance(t *testing.T) { - runCcPrebuiltLibrarySharedTestCase(t, - Bp2buildTestCase{ - Description: "prebuilt library shared with arch variance", - Filesystem: map[string]string{ - "libf.so": "", - "libg.so": "", - }, - Blueprint: ` -cc_prebuilt_library_shared { - name: "libtest", - arch: { - arm64: { srcs: ["libf.so"], }, - arm: { srcs: ["libg.so"], }, - }, - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_prebuilt_library_shared", "libtest", AttrNameToString{ - "shared_library": `select({ - "//build/bazel/platforms/arch:arm": "libg.so", - "//build/bazel/platforms/arch:arm64": "libf.so", - "//conditions:default": None, - })`, - }), - }, - }) -} - -func TestPrebuiltLibrarySharedAdditionalAttrs(t *testing.T) { - runCcPrebuiltLibrarySharedTestCase(t, - Bp2buildTestCase{ - Description: "prebuilt library shared additional attributes", - Filesystem: map[string]string{ - "libf.so": "", - "testdir/1/include.h": "", - "testdir/2/other.h": "", - }, - Blueprint: ` -cc_prebuilt_library_shared { - name: "libtest", - srcs: ["libf.so"], - export_include_dirs: ["testdir/1/"], - export_system_include_dirs: ["testdir/2/"], - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_prebuilt_library_shared", "libtest", AttrNameToString{ - "shared_library": `"libf.so"`, - "export_includes": `["testdir/1/"]`, - "export_system_includes": `["testdir/2/"]`, - }), - }, - }) -} - -func TestPrebuiltLibrarySharedWithExportIncludesArchVariant(t *testing.T) { - runCcPrebuiltLibrarySharedTestCase(t, Bp2buildTestCase{ - Description: "cc_prebuilt_library_shared correctly translates export_includes with arch variance", - Filesystem: map[string]string{ - "libf.so": "", - "libg.so": "", - }, - Blueprint: ` -cc_prebuilt_library_shared { - name: "libtest", - srcs: ["libf.so"], - arch: { - arm: { export_include_dirs: ["testdir/1/"], }, - arm64: { export_include_dirs: ["testdir/2/"], }, - }, - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_prebuilt_library_shared", "libtest", AttrNameToString{ - "shared_library": `"libf.so"`, - "export_includes": `select({ - "//build/bazel/platforms/arch:arm": ["testdir/1/"], - "//build/bazel/platforms/arch:arm64": ["testdir/2/"], - "//conditions:default": [], - })`, - }), - }, - }) -} - -func TestPrebuiltLibrarySharedWithExportSystemIncludesArchVariant(t *testing.T) { - runCcPrebuiltLibrarySharedTestCase(t, Bp2buildTestCase{ - Description: "cc_prebuilt_library_shared correctly translates export_system_includes with arch variance", - Filesystem: map[string]string{ - "libf.so": "", - "libg.so": "", - }, - Blueprint: ` -cc_prebuilt_library_shared { - name: "libtest", - srcs: ["libf.so"], - arch: { - arm: { export_system_include_dirs: ["testdir/1/"], }, - arm64: { export_system_include_dirs: ["testdir/2/"], }, - }, - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_prebuilt_library_shared", "libtest", AttrNameToString{ - "shared_library": `"libf.so"`, - "export_system_includes": `select({ - "//build/bazel/platforms/arch:arm": ["testdir/1/"], - "//build/bazel/platforms/arch:arm64": ["testdir/2/"], - "//conditions:default": [], - })`, - }), - }, - }) -} diff --git a/bp2build/cc_prebuilt_library_shared_test.go b/bp2build/cc_prebuilt_library_shared_test.go deleted file mode 100644 index 58c0a703d2..0000000000 --- a/bp2build/cc_prebuilt_library_shared_test.go +++ /dev/null @@ -1,85 +0,0 @@ -package bp2build - -import ( - "fmt" - "testing" - - "android/soong/cc" -) - -func TestSharedPrebuiltLibrary(t *testing.T) { - RunBp2BuildTestCaseSimple(t, - Bp2buildTestCase{ - Description: "prebuilt library shared simple", - ModuleTypeUnderTest: "cc_prebuilt_library_shared", - ModuleTypeUnderTestFactory: cc.PrebuiltSharedLibraryFactory, - Filesystem: map[string]string{ - "libf.so": "", - }, - Blueprint: ` -cc_prebuilt_library_shared { - name: "libtest", - srcs: ["libf.so"], - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_prebuilt_library_shared", "libtest", AttrNameToString{ - "shared_library": `"libf.so"`, - }), - }, - }) -} - -func TestSharedPrebuiltLibraryWithArchVariance(t *testing.T) { - RunBp2BuildTestCaseSimple(t, - Bp2buildTestCase{ - Description: "prebuilt library shared with arch variance", - ModuleTypeUnderTest: "cc_prebuilt_library_shared", - ModuleTypeUnderTestFactory: cc.PrebuiltSharedLibraryFactory, - Filesystem: map[string]string{ - "libf.so": "", - "libg.so": "", - }, - Blueprint: ` -cc_prebuilt_library_shared { - name: "libtest", - arch: { - arm64: { srcs: ["libf.so"], }, - arm: { srcs: ["libg.so"], }, - }, - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_prebuilt_library_shared", "libtest", AttrNameToString{ - "shared_library": `select({ - "//build/bazel/platforms/arch:arm": "libg.so", - "//build/bazel/platforms/arch:arm64": "libf.so", - "//conditions:default": None, - })`, - }), - }, - }) -} - -func TestSharedPrebuiltLibrarySharedStanzaFails(t *testing.T) { - RunBp2BuildTestCaseSimple(t, - Bp2buildTestCase{ - Description: "prebuilt library shared with shared stanza fails because multiple sources", - ModuleTypeUnderTest: "cc_prebuilt_library_shared", - ModuleTypeUnderTestFactory: cc.PrebuiltSharedLibraryFactory, - Filesystem: map[string]string{ - "libf.so": "", - "libg.so": "", - }, - Blueprint: ` -cc_prebuilt_library_shared { - name: "libtest", - srcs: ["libf.so"], - shared: { - srcs: ["libg.so"], - }, - bazel_module: { bp2build_available: true}, -}`, - ExpectedErr: fmt.Errorf("Expected at most one source file"), - }) -} diff --git a/bp2build/cc_prebuilt_library_static_conversion_test.go b/bp2build/cc_prebuilt_library_static_conversion_test.go deleted file mode 100644 index 77562e7266..0000000000 --- a/bp2build/cc_prebuilt_library_static_conversion_test.go +++ /dev/null @@ -1,199 +0,0 @@ -// Copyright 2022 Google Inc. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -package bp2build - -import ( - "testing" - - "android/soong/cc" -) - -func runCcPrebuiltLibraryStaticTestCase(t *testing.T, tc Bp2buildTestCase) { - t.Parallel() - t.Helper() - (&tc).ModuleTypeUnderTest = "cc_prebuilt_library_static" - (&tc).ModuleTypeUnderTestFactory = cc.PrebuiltStaticLibraryFactory - RunBp2BuildTestCaseSimple(t, tc) -} - -func TestPrebuiltLibraryStaticSimple(t *testing.T) { - runCcPrebuiltLibraryStaticTestCase(t, - Bp2buildTestCase{ - Description: "prebuilt library static simple", - Filesystem: map[string]string{ - "libf.so": "", - }, - Blueprint: ` -cc_prebuilt_library_static { - name: "libtest", - srcs: ["libf.so"], - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_prebuilt_library_static", "libtest", AttrNameToString{ - "static_library": `"libf.so"`, - }), - MakeBazelTarget("cc_prebuilt_library_static", "libtest_alwayslink", AttrNameToString{ - "static_library": `"libf.so"`, - "alwayslink": "True", - }), - }, - }) -} - -func TestPrebuiltLibraryStaticWithArchVariance(t *testing.T) { - runCcPrebuiltLibraryStaticTestCase(t, - Bp2buildTestCase{ - Description: "prebuilt library with arch variance", - Filesystem: map[string]string{ - "libf.so": "", - "libg.so": "", - }, - Blueprint: ` -cc_prebuilt_library_static { - name: "libtest", - arch: { - arm64: { srcs: ["libf.so"], }, - arm: { srcs: ["libg.so"], }, - }, - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_prebuilt_library_static", "libtest", AttrNameToString{ - "static_library": `select({ - "//build/bazel/platforms/arch:arm": "libg.so", - "//build/bazel/platforms/arch:arm64": "libf.so", - "//conditions:default": None, - })`}), - MakeBazelTarget("cc_prebuilt_library_static", "libtest_alwayslink", AttrNameToString{ - "alwayslink": "True", - "static_library": `select({ - "//build/bazel/platforms/arch:arm": "libg.so", - "//build/bazel/platforms/arch:arm64": "libf.so", - "//conditions:default": None, - })`}), - }, - }) -} - -func TestPrebuiltLibraryStaticAdditionalAttrs(t *testing.T) { - runCcPrebuiltLibraryStaticTestCase(t, - Bp2buildTestCase{ - Description: "prebuilt library additional attributes", - Filesystem: map[string]string{ - "libf.so": "", - "testdir/1/include.h": "", - "testdir/2/other.h": "", - }, - Blueprint: ` -cc_prebuilt_library_static { - name: "libtest", - srcs: ["libf.so"], - export_include_dirs: ["testdir/1/"], - export_system_include_dirs: ["testdir/2/"], - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_prebuilt_library_static", "libtest", AttrNameToString{ - "static_library": `"libf.so"`, - "export_includes": `["testdir/1/"]`, - "export_system_includes": `["testdir/2/"]`, - }), - MakeBazelTarget("cc_prebuilt_library_static", "libtest_alwayslink", AttrNameToString{ - "static_library": `"libf.so"`, - "export_includes": `["testdir/1/"]`, - "export_system_includes": `["testdir/2/"]`, - "alwayslink": "True", - }), - }, - }) -} - -func TestPrebuiltLibraryStaticWithExportIncludesArchVariant(t *testing.T) { - runCcPrebuiltLibraryStaticTestCase(t, Bp2buildTestCase{ - Description: "cc_prebuilt_library_static correctly translates export_includes with arch variance", - Filesystem: map[string]string{ - "libf.so": "", - "libg.so": "", - }, - Blueprint: ` -cc_prebuilt_library_static { - name: "libtest", - srcs: ["libf.so"], - arch: { - arm: { export_include_dirs: ["testdir/1/"], }, - arm64: { export_include_dirs: ["testdir/2/"], }, - }, - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_prebuilt_library_static", "libtest", AttrNameToString{ - "static_library": `"libf.so"`, - "export_includes": `select({ - "//build/bazel/platforms/arch:arm": ["testdir/1/"], - "//build/bazel/platforms/arch:arm64": ["testdir/2/"], - "//conditions:default": [], - })`, - }), - MakeBazelTarget("cc_prebuilt_library_static", "libtest_alwayslink", AttrNameToString{ - "alwayslink": "True", - "static_library": `"libf.so"`, - "export_includes": `select({ - "//build/bazel/platforms/arch:arm": ["testdir/1/"], - "//build/bazel/platforms/arch:arm64": ["testdir/2/"], - "//conditions:default": [], - })`, - }), - }, - }) -} - -func TestPrebuiltLibraryStaticWithExportSystemIncludesArchVariant(t *testing.T) { - runCcPrebuiltLibraryStaticTestCase(t, Bp2buildTestCase{ - Description: "cc_prebuilt_library_static correctly translates export_system_includes with arch variance", - Filesystem: map[string]string{ - "libf.so": "", - "libg.so": "", - }, - Blueprint: ` -cc_prebuilt_library_static { - name: "libtest", - srcs: ["libf.so"], - arch: { - arm: { export_system_include_dirs: ["testdir/1/"], }, - arm64: { export_system_include_dirs: ["testdir/2/"], }, - }, - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_prebuilt_library_static", "libtest", AttrNameToString{ - "static_library": `"libf.so"`, - "export_system_includes": `select({ - "//build/bazel/platforms/arch:arm": ["testdir/1/"], - "//build/bazel/platforms/arch:arm64": ["testdir/2/"], - "//conditions:default": [], - })`, - }), - MakeBazelTarget("cc_prebuilt_library_static", "libtest_alwayslink", AttrNameToString{ - "alwayslink": "True", - "static_library": `"libf.so"`, - "export_system_includes": `select({ - "//build/bazel/platforms/arch:arm": ["testdir/1/"], - "//build/bazel/platforms/arch:arm64": ["testdir/2/"], - "//conditions:default": [], - })`, - }), - }, - }) -} diff --git a/bp2build/cc_prebuilt_library_static_test.go b/bp2build/cc_prebuilt_library_static_test.go deleted file mode 100644 index 17da8132a2..0000000000 --- a/bp2build/cc_prebuilt_library_static_test.go +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright 2022 Google Inc. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -package bp2build - -import ( - "fmt" - "testing" - - "android/soong/cc" -) - -func TestStaticPrebuiltLibrary(t *testing.T) { - RunBp2BuildTestCaseSimple(t, - Bp2buildTestCase{ - Description: "prebuilt library static simple", - ModuleTypeUnderTest: "cc_prebuilt_library_static", - ModuleTypeUnderTestFactory: cc.PrebuiltStaticLibraryFactory, - Filesystem: map[string]string{ - "libf.so": "", - }, - Blueprint: ` -cc_prebuilt_library_static { - name: "libtest", - srcs: ["libf.so"], - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_prebuilt_library_static", "libtest", AttrNameToString{ - "static_library": `"libf.so"`, - }), - MakeBazelTarget("cc_prebuilt_library_static", "libtest_alwayslink", AttrNameToString{ - "static_library": `"libf.so"`, - "alwayslink": "True", - }), - }, - }) -} - -func TestStaticPrebuiltLibraryWithArchVariance(t *testing.T) { - RunBp2BuildTestCaseSimple(t, - Bp2buildTestCase{ - Description: "prebuilt library static with arch variance", - ModuleTypeUnderTest: "cc_prebuilt_library_static", - ModuleTypeUnderTestFactory: cc.PrebuiltStaticLibraryFactory, - Filesystem: map[string]string{ - "libf.so": "", - "libg.so": "", - }, - Blueprint: ` -cc_prebuilt_library_static { - name: "libtest", - arch: { - arm64: { srcs: ["libf.so"], }, - arm: { srcs: ["libg.so"], }, - }, - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_prebuilt_library_static", "libtest", AttrNameToString{ - "static_library": `select({ - "//build/bazel/platforms/arch:arm": "libg.so", - "//build/bazel/platforms/arch:arm64": "libf.so", - "//conditions:default": None, - })`}), - MakeBazelTarget("cc_prebuilt_library_static", "libtest_alwayslink", AttrNameToString{ - "alwayslink": "True", - "static_library": `select({ - "//build/bazel/platforms/arch:arm": "libg.so", - "//build/bazel/platforms/arch:arm64": "libf.so", - "//conditions:default": None, - })`}), - }, - }) -} - -func TestStaticPrebuiltLibraryStaticStanzaFails(t *testing.T) { - RunBp2BuildTestCaseSimple(t, - Bp2buildTestCase{ - Description: "prebuilt library with static stanza fails because multiple sources", - ModuleTypeUnderTest: "cc_prebuilt_library_static", - ModuleTypeUnderTestFactory: cc.PrebuiltStaticLibraryFactory, - Filesystem: map[string]string{ - "libf.so": "", - "libg.so": "", - }, - Blueprint: ` -cc_prebuilt_library_static { - name: "libtest", - srcs: ["libf.so"], - static: { - srcs: ["libg.so"], - }, - bazel_module: { bp2build_available: true }, -}`, - ExpectedErr: fmt.Errorf("Expected at most one source file"), - }) -} - -func TestCcLibraryStaticConvertLex(t *testing.T) { - runCcLibrarySharedTestCase(t, Bp2buildTestCase{ - Description: "cc_library_static with lex files", - ModuleTypeUnderTest: "cc_library_static", - ModuleTypeUnderTestFactory: cc.LibraryStaticFactory, - Filesystem: map[string]string{ - "foo.c": "", - "bar.cc": "", - "foo1.l": "", - "bar1.ll": "", - "foo2.l": "", - "bar2.ll": "", - }, - Blueprint: `cc_library_static { - name: "foo_lib", - srcs: ["foo.c", "bar.cc", "foo1.l", "foo2.l", "bar1.ll", "bar2.ll"], - lex: { flags: ["--foo_flags"] }, - include_build_directory: false, - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("genlex", "foo_lib_genlex_l", AttrNameToString{ - "srcs": `[ - "foo1.l", - "foo2.l", - ]`, - "lexopts": `["--foo_flags"]`, - }), - MakeBazelTarget("genlex", "foo_lib_genlex_ll", AttrNameToString{ - "srcs": `[ - "bar1.ll", - "bar2.ll", - ]`, - "lexopts": `["--foo_flags"]`, - }), - MakeBazelTarget("cc_library_static", "foo_lib", AttrNameToString{ - "srcs": `[ - "bar.cc", - ":foo_lib_genlex_ll", - ]`, - "srcs_c": `[ - "foo.c", - ":foo_lib_genlex_l", - ]`, - }), - }, - }) -} diff --git a/bp2build/cc_prebuilt_object_conversion_test.go b/bp2build/cc_prebuilt_object_conversion_test.go deleted file mode 100644 index 903c81634d..0000000000 --- a/bp2build/cc_prebuilt_object_conversion_test.go +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2022 Google Inc. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -package bp2build - -import ( - "fmt" - "testing" - - "android/soong/cc" -) - -func runCcPrebuiltObjectTestCase(t *testing.T, testCase Bp2buildTestCase) { - t.Helper() - description := fmt.Sprintf("cc_prebuilt_object: %s", testCase.Description) - testCase.ModuleTypeUnderTest = "cc_prebuilt_object" - testCase.ModuleTypeUnderTestFactory = cc.PrebuiltObjectFactory - testCase.Description = description - t.Run(description, func(t *testing.T) { - t.Helper() - RunBp2BuildTestCaseSimple(t, testCase) - }) -} - -func TestPrebuiltObject(t *testing.T) { - runCcPrebuiltObjectTestCase(t, - Bp2buildTestCase{ - Description: "simple", - Filesystem: map[string]string{ - "obj.o": "", - }, - Blueprint: ` -cc_prebuilt_object { - name: "objtest", - srcs: ["obj.o"], - bazel_module: { bp2build_available: true }, -}`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_prebuilt_object", "objtest", AttrNameToString{ - "src": `"obj.o"`, - })}, - }) -} - -func TestPrebuiltObjectWithArchVariance(t *testing.T) { - runCcPrebuiltObjectTestCase(t, - Bp2buildTestCase{ - Description: "with arch variance", - Filesystem: map[string]string{ - "obja.o": "", - "objb.o": "", - }, - Blueprint: ` -cc_prebuilt_object { - name: "objtest", - arch: { - arm64: { srcs: ["obja.o"], }, - arm: { srcs: ["objb.o"], }, - }, - bazel_module: { bp2build_available: true }, -}`, ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_prebuilt_object", "objtest", AttrNameToString{ - "src": `select({ - "//build/bazel/platforms/arch:arm": "objb.o", - "//build/bazel/platforms/arch:arm64": "obja.o", - "//conditions:default": None, - })`, - }), - }, - }) -} - -func TestPrebuiltObjectMultipleSrcsFails(t *testing.T) { - runCcPrebuiltObjectTestCase(t, - Bp2buildTestCase{ - Description: "fails because multiple sources", - Filesystem: map[string]string{ - "obja": "", - "objb": "", - }, - Blueprint: ` -cc_prebuilt_object { - name: "objtest", - srcs: ["obja.o", "objb.o"], - bazel_module: { bp2build_available: true }, -}`, - ExpectedErr: fmt.Errorf("Expected at most one source file"), - }) -} - -// TODO: nosrcs test diff --git a/bp2build/cc_test_conversion_test.go b/bp2build/cc_test_conversion_test.go deleted file mode 100644 index 20adddbca4..0000000000 --- a/bp2build/cc_test_conversion_test.go +++ /dev/null @@ -1,262 +0,0 @@ -// Copyright 2022 Google Inc. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package bp2build - -import ( - "fmt" - "testing" - - "android/soong/android" - "android/soong/cc" - "android/soong/genrule" -) - -type ccTestBp2buildTestCase struct { - description string - blueprint string - filesystem map[string]string - targets []testBazelTarget -} - -func registerCcTestModuleTypes(ctx android.RegistrationContext) { - cc.RegisterCCBuildComponents(ctx) - ctx.RegisterModuleType("cc_binary", cc.BinaryFactory) - ctx.RegisterModuleType("cc_library_static", cc.LibraryStaticFactory) - ctx.RegisterModuleType("cc_library", cc.LibraryFactory) - ctx.RegisterModuleType("cc_test_library", cc.TestLibraryFactory) - ctx.RegisterModuleType("genrule", genrule.GenRuleFactory) -} - -func runCcTestTestCase(t *testing.T, testCase ccTestBp2buildTestCase) { - t.Helper() - moduleTypeUnderTest := "cc_test" - description := fmt.Sprintf("%s %s", moduleTypeUnderTest, testCase.description) - t.Run(description, func(t *testing.T) { - t.Helper() - RunBp2BuildTestCase(t, registerCcTestModuleTypes, Bp2buildTestCase{ - ExpectedBazelTargets: generateBazelTargetsForTest(testCase.targets, android.HostAndDeviceSupported), - Filesystem: testCase.filesystem, - ModuleTypeUnderTest: moduleTypeUnderTest, - ModuleTypeUnderTestFactory: cc.TestFactory, - Description: description, - Blueprint: testCase.blueprint, - }) - }) -} - -func TestBasicCcTest(t *testing.T) { - runCcTestTestCase(t, ccTestBp2buildTestCase{ - description: "basic cc_test with commonly used attributes", - blueprint: ` -cc_test { - name: "mytest", - host_supported: true, - srcs: ["test.cpp"], - target: { - android: { - srcs: ["android.cpp"], - shared_libs: ["foolib"], - }, - linux: { - srcs: ["linux.cpp"], - }, - host: { - static_libs: ["hostlib"], - }, - }, - data: [":data_mod", "file.txt"], - data_bins: [":cc_bin"], - data_libs: [":cc_lib"], - cflags: ["-Wall"], -} -` + simpleModuleDoNotConvertBp2build("cc_library", "foolib") + - simpleModuleDoNotConvertBp2build("cc_library_static", "hostlib") + - simpleModuleDoNotConvertBp2build("genrule", "data_mod") + - simpleModuleDoNotConvertBp2build("cc_binary", "cc_bin") + - simpleModuleDoNotConvertBp2build("cc_test_library", "cc_lib"), - targets: []testBazelTarget{ - {"cc_test", "mytest", AttrNameToString{ - "copts": `["-Wall"]`, - "data": `[ - ":data_mod", - "file.txt", - ":cc_bin", - ":cc_lib", - ]`, - "deps": `select({ - "//build/bazel/platforms/os:darwin": [":hostlib"], - "//build/bazel/platforms/os:linux_bionic": [":hostlib"], - "//build/bazel/platforms/os:linux_glibc": [":hostlib"], - "//build/bazel/platforms/os:linux_musl": [":hostlib"], - "//build/bazel/platforms/os:windows": [":hostlib"], - "//conditions:default": [], - })`, - "gtest": "True", - "isolated": "True", - "local_includes": `["."]`, - "dynamic_deps": `select({ - "//build/bazel/platforms/os:android": [":foolib"], - "//conditions:default": [], - })`, - "srcs": `["test.cpp"] + select({ - "//build/bazel/platforms/os:android": [ - "linux.cpp", - "android.cpp", - ], - "//build/bazel/platforms/os:linux_bionic": ["linux.cpp"], - "//build/bazel/platforms/os:linux_glibc": ["linux.cpp"], - "//build/bazel/platforms/os:linux_musl": ["linux.cpp"], - "//conditions:default": [], - })`, - }, - }, - }, - }) -} - -func TestBasicCcTestGtestIsolatedDisabled(t *testing.T) { - runCcTestTestCase(t, ccTestBp2buildTestCase{ - description: "cc test with disabled gtest and isolated props", - blueprint: ` -cc_test { - name: "mytest", - host_supported: true, - srcs: ["test.cpp"], - gtest: false, - isolated: false, -} -`, - targets: []testBazelTarget{ - {"cc_test", "mytest", AttrNameToString{ - "gtest": "False", - "isolated": "False", - "local_includes": `["."]`, - "srcs": `["test.cpp"]`, - }, - }, - }, - }) -} - -func TestCcTest_TestOptions_Tags(t *testing.T) { - runCcTestTestCase(t, ccTestBp2buildTestCase{ - description: "cc test with test_options.tags converted to tags", - blueprint: ` -cc_test { - name: "mytest", - host_supported: true, - srcs: ["test.cpp"], - test_options: { tags: ["no-remote"] }, -} -`, - targets: []testBazelTarget{ - {"cc_test", "mytest", AttrNameToString{ - "tags": `["no-remote"]`, - "local_includes": `["."]`, - "srcs": `["test.cpp"]`, - "gtest": "True", - "isolated": "True", - }, - }, - }, - }) -} - -func TestCcTest_TestConfig(t *testing.T) { - runCcTestTestCase(t, ccTestBp2buildTestCase{ - description: "cc test that sets a test_config", - filesystem: map[string]string{ - "test_config.xml": "", - }, - blueprint: ` -cc_test { - name: "mytest", - srcs: ["test.cpp"], - test_config: "test_config.xml", -} -`, - targets: []testBazelTarget{ - {"cc_test", "mytest", AttrNameToString{ - "gtest": "True", - "isolated": "True", - "local_includes": `["."]`, - "srcs": `["test.cpp"]`, - "target_compatible_with": `["//build/bazel/platforms/os:android"]`, - "test_config": `"test_config.xml"`, - }, - }, - }, - }) -} - -func TestCcTest_TestConfigAndroidTestXML(t *testing.T) { - runCcTestTestCase(t, ccTestBp2buildTestCase{ - description: "cc test that defaults to test config AndroidTest.xml", - filesystem: map[string]string{ - "AndroidTest.xml": "", - }, - blueprint: ` -cc_test { - name: "mytest", - srcs: ["test.cpp"], -} -`, - targets: []testBazelTarget{ - {"cc_test", "mytest", AttrNameToString{ - "gtest": "True", - "isolated": "True", - "local_includes": `["."]`, - "srcs": `["test.cpp"]`, - "target_compatible_with": `["//build/bazel/platforms/os:android"]`, - "test_config": `"AndroidTest.xml"`, - }, - }, - }, - }) -} - -func TestCcTest_TestConfigTemplateOptions(t *testing.T) { - runCcTestTestCase(t, ccTestBp2buildTestCase{ - description: "cc test that sets test config template attributes", - filesystem: map[string]string{ - "test_config_template.xml": "", - }, - blueprint: ` -cc_test { - name: "mytest", - srcs: ["test.cpp"], - test_config_template: "test_config_template.xml", - auto_gen_config: true, -} -`, - targets: []testBazelTarget{ - {"cc_test", "mytest", AttrNameToString{ - "auto_generate_test_config": "True", - "gtest": "True", - "isolated": "True", - "local_includes": `["."]`, - "srcs": `["test.cpp"]`, - "target_compatible_with": `["//build/bazel/platforms/os:android"]`, - "template_configs": `[ - "'\\n '", - "'