blob: 1602b9b020eb16cbb3d0847326b17ae44d19700f [file] [log] [blame]
Liz Kammerea6666f2021-02-17 10:17:28 -05001// 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
15package android
16
Liz Kammerba3ea162021-02-17 13:22:03 -050017import (
Wei Libafb6d62021-12-10 03:14:59 -080018 "bufio"
19 "errors"
Chris Parsons39a16972023-06-08 14:28:51 +000020 "fmt"
Liz Kammerba3ea162021-02-17 13:22:03 -050021 "strings"
22
Chris Parsons39a16972023-06-08 14:28:51 +000023 "android/soong/ui/metrics/bp2build_metrics_proto"
Liz Kammerc86e0942023-08-11 16:15:12 -040024
Liz Kammerbdc60992021-02-24 16:55:11 -050025 "github.com/google/blueprint"
Spandan Das64852422023-08-02 21:58:41 +000026 "github.com/google/blueprint/bootstrap"
Liz Kammerba3ea162021-02-17 13:22:03 -050027 "github.com/google/blueprint/proptools"
Sam Delmerico24c56032022-03-28 19:53:03 +000028
29 "android/soong/android/allowlists"
30)
31
32const (
33 // A sentinel value to be used as a key in Bp2BuildConfig for modules with
34 // no package path. This is also the module dir for top level Android.bp
35 // modules.
36 Bp2BuildTopLevel = "."
Liz Kammerba3ea162021-02-17 13:22:03 -050037)
38
MarkDacekf47e1422023-04-19 16:47:36 +000039type MixedBuildEnabledStatus int
40
41const (
42 // This module can be mixed_built.
43 MixedBuildEnabled = iota
44
45 // There is a technical incompatibility preventing this module from being
46 // bazel-analyzed. Note: the module might also be incompatible.
47 TechnicalIncompatibility
48
49 // This module cannot be mixed_built due to some incompatibility with it
50 // that is not a platform incompatibility. Example: the module-type is not
51 // enabled, or is not bp2build-converted.
52 ModuleIncompatibility
Liz Kammerc13f7852023-05-17 13:01:48 -040053
54 // Missing dependencies. We can't query Bazel for modules if it has missing dependencies, there
55 // will be failures.
56 ModuleMissingDeps
MarkDacekf47e1422023-04-19 16:47:36 +000057)
58
Yu Liu2aa806b2022-09-01 11:54:47 -070059// FileGroupAsLibrary describes a filegroup module that is converted to some library
60// such as aidl_library or proto_library.
61type FileGroupAsLibrary interface {
Vinh Tran444154d2022-08-16 13:10:31 -040062 ShouldConvertToAidlLibrary(ctx BazelConversionPathContext) bool
Yu Liu2aa806b2022-09-01 11:54:47 -070063 ShouldConvertToProtoLibrary(ctx BazelConversionPathContext) bool
Vinh Tran444154d2022-08-16 13:10:31 -040064 GetAidlLibraryLabel(ctx BazelConversionPathContext) string
Yu Liu2aa806b2022-09-01 11:54:47 -070065 GetProtoLibraryLabel(ctx BazelConversionPathContext) string
Vinh Tran444154d2022-08-16 13:10:31 -040066}
67
Sasha Smundaka0954062022-08-02 18:23:58 -070068type BazelConversionStatus struct {
69 // Information about _all_ bp2build targets generated by this module. Multiple targets are
70 // supported as Soong handles some things within a single target that we may choose to split into
71 // multiple targets, e.g. renderscript, protos, yacc within a cc module.
72 Bp2buildInfo []bp2buildInfo `blueprint:"mutated"`
73
74 // UnconvertedBp2buildDep stores the module names of direct dependency that were not converted to
75 // Bazel
76 UnconvertedDeps []string `blueprint:"mutated"`
77
78 // MissingBp2buildDep stores the module names of direct dependency that were not found
79 MissingDeps []string `blueprint:"mutated"`
Chris Parsons39a16972023-06-08 14:28:51 +000080
81 // If non-nil, indicates that the module could not be converted successfully
82 // with bp2build. This will describe the reason the module could not be converted.
83 UnconvertedReason *UnconvertedReason
Cole Faust11edf552023-10-13 11:32:14 -070084
85 // The Partition this module will be installed on.
86 // TODO(b/306200980) Investigate how to handle modules that are installed in multiple
87 // partitions.
88 Partition string `blueprint:"mutated"`
Chris Parsons39a16972023-06-08 14:28:51 +000089}
90
91// The reason a module could not be converted to a BUILD target via bp2build.
92// This should match bp2build_metrics_proto.UnconvertedReason, but omits private
93// proto-related fields that prevent copying this struct.
94type UnconvertedReason struct {
95 // Should correspond to a valid value in bp2build_metrics_proto.UnconvertedReasonType.
96 // A raw int is used here instead, because blueprint logic requires that all transitive
97 // fields of module definitions be primitives.
98 ReasonType int
99 Detail string
Sasha Smundaka0954062022-08-02 18:23:58 -0700100}
101
Romain Jobredeaux8242b432023-05-04 10:16:26 -0400102type BazelModuleProperties struct {
Jingwen Chen01812022021-11-19 14:29:43 +0000103 // The label of the Bazel target replacing this Soong module. When run in conversion mode, this
104 // will import the handcrafted build target into the autogenerated file. Note: this may result in
105 // a conflict due to duplicate targets if bp2build_available is also set.
106 Label *string
107
108 // If true, bp2build will generate the converted Bazel target for this module. Note: this may
109 // cause a conflict due to the duplicate targets if label is also set.
110 //
111 // This is a bool pointer to support tristates: true, false, not set.
112 //
Sasha Smundak39a301c2022-12-29 17:11:49 -0800113 // To opt in a module, set bazel_module: { bp2build_available: true }
114 // To opt out a module, set bazel_module: { bp2build_available: false }
Jingwen Chen01812022021-11-19 14:29:43 +0000115 // To defer the default setting for the directory, do not set the value.
116 Bp2build_available *bool
Liz Kammerbe46fcc2021-11-01 15:32:43 -0400117
118 // CanConvertToBazel is set via InitBazelModule to indicate that a module type can be converted to
119 // Bazel with Bp2build.
120 CanConvertToBazel bool `blueprint:"mutated"`
Jingwen Chen01812022021-11-19 14:29:43 +0000121}
122
Liz Kammerba3ea162021-02-17 13:22:03 -0500123// Properties contains common module properties for Bazel migration purposes.
124type properties struct {
Chris Parsons1bb58da2022-08-30 13:37:57 -0400125 // In "Bazel mixed build" mode, this represents the Bazel target replacing
Liz Kammerba3ea162021-02-17 13:22:03 -0500126 // this Soong module.
Romain Jobredeaux8242b432023-05-04 10:16:26 -0400127 Bazel_module BazelModuleProperties
Liz Kammerba3ea162021-02-17 13:22:03 -0500128}
Liz Kammerea6666f2021-02-17 10:17:28 -0500129
Jingwen Chen25825ca2021-11-15 12:28:43 +0000130// namespacedVariableProperties is a map from a string representing a Soong
Jingwen Chen84817de2021-11-17 10:57:35 +0000131// config variable namespace, like "android" or "vendor_name" to a slice of
132// pointer to a struct containing a single field called Soong_config_variables
133// whose value mirrors the structure in the Blueprint file.
134type namespacedVariableProperties map[string][]interface{}
Jingwen Chena47f28d2021-11-02 16:43:57 +0000135
Liz Kammerea6666f2021-02-17 10:17:28 -0500136// BazelModuleBase contains the property structs with metadata for modules which can be converted to
137// Bazel.
138type BazelModuleBase struct {
Liz Kammerba3ea162021-02-17 13:22:03 -0500139 bazelProperties properties
Jingwen Chena47f28d2021-11-02 16:43:57 +0000140
141 // namespacedVariableProperties is used for soong_config_module_type support
142 // in bp2build. Soong config modules allow users to set module properties
143 // based on custom product variables defined in Android.bp files. These
144 // variables are namespaced to prevent clobbering, especially when set from
145 // Makefiles.
146 namespacedVariableProperties namespacedVariableProperties
147
148 // baseModuleType is set when this module was created from a module type
149 // defined by a soong_config_module_type. Every soong_config_module_type
150 // "wraps" another module type, e.g. a soong_config_module_type can wrap a
151 // cc_defaults to a custom_cc_defaults, or cc_binary to a custom_cc_binary.
152 // This baseModuleType is set to the wrapped module type.
153 baseModuleType string
Liz Kammerea6666f2021-02-17 10:17:28 -0500154}
155
156// Bazelable is specifies the interface for modules that can be converted to Bazel.
157type Bazelable interface {
Liz Kammerba3ea162021-02-17 13:22:03 -0500158 bazelProps() *properties
159 HasHandcraftedLabel() bool
Liz Kammerbdc60992021-02-24 16:55:11 -0500160 HandcraftedLabel() string
161 GetBazelLabel(ctx BazelConversionPathContext, module blueprint.Module) string
Liz Kammerc86e0942023-08-11 16:15:12 -0400162 ShouldConvertWithBp2build(ctx ShouldConvertWithBazelContext) bool
163 shouldConvertWithBp2build(shouldConvertModuleContext, shouldConvertParams) bool
Chris Parsons39a16972023-06-08 14:28:51 +0000164
165 // ConvertWithBp2build either converts the module to a Bazel build target or
166 // declares the module as unconvertible (for logging and metrics).
167 // Modules must implement this function to be bp2build convertible. The function
168 // must either create at least one Bazel target module (using ctx.CreateBazelTargetModule or
169 // its related functions), or declare itself unconvertible using ctx.MarkBp2buildUnconvertible.
Chris Parsons637458d2023-09-19 20:09:00 +0000170 ConvertWithBp2build(ctx Bp2buildMutatorContext)
Jingwen Chena47f28d2021-11-02 16:43:57 +0000171
Jingwen Chen84817de2021-11-17 10:57:35 +0000172 // namespacedVariableProps is a map from a soong config variable namespace
173 // (e.g. acme, android) to a map of interfaces{}, which are really
174 // reflect.Struct pointers, representing the value of the
175 // soong_config_variables property of a module. The struct pointer is the
176 // one with the single member called Soong_config_variables, which itself is
177 // a struct containing fields for each supported feature in that namespace.
178 //
Sasha Smundak39a301c2022-12-29 17:11:49 -0800179 // The reason for using a slice of interface{} is to support defaults
Jingwen Chen84817de2021-11-17 10:57:35 +0000180 // propagation of the struct pointers.
Jingwen Chena47f28d2021-11-02 16:43:57 +0000181 namespacedVariableProps() namespacedVariableProperties
182 setNamespacedVariableProps(props namespacedVariableProperties)
183 BaseModuleType() string
Jingwen Chen84817de2021-11-17 10:57:35 +0000184 SetBaseModuleType(baseModuleType string)
Liz Kammerea6666f2021-02-17 10:17:28 -0500185}
186
Spandan Das5af0bd32022-09-28 20:43:08 +0000187// ApiProvider is implemented by modules that contribute to an API surface
188type ApiProvider interface {
189 ConvertWithApiBp2build(ctx TopDownMutatorContext)
190}
191
Chris Parsonsf874e462022-05-10 13:50:12 -0400192// MixedBuildBuildable is an interface that module types should implement in order
193// to be "handled by Bazel" in a mixed build.
194type MixedBuildBuildable interface {
195 // IsMixedBuildSupported returns true if and only if this module should be
196 // "handled by Bazel" in a mixed build.
197 // This "escape hatch" allows modules with corner-case scenarios to opt out
198 // of being built with Bazel.
199 IsMixedBuildSupported(ctx BaseModuleContext) bool
200
201 // QueueBazelCall invokes request-queueing functions on the BazelContext
202 // so that these requests are handled when Bazel's cquery is invoked.
203 QueueBazelCall(ctx BaseModuleContext)
204
205 // ProcessBazelQueryResponse uses Bazel information (obtained from the BazelContext)
206 // to set module fields and providers to propagate this module's metadata upstream.
207 // This effectively "bridges the gap" between Bazel and Soong in a mixed build.
208 // Soong modules depending on this module should be oblivious to the fact that
209 // this module was handled by Bazel.
210 ProcessBazelQueryResponse(ctx ModuleContext)
211}
212
Liz Kammerea6666f2021-02-17 10:17:28 -0500213// BazelModule is a lightweight wrapper interface around Module for Bazel-convertible modules.
214type BazelModule interface {
215 Module
216 Bazelable
217}
218
219// InitBazelModule is a wrapper function that decorates a BazelModule with Bazel-conversion
220// properties.
221func InitBazelModule(module BazelModule) {
222 module.AddProperties(module.bazelProps())
Liz Kammerbe46fcc2021-11-01 15:32:43 -0400223 module.bazelProps().Bazel_module.CanConvertToBazel = true
Liz Kammerea6666f2021-02-17 10:17:28 -0500224}
225
Chris Parsons2173b5f2023-09-25 19:01:40 +0000226// BazelHandcraftedHook is a load hook to possibly register the current module as
227// a "handcrafted" Bazel target of a given name. If the current module should be
228// registered in this way, the hook function should return the target name. If
229// it should not be registered in this way, this function should return the empty string.
230type BazelHandcraftedHook func(ctx LoadHookContext) string
231
232// AddBazelHandcraftedHook adds a load hook to (maybe) mark the given module so that
233// it is treated by bp2build as if it has a handcrafted Bazel target.
234func AddBazelHandcraftedHook(module BazelModule, hook BazelHandcraftedHook) {
235 AddLoadHook(module, func(ctx LoadHookContext) {
236 var targetName string = hook(ctx)
237 if len(targetName) > 0 {
238 moduleDir := ctx.ModuleDir()
239 if moduleDir == Bp2BuildTopLevel {
240 moduleDir = ""
241 }
242 label := fmt.Sprintf("//%s:%s", moduleDir, targetName)
243 module.bazelProps().Bazel_module.Label = &label
244 }
245 })
246}
247
Liz Kammerea6666f2021-02-17 10:17:28 -0500248// bazelProps returns the Bazel properties for the given BazelModuleBase.
Liz Kammerba3ea162021-02-17 13:22:03 -0500249func (b *BazelModuleBase) bazelProps() *properties {
Liz Kammerea6666f2021-02-17 10:17:28 -0500250 return &b.bazelProperties
251}
252
Jingwen Chena47f28d2021-11-02 16:43:57 +0000253func (b *BazelModuleBase) namespacedVariableProps() namespacedVariableProperties {
254 return b.namespacedVariableProperties
255}
256
257func (b *BazelModuleBase) setNamespacedVariableProps(props namespacedVariableProperties) {
258 b.namespacedVariableProperties = props
259}
260
261func (b *BazelModuleBase) BaseModuleType() string {
262 return b.baseModuleType
263}
264
265func (b *BazelModuleBase) SetBaseModuleType(baseModuleType string) {
266 b.baseModuleType = baseModuleType
267}
268
Liz Kammerba3ea162021-02-17 13:22:03 -0500269// HasHandcraftedLabel returns whether this module has a handcrafted Bazel label.
270func (b *BazelModuleBase) HasHandcraftedLabel() bool {
271 return b.bazelProperties.Bazel_module.Label != nil
272}
273
274// HandcraftedLabel returns the handcrafted label for this module, or empty string if there is none
275func (b *BazelModuleBase) HandcraftedLabel() string {
276 return proptools.String(b.bazelProperties.Bazel_module.Label)
277}
278
Liz Kammerea6666f2021-02-17 10:17:28 -0500279// GetBazelLabel returns the Bazel label for the given BazelModuleBase.
Liz Kammerbdc60992021-02-24 16:55:11 -0500280func (b *BazelModuleBase) GetBazelLabel(ctx BazelConversionPathContext, module blueprint.Module) string {
281 if b.HasHandcraftedLabel() {
282 return b.HandcraftedLabel()
283 }
Liz Kammerbe46fcc2021-11-01 15:32:43 -0400284 if b.ShouldConvertWithBp2build(ctx) {
Liz Kammerbdc60992021-02-24 16:55:11 -0500285 return bp2buildModuleLabel(ctx, module)
286 }
Spandan Das5b18c0c2023-07-14 00:23:29 +0000287 panic(fmt.Errorf("requested non-existent label for module %s", module.Name()))
Liz Kammerea6666f2021-02-17 10:17:28 -0500288}
289
Cole Faust324a92e2022-08-23 15:29:05 -0700290type Bp2BuildConversionAllowlist struct {
Sam Delmerico24c56032022-03-28 19:53:03 +0000291 // Configure modules in these directories to enable bp2build_available: true or false by default.
292 defaultConfig allowlists.Bp2BuildConfig
Jingwen Chen12b4c272021-03-10 02:05:59 -0500293
Rupert Shuttleworth00960792021-05-12 21:20:13 -0400294 // Keep any existing BUILD files (and do not generate new BUILD files) for these directories
Jingwen Chenb643c7a2021-07-26 04:45:48 +0000295 // in the synthetic Bazel workspace.
Sam Delmerico24c56032022-03-28 19:53:03 +0000296 keepExistingBuildFile map[string]bool
Jingwen Chen5d72cba2021-03-25 09:28:38 +0000297
Chris Parsonsef615e52022-08-18 22:04:11 -0400298 // Per-module allowlist to always opt modules into both bp2build and Bazel Dev Mode mixed
299 // builds. These modules are usually in directories with many other modules that are not ready
300 // for conversion.
Jingwen Chen7edadab2022-03-04 07:01:29 +0000301 //
302 // A module can either be in this list or its directory allowlisted entirely
303 // in bp2buildDefaultConfig, but not both at the same time.
Sam Delmerico24c56032022-03-28 19:53:03 +0000304 moduleAlwaysConvert map[string]bool
Sam Delmericofa1831c2022-02-22 18:07:55 +0000305
Chris Parsonsef615e52022-08-18 22:04:11 -0400306 // Per-module-type allowlist to always opt modules in to both bp2build and
307 // Bazel Dev Mode mixed builds when they have the same type as one listed.
Sam Delmerico24c56032022-03-28 19:53:03 +0000308 moduleTypeAlwaysConvert map[string]bool
Sam Delmerico85d831a2022-03-07 19:12:42 +0000309
Chris Parsonsad876012022-08-20 14:48:32 -0400310 // Per-module denylist to always opt modules out of bp2build conversion.
Sam Delmerico24c56032022-03-28 19:53:03 +0000311 moduleDoNotConvert map[string]bool
Sam Delmerico24c56032022-03-28 19:53:03 +0000312}
Liz Kammer5c313582021-12-03 15:23:26 -0500313
Cole Faust324a92e2022-08-23 15:29:05 -0700314// NewBp2BuildAllowlist creates a new, empty Bp2BuildConversionAllowlist
Sam Delmerico24c56032022-03-28 19:53:03 +0000315// which can be populated using builder pattern Set* methods
Cole Faust324a92e2022-08-23 15:29:05 -0700316func NewBp2BuildAllowlist() Bp2BuildConversionAllowlist {
317 return Bp2BuildConversionAllowlist{
Sam Delmerico24c56032022-03-28 19:53:03 +0000318 allowlists.Bp2BuildConfig{},
319 map[string]bool{},
320 map[string]bool{},
321 map[string]bool{},
322 map[string]bool{},
Chris Parsonsbab4d7e2021-04-15 17:27:08 -0400323 }
324}
325
Sam Delmerico24c56032022-03-28 19:53:03 +0000326// SetDefaultConfig copies the entries from defaultConfig into the allowlist
Cole Faust324a92e2022-08-23 15:29:05 -0700327func (a Bp2BuildConversionAllowlist) SetDefaultConfig(defaultConfig allowlists.Bp2BuildConfig) Bp2BuildConversionAllowlist {
Sam Delmerico24c56032022-03-28 19:53:03 +0000328 if a.defaultConfig == nil {
329 a.defaultConfig = allowlists.Bp2BuildConfig{}
330 }
331 for k, v := range defaultConfig {
332 a.defaultConfig[k] = v
333 }
334
335 return a
336}
337
338// SetKeepExistingBuildFile copies the entries from keepExistingBuildFile into the allowlist
Cole Faust324a92e2022-08-23 15:29:05 -0700339func (a Bp2BuildConversionAllowlist) SetKeepExistingBuildFile(keepExistingBuildFile map[string]bool) Bp2BuildConversionAllowlist {
Sam Delmerico24c56032022-03-28 19:53:03 +0000340 if a.keepExistingBuildFile == nil {
341 a.keepExistingBuildFile = map[string]bool{}
342 }
343 for k, v := range keepExistingBuildFile {
344 a.keepExistingBuildFile[k] = v
345 }
346
347 return a
348}
349
350// SetModuleAlwaysConvertList copies the entries from moduleAlwaysConvert into the allowlist
Cole Faust324a92e2022-08-23 15:29:05 -0700351func (a Bp2BuildConversionAllowlist) SetModuleAlwaysConvertList(moduleAlwaysConvert []string) Bp2BuildConversionAllowlist {
Sam Delmerico24c56032022-03-28 19:53:03 +0000352 if a.moduleAlwaysConvert == nil {
353 a.moduleAlwaysConvert = map[string]bool{}
354 }
355 for _, m := range moduleAlwaysConvert {
356 a.moduleAlwaysConvert[m] = true
357 }
358
359 return a
360}
361
362// SetModuleTypeAlwaysConvertList copies the entries from moduleTypeAlwaysConvert into the allowlist
Cole Faust324a92e2022-08-23 15:29:05 -0700363func (a Bp2BuildConversionAllowlist) SetModuleTypeAlwaysConvertList(moduleTypeAlwaysConvert []string) Bp2BuildConversionAllowlist {
Sam Delmerico24c56032022-03-28 19:53:03 +0000364 if a.moduleTypeAlwaysConvert == nil {
365 a.moduleTypeAlwaysConvert = map[string]bool{}
366 }
367 for _, m := range moduleTypeAlwaysConvert {
368 a.moduleTypeAlwaysConvert[m] = true
369 }
370
371 return a
372}
373
374// SetModuleDoNotConvertList copies the entries from moduleDoNotConvert into the allowlist
Cole Faust324a92e2022-08-23 15:29:05 -0700375func (a Bp2BuildConversionAllowlist) SetModuleDoNotConvertList(moduleDoNotConvert []string) Bp2BuildConversionAllowlist {
Sam Delmerico24c56032022-03-28 19:53:03 +0000376 if a.moduleDoNotConvert == nil {
377 a.moduleDoNotConvert = map[string]bool{}
378 }
379 for _, m := range moduleDoNotConvert {
380 a.moduleDoNotConvert[m] = true
381 }
382
383 return a
384}
385
Sam Delmerico24c56032022-03-28 19:53:03 +0000386// ShouldKeepExistingBuildFileForDir returns whether an existing BUILD file should be
387// added to the build symlink forest based on the current global configuration.
Cole Faust324a92e2022-08-23 15:29:05 -0700388func (a Bp2BuildConversionAllowlist) ShouldKeepExistingBuildFileForDir(dir string) bool {
389 if _, ok := a.keepExistingBuildFile[dir]; ok {
Rupert Shuttleworth00960792021-05-12 21:20:13 -0400390 // Exact dir match
Rupert Shuttleworth2a4fc3e2021-04-21 07:10:09 -0400391 return true
392 }
Usta Shresthaea999642022-11-02 01:03:07 -0400393 var i int
Rupert Shuttleworth00960792021-05-12 21:20:13 -0400394 // Check if subtree match
Usta Shresthaea999642022-11-02 01:03:07 -0400395 for {
396 j := strings.Index(dir[i:], "/")
397 if j == -1 {
398 return false //default
399 }
400 prefix := dir[0 : i+j]
401 i = i + j + 1 // skip the "/"
402 if recursive, ok := a.keepExistingBuildFile[prefix]; ok && recursive {
403 return true
Rupert Shuttleworth00960792021-05-12 21:20:13 -0400404 }
405 }
Rupert Shuttleworth2a4fc3e2021-04-21 07:10:09 -0400406}
407
Cole Faust324a92e2022-08-23 15:29:05 -0700408var bp2BuildAllowListKey = NewOnceKey("Bp2BuildAllowlist")
409var bp2buildAllowlist OncePer
410
411func GetBp2BuildAllowList() Bp2BuildConversionAllowlist {
412 return bp2buildAllowlist.Once(bp2BuildAllowListKey, func() interface{} {
413 return NewBp2BuildAllowlist().SetDefaultConfig(allowlists.Bp2buildDefaultConfig).
414 SetKeepExistingBuildFile(allowlists.Bp2buildKeepExistingBuildFile).
415 SetModuleAlwaysConvertList(allowlists.Bp2buildModuleAlwaysConvertList).
416 SetModuleTypeAlwaysConvertList(allowlists.Bp2buildModuleTypeAlwaysConvertList).
Sasha Smundak39a301c2022-12-29 17:11:49 -0800417 SetModuleDoNotConvertList(allowlists.Bp2buildModuleDoNotConvertList)
Cole Faust324a92e2022-08-23 15:29:05 -0700418 }).(Bp2BuildConversionAllowlist)
419}
420
MarkDacekf47e1422023-04-19 16:47:36 +0000421// MixedBuildsEnabled returns a MixedBuildEnabledStatus regarding whether
422// a module is ready to be replaced by a converted or handcrafted Bazel target.
423// As a side effect, calling this method will also log whether this module is
424// mixed build enabled for metrics reporting.
425func MixedBuildsEnabled(ctx BaseModuleContext) MixedBuildEnabledStatus {
MarkDacekf47e1422023-04-19 16:47:36 +0000426 platformIncompatible := isPlatformIncompatible(ctx.Os(), ctx.Arch().ArchType)
427 if platformIncompatible {
428 ctx.Config().LogMixedBuild(ctx, false)
429 return TechnicalIncompatibility
430 }
431
Liz Kammerc13f7852023-05-17 13:01:48 -0400432 if ctx.Config().AllowMissingDependencies() {
433 missingDeps := ctx.getMissingDependencies()
434 // If there are missing dependencies, querying Bazel will fail. Soong instead fails at execution
435 // time, not loading/analysis. disable mixed builds and fall back to Soong to maintain that
436 // behavior.
437 if len(missingDeps) > 0 {
438 ctx.Config().LogMixedBuild(ctx, false)
439 return ModuleMissingDeps
440 }
441 }
442
443 module := ctx.Module()
444 apexInfo := ctx.Provider(ApexInfoProvider).(ApexInfo)
445 withinApex := !apexInfo.IsForPlatform()
Sasha Smundak39a301c2022-12-29 17:11:49 -0800446 mixedBuildEnabled := ctx.Config().IsMixedBuildsEnabled() &&
Sasha Smundak39a301c2022-12-29 17:11:49 -0800447 module.Enabled() &&
448 convertedToBazel(ctx, module) &&
Yu Liue4312402023-01-18 09:15:31 -0800449 ctx.Config().BazelContext.IsModuleNameAllowed(module.Name(), withinApex)
MarkDacekff851b82022-04-21 18:33:17 +0000450 ctx.Config().LogMixedBuild(ctx, mixedBuildEnabled)
MarkDacekf47e1422023-04-19 16:47:36 +0000451
452 if mixedBuildEnabled {
453 return MixedBuildEnabled
454 }
455 return ModuleIncompatibility
MarkDacekff851b82022-04-21 18:33:17 +0000456}
457
Spandan Das64852422023-08-02 21:58:41 +0000458func isGoModule(module blueprint.Module) bool {
459 if _, ok := module.(*bootstrap.GoPackage); ok {
460 return true
461 }
462 if _, ok := module.(*bootstrap.GoBinary); ok {
463 return true
464 }
465 return false
466}
467
Liz Kammer6eff3232021-08-26 08:37:59 -0400468// ConvertedToBazel returns whether this module has been converted (with bp2build or manually) to Bazel.
Jingwen Chen55bc8202021-11-02 06:40:51 +0000469func convertedToBazel(ctx BazelConversionContext, module blueprint.Module) bool {
Spandan Das64852422023-08-02 21:58:41 +0000470 // Special-case bootstrap_go_package and bootstrap_go_binary
471 // These do not implement Bazelable, but have been converted
472 if isGoModule(module) {
473 return true
474 }
Liz Kammer6eff3232021-08-26 08:37:59 -0400475 b, ok := module.(Bazelable)
476 if !ok {
477 return false
478 }
Liz Kammerc86e0942023-08-11 16:15:12 -0400479
480 return b.HasHandcraftedLabel() || b.shouldConvertWithBp2build(ctx, shouldConvertParams{
481 module: module,
482 moduleDir: ctx.OtherModuleDir(module),
483 moduleName: ctx.OtherModuleName(module),
484 moduleType: ctx.OtherModuleType(module),
485 })
486}
487
488type ShouldConvertWithBazelContext interface {
489 ModuleErrorf(format string, args ...interface{})
490 Module() Module
491 Config() Config
492 ModuleType() string
493 ModuleName() string
494 ModuleDir() string
Liz Kammer6eff3232021-08-26 08:37:59 -0400495}
496
Sam Delmerico24c56032022-03-28 19:53:03 +0000497// ShouldConvertWithBp2build returns whether the given BazelModuleBase should be converted with bp2build
Liz Kammerc86e0942023-08-11 16:15:12 -0400498func (b *BazelModuleBase) ShouldConvertWithBp2build(ctx ShouldConvertWithBazelContext) bool {
499 return b.shouldConvertWithBp2build(ctx, shouldConvertParams{
500 module: ctx.Module(),
501 moduleDir: ctx.ModuleDir(),
502 moduleName: ctx.ModuleName(),
503 moduleType: ctx.ModuleType(),
504 })
Liz Kammer6eff3232021-08-26 08:37:59 -0400505}
506
Sam Delmerico24c56032022-03-28 19:53:03 +0000507type bazelOtherModuleContext interface {
508 ModuleErrorf(format string, args ...interface{})
509 Config() Config
510 OtherModuleType(m blueprint.Module) string
511 OtherModuleName(m blueprint.Module) string
512 OtherModuleDir(m blueprint.Module) string
513}
Sam Delmericofa1831c2022-02-22 18:07:55 +0000514
MarkDacekf47e1422023-04-19 16:47:36 +0000515func isPlatformIncompatible(osType OsType, arch ArchType) bool {
516 return osType == Windows || // Windows toolchains are not currently supported.
517 osType == LinuxBionic || // Linux Bionic toolchains are not currently supported.
518 osType == LinuxMusl || // Linux musl toolchains are not currently supported (b/259266326).
519 arch == Riscv64 // TODO(b/262192655) Riscv64 toolchains are not currently supported.
520}
521
Liz Kammerc86e0942023-08-11 16:15:12 -0400522type shouldConvertModuleContext interface {
523 ModuleErrorf(format string, args ...interface{})
524 Config() Config
525}
526
527type shouldConvertParams struct {
528 module blueprint.Module
529 moduleType string
530 moduleDir string
531 moduleName string
532}
533
534func (b *BazelModuleBase) shouldConvertWithBp2build(ctx shouldConvertModuleContext, p shouldConvertParams) bool {
Liz Kammerbe46fcc2021-11-01 15:32:43 -0400535 if !b.bazelProps().Bazel_module.CanConvertToBazel {
536 return false
Jingwen Chen12b4c272021-03-10 02:05:59 -0500537 }
538
Liz Kammerc86e0942023-08-11 16:15:12 -0400539 module := p.module
Spandan Das5af0bd32022-09-28 20:43:08 +0000540
Sam Delmerico85d831a2022-03-07 19:12:42 +0000541 propValue := b.bazelProperties.Bazel_module.Bp2build_available
Liz Kammerc86e0942023-08-11 16:15:12 -0400542 packagePath := moduleDirWithPossibleOverride(ctx, module, p.moduleDir)
Sam Delmerico24c56032022-03-28 19:53:03 +0000543
Sam Delmerico85d831a2022-03-07 19:12:42 +0000544 // Modules in unit tests which are enabled in the allowlist by type or name
545 // trigger this conditional because unit tests run under the "." package path
Sam Delmerico24c56032022-03-28 19:53:03 +0000546 isTestModule := packagePath == Bp2BuildTopLevel && proptools.BoolDefault(propValue, false)
547 if isTestModule {
548 return true
549 }
550
Liz Kammerc86e0942023-08-11 16:15:12 -0400551 moduleName := moduleNameWithPossibleOverride(ctx, module, p.moduleName)
Zi Wang734266b2023-10-06 11:25:06 -0700552 // use "prebuilt_" + original module name as the java_import(_host) module name,
553 // to avoid the failure that a normal module and a prebuilt module with
554 // the same name are both allowlisted. This cannot be applied to all the *_import
555 // module types. For example, android_library_import has to use original module
556 // name here otherwise the *-nodeps targets cannot be handled correctly.
Zi Wang74c9a772023-10-09 17:56:06 -0700557 // TODO(b/304385140): remove this special casing
558 if p.moduleType == "java_import" || p.moduleType == "java_import_host" {
Zi Wang734266b2023-10-06 11:25:06 -0700559 moduleName = module.Name()
560 }
561
Cole Faust324a92e2022-08-23 15:29:05 -0700562 allowlist := ctx.Config().Bp2buildPackageConfig
Zi Wang734266b2023-10-06 11:25:06 -0700563
Sam Delmerico24c56032022-03-28 19:53:03 +0000564 moduleNameAllowed := allowlist.moduleAlwaysConvert[moduleName]
Liz Kammerc86e0942023-08-11 16:15:12 -0400565 moduleTypeAllowed := allowlist.moduleTypeAlwaysConvert[p.moduleType]
Sam Delmerico24c56032022-03-28 19:53:03 +0000566 allowlistConvert := moduleNameAllowed || moduleTypeAllowed
567 if moduleNameAllowed && moduleTypeAllowed {
Sam Delmericoe42edc32023-09-05 19:00:38 +0000568 ctx.ModuleErrorf("A module %q of type %q cannot be in moduleAlwaysConvert and also be in moduleTypeAlwaysConvert", moduleName, p.moduleType)
Sam Delmerico24c56032022-03-28 19:53:03 +0000569 return false
570 }
571
572 if allowlist.moduleDoNotConvert[moduleName] {
Sam Delmerico85d831a2022-03-07 19:12:42 +0000573 if moduleNameAllowed {
Sam Delmericoe42edc32023-09-05 19:00:38 +0000574 ctx.ModuleErrorf("a module %q cannot be in moduleDoNotConvert and also be in moduleAlwaysConvert", moduleName)
Sam Delmerico85d831a2022-03-07 19:12:42 +0000575 }
Sam Delmerico94d26c22022-02-25 21:34:51 +0000576 return false
577 }
578
Sam Delmerico24c56032022-03-28 19:53:03 +0000579 // This is a tristate value: true, false, or unset.
580 if ok, directoryPath := bp2buildDefaultTrueRecursively(packagePath, allowlist.defaultConfig); ok {
581 if moduleNameAllowed {
582 ctx.ModuleErrorf("A module cannot be in a directory marked Bp2BuildDefaultTrue"+
Yu Liu10853f92022-09-14 16:05:22 -0700583 " or Bp2BuildDefaultTrueRecursively and also be in moduleAlwaysConvert. Directory: '%s'"+
584 " Module: '%s'", directoryPath, moduleName)
Sam Delmerico24c56032022-03-28 19:53:03 +0000585 return false
Sam Delmericofa1831c2022-02-22 18:07:55 +0000586 }
587
Jingwen Chen12b4c272021-03-10 02:05:59 -0500588 // Allow modules to explicitly opt-out.
589 return proptools.BoolDefault(propValue, true)
590 }
591
592 // Allow modules to explicitly opt-in.
Sam Delmerico85d831a2022-03-07 19:12:42 +0000593 return proptools.BoolDefault(propValue, allowlistConvert)
Jingwen Chen12b4c272021-03-10 02:05:59 -0500594}
595
596// bp2buildDefaultTrueRecursively checks that the package contains a prefix from the
597// set of package prefixes where all modules must be converted. That is, if the
598// package is x/y/z, and the list contains either x, x/y, or x/y/z, this function will
599// return true.
600//
601// However, if the package is x/y, and it matches a Bp2BuildDefaultFalse "x/y" entry
602// exactly, this module will return false early.
603//
604// This function will also return false if the package doesn't match anything in
605// the config.
Sam Delmerico24c56032022-03-28 19:53:03 +0000606//
607// This function will also return the allowlist entry which caused a particular
608// package to be enabled. Since packages can be enabled via a recursive declaration,
609// the path returned will not always be the same as the one provided.
610func bp2buildDefaultTrueRecursively(packagePath string, config allowlists.Bp2BuildConfig) (bool, string) {
Jingwen Chen294e7742021-08-31 05:58:01 +0000611 // Check if the package path has an exact match in the config.
Sam Delmerico24c56032022-03-28 19:53:03 +0000612 if config[packagePath] == allowlists.Bp2BuildDefaultTrue || config[packagePath] == allowlists.Bp2BuildDefaultTrueRecursively {
613 return true, packagePath
MarkDacek756b2962022-10-13 17:50:17 +0000614 } else if config[packagePath] == allowlists.Bp2BuildDefaultFalse || config[packagePath] == allowlists.Bp2BuildDefaultFalseRecursively {
Sam Delmerico24c56032022-03-28 19:53:03 +0000615 return false, packagePath
Jingwen Chen12b4c272021-03-10 02:05:59 -0500616 }
617
Jingwen Chen91220d72021-03-24 02:18:33 -0400618 // If not, check for the config recursively.
MarkDacek756b2962022-10-13 17:50:17 +0000619 packagePrefix := packagePath
620
621 // e.g. for x/y/z, iterate over x/y, then x, taking the most-specific value from the allowlist.
622 for strings.Contains(packagePrefix, "/") {
623 dirIndex := strings.LastIndex(packagePrefix, "/")
624 packagePrefix = packagePrefix[:dirIndex]
625 switch value := config[packagePrefix]; value {
626 case allowlists.Bp2BuildDefaultTrueRecursively:
Jingwen Chen12b4c272021-03-10 02:05:59 -0500627 // package contains this prefix and this prefix should convert all modules
Sam Delmerico24c56032022-03-28 19:53:03 +0000628 return true, packagePrefix
MarkDacek756b2962022-10-13 17:50:17 +0000629 case allowlists.Bp2BuildDefaultFalseRecursively:
630 //package contains this prefix and this prefix should NOT convert any modules
631 return false, packagePrefix
Jingwen Chen12b4c272021-03-10 02:05:59 -0500632 }
633 // Continue to the next part of the package dir.
MarkDacek756b2962022-10-13 17:50:17 +0000634
Jingwen Chen12b4c272021-03-10 02:05:59 -0500635 }
636
Sam Delmerico24c56032022-03-28 19:53:03 +0000637 return false, packagePath
Liz Kammerea6666f2021-02-17 10:17:28 -0500638}
Liz Kammerba3ea162021-02-17 13:22:03 -0500639
Liz Kammerbe46fcc2021-11-01 15:32:43 -0400640func registerBp2buildConversionMutator(ctx RegisterMutatorsContext) {
Chris Parsons6666d0f2023-09-22 16:21:53 +0000641 ctx.BottomUp("bp2build_conversion", bp2buildConversionMutator).Parallel()
Chris Parsons5f1b3c72023-09-28 20:41:03 +0000642 ctx.BottomUp("bp2build_deps", bp2buildDepsMutator).Parallel()
643}
644
Chris Parsons6666d0f2023-09-22 16:21:53 +0000645func bp2buildConversionMutator(ctx BottomUpMutatorContext) {
Chris Parsons5f1b3c72023-09-28 20:41:03 +0000646 // If an existing BUILD file in the module directory has a target defined
647 // with this same name as this module, assume that this is an existing
648 // definition for this target.
649 if ctx.Config().HasBazelBuildTargetInSource(ctx.ModuleDir(), ctx.ModuleName()) {
650 ctx.MarkBp2buildUnconvertible(bp2build_metrics_proto.UnconvertedReasonType_DEFINED_IN_BUILD_FILE, ctx.ModuleName())
651 return
652 }
Liz Kammerbe46fcc2021-11-01 15:32:43 -0400653 bModule, ok := ctx.Module().(Bazelable)
Chris Parsons39a16972023-06-08 14:28:51 +0000654 if !ok {
655 ctx.MarkBp2buildUnconvertible(bp2build_metrics_proto.UnconvertedReasonType_TYPE_UNSUPPORTED, "")
Liz Kammerbe46fcc2021-11-01 15:32:43 -0400656 return
657 }
Liz Kammerc86e0942023-08-11 16:15:12 -0400658 // There may be cases where the target is created by a macro rather than in a BUILD file, those
659 // should be captured as well.
660 if bModule.HasHandcraftedLabel() {
661 // Defer to the BUILD target. Generating an additional target would
662 // cause a BUILD file conflict.
663 ctx.MarkBp2buildUnconvertible(bp2build_metrics_proto.UnconvertedReasonType_DEFINED_IN_BUILD_FILE, "")
664 return
665 }
Chris Parsons39a16972023-06-08 14:28:51 +0000666 // TODO: b/285631638 - Differentiate between denylisted modules and missing bp2build capabilities.
Liz Kammerc86e0942023-08-11 16:15:12 -0400667 if !bModule.shouldConvertWithBp2build(ctx, shouldConvertParams{
668 module: ctx.Module(),
669 moduleDir: ctx.ModuleDir(),
670 moduleName: ctx.ModuleName(),
671 moduleType: ctx.ModuleType(),
672 }) {
Chris Parsons39a16972023-06-08 14:28:51 +0000673 ctx.MarkBp2buildUnconvertible(bp2build_metrics_proto.UnconvertedReasonType_UNSUPPORTED, "")
674 return
675 }
Chris Parsons0c4de1f2023-09-21 20:36:35 +0000676 if ctx.Module().base().GetUnconvertedReason() != nil {
677 return
678 }
679
Liz Kammerbe46fcc2021-11-01 15:32:43 -0400680 bModule.ConvertWithBp2build(ctx)
Chris Parsons39a16972023-06-08 14:28:51 +0000681
Cole Faust11edf552023-10-13 11:32:14 -0700682 installCtx := &baseModuleContextToModuleInstallPathContext{ctx}
683 ctx.Module().base().setPartitionForBp2build(modulePartition(installCtx, true))
684
Chris Parsons0c4de1f2023-09-21 20:36:35 +0000685 if len(ctx.Module().base().Bp2buildTargets()) == 0 && ctx.Module().base().GetUnconvertedReason() == nil {
Chris Parsons39a16972023-06-08 14:28:51 +0000686 panic(fmt.Errorf("illegal bp2build invariant: module '%s' was neither converted nor marked unconvertible", ctx.ModuleName()))
687 }
Chris Parsons0c4de1f2023-09-21 20:36:35 +0000688
Chris Parsons5f1b3c72023-09-28 20:41:03 +0000689 // If an existing BUILD file in the module directory has a target defined
690 // with the same name as any target generated by this module, assume that this
691 // is an existing definition for this target. (These generated target names
692 // may be different than the module name, as checked at the beginning of this function!)
Chris Parsons0c4de1f2023-09-21 20:36:35 +0000693 for _, targetInfo := range ctx.Module().base().Bp2buildTargets() {
694 if ctx.Config().HasBazelBuildTargetInSource(targetInfo.TargetPackage(), targetInfo.TargetName()) {
695 // Defer to the BUILD target. Generating an additional target would
696 // cause a BUILD file conflict.
697 ctx.MarkBp2buildUnconvertible(bp2build_metrics_proto.UnconvertedReasonType_DEFINED_IN_BUILD_FILE, targetInfo.TargetName())
698 return
699 }
700 }
Liz Kammerbe46fcc2021-11-01 15:32:43 -0400701}
Wei Libafb6d62021-12-10 03:14:59 -0800702
Chris Parsons5f1b3c72023-09-28 20:41:03 +0000703// TODO: b/285631638 - Add this as a new mutator to the bp2build conversion mutators.
704// Currently, this only exists to prepare test coverage for the launch of this feature.
705func bp2buildDepsMutator(ctx BottomUpMutatorContext) {
706 if ctx.Module().base().GetUnconvertedReason() != nil {
707 return
708 }
709
710 if len(ctx.Module().GetMissingBp2buildDeps()) > 0 {
711 exampleDep := ctx.Module().GetMissingBp2buildDeps()[0]
Jingwen Chen464e6a02023-10-05 09:58:00 +0000712 ctx.MarkBp2buildUnconvertible(
713 bp2build_metrics_proto.UnconvertedReasonType_UNCONVERTED_DEP, exampleDep)
Chris Parsons5f1b3c72023-09-28 20:41:03 +0000714 }
715
Jingwen Chen464e6a02023-10-05 09:58:00 +0000716 // Transitively mark modules unconvertible with the following set of conditions.
Chris Parsons5f1b3c72023-09-28 20:41:03 +0000717 ctx.VisitDirectDeps(func(dep Module) {
Jingwen Chen464e6a02023-10-05 09:58:00 +0000718 if dep.base().GetUnconvertedReason() == nil {
719 return
Chris Parsons5f1b3c72023-09-28 20:41:03 +0000720 }
Jingwen Chen464e6a02023-10-05 09:58:00 +0000721
722 if dep.base().GetUnconvertedReason().ReasonType ==
723 int(bp2build_metrics_proto.UnconvertedReasonType_DEFINED_IN_BUILD_FILE) {
724 return
725 }
726
727 if ctx.OtherModuleDependencyTag(dep) != Bp2buildDepTag {
728 return
729 }
730
731 ctx.MarkBp2buildUnconvertible(
732 bp2build_metrics_proto.UnconvertedReasonType_UNCONVERTED_DEP, dep.Name())
Chris Parsons5f1b3c72023-09-28 20:41:03 +0000733 })
734}
735
Wei Libafb6d62021-12-10 03:14:59 -0800736// GetMainClassInManifest scans the manifest file specified in filepath and returns
737// the value of attribute Main-Class in the manifest file if it exists, or returns error.
738// WARNING: this is for bp2build converters of java_* modules only.
739func GetMainClassInManifest(c Config, filepath string) (string, error) {
740 file, err := c.fs.Open(filepath)
741 if err != nil {
742 return "", err
743 }
Liz Kammer0fe123d2022-02-07 10:17:35 -0500744 defer file.Close()
Wei Libafb6d62021-12-10 03:14:59 -0800745 scanner := bufio.NewScanner(file)
746 for scanner.Scan() {
747 line := scanner.Text()
748 if strings.HasPrefix(line, "Main-Class:") {
749 return strings.TrimSpace(line[len("Main-Class:"):]), nil
750 }
751 }
752
753 return "", errors.New("Main-Class is not found.")
754}
Sam Delmerico4ed95e22023-02-03 18:12:15 -0500755
756func AttachValidationActions(ctx ModuleContext, outputFilePath Path, validations Paths) ModuleOutPath {
757 validatedOutputFilePath := PathForModuleOut(ctx, "validated", outputFilePath.Base())
758 ctx.Build(pctx, BuildParams{
759 Rule: CpNoPreserveSymlink,
760 Description: "run validations " + outputFilePath.Base(),
761 Output: validatedOutputFilePath,
762 Input: outputFilePath,
763 Validations: validations,
764 })
765 return validatedOutputFilePath
766}
yikefdca7fe2023-08-17 01:03:37 +0000767
768func RunsOn(hostSupported bool, deviceSupported bool, unitTest bool) []string {
769 var runsOn []string
770
771 if hostSupported && deviceSupported {
772 runsOn = []string{"host_without_device", "device"}
773 } else if hostSupported {
774 if unitTest {
775 runsOn = []string{"host_without_device"}
776 } else {
777 runsOn = []string{"host_with_device"}
778 }
779 } else if deviceSupported {
780 runsOn = []string{"device"}
781 }
782
783 return runsOn
784}