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
This commit is contained in:
Spandan Das
2022-09-28 20:43:08 +00:00
parent d0c8d54f29
commit 5af0bd3e48
14 changed files with 288 additions and 25 deletions

View File

@@ -24,6 +24,7 @@ import (
"time"
"android/soong/android"
"android/soong/bazel"
"android/soong/bp2build"
"android/soong/shared"
"android/soong/ui/metrics/bp2build_metrics_proto"
@@ -48,11 +49,12 @@ var (
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 @@ func init() {
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 @@ func newConfig(availableEnv map[string]string) android.Config {
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 @@ func runQueryView(queryviewDir, queryviewMarker string, configuration android.Co
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 @@ func runQueryView(queryviewDir, queryviewMarker string, configuration android.Co
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 @@ func doChosenActivity(ctx *android.Context, configuration android.Config, extraN
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 @@ func getExistingBazelRelatedFiles(topDir string) ([]string, error) {
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 @@ func runBp2Build(configuration android.Config, extraNinjaDeps []string) {
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)