bazel.go 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628
  1. // 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. package android
  15. import (
  16. "bufio"
  17. "errors"
  18. "fmt"
  19. "strings"
  20. "android/soong/ui/metrics/bp2build_metrics_proto"
  21. "github.com/google/blueprint"
  22. "github.com/google/blueprint/proptools"
  23. "android/soong/android/allowlists"
  24. )
  25. const (
  26. // A sentinel value to be used as a key in Bp2BuildConfig for modules with
  27. // no package path. This is also the module dir for top level Android.bp
  28. // modules.
  29. Bp2BuildTopLevel = "."
  30. )
  31. type MixedBuildEnabledStatus int
  32. const (
  33. // This module can be mixed_built.
  34. MixedBuildEnabled = iota
  35. // There is a technical incompatibility preventing this module from being
  36. // bazel-analyzed. Note: the module might also be incompatible.
  37. TechnicalIncompatibility
  38. // This module cannot be mixed_built due to some incompatibility with it
  39. // that is not a platform incompatibility. Example: the module-type is not
  40. // enabled, or is not bp2build-converted.
  41. ModuleIncompatibility
  42. // Missing dependencies. We can't query Bazel for modules if it has missing dependencies, there
  43. // will be failures.
  44. ModuleMissingDeps
  45. )
  46. // FileGroupAsLibrary describes a filegroup module that is converted to some library
  47. // such as aidl_library or proto_library.
  48. type FileGroupAsLibrary interface {
  49. ShouldConvertToAidlLibrary(ctx BazelConversionPathContext) bool
  50. ShouldConvertToProtoLibrary(ctx BazelConversionPathContext) bool
  51. GetAidlLibraryLabel(ctx BazelConversionPathContext) string
  52. GetProtoLibraryLabel(ctx BazelConversionPathContext) string
  53. }
  54. type BazelConversionStatus struct {
  55. // Information about _all_ bp2build targets generated by this module. Multiple targets are
  56. // supported as Soong handles some things within a single target that we may choose to split into
  57. // multiple targets, e.g. renderscript, protos, yacc within a cc module.
  58. Bp2buildInfo []bp2buildInfo `blueprint:"mutated"`
  59. // UnconvertedBp2buildDep stores the module names of direct dependency that were not converted to
  60. // Bazel
  61. UnconvertedDeps []string `blueprint:"mutated"`
  62. // MissingBp2buildDep stores the module names of direct dependency that were not found
  63. MissingDeps []string `blueprint:"mutated"`
  64. // If non-nil, indicates that the module could not be converted successfully
  65. // with bp2build. This will describe the reason the module could not be converted.
  66. UnconvertedReason *UnconvertedReason
  67. }
  68. // The reason a module could not be converted to a BUILD target via bp2build.
  69. // This should match bp2build_metrics_proto.UnconvertedReason, but omits private
  70. // proto-related fields that prevent copying this struct.
  71. type UnconvertedReason struct {
  72. // Should correspond to a valid value in bp2build_metrics_proto.UnconvertedReasonType.
  73. // A raw int is used here instead, because blueprint logic requires that all transitive
  74. // fields of module definitions be primitives.
  75. ReasonType int
  76. Detail string
  77. }
  78. type BazelModuleProperties struct {
  79. // The label of the Bazel target replacing this Soong module. When run in conversion mode, this
  80. // will import the handcrafted build target into the autogenerated file. Note: this may result in
  81. // a conflict due to duplicate targets if bp2build_available is also set.
  82. Label *string
  83. // If true, bp2build will generate the converted Bazel target for this module. Note: this may
  84. // cause a conflict due to the duplicate targets if label is also set.
  85. //
  86. // This is a bool pointer to support tristates: true, false, not set.
  87. //
  88. // To opt in a module, set bazel_module: { bp2build_available: true }
  89. // To opt out a module, set bazel_module: { bp2build_available: false }
  90. // To defer the default setting for the directory, do not set the value.
  91. Bp2build_available *bool
  92. // CanConvertToBazel is set via InitBazelModule to indicate that a module type can be converted to
  93. // Bazel with Bp2build.
  94. CanConvertToBazel bool `blueprint:"mutated"`
  95. }
  96. // Properties contains common module properties for Bazel migration purposes.
  97. type properties struct {
  98. // In "Bazel mixed build" mode, this represents the Bazel target replacing
  99. // this Soong module.
  100. Bazel_module BazelModuleProperties
  101. }
  102. // namespacedVariableProperties is a map from a string representing a Soong
  103. // config variable namespace, like "android" or "vendor_name" to a slice of
  104. // pointer to a struct containing a single field called Soong_config_variables
  105. // whose value mirrors the structure in the Blueprint file.
  106. type namespacedVariableProperties map[string][]interface{}
  107. // BazelModuleBase contains the property structs with metadata for modules which can be converted to
  108. // Bazel.
  109. type BazelModuleBase struct {
  110. bazelProperties properties
  111. // namespacedVariableProperties is used for soong_config_module_type support
  112. // in bp2build. Soong config modules allow users to set module properties
  113. // based on custom product variables defined in Android.bp files. These
  114. // variables are namespaced to prevent clobbering, especially when set from
  115. // Makefiles.
  116. namespacedVariableProperties namespacedVariableProperties
  117. // baseModuleType is set when this module was created from a module type
  118. // defined by a soong_config_module_type. Every soong_config_module_type
  119. // "wraps" another module type, e.g. a soong_config_module_type can wrap a
  120. // cc_defaults to a custom_cc_defaults, or cc_binary to a custom_cc_binary.
  121. // This baseModuleType is set to the wrapped module type.
  122. baseModuleType string
  123. }
  124. // Bazelable is specifies the interface for modules that can be converted to Bazel.
  125. type Bazelable interface {
  126. bazelProps() *properties
  127. HasHandcraftedLabel() bool
  128. HandcraftedLabel() string
  129. GetBazelLabel(ctx BazelConversionPathContext, module blueprint.Module) string
  130. ShouldConvertWithBp2build(ctx BazelConversionContext) bool
  131. shouldConvertWithBp2build(ctx bazelOtherModuleContext, module blueprint.Module) bool
  132. // ConvertWithBp2build either converts the module to a Bazel build target or
  133. // declares the module as unconvertible (for logging and metrics).
  134. // Modules must implement this function to be bp2build convertible. The function
  135. // must either create at least one Bazel target module (using ctx.CreateBazelTargetModule or
  136. // its related functions), or declare itself unconvertible using ctx.MarkBp2buildUnconvertible.
  137. ConvertWithBp2build(ctx TopDownMutatorContext)
  138. // namespacedVariableProps is a map from a soong config variable namespace
  139. // (e.g. acme, android) to a map of interfaces{}, which are really
  140. // reflect.Struct pointers, representing the value of the
  141. // soong_config_variables property of a module. The struct pointer is the
  142. // one with the single member called Soong_config_variables, which itself is
  143. // a struct containing fields for each supported feature in that namespace.
  144. //
  145. // The reason for using a slice of interface{} is to support defaults
  146. // propagation of the struct pointers.
  147. namespacedVariableProps() namespacedVariableProperties
  148. setNamespacedVariableProps(props namespacedVariableProperties)
  149. BaseModuleType() string
  150. SetBaseModuleType(baseModuleType string)
  151. }
  152. // ApiProvider is implemented by modules that contribute to an API surface
  153. type ApiProvider interface {
  154. ConvertWithApiBp2build(ctx TopDownMutatorContext)
  155. }
  156. // MixedBuildBuildable is an interface that module types should implement in order
  157. // to be "handled by Bazel" in a mixed build.
  158. type MixedBuildBuildable interface {
  159. // IsMixedBuildSupported returns true if and only if this module should be
  160. // "handled by Bazel" in a mixed build.
  161. // This "escape hatch" allows modules with corner-case scenarios to opt out
  162. // of being built with Bazel.
  163. IsMixedBuildSupported(ctx BaseModuleContext) bool
  164. // QueueBazelCall invokes request-queueing functions on the BazelContext
  165. // so that these requests are handled when Bazel's cquery is invoked.
  166. QueueBazelCall(ctx BaseModuleContext)
  167. // ProcessBazelQueryResponse uses Bazel information (obtained from the BazelContext)
  168. // to set module fields and providers to propagate this module's metadata upstream.
  169. // This effectively "bridges the gap" between Bazel and Soong in a mixed build.
  170. // Soong modules depending on this module should be oblivious to the fact that
  171. // this module was handled by Bazel.
  172. ProcessBazelQueryResponse(ctx ModuleContext)
  173. }
  174. // BazelModule is a lightweight wrapper interface around Module for Bazel-convertible modules.
  175. type BazelModule interface {
  176. Module
  177. Bazelable
  178. }
  179. // InitBazelModule is a wrapper function that decorates a BazelModule with Bazel-conversion
  180. // properties.
  181. func InitBazelModule(module BazelModule) {
  182. module.AddProperties(module.bazelProps())
  183. module.bazelProps().Bazel_module.CanConvertToBazel = true
  184. }
  185. // bazelProps returns the Bazel properties for the given BazelModuleBase.
  186. func (b *BazelModuleBase) bazelProps() *properties {
  187. return &b.bazelProperties
  188. }
  189. func (b *BazelModuleBase) namespacedVariableProps() namespacedVariableProperties {
  190. return b.namespacedVariableProperties
  191. }
  192. func (b *BazelModuleBase) setNamespacedVariableProps(props namespacedVariableProperties) {
  193. b.namespacedVariableProperties = props
  194. }
  195. func (b *BazelModuleBase) BaseModuleType() string {
  196. return b.baseModuleType
  197. }
  198. func (b *BazelModuleBase) SetBaseModuleType(baseModuleType string) {
  199. b.baseModuleType = baseModuleType
  200. }
  201. // HasHandcraftedLabel returns whether this module has a handcrafted Bazel label.
  202. func (b *BazelModuleBase) HasHandcraftedLabel() bool {
  203. return b.bazelProperties.Bazel_module.Label != nil
  204. }
  205. // HandcraftedLabel returns the handcrafted label for this module, or empty string if there is none
  206. func (b *BazelModuleBase) HandcraftedLabel() string {
  207. return proptools.String(b.bazelProperties.Bazel_module.Label)
  208. }
  209. // GetBazelLabel returns the Bazel label for the given BazelModuleBase.
  210. func (b *BazelModuleBase) GetBazelLabel(ctx BazelConversionPathContext, module blueprint.Module) string {
  211. if b.HasHandcraftedLabel() {
  212. return b.HandcraftedLabel()
  213. }
  214. if b.ShouldConvertWithBp2build(ctx) {
  215. return bp2buildModuleLabel(ctx, module)
  216. }
  217. panic(fmt.Errorf("requested non-existent label for module ", module.Name()))
  218. }
  219. type Bp2BuildConversionAllowlist struct {
  220. // Configure modules in these directories to enable bp2build_available: true or false by default.
  221. defaultConfig allowlists.Bp2BuildConfig
  222. // Keep any existing BUILD files (and do not generate new BUILD files) for these directories
  223. // in the synthetic Bazel workspace.
  224. keepExistingBuildFile map[string]bool
  225. // Per-module allowlist to always opt modules into both bp2build and Bazel Dev Mode mixed
  226. // builds. These modules are usually in directories with many other modules that are not ready
  227. // for conversion.
  228. //
  229. // A module can either be in this list or its directory allowlisted entirely
  230. // in bp2buildDefaultConfig, but not both at the same time.
  231. moduleAlwaysConvert map[string]bool
  232. // Per-module-type allowlist to always opt modules in to both bp2build and
  233. // Bazel Dev Mode mixed builds when they have the same type as one listed.
  234. moduleTypeAlwaysConvert map[string]bool
  235. // Per-module denylist to always opt modules out of bp2build conversion.
  236. moduleDoNotConvert map[string]bool
  237. }
  238. // NewBp2BuildAllowlist creates a new, empty Bp2BuildConversionAllowlist
  239. // which can be populated using builder pattern Set* methods
  240. func NewBp2BuildAllowlist() Bp2BuildConversionAllowlist {
  241. return Bp2BuildConversionAllowlist{
  242. allowlists.Bp2BuildConfig{},
  243. map[string]bool{},
  244. map[string]bool{},
  245. map[string]bool{},
  246. map[string]bool{},
  247. }
  248. }
  249. // SetDefaultConfig copies the entries from defaultConfig into the allowlist
  250. func (a Bp2BuildConversionAllowlist) SetDefaultConfig(defaultConfig allowlists.Bp2BuildConfig) Bp2BuildConversionAllowlist {
  251. if a.defaultConfig == nil {
  252. a.defaultConfig = allowlists.Bp2BuildConfig{}
  253. }
  254. for k, v := range defaultConfig {
  255. a.defaultConfig[k] = v
  256. }
  257. return a
  258. }
  259. // SetKeepExistingBuildFile copies the entries from keepExistingBuildFile into the allowlist
  260. func (a Bp2BuildConversionAllowlist) SetKeepExistingBuildFile(keepExistingBuildFile map[string]bool) Bp2BuildConversionAllowlist {
  261. if a.keepExistingBuildFile == nil {
  262. a.keepExistingBuildFile = map[string]bool{}
  263. }
  264. for k, v := range keepExistingBuildFile {
  265. a.keepExistingBuildFile[k] = v
  266. }
  267. return a
  268. }
  269. // SetModuleAlwaysConvertList copies the entries from moduleAlwaysConvert into the allowlist
  270. func (a Bp2BuildConversionAllowlist) SetModuleAlwaysConvertList(moduleAlwaysConvert []string) Bp2BuildConversionAllowlist {
  271. if a.moduleAlwaysConvert == nil {
  272. a.moduleAlwaysConvert = map[string]bool{}
  273. }
  274. for _, m := range moduleAlwaysConvert {
  275. a.moduleAlwaysConvert[m] = true
  276. }
  277. return a
  278. }
  279. // SetModuleTypeAlwaysConvertList copies the entries from moduleTypeAlwaysConvert into the allowlist
  280. func (a Bp2BuildConversionAllowlist) SetModuleTypeAlwaysConvertList(moduleTypeAlwaysConvert []string) Bp2BuildConversionAllowlist {
  281. if a.moduleTypeAlwaysConvert == nil {
  282. a.moduleTypeAlwaysConvert = map[string]bool{}
  283. }
  284. for _, m := range moduleTypeAlwaysConvert {
  285. a.moduleTypeAlwaysConvert[m] = true
  286. }
  287. return a
  288. }
  289. // SetModuleDoNotConvertList copies the entries from moduleDoNotConvert into the allowlist
  290. func (a Bp2BuildConversionAllowlist) SetModuleDoNotConvertList(moduleDoNotConvert []string) Bp2BuildConversionAllowlist {
  291. if a.moduleDoNotConvert == nil {
  292. a.moduleDoNotConvert = map[string]bool{}
  293. }
  294. for _, m := range moduleDoNotConvert {
  295. a.moduleDoNotConvert[m] = true
  296. }
  297. return a
  298. }
  299. // ShouldKeepExistingBuildFileForDir returns whether an existing BUILD file should be
  300. // added to the build symlink forest based on the current global configuration.
  301. func (a Bp2BuildConversionAllowlist) ShouldKeepExistingBuildFileForDir(dir string) bool {
  302. if _, ok := a.keepExistingBuildFile[dir]; ok {
  303. // Exact dir match
  304. return true
  305. }
  306. var i int
  307. // Check if subtree match
  308. for {
  309. j := strings.Index(dir[i:], "/")
  310. if j == -1 {
  311. return false //default
  312. }
  313. prefix := dir[0 : i+j]
  314. i = i + j + 1 // skip the "/"
  315. if recursive, ok := a.keepExistingBuildFile[prefix]; ok && recursive {
  316. return true
  317. }
  318. }
  319. }
  320. var bp2BuildAllowListKey = NewOnceKey("Bp2BuildAllowlist")
  321. var bp2buildAllowlist OncePer
  322. func GetBp2BuildAllowList() Bp2BuildConversionAllowlist {
  323. return bp2buildAllowlist.Once(bp2BuildAllowListKey, func() interface{} {
  324. return NewBp2BuildAllowlist().SetDefaultConfig(allowlists.Bp2buildDefaultConfig).
  325. SetKeepExistingBuildFile(allowlists.Bp2buildKeepExistingBuildFile).
  326. SetModuleAlwaysConvertList(allowlists.Bp2buildModuleAlwaysConvertList).
  327. SetModuleTypeAlwaysConvertList(allowlists.Bp2buildModuleTypeAlwaysConvertList).
  328. SetModuleDoNotConvertList(allowlists.Bp2buildModuleDoNotConvertList)
  329. }).(Bp2BuildConversionAllowlist)
  330. }
  331. // MixedBuildsEnabled returns a MixedBuildEnabledStatus regarding whether
  332. // a module is ready to be replaced by a converted or handcrafted Bazel target.
  333. // As a side effect, calling this method will also log whether this module is
  334. // mixed build enabled for metrics reporting.
  335. func MixedBuildsEnabled(ctx BaseModuleContext) MixedBuildEnabledStatus {
  336. platformIncompatible := isPlatformIncompatible(ctx.Os(), ctx.Arch().ArchType)
  337. if platformIncompatible {
  338. ctx.Config().LogMixedBuild(ctx, false)
  339. return TechnicalIncompatibility
  340. }
  341. if ctx.Config().AllowMissingDependencies() {
  342. missingDeps := ctx.getMissingDependencies()
  343. // If there are missing dependencies, querying Bazel will fail. Soong instead fails at execution
  344. // time, not loading/analysis. disable mixed builds and fall back to Soong to maintain that
  345. // behavior.
  346. if len(missingDeps) > 0 {
  347. ctx.Config().LogMixedBuild(ctx, false)
  348. return ModuleMissingDeps
  349. }
  350. }
  351. module := ctx.Module()
  352. apexInfo := ctx.Provider(ApexInfoProvider).(ApexInfo)
  353. withinApex := !apexInfo.IsForPlatform()
  354. mixedBuildEnabled := ctx.Config().IsMixedBuildsEnabled() &&
  355. module.Enabled() &&
  356. convertedToBazel(ctx, module) &&
  357. ctx.Config().BazelContext.IsModuleNameAllowed(module.Name(), withinApex)
  358. ctx.Config().LogMixedBuild(ctx, mixedBuildEnabled)
  359. if mixedBuildEnabled {
  360. return MixedBuildEnabled
  361. }
  362. return ModuleIncompatibility
  363. }
  364. // ConvertedToBazel returns whether this module has been converted (with bp2build or manually) to Bazel.
  365. func convertedToBazel(ctx BazelConversionContext, module blueprint.Module) bool {
  366. b, ok := module.(Bazelable)
  367. if !ok {
  368. return false
  369. }
  370. return b.shouldConvertWithBp2build(ctx, module) || b.HasHandcraftedLabel()
  371. }
  372. // ShouldConvertWithBp2build returns whether the given BazelModuleBase should be converted with bp2build
  373. func (b *BazelModuleBase) ShouldConvertWithBp2build(ctx BazelConversionContext) bool {
  374. return b.shouldConvertWithBp2build(ctx, ctx.Module())
  375. }
  376. type bazelOtherModuleContext interface {
  377. ModuleErrorf(format string, args ...interface{})
  378. Config() Config
  379. OtherModuleType(m blueprint.Module) string
  380. OtherModuleName(m blueprint.Module) string
  381. OtherModuleDir(m blueprint.Module) string
  382. }
  383. func isPlatformIncompatible(osType OsType, arch ArchType) bool {
  384. return osType == Windows || // Windows toolchains are not currently supported.
  385. osType == LinuxBionic || // Linux Bionic toolchains are not currently supported.
  386. osType == LinuxMusl || // Linux musl toolchains are not currently supported (b/259266326).
  387. arch == Riscv64 // TODO(b/262192655) Riscv64 toolchains are not currently supported.
  388. }
  389. func (b *BazelModuleBase) shouldConvertWithBp2build(ctx bazelOtherModuleContext, module blueprint.Module) bool {
  390. if !b.bazelProps().Bazel_module.CanConvertToBazel {
  391. return false
  392. }
  393. // In api_bp2build mode, all soong modules that can provide API contributions should be converted
  394. // This is irrespective of its presence/absence in bp2build allowlists
  395. if ctx.Config().BuildMode == ApiBp2build {
  396. _, providesApis := module.(ApiProvider)
  397. return providesApis
  398. }
  399. propValue := b.bazelProperties.Bazel_module.Bp2build_available
  400. packagePath := moduleDirWithPossibleOverride(ctx, module)
  401. // Modules in unit tests which are enabled in the allowlist by type or name
  402. // trigger this conditional because unit tests run under the "." package path
  403. isTestModule := packagePath == Bp2BuildTopLevel && proptools.BoolDefault(propValue, false)
  404. if isTestModule {
  405. return true
  406. }
  407. moduleName := moduleNameWithPossibleOverride(ctx, module)
  408. allowlist := ctx.Config().Bp2buildPackageConfig
  409. moduleNameAllowed := allowlist.moduleAlwaysConvert[moduleName]
  410. moduleTypeAllowed := allowlist.moduleTypeAlwaysConvert[ctx.OtherModuleType(module)]
  411. allowlistConvert := moduleNameAllowed || moduleTypeAllowed
  412. if moduleNameAllowed && moduleTypeAllowed {
  413. ctx.ModuleErrorf("A module cannot be in moduleAlwaysConvert and also be in moduleTypeAlwaysConvert")
  414. return false
  415. }
  416. if allowlist.moduleDoNotConvert[moduleName] {
  417. if moduleNameAllowed {
  418. ctx.ModuleErrorf("a module cannot be in moduleDoNotConvert and also be in moduleAlwaysConvert")
  419. }
  420. return false
  421. }
  422. // This is a tristate value: true, false, or unset.
  423. if ok, directoryPath := bp2buildDefaultTrueRecursively(packagePath, allowlist.defaultConfig); ok {
  424. if moduleNameAllowed {
  425. ctx.ModuleErrorf("A module cannot be in a directory marked Bp2BuildDefaultTrue"+
  426. " or Bp2BuildDefaultTrueRecursively and also be in moduleAlwaysConvert. Directory: '%s'"+
  427. " Module: '%s'", directoryPath, moduleName)
  428. return false
  429. }
  430. // Allow modules to explicitly opt-out.
  431. return proptools.BoolDefault(propValue, true)
  432. }
  433. // Allow modules to explicitly opt-in.
  434. return proptools.BoolDefault(propValue, allowlistConvert)
  435. }
  436. // bp2buildDefaultTrueRecursively checks that the package contains a prefix from the
  437. // set of package prefixes where all modules must be converted. That is, if the
  438. // package is x/y/z, and the list contains either x, x/y, or x/y/z, this function will
  439. // return true.
  440. //
  441. // However, if the package is x/y, and it matches a Bp2BuildDefaultFalse "x/y" entry
  442. // exactly, this module will return false early.
  443. //
  444. // This function will also return false if the package doesn't match anything in
  445. // the config.
  446. //
  447. // This function will also return the allowlist entry which caused a particular
  448. // package to be enabled. Since packages can be enabled via a recursive declaration,
  449. // the path returned will not always be the same as the one provided.
  450. func bp2buildDefaultTrueRecursively(packagePath string, config allowlists.Bp2BuildConfig) (bool, string) {
  451. // Check if the package path has an exact match in the config.
  452. if config[packagePath] == allowlists.Bp2BuildDefaultTrue || config[packagePath] == allowlists.Bp2BuildDefaultTrueRecursively {
  453. return true, packagePath
  454. } else if config[packagePath] == allowlists.Bp2BuildDefaultFalse || config[packagePath] == allowlists.Bp2BuildDefaultFalseRecursively {
  455. return false, packagePath
  456. }
  457. // If not, check for the config recursively.
  458. packagePrefix := packagePath
  459. // e.g. for x/y/z, iterate over x/y, then x, taking the most-specific value from the allowlist.
  460. for strings.Contains(packagePrefix, "/") {
  461. dirIndex := strings.LastIndex(packagePrefix, "/")
  462. packagePrefix = packagePrefix[:dirIndex]
  463. switch value := config[packagePrefix]; value {
  464. case allowlists.Bp2BuildDefaultTrueRecursively:
  465. // package contains this prefix and this prefix should convert all modules
  466. return true, packagePrefix
  467. case allowlists.Bp2BuildDefaultFalseRecursively:
  468. //package contains this prefix and this prefix should NOT convert any modules
  469. return false, packagePrefix
  470. }
  471. // Continue to the next part of the package dir.
  472. }
  473. return false, packagePath
  474. }
  475. func registerBp2buildConversionMutator(ctx RegisterMutatorsContext) {
  476. ctx.TopDown("bp2build_conversion", bp2buildConversionMutator).Parallel()
  477. }
  478. func bp2buildConversionMutator(ctx TopDownMutatorContext) {
  479. if ctx.Config().HasBazelBuildTargetInSource(ctx) {
  480. // Defer to the BUILD target. Generating an additional target would
  481. // cause a BUILD file conflict.
  482. ctx.MarkBp2buildUnconvertible(bp2build_metrics_proto.UnconvertedReasonType_DEFINED_IN_BUILD_FILE, "")
  483. return
  484. }
  485. bModule, ok := ctx.Module().(Bazelable)
  486. if !ok {
  487. ctx.MarkBp2buildUnconvertible(bp2build_metrics_proto.UnconvertedReasonType_TYPE_UNSUPPORTED, "")
  488. return
  489. }
  490. // TODO: b/285631638 - Differentiate between denylisted modules and missing bp2build capabilities.
  491. if !bModule.shouldConvertWithBp2build(ctx, ctx.Module()) {
  492. ctx.MarkBp2buildUnconvertible(bp2build_metrics_proto.UnconvertedReasonType_UNSUPPORTED, "")
  493. return
  494. }
  495. bModule.ConvertWithBp2build(ctx)
  496. if !ctx.Module().base().IsConvertedByBp2build() && ctx.Module().base().GetUnconvertedReason() == nil {
  497. panic(fmt.Errorf("illegal bp2build invariant: module '%s' was neither converted nor marked unconvertible", ctx.ModuleName()))
  498. }
  499. }
  500. func registerApiBp2buildConversionMutator(ctx RegisterMutatorsContext) {
  501. ctx.TopDown("apiBp2build_conversion", convertWithApiBp2build).Parallel()
  502. }
  503. // Generate API contribution targets if the Soong module provides APIs
  504. func convertWithApiBp2build(ctx TopDownMutatorContext) {
  505. if m, ok := ctx.Module().(ApiProvider); ok {
  506. m.ConvertWithApiBp2build(ctx)
  507. }
  508. }
  509. // GetMainClassInManifest scans the manifest file specified in filepath and returns
  510. // the value of attribute Main-Class in the manifest file if it exists, or returns error.
  511. // WARNING: this is for bp2build converters of java_* modules only.
  512. func GetMainClassInManifest(c Config, filepath string) (string, error) {
  513. file, err := c.fs.Open(filepath)
  514. if err != nil {
  515. return "", err
  516. }
  517. defer file.Close()
  518. scanner := bufio.NewScanner(file)
  519. for scanner.Scan() {
  520. line := scanner.Text()
  521. if strings.HasPrefix(line, "Main-Class:") {
  522. return strings.TrimSpace(line[len("Main-Class:"):]), nil
  523. }
  524. }
  525. return "", errors.New("Main-Class is not found.")
  526. }
  527. func AttachValidationActions(ctx ModuleContext, outputFilePath Path, validations Paths) ModuleOutPath {
  528. validatedOutputFilePath := PathForModuleOut(ctx, "validated", outputFilePath.Base())
  529. ctx.Build(pctx, BuildParams{
  530. Rule: CpNoPreserveSymlink,
  531. Description: "run validations " + outputFilePath.Base(),
  532. Output: validatedOutputFilePath,
  533. Input: outputFilePath,
  534. Validations: validations,
  535. })
  536. return validatedOutputFilePath
  537. }