filegroup.go 12 KB

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