filegroup.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. // Copyright 2016 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. "path/filepath"
  17. "regexp"
  18. "strings"
  19. "android/soong/bazel"
  20. "android/soong/bazel/cquery"
  21. "github.com/google/blueprint"
  22. )
  23. func init() {
  24. RegisterFilegroupBuildComponents(InitRegistrationContext)
  25. }
  26. var PrepareForTestWithFilegroup = FixtureRegisterWithContext(func(ctx RegistrationContext) {
  27. RegisterFilegroupBuildComponents(ctx)
  28. })
  29. func RegisterFilegroupBuildComponents(ctx RegistrationContext) {
  30. ctx.RegisterModuleType("filegroup", FileGroupFactory)
  31. ctx.RegisterModuleType("filegroup_defaults", FileGroupDefaultsFactory)
  32. }
  33. var convertedProtoLibrarySuffix = "_bp2build_converted"
  34. // IsFilegroup checks that a module is a filegroup type
  35. func IsFilegroup(ctx bazel.OtherModuleContext, m blueprint.Module) bool {
  36. return ctx.OtherModuleType(m) == "filegroup"
  37. }
  38. var (
  39. // ignoring case, checks for proto or protos as an independent word in the name, whether at the
  40. // beginning, end, or middle. e.g. "proto.foo", "bar-protos", "baz_proto_srcs" would all match
  41. filegroupLikelyProtoPattern = regexp.MustCompile("(?i)(^|[^a-z])proto(s)?([^a-z]|$)")
  42. filegroupLikelyAidlPattern = regexp.MustCompile("(?i)(^|[^a-z])aidl([^a-z]|$)")
  43. ProtoSrcLabelPartition = bazel.LabelPartition{
  44. Extensions: []string{".proto"},
  45. LabelMapper: isFilegroupWithPattern(filegroupLikelyProtoPattern),
  46. }
  47. AidlSrcLabelPartition = bazel.LabelPartition{
  48. Extensions: []string{".aidl"},
  49. LabelMapper: isFilegroupWithPattern(filegroupLikelyAidlPattern),
  50. }
  51. )
  52. func isFilegroupWithPattern(pattern *regexp.Regexp) bazel.LabelMapper {
  53. return func(ctx bazel.OtherModuleContext, label bazel.Label) (string, bool) {
  54. m, exists := ctx.ModuleFromName(label.OriginalModuleName)
  55. labelStr := label.Label
  56. if !exists || !IsFilegroup(ctx, m) {
  57. return labelStr, false
  58. }
  59. likelyMatched := pattern.MatchString(label.OriginalModuleName)
  60. return labelStr, likelyMatched
  61. }
  62. }
  63. // https://docs.bazel.build/versions/master/be/general.html#filegroup
  64. type bazelFilegroupAttributes struct {
  65. Srcs bazel.LabelListAttribute
  66. Applicable_licenses bazel.LabelListAttribute
  67. }
  68. type bazelAidlLibraryAttributes struct {
  69. Srcs bazel.LabelListAttribute
  70. Strip_import_prefix *string
  71. Deps bazel.LabelListAttribute
  72. }
  73. // api srcs can be contained in filegroups.
  74. // this should be generated in api_bp2build workspace as well.
  75. func (fg *fileGroup) ConvertWithApiBp2build(ctx TopDownMutatorContext) {
  76. fg.ConvertWithBp2build(ctx)
  77. }
  78. // ConvertWithBp2build performs bp2build conversion of filegroup
  79. func (fg *fileGroup) ConvertWithBp2build(ctx TopDownMutatorContext) {
  80. srcs := bazel.MakeLabelListAttribute(
  81. BazelLabelForModuleSrcExcludes(ctx, fg.properties.Srcs, fg.properties.Exclude_srcs))
  82. // For Bazel compatibility, don't generate the filegroup if there is only 1
  83. // source file, and that the source file is named the same as the module
  84. // itself. In Bazel, eponymous filegroups like this would be an error.
  85. //
  86. // Instead, dependents on this single-file filegroup can just depend
  87. // on the file target, instead of rule target, directly.
  88. //
  89. // You may ask: what if a filegroup has multiple files, and one of them
  90. // shares the name? The answer: we haven't seen that in the wild, and
  91. // should lock Soong itself down to prevent the behavior. For now,
  92. // we raise an error if bp2build sees this problem.
  93. for _, f := range srcs.Value.Includes {
  94. if f.Label == fg.Name() {
  95. if len(srcs.Value.Includes) > 1 {
  96. ctx.ModuleErrorf("filegroup '%s' cannot contain a file with the same name", fg.Name())
  97. }
  98. return
  99. }
  100. }
  101. // Convert module that has only AIDL files to aidl_library
  102. // If the module has a mixed bag of AIDL and non-AIDL files, split the filegroup manually
  103. // and then convert
  104. if fg.ShouldConvertToAidlLibrary(ctx) {
  105. tags := []string{"apex_available=//apex_available:anyapex"}
  106. deps := bazel.MakeLabelListAttribute(BazelLabelForModuleDeps(ctx, fg.properties.Aidl.Deps))
  107. attrs := &bazelAidlLibraryAttributes{
  108. Srcs: srcs,
  109. Strip_import_prefix: fg.properties.Path,
  110. Deps: deps,
  111. }
  112. props := bazel.BazelTargetModuleProperties{
  113. Rule_class: "aidl_library",
  114. Bzl_load_location: "//build/bazel/rules/aidl:aidl_library.bzl",
  115. }
  116. ctx.CreateBazelTargetModule(
  117. props,
  118. CommonAttributes{
  119. Name: fg.Name(),
  120. Tags: bazel.MakeStringListAttribute(tags),
  121. },
  122. attrs)
  123. } else {
  124. if fg.ShouldConvertToProtoLibrary(ctx) {
  125. attrs := &ProtoAttrs{
  126. Srcs: srcs,
  127. Strip_import_prefix: fg.properties.Path,
  128. }
  129. tags := []string{
  130. "apex_available=//apex_available:anyapex",
  131. // TODO(b/246997908): we can remove this tag if we could figure out a solution for this bug.
  132. "manual",
  133. }
  134. ctx.CreateBazelTargetModule(
  135. bazel.BazelTargetModuleProperties{Rule_class: "proto_library"},
  136. CommonAttributes{
  137. Name: fg.Name() + convertedProtoLibrarySuffix,
  138. Tags: bazel.MakeStringListAttribute(tags),
  139. },
  140. attrs)
  141. }
  142. // TODO(b/242847534): Still convert to a filegroup because other unconverted
  143. // modules may depend on the filegroup
  144. attrs := &bazelFilegroupAttributes{
  145. Srcs: srcs,
  146. }
  147. props := bazel.BazelTargetModuleProperties{
  148. Rule_class: "filegroup",
  149. Bzl_load_location: "//build/bazel/rules:filegroup.bzl",
  150. }
  151. ctx.CreateBazelTargetModule(props, CommonAttributes{Name: fg.Name()}, attrs)
  152. }
  153. }
  154. type fileGroupProperties struct {
  155. // srcs lists files that will be included in this filegroup
  156. Srcs []string `android:"path"`
  157. Exclude_srcs []string `android:"path"`
  158. // The base path to the files. May be used by other modules to determine which portion
  159. // of the path to use. For example, when a filegroup is used as data in a cc_test rule,
  160. // the base path is stripped off the path and the remaining path is used as the
  161. // installation directory.
  162. Path *string
  163. // Create a make variable with the specified name that contains the list of files in the
  164. // filegroup, relative to the root of the source tree.
  165. Export_to_make_var *string
  166. // aidl is explicitly provided for implicit aidl dependencies
  167. // TODO(b/278298615): aidl prop is a no-op in Soong and is an escape hatch
  168. // to include implicit aidl dependencies for bazel migration compatibility
  169. Aidl struct {
  170. // List of aidl files or filegroup depended on by srcs
  171. Deps []string `android:"path"`
  172. }
  173. }
  174. type fileGroup struct {
  175. ModuleBase
  176. BazelModuleBase
  177. DefaultableModuleBase
  178. FileGroupAsLibrary
  179. properties fileGroupProperties
  180. srcs Paths
  181. }
  182. var _ MixedBuildBuildable = (*fileGroup)(nil)
  183. var _ SourceFileProducer = (*fileGroup)(nil)
  184. var _ FileGroupAsLibrary = (*fileGroup)(nil)
  185. // filegroup contains a list of files that are referenced by other modules
  186. // properties (such as "srcs") using the syntax ":<name>". filegroup are
  187. // also be used to export files across package boundaries.
  188. func FileGroupFactory() Module {
  189. module := &fileGroup{}
  190. module.AddProperties(&module.properties)
  191. InitAndroidModule(module)
  192. InitBazelModule(module)
  193. InitDefaultableModule(module)
  194. return module
  195. }
  196. var _ blueprint.JSONActionSupplier = (*fileGroup)(nil)
  197. func (fg *fileGroup) JSONActions() []blueprint.JSONAction {
  198. ins := make([]string, 0, len(fg.srcs))
  199. outs := make([]string, 0, len(fg.srcs))
  200. for _, p := range fg.srcs {
  201. ins = append(ins, p.String())
  202. outs = append(outs, p.Rel())
  203. }
  204. return []blueprint.JSONAction{
  205. blueprint.JSONAction{
  206. Inputs: ins,
  207. Outputs: outs,
  208. },
  209. }
  210. }
  211. func (fg *fileGroup) GenerateAndroidBuildActions(ctx ModuleContext) {
  212. fg.srcs = PathsForModuleSrcExcludes(ctx, fg.properties.Srcs, fg.properties.Exclude_srcs)
  213. if fg.properties.Path != nil {
  214. fg.srcs = PathsWithModuleSrcSubDir(ctx, fg.srcs, String(fg.properties.Path))
  215. }
  216. }
  217. func (fg *fileGroup) Srcs() Paths {
  218. return append(Paths{}, fg.srcs...)
  219. }
  220. func (fg *fileGroup) MakeVars(ctx MakeVarsModuleContext) {
  221. if makeVar := String(fg.properties.Export_to_make_var); makeVar != "" {
  222. ctx.StrictRaw(makeVar, strings.Join(fg.srcs.Strings(), " "))
  223. }
  224. }
  225. func (fg *fileGroup) QueueBazelCall(ctx BaseModuleContext) {
  226. bazelCtx := ctx.Config().BazelContext
  227. bazelCtx.QueueBazelRequest(
  228. fg.GetBazelLabel(ctx, fg),
  229. cquery.GetOutputFiles,
  230. configKey{arch: Common.String(), osType: CommonOS})
  231. }
  232. func (fg *fileGroup) IsMixedBuildSupported(ctx BaseModuleContext) bool {
  233. // TODO(b/247782695), TODO(b/242847534) Fix mixed builds for filegroups
  234. return false
  235. }
  236. func (fg *fileGroup) ProcessBazelQueryResponse(ctx ModuleContext) {
  237. bazelCtx := ctx.Config().BazelContext
  238. // This is a short-term solution because we rely on info from Android.bp to handle
  239. // a converted module. This will block when we want to remove Android.bp for all
  240. // converted modules at some point.
  241. // TODO(b/242847534): Implement a long-term solution in which we don't need to rely
  242. // on info form Android.bp for modules that are already converted to Bazel
  243. relativeRoot := ctx.ModuleDir()
  244. if fg.properties.Path != nil {
  245. relativeRoot = filepath.Join(relativeRoot, *fg.properties.Path)
  246. }
  247. filePaths, err := bazelCtx.GetOutputFiles(fg.GetBazelLabel(ctx, fg), configKey{arch: Common.String(), osType: CommonOS})
  248. if err != nil {
  249. ctx.ModuleErrorf(err.Error())
  250. return
  251. }
  252. bazelOuts := make(Paths, 0, len(filePaths))
  253. for _, p := range filePaths {
  254. bazelOuts = append(bazelOuts, PathForBazelOutRelative(ctx, relativeRoot, p))
  255. }
  256. fg.srcs = bazelOuts
  257. }
  258. func (fg *fileGroup) ShouldConvertToAidlLibrary(ctx BazelConversionPathContext) bool {
  259. return fg.shouldConvertToLibrary(ctx, ".aidl")
  260. }
  261. func (fg *fileGroup) ShouldConvertToProtoLibrary(ctx BazelConversionPathContext) bool {
  262. return fg.shouldConvertToLibrary(ctx, ".proto")
  263. }
  264. func (fg *fileGroup) shouldConvertToLibrary(ctx BazelConversionPathContext, suffix string) bool {
  265. if len(fg.properties.Srcs) == 0 || !fg.ShouldConvertWithBp2build(ctx) {
  266. return false
  267. }
  268. for _, src := range fg.properties.Srcs {
  269. if !strings.HasSuffix(src, suffix) {
  270. return false
  271. }
  272. }
  273. return true
  274. }
  275. func (fg *fileGroup) GetAidlLibraryLabel(ctx BazelConversionPathContext) string {
  276. return fg.getFileGroupAsLibraryLabel(ctx)
  277. }
  278. func (fg *fileGroup) GetProtoLibraryLabel(ctx BazelConversionPathContext) string {
  279. return fg.getFileGroupAsLibraryLabel(ctx) + convertedProtoLibrarySuffix
  280. }
  281. func (fg *fileGroup) getFileGroupAsLibraryLabel(ctx BazelConversionPathContext) string {
  282. if ctx.OtherModuleDir(fg.module) == ctx.ModuleDir() {
  283. return ":" + fg.Name()
  284. } else {
  285. return fg.GetBazelLabel(ctx, fg)
  286. }
  287. }
  288. // Given a name in srcs prop, check to see if the name references a filegroup
  289. // and the filegroup is converted to aidl_library
  290. func IsConvertedToAidlLibrary(ctx BazelConversionPathContext, name string) bool {
  291. if fg, ok := ToFileGroupAsLibrary(ctx, name); ok {
  292. return fg.ShouldConvertToAidlLibrary(ctx)
  293. }
  294. return false
  295. }
  296. func ToFileGroupAsLibrary(ctx BazelConversionPathContext, name string) (FileGroupAsLibrary, bool) {
  297. if module, ok := ctx.ModuleFromName(name); ok {
  298. if IsFilegroup(ctx, module) {
  299. if fg, ok := module.(FileGroupAsLibrary); ok {
  300. return fg, true
  301. }
  302. }
  303. }
  304. return nil, false
  305. }
  306. // Defaults
  307. type FileGroupDefaults struct {
  308. ModuleBase
  309. DefaultsModuleBase
  310. }
  311. func FileGroupDefaultsFactory() Module {
  312. module := &FileGroupDefaults{}
  313. module.AddProperties(&fileGroupProperties{})
  314. InitDefaultsModule(module)
  315. return module
  316. }