bp2build.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. // Copyright 2020 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 bp2build
  15. import (
  16. "android/soong/starlark_import"
  17. "fmt"
  18. "os"
  19. "path/filepath"
  20. "strings"
  21. "android/soong/android"
  22. "android/soong/bazel"
  23. "android/soong/shared"
  24. )
  25. func deleteFilesExcept(ctx *CodegenContext, rootOutputPath android.OutputPath, except []BazelFile) {
  26. // Delete files that should no longer be present.
  27. bp2buildDirAbs := shared.JoinPath(ctx.topDir, rootOutputPath.String())
  28. filesToDelete := make(map[string]struct{})
  29. err := filepath.Walk(bp2buildDirAbs,
  30. func(path string, info os.FileInfo, err error) error {
  31. if err != nil {
  32. return err
  33. }
  34. if !info.IsDir() {
  35. relPath, err := filepath.Rel(bp2buildDirAbs, path)
  36. if err != nil {
  37. return err
  38. }
  39. filesToDelete[relPath] = struct{}{}
  40. }
  41. return nil
  42. })
  43. if err != nil {
  44. fmt.Printf("ERROR reading %s: %s", bp2buildDirAbs, err)
  45. os.Exit(1)
  46. }
  47. for _, bazelFile := range except {
  48. filePath := filepath.Join(bazelFile.Dir, bazelFile.Basename)
  49. delete(filesToDelete, filePath)
  50. }
  51. for f, _ := range filesToDelete {
  52. absPath := shared.JoinPath(bp2buildDirAbs, f)
  53. if err := os.RemoveAll(absPath); err != nil {
  54. fmt.Printf("ERROR deleting %s: %s", absPath, err)
  55. os.Exit(1)
  56. }
  57. }
  58. }
  59. // Codegen is the backend of bp2build. The code generator is responsible for
  60. // writing .bzl files that are equivalent to Android.bp files that are capable
  61. // of being built with Bazel.
  62. func Codegen(ctx *CodegenContext) *CodegenMetrics {
  63. // This directory stores BUILD files that could be eventually checked-in.
  64. bp2buildDir := android.PathForOutput(ctx, "bp2build")
  65. res, errs := GenerateBazelTargets(ctx, true)
  66. if len(errs) > 0 {
  67. errMsgs := make([]string, len(errs))
  68. for i, err := range errs {
  69. errMsgs[i] = fmt.Sprintf("%q", err)
  70. }
  71. fmt.Printf("ERROR: Encountered %d error(s): \nERROR: %s", len(errs), strings.Join(errMsgs, "\n"))
  72. os.Exit(1)
  73. }
  74. bp2buildFiles := CreateBazelFiles(ctx.Config(), nil, res.buildFileToTargets, ctx.mode)
  75. writeFiles(ctx, bp2buildDir, bp2buildFiles)
  76. // Delete files under the bp2build root which weren't just written. An
  77. // alternative would have been to delete the whole directory and write these
  78. // files. However, this would regenerate files which were otherwise unchanged
  79. // since the last bp2build run, which would have negative incremental
  80. // performance implications.
  81. deleteFilesExcept(ctx, bp2buildDir, bp2buildFiles)
  82. injectionFiles, err := CreateSoongInjectionDirFiles(ctx, res.metrics)
  83. if err != nil {
  84. fmt.Printf("%s\n", err.Error())
  85. os.Exit(1)
  86. }
  87. writeFiles(ctx, android.PathForOutput(ctx, bazel.SoongInjectionDirName), injectionFiles)
  88. starlarkDeps, err := starlark_import.GetNinjaDeps()
  89. if err != nil {
  90. fmt.Fprintf(os.Stderr, "%s\n", err)
  91. os.Exit(1)
  92. }
  93. ctx.AddNinjaFileDeps(starlarkDeps...)
  94. return &res.metrics
  95. }
  96. // Wrapper function that will be responsible for all files in soong_injection directory
  97. // This includes
  98. // 1. config value(s) that are hardcoded in Soong
  99. // 2. product_config variables
  100. func CreateSoongInjectionDirFiles(ctx *CodegenContext, metrics CodegenMetrics) ([]BazelFile, error) {
  101. var ret []BazelFile
  102. productConfigFiles, err := CreateProductConfigFiles(ctx)
  103. if err != nil {
  104. return nil, err
  105. }
  106. ret = append(ret, productConfigFiles...)
  107. injectionFiles, err := soongInjectionFiles(ctx.Config(), metrics)
  108. if err != nil {
  109. return nil, err
  110. }
  111. ret = append(ret, injectionFiles...)
  112. return ret, nil
  113. }
  114. // Get the output directory and create it if it doesn't exist.
  115. func getOrCreateOutputDir(outputDir android.OutputPath, ctx android.PathContext, dir string) android.OutputPath {
  116. dirPath := outputDir.Join(ctx, dir)
  117. if err := android.CreateOutputDirIfNonexistent(dirPath, os.ModePerm); err != nil {
  118. fmt.Printf("ERROR: path %s: %s", dirPath, err.Error())
  119. }
  120. return dirPath
  121. }
  122. // writeFiles materializes a list of BazelFile rooted at outputDir.
  123. func writeFiles(ctx android.PathContext, outputDir android.OutputPath, files []BazelFile) {
  124. for _, f := range files {
  125. p := getOrCreateOutputDir(outputDir, ctx, f.Dir).Join(ctx, f.Basename)
  126. if err := writeFile(p, f.Contents); err != nil {
  127. panic(fmt.Errorf("Failed to write %q (dir %q) due to %q", f.Basename, f.Dir, err))
  128. }
  129. }
  130. }
  131. func writeFile(pathToFile android.OutputPath, content string) error {
  132. // These files are made editable to allow users to modify and iterate on them
  133. // in the source tree.
  134. return android.WriteFileToOutputDir(pathToFile, []byte(content), 0644)
  135. }