Create a new mode in soong_ui to generate API only BUILD files
The generated Bazel workspace will only contain api specific targets.
This is feasible since these targets do not have any cross dependencies
with the targets in the bp2build workspace
The advantages of a new mode are
1. Does not pollute bp2build workspace with api targets
2. Does not block api targets with the current allowlist conversion
mechansims in bp2build
(In the future we might want to combine these two workspaces)
A Soong module type will generate a Bazel target if it implements
ApiProvider interface
Test: m apigen
Test: m nothing
Change-Id: I69c57ca6539f932e0ad554ce84a87fb7936fdba0
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 0b8cc88..770ad0c 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -24,6 +24,7 @@
"time"
"android/soong/android"
+ "android/soong/bazel"
"android/soong/bp2build"
"android/soong/shared"
"android/soong/ui/metrics/bp2build_metrics_proto"
@@ -48,11 +49,12 @@
delveListen string
delvePath string
- moduleGraphFile string
- moduleActionsFile string
- docFile string
- bazelQueryViewDir string
- bp2buildMarker string
+ moduleGraphFile string
+ moduleActionsFile string
+ docFile string
+ bazelQueryViewDir string
+ bazelApiBp2buildDir string
+ bp2buildMarker string
cmdlineArgs bootstrap.Args
)
@@ -81,6 +83,7 @@
flag.StringVar(&moduleActionsFile, "module_actions_file", "", "JSON file to output inputs/outputs of actions of modules")
flag.StringVar(&docFile, "soong_docs", "", "build documentation file to output")
flag.StringVar(&bazelQueryViewDir, "bazel_queryview_dir", "", "path to the bazel queryview directory relative to --top")
+ flag.StringVar(&bazelApiBp2buildDir, "bazel_api_bp2build_dir", "", "path to the bazel api_bp2build directory relative to --top")
flag.StringVar(&bp2buildMarker, "bp2build_marker", "", "If set, run bp2build, touch the specified marker file then exit")
flag.StringVar(&cmdlineArgs.OutFile, "o", "build.ninja", "the Ninja file to output")
flag.BoolVar(&cmdlineArgs.EmptyNinjaFile, "empty-ninja-file", false, "write out a 0-byte ninja file")
@@ -129,6 +132,8 @@
buildMode = android.Bp2build
} else if bazelQueryViewDir != "" {
buildMode = android.GenerateQueryView
+ } else if bazelApiBp2buildDir != "" {
+ buildMode = android.ApiBp2build
} else if moduleGraphFile != "" {
buildMode = android.GenerateModuleGraph
} else if docFile != "" {
@@ -178,7 +183,7 @@
defer ctx.EventHandler.End("queryview")
codegenContext := bp2build.NewCodegenContext(configuration, *ctx, bp2build.QueryView)
absoluteQueryViewDir := shared.JoinPath(topDir, queryviewDir)
- if err := createBazelQueryView(codegenContext, absoluteQueryViewDir); err != nil {
+ if err := createBazelWorkspace(codegenContext, absoluteQueryViewDir); err != nil {
fmt.Fprintf(os.Stderr, "%s", err)
os.Exit(1)
}
@@ -186,6 +191,96 @@
touch(shared.JoinPath(topDir, queryviewMarker))
}
+// Run the code-generation phase to convert API contributions to BUILD files.
+// Return marker file for the new synthetic workspace
+func runApiBp2build(configuration android.Config, extraNinjaDeps []string) string {
+ // Create a new context and register mutators that are only meaningful to API export
+ ctx := android.NewContext(configuration)
+ ctx.EventHandler.Begin("api_bp2build")
+ defer ctx.EventHandler.End("api_bp2build")
+ ctx.SetNameInterface(newNameResolver(configuration))
+ ctx.RegisterForApiBazelConversion()
+
+ // Register the Android.bp files in the tree
+ // Add them to the workspace's .d file
+ ctx.SetModuleListFile(cmdlineArgs.ModuleListFile)
+ if paths, err := ctx.ListModulePaths("."); err == nil {
+ extraNinjaDeps = append(extraNinjaDeps, paths...)
+ } else {
+ panic(err)
+ }
+
+ // Run the loading and analysis phase
+ ninjaDeps := bootstrap.RunBlueprint(cmdlineArgs,
+ bootstrap.StopBeforePrepareBuildActions,
+ ctx.Context,
+ configuration)
+ ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
+
+ // Add the globbed dependencies
+ globs := writeBuildGlobsNinjaFile(ctx, configuration.SoongOutDir(), configuration)
+ ninjaDeps = append(ninjaDeps, globs...)
+
+ // Run codegen to generate BUILD files
+ codegenContext := bp2build.NewCodegenContext(configuration, *ctx, bp2build.ApiBp2build)
+ absoluteApiBp2buildDir := shared.JoinPath(topDir, bazelApiBp2buildDir)
+ if err := createBazelWorkspace(codegenContext, absoluteApiBp2buildDir); err != nil {
+ fmt.Fprintf(os.Stderr, "%s", err)
+ os.Exit(1)
+ }
+ ninjaDeps = append(ninjaDeps, codegenContext.AdditionalNinjaDeps()...)
+
+ // Create soong_injection repository
+ soongInjectionFiles := bp2build.CreateSoongInjectionFiles(configuration, bp2build.CodegenMetrics{})
+ absoluteSoongInjectionDir := shared.JoinPath(topDir, configuration.SoongOutDir(), bazel.SoongInjectionDirName)
+ for _, file := range soongInjectionFiles {
+ writeReadOnlyFile(absoluteSoongInjectionDir, file)
+ }
+
+ workspace := shared.JoinPath(configuration.SoongOutDir(), "api_bp2build")
+
+ excludes := bazelArtifacts()
+ // Exclude all src BUILD files
+ excludes = append(excludes, apiBuildFileExcludes()...)
+
+ // Create the symlink forest
+ symlinkDeps := bp2build.PlantSymlinkForest(
+ configuration,
+ topDir,
+ workspace,
+ bazelApiBp2buildDir,
+ ".",
+ excludes)
+ ninjaDeps = append(ninjaDeps, symlinkDeps...)
+
+ workspaceMarkerFile := workspace + ".marker"
+ writeDepFile(workspaceMarkerFile, *ctx.EventHandler, ninjaDeps)
+ touch(shared.JoinPath(topDir, workspaceMarkerFile))
+ return workspaceMarkerFile
+}
+
+// With some exceptions, api_bp2build does not have any dependencies on the checked-in BUILD files
+// Exclude them from the generated workspace to prevent unrelated errors during the loading phase
+func apiBuildFileExcludes() []string {
+ ret := make([]string, 0)
+
+ srcs, err := getExistingBazelRelatedFiles(topDir)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Error determining existing Bazel-related files: %s\n", err)
+ os.Exit(1)
+ }
+ for _, src := range srcs {
+ if src != "WORKSPACE" &&
+ src != "BUILD" &&
+ src != "BUILD.bazel" &&
+ !strings.HasPrefix(src, "build/bazel") &&
+ !strings.HasPrefix(src, "prebuilts/clang") {
+ ret = append(ret, src)
+ }
+ }
+ return ret
+}
+
func writeMetrics(configuration android.Config, eventHandler metrics.EventHandler, metricsDir string) {
if len(metricsDir) < 1 {
fmt.Fprintf(os.Stderr, "\nMissing required env var for generating soong metrics: LOG_DIR\n")
@@ -248,6 +343,8 @@
return bp2buildMarker
} else if configuration.IsMixedBuildsEnabled() {
runMixedModeBuild(configuration, ctx, extraNinjaDeps)
+ } else if configuration.BuildMode == android.ApiBp2build {
+ return runApiBp2build(configuration, extraNinjaDeps)
} else {
var stopBefore bootstrap.StopBefore
if configuration.BuildMode == android.GenerateModuleGraph {
@@ -476,6 +573,16 @@
return files, nil
}
+func bazelArtifacts() []string {
+ return []string{
+ "bazel-bin",
+ "bazel-genfiles",
+ "bazel-out",
+ "bazel-testlogs",
+ "bazel-" + filepath.Base(topDir),
+ }
+}
+
// Run Soong in the bp2build mode. This creates a standalone context that registers
// an alternate pipeline of mutators and singletons specifically for generating
// Bazel BUILD files instead of Ninja files.
@@ -524,13 +631,7 @@
generatedRoot := shared.JoinPath(configuration.SoongOutDir(), "bp2build")
workspaceRoot := shared.JoinPath(configuration.SoongOutDir(), "workspace")
- excludes := []string{
- "bazel-bin",
- "bazel-genfiles",
- "bazel-out",
- "bazel-testlogs",
- "bazel-" + filepath.Base(topDir),
- }
+ excludes := bazelArtifacts()
if outDir[0] != '/' {
excludes = append(excludes, outDir)