blob: c9781647bd5cb84c1468bd4108cc23c6cf9eac0d [file] [log] [blame]
Alex Márquez Pérez Muñíz Díaz Púras Thaureauxf5a3eac2021-08-23 17:05:17 +00001// Copyright 2021 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
Liz Kammer2dd9ca42020-11-25 16:06:39 -080015package bp2build
16
Alex Márquez Pérez Muñíz Díaz Púras Thaureaux0da7ce62021-08-23 17:04:20 +000017/*
18For shareable/common bp2build testing functionality and dumping ground for
19specific-but-shared functionality among tests in package
20*/
21
Liz Kammer2dd9ca42020-11-25 16:06:39 -080022import (
Liz Kammer7a210ac2021-09-22 15:52:58 -040023 "fmt"
Chris Parsons5011e612023-09-13 23:33:20 +000024 "path/filepath"
Chris Parsonscd209032023-09-19 01:12:48 +000025 "regexp"
Zi Wangfba0a212023-03-07 16:48:19 -080026 "sort"
Alex Márquez Pérez Muñíz Díaz Púras Thaureaux1c92aef2021-08-23 16:10:00 +000027 "strings"
Rupert Shuttleworth06559d02021-05-19 09:14:26 -040028 "testing"
29
Chris Parsons2173b5f2023-09-25 19:01:40 +000030 "android/soong/ui/metrics/bp2build_metrics_proto"
Cole Faust11edf552023-10-13 11:32:14 -070031
Spandan Das5af0bd32022-09-28 20:43:08 +000032 "github.com/google/blueprint/proptools"
33
Liz Kammer2dd9ca42020-11-25 16:06:39 -080034 "android/soong/android"
Sam Delmerico24c56032022-03-28 19:53:03 +000035 "android/soong/android/allowlists"
Jingwen Chen73850672020-12-14 08:25:34 -050036 "android/soong/bazel"
Liz Kammer2dd9ca42020-11-25 16:06:39 -080037)
38
Jingwen Chen91220d72021-03-24 02:18:33 -040039var (
Rupert Shuttleworth06559d02021-05-19 09:14:26 -040040 buildDir string
Jingwen Chen91220d72021-03-24 02:18:33 -040041)
42
Chris Parsonscd209032023-09-19 01:12:48 +000043var labelRegex = regexp.MustCompile(`^//([^: ]+):([^ ]+)$`)
44var simpleModuleNameRegex = regexp.MustCompile(`^[^: /]+$`)
45
Jingwen Chen5146ac02021-09-02 11:44:42 +000046func checkError(t *testing.T, errs []error, expectedErr error) bool {
Alex Márquez Pérez Muñíz Díaz Púras Thaureaux1c92aef2021-08-23 16:10:00 +000047 t.Helper()
Jingwen Chen5146ac02021-09-02 11:44:42 +000048
Jingwen Chen5146ac02021-09-02 11:44:42 +000049 if len(errs) != 1 {
Liz Kammer6eff3232021-08-26 08:37:59 -040050 return false
Jingwen Chen5146ac02021-09-02 11:44:42 +000051 }
Liz Kammer54309532021-12-14 12:21:22 -050052 if strings.Contains(errs[0].Error(), expectedErr.Error()) {
Jingwen Chen5146ac02021-09-02 11:44:42 +000053 return true
54 }
55
56 return false
57}
58
Sam Delmerico3177a6e2022-06-21 19:28:33 +000059func errored(t *testing.T, tc Bp2buildTestCase, errs []error) bool {
Jingwen Chen5146ac02021-09-02 11:44:42 +000060 t.Helper()
Sam Delmerico3177a6e2022-06-21 19:28:33 +000061 if tc.ExpectedErr != nil {
Jingwen Chen5146ac02021-09-02 11:44:42 +000062 // Rely on checkErrors, as this test case is expected to have an error.
63 return false
64 }
65
Alex Márquez Pérez Muñíz Díaz Púras Thaureaux1c92aef2021-08-23 16:10:00 +000066 if len(errs) > 0 {
67 for _, err := range errs {
Sam Delmerico3177a6e2022-06-21 19:28:33 +000068 t.Errorf("%s: %s", tc.Description, err)
Alex Márquez Pérez Muñíz Díaz Púras Thaureaux1c92aef2021-08-23 16:10:00 +000069 }
70 return true
71 }
Jingwen Chen5146ac02021-09-02 11:44:42 +000072
73 // All good, continue execution.
Alex Márquez Pérez Muñíz Díaz Púras Thaureaux1c92aef2021-08-23 16:10:00 +000074 return false
75}
76
Trevor Radcliffe1b4b2d92022-09-01 18:57:01 +000077func RunBp2BuildTestCaseSimple(t *testing.T, tc Bp2buildTestCase) {
Alex Márquez Pérez Muñíz Díaz Púras Thaureaux1c92aef2021-08-23 16:10:00 +000078 t.Helper()
Sam Delmerico3177a6e2022-06-21 19:28:33 +000079 RunBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, tc)
Alex Márquez Pérez Muñíz Díaz Púras Thaureaux1c92aef2021-08-23 16:10:00 +000080}
81
Sam Delmerico3177a6e2022-06-21 19:28:33 +000082type Bp2buildTestCase struct {
83 Description string
84 ModuleTypeUnderTest string
85 ModuleTypeUnderTestFactory android.ModuleFactory
Sam Delmerico5840afc2023-06-12 15:44:03 -040086 // Text to add to the toplevel, root Android.bp file. If Dir is not set, all
87 // ExpectedBazelTargets are assumed to be generated by this file.
Chris Parsons39a16972023-06-08 14:28:51 +000088 Blueprint string
Sam Delmerico5840afc2023-06-12 15:44:03 -040089 // ExpectedBazelTargets compares the BazelTargets generated in `Dir` (if not empty).
90 // Otherwise, it checks the BazelTargets generated by `Blueprint` in the root directory.
91 ExpectedBazelTargets []string
Chris Parsons2173b5f2023-09-25 19:01:40 +000092 // ExpectedConvertedModules asserts that modules in this list are labeled as "converted
93 // by bp2build" in the metrics reported by bp2build.
94 ExpectedConvertedModules []string
95 // ExpectedHandcraftedModules asserts that modules in this list are labeled as "handcrafted
96 // in build files" in the metrics reported by bp2build. Such modules are either explicitly
97 // defined in a BUILD file (by name), or registered as "otherwise implicitly handled"
98 // by bp2build (for example, by macros owned by other modules).
99 ExpectedHandcraftedModules []string
100
Chris Parsons5011e612023-09-13 23:33:20 +0000101 // AlreadyExistingBuildContents, if non-empty, simulates an already-present source BUILD file
102 // in the directory under test. The BUILD file has the given contents. This BUILD file
103 // will also be treated as "BUILD file to keep" by the simulated bp2build environment.
104 AlreadyExistingBuildContents string
Chris Parsonscd209032023-09-19 01:12:48 +0000105
106 // StubbedBuildDefinitions, if non-empty, adds stub definitions to already-present source
107 // BUILD files for each bazel label given. The BUILD files with these stub definitions
108 // are added to the BUILD file given in AlreadyExistingBuildContents.
109 // Labels may be of the form //pkg/to:target_name (which would be defined in pkg/to/BUILD.bazel)
110 // or `target_name` (which would be defined in ./BUILD.bazel).
Chris Parsons5011e612023-09-13 23:33:20 +0000111 StubbedBuildDefinitions []string
112
113 Filesystem map[string]string
Sam Delmerico5840afc2023-06-12 15:44:03 -0400114 // Dir sets the directory which will be compared against the targets in ExpectedBazelTargets.
115 // This should used in conjunction with the Filesystem property to check for targets
116 // generated from a directory that is not the root.
117 // If not set, all ExpectedBazelTargets are assumed to be generated by the text in the
118 // Blueprint property.
119 Dir string
Trevor Radcliffe58ea4512022-04-07 20:36:39 +0000120 // An error with a string contained within the string of the expected error
Sam Delmerico3177a6e2022-06-21 19:28:33 +0000121 ExpectedErr error
122 UnconvertedDepsMode unconvertedDepsMode
Jingwen Chen0eeaeb82022-09-21 10:27:42 +0000123
124 // For every directory listed here, the BUILD file for that directory will
125 // be merged with the generated BUILD file. This allows custom BUILD targets
126 // to be used in tests, or use BUILD files to draw package boundaries.
127 KeepBuildFileForDirs []string
Alex Márquez Pérez Muñíz Díaz Púras Thaureaux1c92aef2021-08-23 16:10:00 +0000128
Cole Faust11edf552023-10-13 11:32:14 -0700129 // An extra FixturePreparer to use when running the test. If you need multiple extra
130 // FixturePreparers, use android.GroupFixturePreparers()
131 ExtraFixturePreparer android.FixturePreparer
132
133 // If bp2build_product_config.go should run as part of the test.
134 RunBp2buildProductConfig bool
Chris Parsons2173b5f2023-09-25 19:01:40 +0000135}
136
Sam Delmerico3177a6e2022-06-21 19:28:33 +0000137func RunBp2BuildTestCase(t *testing.T, registerModuleTypes func(ctx android.RegistrationContext), tc Bp2buildTestCase) {
Liz Kammerffc17e42022-11-23 09:42:05 -0500138 t.Helper()
Cole Faust11edf552023-10-13 11:32:14 -0700139 preparers := []android.FixturePreparer{
140 android.FixtureRegisterWithContext(registerModuleTypes),
141 }
142 if tc.ExtraFixturePreparer != nil {
143 preparers = append(preparers, tc.ExtraFixturePreparer)
144 }
145 preparers = append(preparers, android.FixtureSetTestRunner(&bazelTestRunner{generateProductConfigTargets: tc.RunBp2buildProductConfig}))
146 bp2buildSetup := android.GroupFixturePreparers(
147 preparers...,
148 )
149 runBp2BuildTestCaseWithSetup(t, bp2buildSetup, tc)
Spandan Das5af0bd32022-09-28 20:43:08 +0000150}
151
Paul Duffin4c0765a2022-10-29 17:48:00 +0100152func runBp2BuildTestCaseWithSetup(t *testing.T, extraPreparer android.FixturePreparer, tc Bp2buildTestCase) {
Alex Márquez Pérez Muñíz Díaz Púras Thaureaux1c92aef2021-08-23 16:10:00 +0000153 t.Helper()
Chris Parsonscd209032023-09-19 01:12:48 +0000154 if tc.Filesystem == nil {
155 tc.Filesystem = map[string]string{}
156 }
Chris Parsons5011e612023-09-13 23:33:20 +0000157 checkDir := "."
158 if tc.Dir != "" {
159 checkDir = tc.Dir
160 }
161 keepExistingBuildDirs := tc.KeepBuildFileForDirs
162 buildFilesToParse := []string{}
Chris Parsonscd209032023-09-19 01:12:48 +0000163
164 if len(tc.StubbedBuildDefinitions) > 0 {
165 for _, buildDef := range tc.StubbedBuildDefinitions {
166 globalLabelMatch := labelRegex.FindStringSubmatch(buildDef)
167 var dir, targetName string
168 if len(globalLabelMatch) > 0 {
169 dir = globalLabelMatch[1]
170 targetName = globalLabelMatch[2]
171 } else {
172 if !simpleModuleNameRegex.MatchString(buildDef) {
173 t.Errorf("Stubbed build definition '%s' must be either a simple module name or of global target syntax (//foo/bar:baz).", buildDef)
174 return
175 }
176 dir = "."
177 targetName = buildDef
178 }
179 buildFilePath := filepath.Join(dir, "BUILD")
180 tc.Filesystem[buildFilePath] +=
181 MakeBazelTarget(
182 "bp2build_test_stub",
183 targetName,
184 AttrNameToString{})
185 keepExistingBuildDirs = append(keepExistingBuildDirs, dir)
186 buildFilesToParse = append(buildFilesToParse, buildFilePath)
187 }
188 }
189 if len(tc.AlreadyExistingBuildContents) > 0 {
190 buildFilePath := filepath.Join(checkDir, "BUILD")
191 tc.Filesystem[buildFilePath] += tc.AlreadyExistingBuildContents
192 keepExistingBuildDirs = append(keepExistingBuildDirs, checkDir)
193 buildFilesToParse = append(buildFilesToParse, buildFilePath)
194 }
Alex Márquez Pérez Muñíz Díaz Púras Thaureaux1c92aef2021-08-23 16:10:00 +0000195 filesystem := make(map[string][]byte)
Sam Delmerico3177a6e2022-06-21 19:28:33 +0000196 for f, content := range tc.Filesystem {
Alex Márquez Pérez Muñíz Díaz Púras Thaureaux1c92aef2021-08-23 16:10:00 +0000197 filesystem[f] = []byte(content)
198 }
Paul Duffin4c0765a2022-10-29 17:48:00 +0100199 preparers := []android.FixturePreparer{
200 extraPreparer,
201 android.FixtureMergeMockFs(filesystem),
202 android.FixtureWithRootAndroidBp(tc.Blueprint),
203 android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
204 ctx.RegisterModuleType(tc.ModuleTypeUnderTest, tc.ModuleTypeUnderTestFactory)
205 }),
Chris Parsons5011e612023-09-13 23:33:20 +0000206 android.FixtureModifyContextWithMockFs(func(ctx *android.TestContext) {
Paul Duffin4c0765a2022-10-29 17:48:00 +0100207 // A default configuration for tests to not have to specify bp2build_available on top level
208 // targets.
209 bp2buildConfig := android.NewBp2BuildAllowlist().SetDefaultConfig(
210 allowlists.Bp2BuildConfig{
211 android.Bp2BuildTopLevel: allowlists.Bp2BuildDefaultTrueRecursively,
212 },
213 )
Chris Parsons5011e612023-09-13 23:33:20 +0000214 for _, f := range keepExistingBuildDirs {
Paul Duffin4c0765a2022-10-29 17:48:00 +0100215 bp2buildConfig.SetKeepExistingBuildFile(map[string]bool{
216 f: /*recursive=*/ false,
217 })
218 }
219 ctx.RegisterBp2BuildConfig(bp2buildConfig)
Chris Parsons39a16972023-06-08 14:28:51 +0000220 // This setting is added to bp2build invocations. It prevents bp2build
221 // from cloning modules to their original state after mutators run. This
222 // would lose some data intentionally set by these mutators.
223 ctx.SkipCloneModulesAfterMutators = true
Chris Parsons5011e612023-09-13 23:33:20 +0000224 err := ctx.RegisterExistingBazelTargets(".", buildFilesToParse)
225 if err != nil {
226 t.Errorf("error parsing build files in test setup: %s", err)
227 }
Paul Duffin4c0765a2022-10-29 17:48:00 +0100228 }),
229 android.FixtureModifyEnv(func(env map[string]string) {
230 if tc.UnconvertedDepsMode == errorModulesUnconvertedDeps {
231 env["BP2BUILD_ERROR_UNCONVERTED"] = "true"
232 }
233 }),
Jingwen Chen5146ac02021-09-02 11:44:42 +0000234 }
235
Paul Duffin4c0765a2022-10-29 17:48:00 +0100236 preparer := android.GroupFixturePreparers(preparers...)
237 if tc.ExpectedErr != nil {
238 pattern := "\\Q" + tc.ExpectedErr.Error() + "\\E"
239 preparer = preparer.ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern(pattern))
240 }
241 result := preparer.RunTestWithCustomResult(t).(*BazelTestResult)
242 if len(result.Errs) > 0 {
Alex Márquez Pérez Muñíz Díaz Púras Thaureaux1c92aef2021-08-23 16:10:00 +0000243 return
244 }
245
Paul Duffin4c0765a2022-10-29 17:48:00 +0100246 expectedTargets := map[string][]string{
247 checkDir: tc.ExpectedBazelTargets,
Liz Kammer6eff3232021-08-26 08:37:59 -0400248 }
Paul Duffin4c0765a2022-10-29 17:48:00 +0100249
Chris Parsons2173b5f2023-09-25 19:01:40 +0000250 result.CompareAllBazelTargets(t, tc, expectedTargets, true)
Paul Duffin4c0765a2022-10-29 17:48:00 +0100251}
252
Chris Parsons73f411b2023-06-20 21:46:57 +0000253// bazelTestRunner customizes the test fixture mechanism to run tests of the bp2build build mode.
Cole Faust11edf552023-10-13 11:32:14 -0700254type bazelTestRunner struct {
255 generateProductConfigTargets bool
256}
Paul Duffin4c0765a2022-10-29 17:48:00 +0100257
258func (b *bazelTestRunner) FinalPreparer(result *android.TestResult) android.CustomTestResult {
259 ctx := result.TestContext
Chris Parsons73f411b2023-06-20 21:46:57 +0000260 ctx.RegisterForBazelConversion()
Paul Duffin4c0765a2022-10-29 17:48:00 +0100261
262 return &BazelTestResult{TestResult: result}
263}
264
265func (b *bazelTestRunner) PostParseProcessor(result android.CustomTestResult) {
266 bazelResult := result.(*BazelTestResult)
267 ctx := bazelResult.TestContext
268 config := bazelResult.Config
269 _, errs := ctx.ResolveDependencies(config)
270 if bazelResult.CollateErrs(errs) {
271 return
272 }
273
Chris Parsons73f411b2023-06-20 21:46:57 +0000274 codegenCtx := NewCodegenContext(config, ctx.Context, Bp2Build, "")
Paul Duffin4c0765a2022-10-29 17:48:00 +0100275 res, errs := GenerateBazelTargets(codegenCtx, false)
276 if bazelResult.CollateErrs(errs) {
277 return
278 }
Cole Faust11edf552023-10-13 11:32:14 -0700279 if b.generateProductConfigTargets {
280 productConfig, err := createProductConfigFiles(codegenCtx, res.moduleNameToPartition, res.metrics.convertedModulePathMap)
281 if err != nil {
282 bazelResult.CollateErrs([]error{err})
283 return
284 }
285 for k, v := range productConfig.bp2buildTargets {
286 res.buildFileToTargets[k] = append(res.buildFileToTargets[k], v...)
287 }
288 }
Paul Duffin4c0765a2022-10-29 17:48:00 +0100289
290 // Store additional data for access by tests.
291 bazelResult.conversionResults = res
292}
293
294// BazelTestResult is a wrapper around android.TestResult to provide type safe access to the bazel
295// specific data stored by the bazelTestRunner.
296type BazelTestResult struct {
297 *android.TestResult
298
299 // The result returned by the GenerateBazelTargets function.
300 conversionResults
301}
302
303// CompareAllBazelTargets compares the BazelTargets produced by the test for all the directories
304// with the supplied set of expected targets.
305//
306// If ignoreUnexpected=false then this enforces an exact match where every BazelTarget produced must
307// have a corresponding expected BazelTarget.
308//
309// If ignoreUnexpected=true then it will ignore directories for which there are no expected targets.
Chris Parsons2173b5f2023-09-25 19:01:40 +0000310func (b BazelTestResult) CompareAllBazelTargets(t *testing.T, tc Bp2buildTestCase, expectedTargets map[string][]string, ignoreUnexpected bool) {
Liz Kammer2b3f56e2023-03-23 11:51:49 -0400311 t.Helper()
Paul Duffin4c0765a2022-10-29 17:48:00 +0100312 actualTargets := b.buildFileToTargets
313
314 // Generate the sorted set of directories to check.
Cole Faust18994c72023-02-28 16:02:16 -0800315 dirsToCheck := android.SortedKeys(expectedTargets)
Paul Duffin4c0765a2022-10-29 17:48:00 +0100316 if !ignoreUnexpected {
317 // This needs to perform an exact match so add the directories in which targets were
318 // produced to the list of directories to check.
Cole Faust18994c72023-02-28 16:02:16 -0800319 dirsToCheck = append(dirsToCheck, android.SortedKeys(actualTargets)...)
Paul Duffin4c0765a2022-10-29 17:48:00 +0100320 dirsToCheck = android.SortedUniqueStrings(dirsToCheck)
321 }
322
323 for _, dir := range dirsToCheck {
324 expected := expectedTargets[dir]
325 actual := actualTargets[dir]
326
327 if expected == nil {
328 if actual != nil {
329 t.Errorf("did not expect any bazel modules in %q but found %d", dir, len(actual))
330 }
331 } else if actual == nil {
332 expectedCount := len(expected)
333 if expectedCount > 0 {
334 t.Errorf("expected %d bazel modules in %q but did not find any", expectedCount, dir)
335 }
336 } else {
Chris Parsons2173b5f2023-09-25 19:01:40 +0000337 b.CompareBazelTargets(t, tc.Description, expected, actual)
338 }
339 }
340
341 for _, module := range tc.ExpectedConvertedModules {
342 if _, found := b.metrics.convertedModulePathMap[module]; !found {
343 t.Errorf("expected %s to be generated by bp2build, but was not. Map of converted modules: %s", module, b.metrics.convertedModulePathMap)
344 }
345 }
346
347 for _, module := range tc.ExpectedHandcraftedModules {
348 if reason, found := b.metrics.serialized.UnconvertedModules[module]; !found {
349 t.Errorf("expected %s to be marked 'unconverted' by bp2build, but was not found. Full list: %s",
350 module, b.metrics.serialized.UnconvertedModules)
351 } else {
352 if reason.Type != bp2build_metrics_proto.UnconvertedReasonType_DEFINED_IN_BUILD_FILE {
353 t.Errorf("expected %s to be marked 'handcrafted' by bp2build, but was disabled for another reason: %s", module, reason)
354 }
Paul Duffin4c0765a2022-10-29 17:48:00 +0100355 }
356 }
357}
358
359func (b BazelTestResult) CompareBazelTargets(t *testing.T, description string, expectedContents []string, actualTargets BazelTargets) {
Liz Kammer748d7072023-01-25 12:07:43 -0500360 t.Helper()
Paul Duffin4c0765a2022-10-29 17:48:00 +0100361 if actualCount, expectedCount := len(actualTargets), len(expectedContents); actualCount != expectedCount {
Sasha Smundak9d2f1742022-08-04 13:28:38 -0700362 t.Errorf("%s: Expected %d bazel target (%s), got %d (%s)",
Paul Duffin4c0765a2022-10-29 17:48:00 +0100363 description, expectedCount, expectedContents, actualCount, actualTargets)
Alex Márquez Pérez Muñíz Díaz Púras Thaureaux1c92aef2021-08-23 16:10:00 +0000364 } else {
Zi Wangfba0a212023-03-07 16:48:19 -0800365 sort.SliceStable(actualTargets, func(i, j int) bool {
366 return actualTargets[i].name < actualTargets[j].name
367 })
368 sort.SliceStable(expectedContents, func(i, j int) bool {
369 return getTargetName(expectedContents[i]) < getTargetName(expectedContents[j])
370 })
Paul Duffin4c0765a2022-10-29 17:48:00 +0100371 for i, actualTarget := range actualTargets {
372 if w, g := expectedContents[i], actualTarget.content; w != g {
Alex Márquez Pérez Muñíz Díaz Púras Thaureaux1c92aef2021-08-23 16:10:00 +0000373 t.Errorf(
Paul Duffin4c0765a2022-10-29 17:48:00 +0100374 "%s[%d]: Expected generated Bazel target to be `%s`, got `%s`",
375 description, i, w, g)
Alex Márquez Pérez Muñíz Díaz Púras Thaureaux1c92aef2021-08-23 16:10:00 +0000376 }
377 }
378 }
379}
380
Liz Kammer2dd9ca42020-11-25 16:06:39 -0800381type nestedProps struct {
Liz Kammer46fb7ab2021-12-01 10:09:34 -0500382 Nested_prop *string
Liz Kammer2dd9ca42020-11-25 16:06:39 -0800383}
384
Liz Kammer32a03392021-09-14 11:17:21 -0400385type EmbeddedProps struct {
Liz Kammer46fb7ab2021-12-01 10:09:34 -0500386 Embedded_prop *string
Liz Kammer32a03392021-09-14 11:17:21 -0400387}
388
389type OtherEmbeddedProps struct {
Liz Kammer46fb7ab2021-12-01 10:09:34 -0500390 Other_embedded_prop *string
Liz Kammer32a03392021-09-14 11:17:21 -0400391}
392
Liz Kammer2dd9ca42020-11-25 16:06:39 -0800393type customProps struct {
Liz Kammer32a03392021-09-14 11:17:21 -0400394 EmbeddedProps
395 *OtherEmbeddedProps
396
Liz Kammer2dd9ca42020-11-25 16:06:39 -0800397 Bool_prop bool
398 Bool_ptr_prop *bool
399 // Ensure that properties tagged `blueprint:mutated` are omitted
Alex Márquez Pérez Muñíz Díaz Púras Thaureaux3a019a62022-06-23 16:02:44 +0000400 Int_prop int `blueprint:"mutated"`
401 Int64_ptr_prop *int64
402 String_prop string
403 String_literal_prop *string `android:"arch_variant"`
404 String_ptr_prop *string
405 String_list_prop []string
Liz Kammer2dd9ca42020-11-25 16:06:39 -0800406
407 Nested_props nestedProps
408 Nested_props_ptr *nestedProps
Liz Kammer4562a3b2021-04-21 18:15:34 -0400409
Liz Kammer32b77cf2021-08-04 15:17:02 -0400410 Arch_paths []string `android:"path,arch_variant"`
411 Arch_paths_exclude []string `android:"path,arch_variant"`
Liz Kammerbe46fcc2021-11-01 15:32:43 -0400412
413 // Prop used to indicate this conversion should be 1 module -> multiple targets
414 One_to_many_prop *bool
Spandan Das5af0bd32022-09-28 20:43:08 +0000415
Chris Parsons5f1b3c72023-09-28 20:41:03 +0000416 // Prop used to simulate an unsupported property in bp2build conversion. If this
417 // is true, this module should be treated as "unconvertible" via bp2build.
418 Does_not_convert_to_bazel *bool
419
Spandan Das5af0bd32022-09-28 20:43:08 +0000420 Api *string // File describing the APIs of this module
Spandan Das6a448ec2023-04-19 17:36:12 +0000421
422 Test_config_setting *bool // Used to test generation of config_setting targets
Spandan Das3131d672023-08-03 22:33:47 +0000423
424 Dir *string // Dir in which the Bazel Target will be created
Liz Kammer2dd9ca42020-11-25 16:06:39 -0800425}
426
427type customModule struct {
428 android.ModuleBase
Liz Kammerea6666f2021-02-17 10:17:28 -0500429 android.BazelModuleBase
Liz Kammer2dd9ca42020-11-25 16:06:39 -0800430
431 props customProps
432}
433
434// OutputFiles is needed because some instances of this module use dist with a
435// tag property which requires the module implements OutputFileProducer.
436func (m *customModule) OutputFiles(tag string) (android.Paths, error) {
437 return android.PathsForTesting("path" + tag), nil
438}
439
440func (m *customModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
441 // nothing for now.
442}
443
444func customModuleFactoryBase() android.Module {
445 module := &customModule{}
446 module.AddProperties(&module.props)
Liz Kammerea6666f2021-02-17 10:17:28 -0500447 android.InitBazelModule(module)
Liz Kammer2dd9ca42020-11-25 16:06:39 -0800448 return module
449}
450
Liz Kammerdfeb1202022-05-13 17:20:20 -0400451func customModuleFactoryHostAndDevice() android.Module {
Liz Kammer2dd9ca42020-11-25 16:06:39 -0800452 m := customModuleFactoryBase()
Liz Kammer4562a3b2021-04-21 18:15:34 -0400453 android.InitAndroidArchModule(m, android.HostAndDeviceSupported, android.MultilibBoth)
Liz Kammer2dd9ca42020-11-25 16:06:39 -0800454 return m
455}
456
Liz Kammerdfeb1202022-05-13 17:20:20 -0400457func customModuleFactoryDeviceSupported() android.Module {
458 m := customModuleFactoryBase()
459 android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibBoth)
460 return m
461}
462
463func customModuleFactoryHostSupported() android.Module {
464 m := customModuleFactoryBase()
465 android.InitAndroidArchModule(m, android.HostSupported, android.MultilibBoth)
466 return m
467}
468
469func customModuleFactoryHostAndDeviceDefault() android.Module {
470 m := customModuleFactoryBase()
471 android.InitAndroidArchModule(m, android.HostAndDeviceDefault, android.MultilibBoth)
472 return m
473}
474
475func customModuleFactoryNeitherHostNorDeviceSupported() android.Module {
476 m := customModuleFactoryBase()
477 android.InitAndroidArchModule(m, android.NeitherHostNorDeviceSupported, android.MultilibBoth)
478 return m
479}
480
Liz Kammer2dd9ca42020-11-25 16:06:39 -0800481type testProps struct {
482 Test_prop struct {
483 Test_string_prop string
484 }
485}
486
487type customTestModule struct {
488 android.ModuleBase
489
490 props customProps
491 test_props testProps
492}
493
494func (m *customTestModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
495 // nothing for now.
496}
497
498func customTestModuleFactoryBase() android.Module {
499 m := &customTestModule{}
500 m.AddProperties(&m.props)
501 m.AddProperties(&m.test_props)
502 return m
503}
504
505func customTestModuleFactory() android.Module {
506 m := customTestModuleFactoryBase()
507 android.InitAndroidModule(m)
508 return m
509}
510
511type customDefaultsModule struct {
512 android.ModuleBase
513 android.DefaultsModuleBase
514}
515
516func customDefaultsModuleFactoryBase() android.DefaultsModule {
517 module := &customDefaultsModule{}
518 module.AddProperties(&customProps{})
519 return module
520}
521
522func customDefaultsModuleFactoryBasic() android.Module {
523 return customDefaultsModuleFactoryBase()
524}
525
526func customDefaultsModuleFactory() android.Module {
527 m := customDefaultsModuleFactoryBase()
528 android.InitDefaultsModule(m)
529 return m
530}
Jingwen Chen73850672020-12-14 08:25:34 -0500531
Liz Kammer32a03392021-09-14 11:17:21 -0400532type EmbeddedAttr struct {
Liz Kammer46fb7ab2021-12-01 10:09:34 -0500533 Embedded_attr *string
Liz Kammer32a03392021-09-14 11:17:21 -0400534}
535
536type OtherEmbeddedAttr struct {
Liz Kammer46fb7ab2021-12-01 10:09:34 -0500537 Other_embedded_attr *string
Liz Kammer32a03392021-09-14 11:17:21 -0400538}
539
Jingwen Chen73850672020-12-14 08:25:34 -0500540type customBazelModuleAttributes struct {
Liz Kammer32a03392021-09-14 11:17:21 -0400541 EmbeddedAttr
542 *OtherEmbeddedAttr
Alex Márquez Pérez Muñíz Díaz Púras Thaureaux3a019a62022-06-23 16:02:44 +0000543 String_literal_prop bazel.StringAttribute
544 String_ptr_prop *string
545 String_list_prop []string
546 Arch_paths bazel.LabelListAttribute
Spandan Das5af0bd32022-09-28 20:43:08 +0000547 Api bazel.LabelAttribute
Jingwen Chen73850672020-12-14 08:25:34 -0500548}
549
Spandan Das3131d672023-08-03 22:33:47 +0000550func (m *customModule) dir() *string {
551 return m.props.Dir
552}
553
Chris Parsons637458d2023-09-19 20:09:00 +0000554func (m *customModule) ConvertWithBp2build(ctx android.Bp2buildMutatorContext) {
Chris Parsons5f1b3c72023-09-28 20:41:03 +0000555 if p := m.props.Does_not_convert_to_bazel; p != nil && *p {
556 ctx.MarkBp2buildUnconvertible(bp2build_metrics_proto.UnconvertedReasonType_PROPERTY_UNSUPPORTED, "")
557 return
558 }
Liz Kammerbe46fcc2021-11-01 15:32:43 -0400559 if p := m.props.One_to_many_prop; p != nil && *p {
560 customBp2buildOneToMany(ctx, m)
561 return
562 }
Liz Kammer4562a3b2021-04-21 18:15:34 -0400563
Alex Márquez Pérez Muñíz Díaz Púras Thaureaux3a019a62022-06-23 16:02:44 +0000564 paths := bazel.LabelListAttribute{}
565 strAttr := bazel.StringAttribute{}
Liz Kammerbe46fcc2021-11-01 15:32:43 -0400566 for axis, configToProps := range m.GetArchVariantProperties(ctx, &customProps{}) {
567 for config, props := range configToProps {
Alex Márquez Pérez Muñíz Díaz Púras Thaureaux3a019a62022-06-23 16:02:44 +0000568 if custProps, ok := props.(*customProps); ok {
569 if custProps.Arch_paths != nil {
570 paths.SetSelectValue(axis, config, android.BazelLabelForModuleSrcExcludes(ctx, custProps.Arch_paths, custProps.Arch_paths_exclude))
571 }
572 if custProps.String_literal_prop != nil {
573 strAttr.SetSelectValue(axis, config, custProps.String_literal_prop)
574 }
Liz Kammer4562a3b2021-04-21 18:15:34 -0400575 }
576 }
Jingwen Chen73850672020-12-14 08:25:34 -0500577 }
Liz Kammer9e2a5a72023-09-19 08:41:14 -0400578 productVariableProps, errs := android.ProductVariableProperties(ctx, ctx.Module())
579 for _, err := range errs {
580 ctx.ModuleErrorf("ProductVariableProperties error: %s", err)
581 }
Liz Kammer9d2d4102022-12-21 14:51:37 -0500582 if props, ok := productVariableProps["String_literal_prop"]; ok {
583 for c, p := range props {
584 if val, ok := p.(*string); ok {
585 strAttr.SetSelectValue(c.ConfigurationAxis(), c.SelectKey(), val)
586 }
587 }
588 }
Liz Kammerbe46fcc2021-11-01 15:32:43 -0400589
590 paths.ResolveExcludes()
591
592 attrs := &customBazelModuleAttributes{
Alex Márquez Pérez Muñíz Díaz Púras Thaureaux3a019a62022-06-23 16:02:44 +0000593 String_literal_prop: strAttr,
594 String_ptr_prop: m.props.String_ptr_prop,
595 String_list_prop: m.props.String_list_prop,
596 Arch_paths: paths,
Liz Kammerbe46fcc2021-11-01 15:32:43 -0400597 }
Alex Márquez Pérez Muñíz Díaz Púras Thaureaux3a019a62022-06-23 16:02:44 +0000598
Liz Kammerbe46fcc2021-11-01 15:32:43 -0400599 attrs.Embedded_attr = m.props.Embedded_prop
600 if m.props.OtherEmbeddedProps != nil {
601 attrs.OtherEmbeddedAttr = &OtherEmbeddedAttr{Other_embedded_attr: m.props.OtherEmbeddedProps.Other_embedded_prop}
602 }
603
604 props := bazel.BazelTargetModuleProperties{
605 Rule_class: "custom",
606 }
607
Spandan Das3131d672023-08-03 22:33:47 +0000608 ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: m.Name(), Dir: m.dir()}, attrs)
Spandan Das6a448ec2023-04-19 17:36:12 +0000609
610 if proptools.Bool(m.props.Test_config_setting) {
611 m.createConfigSetting(ctx)
612 }
613
614}
615
Chris Parsons637458d2023-09-19 20:09:00 +0000616func (m *customModule) createConfigSetting(ctx android.Bp2buildMutatorContext) {
Spandan Das6a448ec2023-04-19 17:36:12 +0000617 csa := bazel.ConfigSettingAttributes{
618 Flag_values: bazel.StringMapAttribute{
619 "//build/bazel/rules/my_string_setting": m.Name(),
620 },
621 }
622 ca := android.CommonAttributes{
623 Name: m.Name() + "_config_setting",
624 }
625 ctx.CreateBazelConfigSetting(
626 csa,
627 ca,
628 ctx.ModuleDir(),
629 )
Jingwen Chen73850672020-12-14 08:25:34 -0500630}
Jingwen Chen40067de2021-01-26 21:58:43 -0500631
632// A bp2build mutator that uses load statements and creates a 1:M mapping from
633// module to target.
Chris Parsons637458d2023-09-19 20:09:00 +0000634func customBp2buildOneToMany(ctx android.Bp2buildMutatorContext, m *customModule) {
Jingwen Chen77e8b7b2021-02-05 03:03:24 -0500635
Liz Kammerbe46fcc2021-11-01 15:32:43 -0400636 baseName := m.Name()
637 attrs := &customBazelModuleAttributes{}
Jingwen Chen1fd14692021-02-05 03:01:50 -0500638
Liz Kammerbe46fcc2021-11-01 15:32:43 -0400639 myLibraryProps := bazel.BazelTargetModuleProperties{
640 Rule_class: "my_library",
641 Bzl_load_location: "//build/bazel/rules:rules.bzl",
Jingwen Chen40067de2021-01-26 21:58:43 -0500642 }
Liz Kammerbe46fcc2021-11-01 15:32:43 -0400643 ctx.CreateBazelTargetModule(myLibraryProps, android.CommonAttributes{Name: baseName}, attrs)
644
645 protoLibraryProps := bazel.BazelTargetModuleProperties{
646 Rule_class: "proto_library",
647 Bzl_load_location: "//build/bazel/rules:proto.bzl",
648 }
649 ctx.CreateBazelTargetModule(protoLibraryProps, android.CommonAttributes{Name: baseName + "_proto_library_deps"}, attrs)
650
651 myProtoLibraryProps := bazel.BazelTargetModuleProperties{
652 Rule_class: "my_proto_library",
653 Bzl_load_location: "//build/bazel/rules:proto.bzl",
654 }
655 ctx.CreateBazelTargetModule(myProtoLibraryProps, android.CommonAttributes{Name: baseName + "_my_proto_library_deps"}, attrs)
Jingwen Chen40067de2021-01-26 21:58:43 -0500656}
Jingwen Chenba369ad2021-02-22 10:19:34 -0500657
658// Helper method for tests to easily access the targets in a dir.
Liz Kammer6eff3232021-08-26 08:37:59 -0400659func generateBazelTargetsForDir(codegenCtx *CodegenContext, dir string) (BazelTargets, []error) {
Rupert Shuttleworth2a4fc3e2021-04-21 07:10:09 -0400660 // TODO: Set generateFilegroups to true and/or remove the generateFilegroups argument completely
Liz Kammer6eff3232021-08-26 08:37:59 -0400661 res, err := GenerateBazelTargets(codegenCtx, false)
Alix94e26032022-08-16 20:37:33 +0000662 if err != nil {
663 return BazelTargets{}, err
664 }
Liz Kammer6eff3232021-08-26 08:37:59 -0400665 return res.buildFileToTargets[dir], err
Jingwen Chenba369ad2021-02-22 10:19:34 -0500666}
Liz Kammer32b77cf2021-08-04 15:17:02 -0400667
668func registerCustomModuleForBp2buildConversion(ctx *android.TestContext) {
Liz Kammerdfeb1202022-05-13 17:20:20 -0400669 ctx.RegisterModuleType("custom", customModuleFactoryHostAndDevice)
Liz Kammer32b77cf2021-08-04 15:17:02 -0400670 ctx.RegisterForBazelConversion()
671}
Liz Kammer7a210ac2021-09-22 15:52:58 -0400672
Chris Parsonscd209032023-09-19 01:12:48 +0000673func simpleModule(typ, name string) string {
Liz Kammer7a210ac2021-09-22 15:52:58 -0400674 return fmt.Sprintf(`
675%s {
676 name: "%s",
Liz Kammer7a210ac2021-09-22 15:52:58 -0400677}`, typ, name)
678}
Liz Kammer78cfdaa2021-11-08 12:56:31 -0500679
Sam Delmerico3177a6e2022-06-21 19:28:33 +0000680type AttrNameToString map[string]string
Liz Kammer78cfdaa2021-11-08 12:56:31 -0500681
Sam Delmerico3177a6e2022-06-21 19:28:33 +0000682func (a AttrNameToString) clone() AttrNameToString {
683 newAttrs := make(AttrNameToString, len(a))
Liz Kammerdfeb1202022-05-13 17:20:20 -0400684 for k, v := range a {
685 newAttrs[k] = v
686 }
687 return newAttrs
688}
689
690// makeBazelTargetNoRestrictions returns bazel target build file definition that can be host or
691// device specific, or independent of host/device.
Sam Delmerico3177a6e2022-06-21 19:28:33 +0000692func makeBazelTargetHostOrDevice(typ, name string, attrs AttrNameToString, hod android.HostOrDeviceSupported) string {
Liz Kammerdfeb1202022-05-13 17:20:20 -0400693 if _, ok := attrs["target_compatible_with"]; !ok {
694 switch hod {
695 case android.HostSupported:
696 attrs["target_compatible_with"] = `select({
Jingwen Chen9c2e3ee2023-10-11 10:51:28 +0000697 "//build/bazel_common_rules/platforms/os:android": ["@platforms//:incompatible"],
Liz Kammerdfeb1202022-05-13 17:20:20 -0400698 "//conditions:default": [],
699 })`
700 case android.DeviceSupported:
Jingwen Chen9c2e3ee2023-10-11 10:51:28 +0000701 attrs["target_compatible_with"] = `["//build/bazel_common_rules/platforms/os:android"]`
Liz Kammerdfeb1202022-05-13 17:20:20 -0400702 }
703 }
704
Liz Kammer78cfdaa2021-11-08 12:56:31 -0500705 attrStrings := make([]string, 0, len(attrs)+1)
Sasha Smundakfb589492022-08-04 11:13:27 -0700706 if name != "" {
707 attrStrings = append(attrStrings, fmt.Sprintf(` name = "%s",`, name))
708 }
Cole Faust18994c72023-02-28 16:02:16 -0800709 for _, k := range android.SortedKeys(attrs) {
Liz Kammer78cfdaa2021-11-08 12:56:31 -0500710 attrStrings = append(attrStrings, fmt.Sprintf(" %s = %s,", k, attrs[k]))
711 }
712 return fmt.Sprintf(`%s(
713%s
714)`, typ, strings.Join(attrStrings, "\n"))
715}
Liz Kammerdfeb1202022-05-13 17:20:20 -0400716
Sam Delmerico3177a6e2022-06-21 19:28:33 +0000717// MakeBazelTargetNoRestrictions returns bazel target build file definition that does not add a
Liz Kammerdfeb1202022-05-13 17:20:20 -0400718// target_compatible_with. This is useful for module types like filegroup and genrule that arch not
719// arch variant
Sam Delmerico3177a6e2022-06-21 19:28:33 +0000720func MakeBazelTargetNoRestrictions(typ, name string, attrs AttrNameToString) string {
Liz Kammerdfeb1202022-05-13 17:20:20 -0400721 return makeBazelTargetHostOrDevice(typ, name, attrs, android.HostAndDeviceDefault)
722}
723
724// makeBazelTargetNoRestrictions returns bazel target build file definition that is device specific
725// as this is the most common default in Soong.
Alixe06d75b2022-08-31 18:28:19 +0000726func MakeBazelTarget(typ, name string, attrs AttrNameToString) string {
Liz Kammerdfeb1202022-05-13 17:20:20 -0400727 return makeBazelTargetHostOrDevice(typ, name, attrs, android.DeviceSupported)
728}
Sasha Smundak9d2f1742022-08-04 13:28:38 -0700729
730type ExpectedRuleTarget struct {
731 Rule string
732 Name string
733 Attrs AttrNameToString
734 Hod android.HostOrDeviceSupported
735}
736
737func (ebr ExpectedRuleTarget) String() string {
738 return makeBazelTargetHostOrDevice(ebr.Rule, ebr.Name, ebr.Attrs, ebr.Hod)
739}
Trevor Radcliffe087af542022-09-16 15:36:10 +0000740
741func makeCcStubSuiteTargets(name string, attrs AttrNameToString) string {
742 if _, hasStubs := attrs["stubs_symbol_file"]; !hasStubs {
743 return ""
744 }
745 STUB_SUITE_ATTRS := map[string]string{
Spandan Das04f9f4c2023-09-13 23:59:05 +0000746 "api_surface": "api_surface",
Sam Delmerico5f906492023-03-15 18:06:18 -0400747 "stubs_symbol_file": "symbol_file",
748 "stubs_versions": "versions",
749 "soname": "soname",
750 "source_library_label": "source_library_label",
Trevor Radcliffe087af542022-09-16 15:36:10 +0000751 }
752
753 stubSuiteAttrs := AttrNameToString{}
754 for key, _ := range attrs {
755 if _, stubSuiteAttr := STUB_SUITE_ATTRS[key]; stubSuiteAttr {
756 stubSuiteAttrs[STUB_SUITE_ATTRS[key]] = attrs[key]
Sam Delmerico5f906492023-03-15 18:06:18 -0400757 } else {
758 panic(fmt.Sprintf("unused cc_stub_suite attr %q\n", key))
Trevor Radcliffe087af542022-09-16 15:36:10 +0000759 }
760 }
761 return MakeBazelTarget("cc_stub_suite", name+"_stub_libs", stubSuiteAttrs)
762}
Alix341484b2022-10-31 19:08:18 +0000763
764func MakeNeverlinkDuplicateTarget(moduleType string, name string) string {
Liz Kammer02914402023-08-07 13:38:18 -0400765 return MakeNeverlinkDuplicateTargetWithAttrs(moduleType, name, AttrNameToString{
766 "sdk_version": `"current"`, // use as default
767 })
Romain Jobredeaux2eef2e12023-02-24 12:07:08 -0500768}
769
770func MakeNeverlinkDuplicateTargetWithAttrs(moduleType string, name string, extraAttrs AttrNameToString) string {
771 attrs := extraAttrs
772 attrs["neverlink"] = `True`
773 attrs["exports"] = `[":` + name + `"]`
774 return MakeBazelTarget(moduleType, name+"-neverlink", attrs)
Alix341484b2022-10-31 19:08:18 +0000775}
Zi Wangfba0a212023-03-07 16:48:19 -0800776
777func getTargetName(targetContent string) string {
778 data := strings.Split(targetContent, "name = \"")
779 if len(data) < 2 {
780 return ""
781 } else {
782 endIndex := strings.Index(data[1], "\"")
783 return data[1][:endIndex]
784 }
785}