filegroup.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  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. }
  67. type bazelAidlLibraryAttributes struct {
  68. Srcs bazel.LabelListAttribute
  69. Strip_import_prefix *string
  70. }
  71. // api srcs can be contained in filegroups.
  72. // this should be generated in api_bp2build workspace as well.
  73. func (fg *fileGroup) ConvertWithApiBp2build(ctx TopDownMutatorContext) {
  74. fg.ConvertWithBp2build(ctx)
  75. }
  76. // ConvertWithBp2build performs bp2build conversion of filegroup
  77. func (fg *fileGroup) ConvertWithBp2build(ctx TopDownMutatorContext) {
  78. srcs := bazel.MakeLabelListAttribute(
  79. BazelLabelForModuleSrcExcludes(ctx, fg.properties.Srcs, fg.properties.Exclude_srcs))
  80. // For Bazel compatibility, don't generate the filegroup if there is only 1
  81. // source file, and that the source file is named the same as the module
  82. // itself. In Bazel, eponymous filegroups like this would be an error.
  83. //
  84. // Instead, dependents on this single-file filegroup can just depend
  85. // on the file target, instead of rule target, directly.
  86. //
  87. // You may ask: what if a filegroup has multiple files, and one of them
  88. // shares the name? The answer: we haven't seen that in the wild, and
  89. // should lock Soong itself down to prevent the behavior. For now,
  90. // we raise an error if bp2build sees this problem.
  91. for _, f := range srcs.Value.Includes {
  92. if f.Label == fg.Name() {
  93. if len(srcs.Value.Includes) > 1 {
  94. ctx.ModuleErrorf("filegroup '%s' cannot contain a file with the same name", fg.Name())
  95. }
  96. return
  97. }
  98. }
  99. // Convert module that has only AIDL files to aidl_library
  100. // If the module has a mixed bag of AIDL and non-AIDL files, split the filegroup manually
  101. // and then convert
  102. if fg.ShouldConvertToAidlLibrary(ctx) {
  103. attrs := &bazelAidlLibraryAttributes{
  104. Srcs: srcs,
  105. Strip_import_prefix: fg.properties.Path,
  106. }
  107. props := bazel.BazelTargetModuleProperties{
  108. Rule_class: "aidl_library",
  109. Bzl_load_location: "//build/bazel/rules/aidl:library.bzl",
  110. }
  111. ctx.CreateBazelTargetModule(props, CommonAttributes{Name: fg.Name()}, attrs)
  112. } else {
  113. if fg.ShouldConvertToProtoLibrary(ctx) {
  114. // TODO(b/246997908): we can remove this tag if we could figure out a
  115. // solution for this bug.
  116. attrs := &ProtoAttrs{
  117. Srcs: srcs,
  118. Strip_import_prefix: fg.properties.Path,
  119. }
  120. tags := []string{"manual"}
  121. ctx.CreateBazelTargetModule(
  122. bazel.BazelTargetModuleProperties{Rule_class: "proto_library"},
  123. CommonAttributes{
  124. Name: fg.Name() + convertedProtoLibrarySuffix,
  125. Tags: bazel.MakeStringListAttribute(tags),
  126. },
  127. attrs)
  128. }
  129. // TODO(b/242847534): Still convert to a filegroup because other unconverted
  130. // modules may depend on the filegroup
  131. attrs := &bazelFilegroupAttributes{
  132. Srcs: srcs,
  133. }
  134. props := bazel.BazelTargetModuleProperties{
  135. Rule_class: "filegroup",
  136. Bzl_load_location: "//build/bazel/rules:filegroup.bzl",
  137. }
  138. ctx.CreateBazelTargetModule(props, CommonAttributes{Name: fg.Name()}, attrs)
  139. }
  140. }
  141. type fileGroupProperties struct {
  142. // srcs lists files that will be included in this filegroup
  143. Srcs []string `android:"path"`
  144. Exclude_srcs []string `android:"path"`
  145. // The base path to the files. May be used by other modules to determine which portion
  146. // of the path to use. For example, when a filegroup is used as data in a cc_test rule,
  147. // the base path is stripped off the path and the remaining path is used as the
  148. // installation directory.
  149. Path *string
  150. // Create a make variable with the specified name that contains the list of files in the
  151. // filegroup, relative to the root of the source tree.
  152. Export_to_make_var *string
  153. }
  154. type fileGroup struct {
  155. ModuleBase
  156. BazelModuleBase
  157. DefaultableModuleBase
  158. FileGroupAsLibrary
  159. properties fileGroupProperties
  160. srcs Paths
  161. }
  162. var _ MixedBuildBuildable = (*fileGroup)(nil)
  163. var _ SourceFileProducer = (*fileGroup)(nil)
  164. var _ FileGroupAsLibrary = (*fileGroup)(nil)
  165. // filegroup contains a list of files that are referenced by other modules
  166. // properties (such as "srcs") using the syntax ":<name>". filegroup are
  167. // also be used to export files across package boundaries.
  168. func FileGroupFactory() Module {
  169. module := &fileGroup{}
  170. module.AddProperties(&module.properties)
  171. InitAndroidModule(module)
  172. InitBazelModule(module)
  173. InitDefaultableModule(module)
  174. return module
  175. }
  176. var _ blueprint.JSONActionSupplier = (*fileGroup)(nil)
  177. func (fg *fileGroup) JSONActions() []blueprint.JSONAction {
  178. ins := make([]string, 0, len(fg.srcs))
  179. outs := make([]string, 0, len(fg.srcs))
  180. for _, p := range fg.srcs {
  181. ins = append(ins, p.String())
  182. outs = append(outs, p.Rel())
  183. }
  184. return []blueprint.JSONAction{
  185. blueprint.JSONAction{
  186. Inputs: ins,
  187. Outputs: outs,
  188. },
  189. }
  190. }
  191. func (fg *fileGroup) GenerateAndroidBuildActions(ctx ModuleContext) {
  192. fg.srcs = PathsForModuleSrcExcludes(ctx, fg.properties.Srcs, fg.properties.Exclude_srcs)
  193. if fg.properties.Path != nil {
  194. fg.srcs = PathsWithModuleSrcSubDir(ctx, fg.srcs, String(fg.properties.Path))
  195. }
  196. }
  197. func (fg *fileGroup) Srcs() Paths {
  198. return append(Paths{}, fg.srcs...)
  199. }
  200. func (fg *fileGroup) MakeVars(ctx MakeVarsModuleContext) {
  201. if makeVar := String(fg.properties.Export_to_make_var); makeVar != "" {
  202. ctx.StrictRaw(makeVar, strings.Join(fg.srcs.Strings(), " "))
  203. }
  204. }
  205. func (fg *fileGroup) QueueBazelCall(ctx BaseModuleContext) {
  206. bazelCtx := ctx.Config().BazelContext
  207. bazelCtx.QueueBazelRequest(
  208. fg.GetBazelLabel(ctx, fg),
  209. cquery.GetOutputFiles,
  210. configKey{arch: Common.String(), osType: CommonOS})
  211. }
  212. func (fg *fileGroup) IsMixedBuildSupported(ctx BaseModuleContext) bool {
  213. // TODO(b/247782695), TODO(b/242847534) Fix mixed builds for filegroups
  214. return false
  215. }
  216. func (fg *fileGroup) ProcessBazelQueryResponse(ctx ModuleContext) {
  217. bazelCtx := ctx.Config().BazelContext
  218. // This is a short-term solution because we rely on info from Android.bp to handle
  219. // a converted module. This will block when we want to remove Android.bp for all
  220. // converted modules at some point.
  221. // TODO(b/242847534): Implement a long-term solution in which we don't need to rely
  222. // on info form Android.bp for modules that are already converted to Bazel
  223. relativeRoot := ctx.ModuleDir()
  224. if fg.properties.Path != nil {
  225. relativeRoot = filepath.Join(relativeRoot, *fg.properties.Path)
  226. }
  227. filePaths, err := bazelCtx.GetOutputFiles(fg.GetBazelLabel(ctx, fg), configKey{arch: Common.String(), osType: CommonOS})
  228. if err != nil {
  229. ctx.ModuleErrorf(err.Error())
  230. return
  231. }
  232. bazelOuts := make(Paths, 0, len(filePaths))
  233. for _, p := range filePaths {
  234. bazelOuts = append(bazelOuts, PathForBazelOutRelative(ctx, relativeRoot, p))
  235. }
  236. fg.srcs = bazelOuts
  237. }
  238. func (fg *fileGroup) ShouldConvertToAidlLibrary(ctx BazelConversionPathContext) bool {
  239. return fg.shouldConvertToLibrary(ctx, ".aidl")
  240. }
  241. func (fg *fileGroup) ShouldConvertToProtoLibrary(ctx BazelConversionPathContext) bool {
  242. return fg.shouldConvertToLibrary(ctx, ".proto")
  243. }
  244. func (fg *fileGroup) shouldConvertToLibrary(ctx BazelConversionPathContext, suffix string) bool {
  245. if len(fg.properties.Srcs) == 0 || !fg.ShouldConvertWithBp2build(ctx) {
  246. return false
  247. }
  248. for _, src := range fg.properties.Srcs {
  249. if !strings.HasSuffix(src, suffix) {
  250. return false
  251. }
  252. }
  253. return true
  254. }
  255. func (fg *fileGroup) GetAidlLibraryLabel(ctx BazelConversionPathContext) string {
  256. return fg.getFileGroupAsLibraryLabel(ctx)
  257. }
  258. func (fg *fileGroup) GetProtoLibraryLabel(ctx BazelConversionPathContext) string {
  259. return fg.getFileGroupAsLibraryLabel(ctx) + convertedProtoLibrarySuffix
  260. }
  261. func (fg *fileGroup) getFileGroupAsLibraryLabel(ctx BazelConversionPathContext) string {
  262. if ctx.OtherModuleDir(fg.module) == ctx.ModuleDir() {
  263. return ":" + fg.Name()
  264. } else {
  265. return fg.GetBazelLabel(ctx, fg)
  266. }
  267. }
  268. // Given a name in srcs prop, check to see if the name references a filegroup
  269. // and the filegroup is converted to aidl_library
  270. func IsConvertedToAidlLibrary(ctx BazelConversionPathContext, name string) bool {
  271. if fg, ok := ToFileGroupAsLibrary(ctx, name); ok {
  272. return fg.ShouldConvertToAidlLibrary(ctx)
  273. }
  274. return false
  275. }
  276. func ToFileGroupAsLibrary(ctx BazelConversionPathContext, name string) (FileGroupAsLibrary, bool) {
  277. if module, ok := ctx.ModuleFromName(name); ok {
  278. if IsFilegroup(ctx, module) {
  279. if fg, ok := module.(FileGroupAsLibrary); ok {
  280. return fg, true
  281. }
  282. }
  283. }
  284. return nil, false
  285. }
  286. // Defaults
  287. type FileGroupDefaults struct {
  288. ModuleBase
  289. DefaultsModuleBase
  290. }
  291. func FileGroupDefaultsFactory() Module {
  292. module := &FileGroupDefaults{}
  293. module.AddProperties(&fileGroupProperties{})
  294. InitDefaultsModule(module)
  295. return module
  296. }