|  | // 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 | 
|  |  | 
|  | /* | 
|  | For shareable/common bp2build testing functionality and dumping ground for | 
|  | specific-but-shared functionality among tests in package | 
|  | */ | 
|  |  | 
|  | import ( | 
|  | "fmt" | 
|  | "path/filepath" | 
|  | "regexp" | 
|  | "sort" | 
|  | "strings" | 
|  | "testing" | 
|  |  | 
|  | "android/soong/ui/metrics/bp2build_metrics_proto" | 
|  | "github.com/google/blueprint/proptools" | 
|  |  | 
|  | "android/soong/android" | 
|  | "android/soong/android/allowlists" | 
|  | "android/soong/bazel" | 
|  | ) | 
|  |  | 
|  | var ( | 
|  | buildDir string | 
|  | ) | 
|  |  | 
|  | var labelRegex = regexp.MustCompile(`^//([^: ]+):([^ ]+)$`) | 
|  | var simpleModuleNameRegex = regexp.MustCompile(`^[^: /]+$`) | 
|  |  | 
|  | func checkError(t *testing.T, errs []error, expectedErr error) bool { | 
|  | t.Helper() | 
|  |  | 
|  | if len(errs) != 1 { | 
|  | return false | 
|  | } | 
|  | if strings.Contains(errs[0].Error(), expectedErr.Error()) { | 
|  | return true | 
|  | } | 
|  |  | 
|  | return false | 
|  | } | 
|  |  | 
|  | func errored(t *testing.T, tc Bp2buildTestCase, errs []error) bool { | 
|  | t.Helper() | 
|  | if tc.ExpectedErr != nil { | 
|  | // Rely on checkErrors, as this test case is expected to have an error. | 
|  | return false | 
|  | } | 
|  |  | 
|  | if len(errs) > 0 { | 
|  | for _, err := range errs { | 
|  | t.Errorf("%s: %s", tc.Description, err) | 
|  | } | 
|  | return true | 
|  | } | 
|  |  | 
|  | // All good, continue execution. | 
|  | return false | 
|  | } | 
|  |  | 
|  | func RunBp2BuildTestCaseSimple(t *testing.T, tc Bp2buildTestCase) { | 
|  | t.Helper() | 
|  | RunBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, tc) | 
|  | } | 
|  |  | 
|  | type Bp2buildTestCase struct { | 
|  | Description                string | 
|  | ModuleTypeUnderTest        string | 
|  | ModuleTypeUnderTestFactory android.ModuleFactory | 
|  | // Text to add to the toplevel, root Android.bp file. If Dir is not set, all | 
|  | // ExpectedBazelTargets are assumed to be generated by this file. | 
|  | Blueprint string | 
|  | // ExpectedBazelTargets compares the BazelTargets generated in `Dir` (if not empty). | 
|  | // Otherwise, it checks the BazelTargets generated by `Blueprint` in the root directory. | 
|  | ExpectedBazelTargets []string | 
|  | // ExpectedConvertedModules asserts that modules in this list are labeled as "converted | 
|  | // by bp2build" in the metrics reported by bp2build. | 
|  | ExpectedConvertedModules []string | 
|  | // ExpectedHandcraftedModules asserts that modules in this list are labeled as "handcrafted | 
|  | // in build files" in the metrics reported by bp2build. Such modules are either explicitly | 
|  | // defined in a BUILD file (by name), or registered as "otherwise implicitly handled" | 
|  | // by bp2build (for example, by macros owned by other modules). | 
|  | ExpectedHandcraftedModules []string | 
|  |  | 
|  | // AlreadyExistingBuildContents, if non-empty, simulates an already-present source BUILD file | 
|  | // in the directory under test. The BUILD file has the given contents. This BUILD file | 
|  | // will also be treated as "BUILD file to keep" by the simulated bp2build environment. | 
|  | AlreadyExistingBuildContents string | 
|  |  | 
|  | // StubbedBuildDefinitions, if non-empty, adds stub definitions to already-present source | 
|  | // BUILD files for each bazel label given. The BUILD files with these stub definitions | 
|  | // are added to the BUILD file given in AlreadyExistingBuildContents. | 
|  | // Labels may be of the form //pkg/to:target_name (which would be defined in pkg/to/BUILD.bazel) | 
|  | // or `target_name` (which would be defined in ./BUILD.bazel). | 
|  | StubbedBuildDefinitions []string | 
|  |  | 
|  | Filesystem map[string]string | 
|  | // Dir sets the directory which will be compared against the targets in ExpectedBazelTargets. | 
|  | // This should used in conjunction with the Filesystem property to check for targets | 
|  | // generated from a directory that is not the root. | 
|  | // If not set, all ExpectedBazelTargets are assumed to be generated by the text in the | 
|  | // Blueprint property. | 
|  | Dir string | 
|  | // An error with a string contained within the string of the expected error | 
|  | ExpectedErr         error | 
|  | UnconvertedDepsMode unconvertedDepsMode | 
|  |  | 
|  | // For every directory listed here, the BUILD file for that directory will | 
|  | // be merged with the generated BUILD file. This allows custom BUILD targets | 
|  | // to be used in tests, or use BUILD files to draw package boundaries. | 
|  | KeepBuildFileForDirs []string | 
|  |  | 
|  | // If true, the bp2build_deps mutator is used for this test. This is an | 
|  | // experimental mutator that will disable modules which have transitive | 
|  | // dependencies with no bazel definition. | 
|  | // TODO: b/285631638 - Enable this feature by default. | 
|  | DepsMutator bool | 
|  | } | 
|  |  | 
|  | func RunBp2BuildTestCaseExtraContext(t *testing.T, registerModuleTypes func(ctx android.RegistrationContext), modifyContext func(ctx *android.TestContext), tc Bp2buildTestCase) { | 
|  | t.Helper() | 
|  | preparers := []android.FixturePreparer{ | 
|  | android.FixtureRegisterWithContext(registerModuleTypes), | 
|  | } | 
|  | if modifyContext != nil { | 
|  | preparers = append(preparers, android.FixtureModifyContext(modifyContext)) | 
|  | } | 
|  | if tc.DepsMutator { | 
|  | preparers = append(preparers, android.FixtureModifyConfig(func(cfg android.Config) { | 
|  | cfg.Bp2buildDepsMutator = true | 
|  | })) | 
|  | } | 
|  | preparers = append(preparers, SetBp2BuildTestRunner) | 
|  | bp2buildSetup := android.GroupFixturePreparers( | 
|  | preparers..., | 
|  | ) | 
|  | runBp2BuildTestCaseWithSetup(t, bp2buildSetup, tc) | 
|  | } | 
|  |  | 
|  | func RunBp2BuildTestCase(t *testing.T, registerModuleTypes func(ctx android.RegistrationContext), tc Bp2buildTestCase) { | 
|  | t.Helper() | 
|  | RunBp2BuildTestCaseExtraContext(t, registerModuleTypes, nil, tc) | 
|  | } | 
|  |  | 
|  | func runBp2BuildTestCaseWithSetup(t *testing.T, extraPreparer android.FixturePreparer, tc Bp2buildTestCase) { | 
|  | t.Helper() | 
|  | if tc.Filesystem == nil { | 
|  | tc.Filesystem = map[string]string{} | 
|  | } | 
|  | checkDir := "." | 
|  | if tc.Dir != "" { | 
|  | checkDir = tc.Dir | 
|  | } | 
|  | keepExistingBuildDirs := tc.KeepBuildFileForDirs | 
|  | buildFilesToParse := []string{} | 
|  |  | 
|  | if len(tc.StubbedBuildDefinitions) > 0 { | 
|  | for _, buildDef := range tc.StubbedBuildDefinitions { | 
|  | globalLabelMatch := labelRegex.FindStringSubmatch(buildDef) | 
|  | var dir, targetName string | 
|  | if len(globalLabelMatch) > 0 { | 
|  | dir = globalLabelMatch[1] | 
|  | targetName = globalLabelMatch[2] | 
|  | } else { | 
|  | if !simpleModuleNameRegex.MatchString(buildDef) { | 
|  | t.Errorf("Stubbed build definition '%s' must be either a simple module name or of global target syntax (//foo/bar:baz).", buildDef) | 
|  | return | 
|  | } | 
|  | dir = "." | 
|  | targetName = buildDef | 
|  | } | 
|  | buildFilePath := filepath.Join(dir, "BUILD") | 
|  | tc.Filesystem[buildFilePath] += | 
|  | MakeBazelTarget( | 
|  | "bp2build_test_stub", | 
|  | targetName, | 
|  | AttrNameToString{}) | 
|  | keepExistingBuildDirs = append(keepExistingBuildDirs, dir) | 
|  | buildFilesToParse = append(buildFilesToParse, buildFilePath) | 
|  | } | 
|  | } | 
|  | if len(tc.AlreadyExistingBuildContents) > 0 { | 
|  | buildFilePath := filepath.Join(checkDir, "BUILD") | 
|  | tc.Filesystem[buildFilePath] += tc.AlreadyExistingBuildContents | 
|  | keepExistingBuildDirs = append(keepExistingBuildDirs, checkDir) | 
|  | buildFilesToParse = append(buildFilesToParse, buildFilePath) | 
|  | } | 
|  | filesystem := make(map[string][]byte) | 
|  | for f, content := range tc.Filesystem { | 
|  | filesystem[f] = []byte(content) | 
|  | } | 
|  | preparers := []android.FixturePreparer{ | 
|  | extraPreparer, | 
|  | android.FixtureMergeMockFs(filesystem), | 
|  | android.FixtureWithRootAndroidBp(tc.Blueprint), | 
|  | android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) { | 
|  | ctx.RegisterModuleType(tc.ModuleTypeUnderTest, tc.ModuleTypeUnderTestFactory) | 
|  | }), | 
|  | android.FixtureModifyContextWithMockFs(func(ctx *android.TestContext) { | 
|  | // A default configuration for tests to not have to specify bp2build_available on top level | 
|  | // targets. | 
|  | bp2buildConfig := android.NewBp2BuildAllowlist().SetDefaultConfig( | 
|  | allowlists.Bp2BuildConfig{ | 
|  | android.Bp2BuildTopLevel: allowlists.Bp2BuildDefaultTrueRecursively, | 
|  | }, | 
|  | ) | 
|  | for _, f := range keepExistingBuildDirs { | 
|  | bp2buildConfig.SetKeepExistingBuildFile(map[string]bool{ | 
|  | f: /*recursive=*/ false, | 
|  | }) | 
|  | } | 
|  | ctx.RegisterBp2BuildConfig(bp2buildConfig) | 
|  | // This setting is added to bp2build invocations. It prevents bp2build | 
|  | // from cloning modules to their original state after mutators run. This | 
|  | // would lose some data intentionally set by these mutators. | 
|  | ctx.SkipCloneModulesAfterMutators = true | 
|  | err := ctx.RegisterExistingBazelTargets(".", buildFilesToParse) | 
|  | if err != nil { | 
|  | t.Errorf("error parsing build files in test setup: %s", err) | 
|  | } | 
|  | }), | 
|  | android.FixtureModifyEnv(func(env map[string]string) { | 
|  | if tc.UnconvertedDepsMode == errorModulesUnconvertedDeps { | 
|  | env["BP2BUILD_ERROR_UNCONVERTED"] = "true" | 
|  | } | 
|  | }), | 
|  | } | 
|  |  | 
|  | preparer := android.GroupFixturePreparers(preparers...) | 
|  | if tc.ExpectedErr != nil { | 
|  | pattern := "\\Q" + tc.ExpectedErr.Error() + "\\E" | 
|  | preparer = preparer.ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern(pattern)) | 
|  | } | 
|  | result := preparer.RunTestWithCustomResult(t).(*BazelTestResult) | 
|  | if len(result.Errs) > 0 { | 
|  | return | 
|  | } | 
|  |  | 
|  | expectedTargets := map[string][]string{ | 
|  | checkDir: tc.ExpectedBazelTargets, | 
|  | } | 
|  |  | 
|  | result.CompareAllBazelTargets(t, tc, expectedTargets, true) | 
|  | } | 
|  |  | 
|  | // SetBp2BuildTestRunner customizes the test fixture mechanism to run tests in Bp2Build mode. | 
|  | var SetBp2BuildTestRunner = android.FixtureSetTestRunner(&bazelTestRunner{}) | 
|  |  | 
|  | // bazelTestRunner customizes the test fixture mechanism to run tests of the bp2build build mode. | 
|  | type bazelTestRunner struct{} | 
|  |  | 
|  | func (b *bazelTestRunner) FinalPreparer(result *android.TestResult) android.CustomTestResult { | 
|  | ctx := result.TestContext | 
|  | ctx.RegisterForBazelConversion() | 
|  |  | 
|  | return &BazelTestResult{TestResult: result} | 
|  | } | 
|  |  | 
|  | func (b *bazelTestRunner) PostParseProcessor(result android.CustomTestResult) { | 
|  | bazelResult := result.(*BazelTestResult) | 
|  | ctx := bazelResult.TestContext | 
|  | config := bazelResult.Config | 
|  | _, errs := ctx.ResolveDependencies(config) | 
|  | if bazelResult.CollateErrs(errs) { | 
|  | return | 
|  | } | 
|  |  | 
|  | codegenCtx := NewCodegenContext(config, ctx.Context, Bp2Build, "") | 
|  | res, errs := GenerateBazelTargets(codegenCtx, false) | 
|  | if bazelResult.CollateErrs(errs) { | 
|  | return | 
|  | } | 
|  |  | 
|  | // Store additional data for access by tests. | 
|  | bazelResult.conversionResults = res | 
|  | } | 
|  |  | 
|  | // BazelTestResult is a wrapper around android.TestResult to provide type safe access to the bazel | 
|  | // specific data stored by the bazelTestRunner. | 
|  | type BazelTestResult struct { | 
|  | *android.TestResult | 
|  |  | 
|  | // The result returned by the GenerateBazelTargets function. | 
|  | conversionResults | 
|  | } | 
|  |  | 
|  | // CompareAllBazelTargets compares the BazelTargets produced by the test for all the directories | 
|  | // with the supplied set of expected targets. | 
|  | // | 
|  | // If ignoreUnexpected=false then this enforces an exact match where every BazelTarget produced must | 
|  | // have a corresponding expected BazelTarget. | 
|  | // | 
|  | // If ignoreUnexpected=true then it will ignore directories for which there are no expected targets. | 
|  | func (b BazelTestResult) CompareAllBazelTargets(t *testing.T, tc Bp2buildTestCase, expectedTargets map[string][]string, ignoreUnexpected bool) { | 
|  | t.Helper() | 
|  | actualTargets := b.buildFileToTargets | 
|  |  | 
|  | // Generate the sorted set of directories to check. | 
|  | dirsToCheck := android.SortedKeys(expectedTargets) | 
|  | if !ignoreUnexpected { | 
|  | // This needs to perform an exact match so add the directories in which targets were | 
|  | // produced to the list of directories to check. | 
|  | dirsToCheck = append(dirsToCheck, android.SortedKeys(actualTargets)...) | 
|  | dirsToCheck = android.SortedUniqueStrings(dirsToCheck) | 
|  | } | 
|  |  | 
|  | for _, dir := range dirsToCheck { | 
|  | expected := expectedTargets[dir] | 
|  | actual := actualTargets[dir] | 
|  |  | 
|  | if expected == nil { | 
|  | if actual != nil { | 
|  | t.Errorf("did not expect any bazel modules in %q but found %d", dir, len(actual)) | 
|  | } | 
|  | } else if actual == nil { | 
|  | expectedCount := len(expected) | 
|  | if expectedCount > 0 { | 
|  | t.Errorf("expected %d bazel modules in %q but did not find any", expectedCount, dir) | 
|  | } | 
|  | } else { | 
|  | b.CompareBazelTargets(t, tc.Description, expected, actual) | 
|  | } | 
|  | } | 
|  |  | 
|  | for _, module := range tc.ExpectedConvertedModules { | 
|  | if _, found := b.metrics.convertedModulePathMap[module]; !found { | 
|  | t.Errorf("expected %s to be generated by bp2build, but was not. Map of converted modules: %s", module, b.metrics.convertedModulePathMap) | 
|  | } | 
|  | } | 
|  |  | 
|  | for _, module := range tc.ExpectedHandcraftedModules { | 
|  | if reason, found := b.metrics.serialized.UnconvertedModules[module]; !found { | 
|  | t.Errorf("expected %s to be marked 'unconverted' by bp2build, but was not found. Full list: %s", | 
|  | module, b.metrics.serialized.UnconvertedModules) | 
|  | } else { | 
|  | if reason.Type != bp2build_metrics_proto.UnconvertedReasonType_DEFINED_IN_BUILD_FILE { | 
|  | t.Errorf("expected %s to be marked 'handcrafted' by bp2build, but was disabled for another reason: %s", module, reason) | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | func (b BazelTestResult) CompareBazelTargets(t *testing.T, description string, expectedContents []string, actualTargets BazelTargets) { | 
|  | t.Helper() | 
|  | if actualCount, expectedCount := len(actualTargets), len(expectedContents); actualCount != expectedCount { | 
|  | t.Errorf("%s: Expected %d bazel target (%s), got %d (%s)", | 
|  | description, expectedCount, expectedContents, actualCount, actualTargets) | 
|  | } else { | 
|  | sort.SliceStable(actualTargets, func(i, j int) bool { | 
|  | return actualTargets[i].name < actualTargets[j].name | 
|  | }) | 
|  | sort.SliceStable(expectedContents, func(i, j int) bool { | 
|  | return getTargetName(expectedContents[i]) < getTargetName(expectedContents[j]) | 
|  | }) | 
|  | for i, actualTarget := range actualTargets { | 
|  | if w, g := expectedContents[i], actualTarget.content; w != g { | 
|  | t.Errorf( | 
|  | "%s[%d]: Expected generated Bazel target to be `%s`, got `%s`", | 
|  | description, i, w, g) | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | type nestedProps struct { | 
|  | Nested_prop *string | 
|  | } | 
|  |  | 
|  | type EmbeddedProps struct { | 
|  | Embedded_prop *string | 
|  | } | 
|  |  | 
|  | type OtherEmbeddedProps struct { | 
|  | Other_embedded_prop *string | 
|  | } | 
|  |  | 
|  | type customProps struct { | 
|  | EmbeddedProps | 
|  | *OtherEmbeddedProps | 
|  |  | 
|  | Bool_prop     bool | 
|  | Bool_ptr_prop *bool | 
|  | // Ensure that properties tagged `blueprint:mutated` are omitted | 
|  | Int_prop            int `blueprint:"mutated"` | 
|  | Int64_ptr_prop      *int64 | 
|  | String_prop         string | 
|  | String_literal_prop *string `android:"arch_variant"` | 
|  | String_ptr_prop     *string | 
|  | String_list_prop    []string | 
|  |  | 
|  | Nested_props     nestedProps | 
|  | Nested_props_ptr *nestedProps | 
|  |  | 
|  | Arch_paths         []string `android:"path,arch_variant"` | 
|  | Arch_paths_exclude []string `android:"path,arch_variant"` | 
|  |  | 
|  | // Prop used to indicate this conversion should be 1 module -> multiple targets | 
|  | One_to_many_prop *bool | 
|  |  | 
|  | // Prop used to simulate an unsupported property in bp2build conversion. If this | 
|  | // is true, this module should be treated as "unconvertible" via bp2build. | 
|  | Does_not_convert_to_bazel *bool | 
|  |  | 
|  | Api *string // File describing the APIs of this module | 
|  |  | 
|  | Test_config_setting *bool // Used to test generation of config_setting targets | 
|  |  | 
|  | Dir *string // Dir in which the Bazel Target will be created | 
|  | } | 
|  |  | 
|  | type customModule struct { | 
|  | android.ModuleBase | 
|  | android.BazelModuleBase | 
|  |  | 
|  | props customProps | 
|  | } | 
|  |  | 
|  | // OutputFiles is needed because some instances of this module use dist with a | 
|  | // tag property which requires the module implements OutputFileProducer. | 
|  | func (m *customModule) OutputFiles(tag string) (android.Paths, error) { | 
|  | return android.PathsForTesting("path" + tag), nil | 
|  | } | 
|  |  | 
|  | func (m *customModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { | 
|  | // nothing for now. | 
|  | } | 
|  |  | 
|  | func customModuleFactoryBase() android.Module { | 
|  | module := &customModule{} | 
|  | module.AddProperties(&module.props) | 
|  | android.InitBazelModule(module) | 
|  | return module | 
|  | } | 
|  |  | 
|  | func customModuleFactoryHostAndDevice() android.Module { | 
|  | m := customModuleFactoryBase() | 
|  | android.InitAndroidArchModule(m, android.HostAndDeviceSupported, android.MultilibBoth) | 
|  | return m | 
|  | } | 
|  |  | 
|  | func customModuleFactoryDeviceSupported() android.Module { | 
|  | m := customModuleFactoryBase() | 
|  | android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibBoth) | 
|  | return m | 
|  | } | 
|  |  | 
|  | func customModuleFactoryHostSupported() android.Module { | 
|  | m := customModuleFactoryBase() | 
|  | android.InitAndroidArchModule(m, android.HostSupported, android.MultilibBoth) | 
|  | return m | 
|  | } | 
|  |  | 
|  | func customModuleFactoryHostAndDeviceDefault() android.Module { | 
|  | m := customModuleFactoryBase() | 
|  | android.InitAndroidArchModule(m, android.HostAndDeviceDefault, android.MultilibBoth) | 
|  | return m | 
|  | } | 
|  |  | 
|  | func customModuleFactoryNeitherHostNorDeviceSupported() android.Module { | 
|  | m := customModuleFactoryBase() | 
|  | android.InitAndroidArchModule(m, android.NeitherHostNorDeviceSupported, android.MultilibBoth) | 
|  | return m | 
|  | } | 
|  |  | 
|  | type testProps struct { | 
|  | Test_prop struct { | 
|  | Test_string_prop string | 
|  | } | 
|  | } | 
|  |  | 
|  | type customTestModule struct { | 
|  | android.ModuleBase | 
|  |  | 
|  | props      customProps | 
|  | test_props testProps | 
|  | } | 
|  |  | 
|  | func (m *customTestModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { | 
|  | // nothing for now. | 
|  | } | 
|  |  | 
|  | func customTestModuleFactoryBase() android.Module { | 
|  | m := &customTestModule{} | 
|  | m.AddProperties(&m.props) | 
|  | m.AddProperties(&m.test_props) | 
|  | return m | 
|  | } | 
|  |  | 
|  | func customTestModuleFactory() android.Module { | 
|  | m := customTestModuleFactoryBase() | 
|  | android.InitAndroidModule(m) | 
|  | return m | 
|  | } | 
|  |  | 
|  | type customDefaultsModule struct { | 
|  | android.ModuleBase | 
|  | android.DefaultsModuleBase | 
|  | } | 
|  |  | 
|  | func customDefaultsModuleFactoryBase() android.DefaultsModule { | 
|  | module := &customDefaultsModule{} | 
|  | module.AddProperties(&customProps{}) | 
|  | return module | 
|  | } | 
|  |  | 
|  | func customDefaultsModuleFactoryBasic() android.Module { | 
|  | return customDefaultsModuleFactoryBase() | 
|  | } | 
|  |  | 
|  | func customDefaultsModuleFactory() android.Module { | 
|  | m := customDefaultsModuleFactoryBase() | 
|  | android.InitDefaultsModule(m) | 
|  | return m | 
|  | } | 
|  |  | 
|  | type EmbeddedAttr struct { | 
|  | Embedded_attr *string | 
|  | } | 
|  |  | 
|  | type OtherEmbeddedAttr struct { | 
|  | Other_embedded_attr *string | 
|  | } | 
|  |  | 
|  | type customBazelModuleAttributes struct { | 
|  | EmbeddedAttr | 
|  | *OtherEmbeddedAttr | 
|  | String_literal_prop bazel.StringAttribute | 
|  | String_ptr_prop     *string | 
|  | String_list_prop    []string | 
|  | Arch_paths          bazel.LabelListAttribute | 
|  | Api                 bazel.LabelAttribute | 
|  | } | 
|  |  | 
|  | func (m *customModule) dir() *string { | 
|  | return m.props.Dir | 
|  | } | 
|  |  | 
|  | func (m *customModule) ConvertWithBp2build(ctx android.Bp2buildMutatorContext) { | 
|  | if p := m.props.Does_not_convert_to_bazel; p != nil && *p { | 
|  | ctx.MarkBp2buildUnconvertible(bp2build_metrics_proto.UnconvertedReasonType_PROPERTY_UNSUPPORTED, "") | 
|  | return | 
|  | } | 
|  | if p := m.props.One_to_many_prop; p != nil && *p { | 
|  | customBp2buildOneToMany(ctx, m) | 
|  | return | 
|  | } | 
|  |  | 
|  | paths := bazel.LabelListAttribute{} | 
|  | strAttr := bazel.StringAttribute{} | 
|  | for axis, configToProps := range m.GetArchVariantProperties(ctx, &customProps{}) { | 
|  | for config, props := range configToProps { | 
|  | if custProps, ok := props.(*customProps); ok { | 
|  | if custProps.Arch_paths != nil { | 
|  | paths.SetSelectValue(axis, config, android.BazelLabelForModuleSrcExcludes(ctx, custProps.Arch_paths, custProps.Arch_paths_exclude)) | 
|  | } | 
|  | if custProps.String_literal_prop != nil { | 
|  | strAttr.SetSelectValue(axis, config, custProps.String_literal_prop) | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | productVariableProps, errs := android.ProductVariableProperties(ctx, ctx.Module()) | 
|  | for _, err := range errs { | 
|  | ctx.ModuleErrorf("ProductVariableProperties error: %s", err) | 
|  | } | 
|  | if props, ok := productVariableProps["String_literal_prop"]; ok { | 
|  | for c, p := range props { | 
|  | if val, ok := p.(*string); ok { | 
|  | strAttr.SetSelectValue(c.ConfigurationAxis(), c.SelectKey(), val) | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | paths.ResolveExcludes() | 
|  |  | 
|  | attrs := &customBazelModuleAttributes{ | 
|  | String_literal_prop: strAttr, | 
|  | String_ptr_prop:     m.props.String_ptr_prop, | 
|  | String_list_prop:    m.props.String_list_prop, | 
|  | Arch_paths:          paths, | 
|  | } | 
|  |  | 
|  | attrs.Embedded_attr = m.props.Embedded_prop | 
|  | if m.props.OtherEmbeddedProps != nil { | 
|  | attrs.OtherEmbeddedAttr = &OtherEmbeddedAttr{Other_embedded_attr: m.props.OtherEmbeddedProps.Other_embedded_prop} | 
|  | } | 
|  |  | 
|  | props := bazel.BazelTargetModuleProperties{ | 
|  | Rule_class: "custom", | 
|  | } | 
|  |  | 
|  | ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: m.Name(), Dir: m.dir()}, attrs) | 
|  |  | 
|  | if proptools.Bool(m.props.Test_config_setting) { | 
|  | m.createConfigSetting(ctx) | 
|  | } | 
|  |  | 
|  | } | 
|  |  | 
|  | func (m *customModule) createConfigSetting(ctx android.Bp2buildMutatorContext) { | 
|  | csa := bazel.ConfigSettingAttributes{ | 
|  | Flag_values: bazel.StringMapAttribute{ | 
|  | "//build/bazel/rules/my_string_setting": m.Name(), | 
|  | }, | 
|  | } | 
|  | ca := android.CommonAttributes{ | 
|  | Name: m.Name() + "_config_setting", | 
|  | } | 
|  | ctx.CreateBazelConfigSetting( | 
|  | csa, | 
|  | ca, | 
|  | ctx.ModuleDir(), | 
|  | ) | 
|  | } | 
|  |  | 
|  | // A bp2build mutator that uses load statements and creates a 1:M mapping from | 
|  | // module to target. | 
|  | func customBp2buildOneToMany(ctx android.Bp2buildMutatorContext, m *customModule) { | 
|  |  | 
|  | baseName := m.Name() | 
|  | attrs := &customBazelModuleAttributes{} | 
|  |  | 
|  | myLibraryProps := bazel.BazelTargetModuleProperties{ | 
|  | Rule_class:        "my_library", | 
|  | Bzl_load_location: "//build/bazel/rules:rules.bzl", | 
|  | } | 
|  | ctx.CreateBazelTargetModule(myLibraryProps, android.CommonAttributes{Name: baseName}, attrs) | 
|  |  | 
|  | protoLibraryProps := bazel.BazelTargetModuleProperties{ | 
|  | Rule_class:        "proto_library", | 
|  | Bzl_load_location: "//build/bazel/rules:proto.bzl", | 
|  | } | 
|  | ctx.CreateBazelTargetModule(protoLibraryProps, android.CommonAttributes{Name: baseName + "_proto_library_deps"}, attrs) | 
|  |  | 
|  | myProtoLibraryProps := bazel.BazelTargetModuleProperties{ | 
|  | Rule_class:        "my_proto_library", | 
|  | Bzl_load_location: "//build/bazel/rules:proto.bzl", | 
|  | } | 
|  | ctx.CreateBazelTargetModule(myProtoLibraryProps, android.CommonAttributes{Name: baseName + "_my_proto_library_deps"}, attrs) | 
|  | } | 
|  |  | 
|  | // Helper method for tests to easily access the targets in a dir. | 
|  | func generateBazelTargetsForDir(codegenCtx *CodegenContext, dir string) (BazelTargets, []error) { | 
|  | // TODO: Set generateFilegroups to true and/or remove the generateFilegroups argument completely | 
|  | res, err := GenerateBazelTargets(codegenCtx, false) | 
|  | if err != nil { | 
|  | return BazelTargets{}, err | 
|  | } | 
|  | return res.buildFileToTargets[dir], err | 
|  | } | 
|  |  | 
|  | func registerCustomModuleForBp2buildConversion(ctx *android.TestContext) { | 
|  | ctx.RegisterModuleType("custom", customModuleFactoryHostAndDevice) | 
|  | ctx.RegisterForBazelConversion() | 
|  | } | 
|  |  | 
|  | func simpleModule(typ, name string) string { | 
|  | return fmt.Sprintf(` | 
|  | %s { | 
|  | name: "%s", | 
|  | }`, typ, name) | 
|  | } | 
|  |  | 
|  | type AttrNameToString map[string]string | 
|  |  | 
|  | func (a AttrNameToString) clone() AttrNameToString { | 
|  | newAttrs := make(AttrNameToString, len(a)) | 
|  | for k, v := range a { | 
|  | newAttrs[k] = v | 
|  | } | 
|  | return newAttrs | 
|  | } | 
|  |  | 
|  | // makeBazelTargetNoRestrictions returns bazel target build file definition that can be host or | 
|  | // device specific, or independent of host/device. | 
|  | func makeBazelTargetHostOrDevice(typ, name string, attrs AttrNameToString, hod android.HostOrDeviceSupported) string { | 
|  | if _, ok := attrs["target_compatible_with"]; !ok { | 
|  | switch hod { | 
|  | case android.HostSupported: | 
|  | attrs["target_compatible_with"] = `select({ | 
|  | "//build/bazel/platforms/os:android": ["@platforms//:incompatible"], | 
|  | "//conditions:default": [], | 
|  | })` | 
|  | case android.DeviceSupported: | 
|  | attrs["target_compatible_with"] = `["//build/bazel/platforms/os:android"]` | 
|  | } | 
|  | } | 
|  |  | 
|  | attrStrings := make([]string, 0, len(attrs)+1) | 
|  | if name != "" { | 
|  | attrStrings = append(attrStrings, fmt.Sprintf(`    name = "%s",`, name)) | 
|  | } | 
|  | for _, k := range android.SortedKeys(attrs) { | 
|  | attrStrings = append(attrStrings, fmt.Sprintf("    %s = %s,", k, attrs[k])) | 
|  | } | 
|  | return fmt.Sprintf(`%s( | 
|  | %s | 
|  | )`, typ, strings.Join(attrStrings, "\n")) | 
|  | } | 
|  |  | 
|  | // MakeBazelTargetNoRestrictions returns bazel target build file definition that does not add a | 
|  | // target_compatible_with.  This is useful for module types like filegroup and genrule that arch not | 
|  | // arch variant | 
|  | func MakeBazelTargetNoRestrictions(typ, name string, attrs AttrNameToString) string { | 
|  | return makeBazelTargetHostOrDevice(typ, name, attrs, android.HostAndDeviceDefault) | 
|  | } | 
|  |  | 
|  | // makeBazelTargetNoRestrictions returns bazel target build file definition that is device specific | 
|  | // as this is the most common default in Soong. | 
|  | func MakeBazelTarget(typ, name string, attrs AttrNameToString) string { | 
|  | return makeBazelTargetHostOrDevice(typ, name, attrs, android.DeviceSupported) | 
|  | } | 
|  |  | 
|  | type ExpectedRuleTarget struct { | 
|  | Rule  string | 
|  | Name  string | 
|  | Attrs AttrNameToString | 
|  | Hod   android.HostOrDeviceSupported | 
|  | } | 
|  |  | 
|  | func (ebr ExpectedRuleTarget) String() string { | 
|  | return makeBazelTargetHostOrDevice(ebr.Rule, ebr.Name, ebr.Attrs, ebr.Hod) | 
|  | } | 
|  |  | 
|  | func makeCcStubSuiteTargets(name string, attrs AttrNameToString) string { | 
|  | if _, hasStubs := attrs["stubs_symbol_file"]; !hasStubs { | 
|  | return "" | 
|  | } | 
|  | STUB_SUITE_ATTRS := map[string]string{ | 
|  | "api_surface":          "api_surface", | 
|  | "stubs_symbol_file":    "symbol_file", | 
|  | "stubs_versions":       "versions", | 
|  | "soname":               "soname", | 
|  | "source_library_label": "source_library_label", | 
|  | } | 
|  |  | 
|  | stubSuiteAttrs := AttrNameToString{} | 
|  | for key, _ := range attrs { | 
|  | if _, stubSuiteAttr := STUB_SUITE_ATTRS[key]; stubSuiteAttr { | 
|  | stubSuiteAttrs[STUB_SUITE_ATTRS[key]] = attrs[key] | 
|  | } else { | 
|  | panic(fmt.Sprintf("unused cc_stub_suite attr %q\n", key)) | 
|  | } | 
|  | } | 
|  | return MakeBazelTarget("cc_stub_suite", name+"_stub_libs", stubSuiteAttrs) | 
|  | } | 
|  |  | 
|  | func MakeNeverlinkDuplicateTarget(moduleType string, name string) string { | 
|  | return MakeNeverlinkDuplicateTargetWithAttrs(moduleType, name, AttrNameToString{ | 
|  | "sdk_version": `"current"`, // use as default | 
|  | }) | 
|  | } | 
|  |  | 
|  | func MakeNeverlinkDuplicateTargetWithAttrs(moduleType string, name string, extraAttrs AttrNameToString) string { | 
|  | attrs := extraAttrs | 
|  | attrs["neverlink"] = `True` | 
|  | attrs["exports"] = `[":` + name + `"]` | 
|  | return MakeBazelTarget(moduleType, name+"-neverlink", attrs) | 
|  | } | 
|  |  | 
|  | func getTargetName(targetContent string) string { | 
|  | data := strings.Split(targetContent, "name = \"") | 
|  | if len(data) < 2 { | 
|  | return "" | 
|  | } else { | 
|  | endIndex := strings.Index(data[1], "\"") | 
|  | return data[1][:endIndex] | 
|  | } | 
|  | } |