Merge "Create a new mode in soong_ui to generate API only BUILD files"

This commit is contained in:
Spandan Das 2022-10-05 01:37:28 +00:00 committed by Gerrit Code Review
commit ee08eb3c81
14 changed files with 288 additions and 25 deletions

View file

@ -134,6 +134,11 @@ type Bazelable interface {
SetBaseModuleType(baseModuleType string) SetBaseModuleType(baseModuleType string)
} }
// ApiProvider is implemented by modules that contribute to an API surface
type ApiProvider interface {
ConvertWithApiBp2build(ctx TopDownMutatorContext)
}
// MixedBuildBuildable is an interface that module types should implement in order // MixedBuildBuildable is an interface that module types should implement in order
// to be "handled by Bazel" in a mixed build. // to be "handled by Bazel" in a mixed build.
type MixedBuildBuildable interface { type MixedBuildBuildable interface {
@ -415,6 +420,13 @@ func (b *BazelModuleBase) shouldConvertWithBp2build(ctx bazelOtherModuleContext,
return false return false
} }
// In api_bp2build mode, all soong modules that can provide API contributions should be converted
// This is irrespective of its presence/absence in bp2build allowlists
if ctx.Config().BuildMode == ApiBp2build {
_, providesApis := module.(ApiProvider)
return providesApis
}
propValue := b.bazelProperties.Bazel_module.Bp2build_available propValue := b.bazelProperties.Bazel_module.Bp2build_available
packagePath := ctx.OtherModuleDir(module) packagePath := ctx.OtherModuleDir(module)
@ -510,6 +522,17 @@ func convertWithBp2build(ctx TopDownMutatorContext) {
bModule.ConvertWithBp2build(ctx) bModule.ConvertWithBp2build(ctx)
} }
func registerApiBp2buildConversionMutator(ctx RegisterMutatorsContext) {
ctx.TopDown("apiBp2build_conversion", convertWithApiBp2build).Parallel()
}
// Generate API contribution targets if the Soong module provides APIs
func convertWithApiBp2build(ctx TopDownMutatorContext) {
if m, ok := ctx.Module().(ApiProvider); ok {
m.ConvertWithApiBp2build(ctx)
}
}
// GetMainClassInManifest scans the manifest file specified in filepath and returns // GetMainClassInManifest scans the manifest file specified in filepath and returns
// the value of attribute Main-Class in the manifest file if it exists, or returns error. // the value of attribute Main-Class in the manifest file if it exists, or returns error.
// WARNING: this is for bp2build converters of java_* modules only. // WARNING: this is for bp2build converters of java_* modules only.

View file

@ -83,6 +83,9 @@ const (
// express build semantics. // express build semantics.
GenerateQueryView GenerateQueryView
// Generate BUILD files for API contributions to API surfaces
ApiBp2build
// Create a JSON representation of the module graph and exit. // Create a JSON representation of the module graph and exit.
GenerateModuleGraph GenerateModuleGraph

View file

@ -31,22 +31,33 @@ import (
// RegisterMutatorsForBazelConversion is a alternate registration pipeline for bp2build. Exported for testing. // RegisterMutatorsForBazelConversion is a alternate registration pipeline for bp2build. Exported for testing.
func RegisterMutatorsForBazelConversion(ctx *Context, preArchMutators []RegisterMutatorFunc) { func RegisterMutatorsForBazelConversion(ctx *Context, preArchMutators []RegisterMutatorFunc) {
bp2buildMutators := append(preArchMutators, registerBp2buildConversionMutator)
registerMutatorsForBazelConversion(ctx, bp2buildMutators)
}
// RegisterMutatorsForApiBazelConversion is an alternate registration pipeline for api_bp2build
// This pipeline restricts generation of Bazel targets to Soong modules that contribute APIs
func RegisterMutatorsForApiBazelConversion(ctx *Context, preArchMutators []RegisterMutatorFunc) {
bp2buildMutators := append(preArchMutators, registerApiBp2buildConversionMutator)
registerMutatorsForBazelConversion(ctx, bp2buildMutators)
}
func registerMutatorsForBazelConversion(ctx *Context, bp2buildMutators []RegisterMutatorFunc) {
mctx := &registerMutatorsContext{ mctx := &registerMutatorsContext{
bazelConversionMode: true, bazelConversionMode: true,
} }
bp2buildMutators := append([]RegisterMutatorFunc{ allMutators := append([]RegisterMutatorFunc{
RegisterNamespaceMutator, RegisterNamespaceMutator,
RegisterDefaultsPreArchMutators, RegisterDefaultsPreArchMutators,
// TODO(b/165114590): this is required to resolve deps that are only prebuilts, but we should // TODO(b/165114590): this is required to resolve deps that are only prebuilts, but we should
// evaluate the impact on conversion. // evaluate the impact on conversion.
RegisterPrebuiltsPreArchMutators, RegisterPrebuiltsPreArchMutators,
}, },
preArchMutators...) bp2buildMutators...)
bp2buildMutators = append(bp2buildMutators, registerBp2buildConversionMutator)
// Register bp2build mutators // Register bp2build mutators
for _, f := range bp2buildMutators { for _, f := range allMutators {
f(mctx) f(mctx)
} }

View file

@ -180,6 +180,16 @@ func (ctx *Context) RegisterForBazelConversion() {
RegisterMutatorsForBazelConversion(ctx, bp2buildPreArchMutators) RegisterMutatorsForBazelConversion(ctx, bp2buildPreArchMutators)
} }
// RegisterForApiBazelConversion is similar to RegisterForBazelConversion except that
// it only generates API targets in the generated workspace
func (ctx *Context) RegisterForApiBazelConversion() {
for _, t := range moduleTypes {
t.register(ctx)
}
RegisterMutatorsForApiBazelConversion(ctx, bp2buildPreArchMutators)
}
// Register the pipeline of singletons, module types, and mutators for // Register the pipeline of singletons, module types, and mutators for
// generating build.ninja and other files for Kati, from Android.bp files. // generating build.ninja and other files for Kati, from Android.bp files.
func (ctx *Context) Register() { func (ctx *Context) Register() {

View file

@ -461,6 +461,12 @@ func (ctx *TestContext) RegisterForBazelConversion() {
RegisterMutatorsForBazelConversion(ctx.Context, ctx.bp2buildPreArch) RegisterMutatorsForBazelConversion(ctx.Context, ctx.bp2buildPreArch)
} }
// RegisterForApiBazelConversion prepares a test context for API bp2build conversion.
func (ctx *TestContext) RegisterForApiBazelConversion() {
ctx.config.BuildMode = ApiBp2build
RegisterMutatorsForApiBazelConversion(ctx.Context, ctx.bp2buildPreArch)
}
func (ctx *TestContext) ParseFileList(rootDir string, filePaths []string) (deps []string, errs []error) { func (ctx *TestContext) ParseFileList(rootDir string, filePaths []string) (deps []string, errs []error) {
// This function adapts the old style ParseFileList calls that are spread throughout the tests // This function adapts the old style ParseFileList calls that are spread throughout the tests
// to the new style that takes a config. // to the new style that takes a config.

View file

@ -163,6 +163,9 @@ const (
// This mode is used for discovering and introspecting the existing Soong // This mode is used for discovering and introspecting the existing Soong
// module graph. // module graph.
QueryView QueryView
// ApiBp2build - generate BUILD files for API contribution targets
ApiBp2build
) )
type unconvertedDepsMode int type unconvertedDepsMode int
@ -181,6 +184,8 @@ func (mode CodegenMode) String() string {
return "Bp2Build" return "Bp2Build"
case QueryView: case QueryView:
return "QueryView" return "QueryView"
case ApiBp2build:
return "ApiBp2build"
default: default:
return fmt.Sprintf("%d", mode) return fmt.Sprintf("%d", mode)
} }
@ -327,6 +332,10 @@ func GenerateBazelTargets(ctx *CodegenContext, generateFilegroups bool) (convers
errs = append(errs, err) errs = append(errs, err)
} }
targets = append(targets, t) targets = append(targets, t)
case ApiBp2build:
if aModule, ok := m.(android.Module); ok && aModule.IsConvertedByBp2build() {
targets, errs = generateBazelTargets(bpCtx, aModule)
}
default: default:
errs = append(errs, fmt.Errorf("Unknown code-generation mode: %s", ctx.Mode())) errs = append(errs, fmt.Errorf("Unknown code-generation mode: %s", ctx.Mode()))
return return

View file

@ -1853,3 +1853,27 @@ filegroup {
}, },
}) })
} }
func TestGenerateApiBazelTargets(t *testing.T) {
bp := `
custom {
name: "foo",
api: "foo.txt",
}
`
expectedBazelTarget := MakeBazelTarget(
"custom_api_contribution",
"foo",
AttrNameToString{
"api": `"foo.txt"`,
},
)
registerCustomModule := func(ctx android.RegistrationContext) {
ctx.RegisterModuleType("custom", customModuleFactoryHostAndDevice)
}
RunApiBp2BuildTestCase(t, registerCustomModule, Bp2buildTestCase{
Blueprint: bp,
ExpectedBazelTargets: []string{expectedBazelTarget},
Description: "Generating API contribution Bazel targets for custom module",
})
}

View file

@ -85,6 +85,7 @@ custom = rule(
"soong_module_name": attr.string(mandatory = True), "soong_module_name": attr.string(mandatory = True),
"soong_module_variant": attr.string(), "soong_module_variant": attr.string(),
"soong_module_deps": attr.label_list(providers = [SoongModuleInfo]), "soong_module_deps": attr.label_list(providers = [SoongModuleInfo]),
"api": attr.string(),
"arch_paths": attr.string_list(), "arch_paths": attr.string_list(),
"arch_paths_exclude": attr.string_list(), "arch_paths_exclude": attr.string_list(),
# bazel_module start # bazel_module start
@ -119,6 +120,7 @@ custom_defaults = rule(
"soong_module_name": attr.string(mandatory = True), "soong_module_name": attr.string(mandatory = True),
"soong_module_variant": attr.string(), "soong_module_variant": attr.string(),
"soong_module_deps": attr.label_list(providers = [SoongModuleInfo]), "soong_module_deps": attr.label_list(providers = [SoongModuleInfo]),
"api": attr.string(),
"arch_paths": attr.string_list(), "arch_paths": attr.string_list(),
"arch_paths_exclude": attr.string_list(), "arch_paths_exclude": attr.string_list(),
"bool_prop": attr.bool(), "bool_prop": attr.bool(),
@ -149,6 +151,7 @@ custom_test_ = rule(
"soong_module_name": attr.string(mandatory = True), "soong_module_name": attr.string(mandatory = True),
"soong_module_variant": attr.string(), "soong_module_variant": attr.string(),
"soong_module_deps": attr.label_list(providers = [SoongModuleInfo]), "soong_module_deps": attr.label_list(providers = [SoongModuleInfo]),
"api": attr.string(),
"arch_paths": attr.string_list(), "arch_paths": attr.string_list(),
"arch_paths_exclude": attr.string_list(), "arch_paths_exclude": attr.string_list(),
"bool_prop": attr.bool(), "bool_prop": attr.bool(),

View file

@ -97,7 +97,7 @@ func createBuildFiles(buildToTargets map[string]BazelTargets, mode CodegenMode)
targets.sort() targets.sort()
var content string var content string
if mode == Bp2Build { if mode == Bp2Build || mode == ApiBp2build {
content = `# READ THIS FIRST: content = `# READ THIS FIRST:
# This file was automatically generated by bp2build for the Bazel migration project. # This file was automatically generated by bp2build for the Bazel migration project.
# Feel free to edit or test it, but do *not* check it into your version control system. # Feel free to edit or test it, but do *not* check it into your version control system.

View file

@ -24,6 +24,8 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/google/blueprint/proptools"
"android/soong/android" "android/soong/android"
"android/soong/android/allowlists" "android/soong/android/allowlists"
"android/soong/bazel" "android/soong/bazel"
@ -88,6 +90,22 @@ type Bp2buildTestCase struct {
} }
func RunBp2BuildTestCase(t *testing.T, registerModuleTypes func(ctx android.RegistrationContext), tc Bp2buildTestCase) { func RunBp2BuildTestCase(t *testing.T, registerModuleTypes func(ctx android.RegistrationContext), tc Bp2buildTestCase) {
bp2buildSetup := func(ctx *android.TestContext) {
registerModuleTypes(ctx)
ctx.RegisterForBazelConversion()
}
runBp2BuildTestCaseWithSetup(t, bp2buildSetup, tc)
}
func RunApiBp2BuildTestCase(t *testing.T, registerModuleTypes func(ctx android.RegistrationContext), tc Bp2buildTestCase) {
apiBp2BuildSetup := func(ctx *android.TestContext) {
registerModuleTypes(ctx)
ctx.RegisterForApiBazelConversion()
}
runBp2BuildTestCaseWithSetup(t, apiBp2BuildSetup, tc)
}
func runBp2BuildTestCaseWithSetup(t *testing.T, setup func(ctx *android.TestContext), tc Bp2buildTestCase) {
t.Helper() t.Helper()
dir := "." dir := "."
filesystem := make(map[string][]byte) filesystem := make(map[string][]byte)
@ -103,7 +121,7 @@ func RunBp2BuildTestCase(t *testing.T, registerModuleTypes func(ctx android.Regi
config := android.TestConfig(buildDir, nil, tc.Blueprint, filesystem) config := android.TestConfig(buildDir, nil, tc.Blueprint, filesystem)
ctx := android.NewTestContext(config) ctx := android.NewTestContext(config)
registerModuleTypes(ctx) setup(ctx)
ctx.RegisterModuleType(tc.ModuleTypeUnderTest, tc.ModuleTypeUnderTestFactory) ctx.RegisterModuleType(tc.ModuleTypeUnderTest, tc.ModuleTypeUnderTestFactory)
// A default configuration for tests to not have to specify bp2build_available on top level targets. // A default configuration for tests to not have to specify bp2build_available on top level targets.
@ -118,7 +136,6 @@ func RunBp2BuildTestCase(t *testing.T, registerModuleTypes func(ctx android.Regi
}) })
} }
ctx.RegisterBp2BuildConfig(bp2buildConfig) ctx.RegisterBp2BuildConfig(bp2buildConfig)
ctx.RegisterForBazelConversion()
_, parseErrs := ctx.ParseFileList(dir, toParse) _, parseErrs := ctx.ParseFileList(dir, toParse)
if errored(t, tc, parseErrs) { if errored(t, tc, parseErrs) {
@ -198,6 +215,8 @@ type customProps struct {
// Prop used to indicate this conversion should be 1 module -> multiple targets // Prop used to indicate this conversion should be 1 module -> multiple targets
One_to_many_prop *bool One_to_many_prop *bool
Api *string // File describing the APIs of this module
} }
type customModule struct { type customModule struct {
@ -320,6 +339,7 @@ type customBazelModuleAttributes struct {
String_ptr_prop *string String_ptr_prop *string
String_list_prop []string String_list_prop []string
Arch_paths bazel.LabelListAttribute Arch_paths bazel.LabelListAttribute
Api bazel.LabelAttribute
} }
func (m *customModule) ConvertWithBp2build(ctx android.TopDownMutatorContext) { func (m *customModule) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
@ -364,6 +384,23 @@ func (m *customModule) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: m.Name()}, attrs) ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: m.Name()}, attrs)
} }
var _ android.ApiProvider = (*customModule)(nil)
func (c *customModule) ConvertWithApiBp2build(ctx android.TopDownMutatorContext) {
props := bazel.BazelTargetModuleProperties{
Rule_class: "custom_api_contribution",
}
apiAttribute := bazel.MakeLabelAttribute(
android.BazelLabelForModuleSrcSingle(ctx, proptools.String(c.props.Api)).Label,
)
attrs := &customBazelModuleAttributes{
Api: *apiAttribute,
}
ctx.CreateBazelTargetModule(props,
android.CommonAttributes{Name: c.Name()},
attrs)
}
// A bp2build mutator that uses load statements and creates a 1:M mapping from // A bp2build mutator that uses load statements and creates a 1:M mapping from
// module to target. // module to target.
func customBp2buildOneToMany(ctx android.TopDownMutatorContext, m *customModule) { func customBp2buildOneToMany(ctx android.TopDownMutatorContext, m *customModule) {

View file

@ -24,6 +24,7 @@ import (
"time" "time"
"android/soong/android" "android/soong/android"
"android/soong/bazel"
"android/soong/bp2build" "android/soong/bp2build"
"android/soong/shared" "android/soong/shared"
"android/soong/ui/metrics/bp2build_metrics_proto" "android/soong/ui/metrics/bp2build_metrics_proto"
@ -48,11 +49,12 @@ var (
delveListen string delveListen string
delvePath string delvePath string
moduleGraphFile string moduleGraphFile string
moduleActionsFile string moduleActionsFile string
docFile string docFile string
bazelQueryViewDir string bazelQueryViewDir string
bp2buildMarker string bazelApiBp2buildDir string
bp2buildMarker string
cmdlineArgs bootstrap.Args 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(&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(&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(&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(&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.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") 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 buildMode = android.Bp2build
} else if bazelQueryViewDir != "" { } else if bazelQueryViewDir != "" {
buildMode = android.GenerateQueryView buildMode = android.GenerateQueryView
} else if bazelApiBp2buildDir != "" {
buildMode = android.ApiBp2build
} else if moduleGraphFile != "" { } else if moduleGraphFile != "" {
buildMode = android.GenerateModuleGraph buildMode = android.GenerateModuleGraph
} else if docFile != "" { } else if docFile != "" {
@ -178,7 +183,7 @@ func runQueryView(queryviewDir, queryviewMarker string, configuration android.Co
defer ctx.EventHandler.End("queryview") defer ctx.EventHandler.End("queryview")
codegenContext := bp2build.NewCodegenContext(configuration, *ctx, bp2build.QueryView) codegenContext := bp2build.NewCodegenContext(configuration, *ctx, bp2build.QueryView)
absoluteQueryViewDir := shared.JoinPath(topDir, queryviewDir) 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) fmt.Fprintf(os.Stderr, "%s", err)
os.Exit(1) os.Exit(1)
} }
@ -186,6 +191,96 @@ func runQueryView(queryviewDir, queryviewMarker string, configuration android.Co
touch(shared.JoinPath(topDir, queryviewMarker)) 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) { func writeMetrics(configuration android.Config, eventHandler metrics.EventHandler, metricsDir string) {
if len(metricsDir) < 1 { if len(metricsDir) < 1 {
fmt.Fprintf(os.Stderr, "\nMissing required env var for generating soong metrics: LOG_DIR\n") 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 return bp2buildMarker
} else if configuration.IsMixedBuildsEnabled() { } else if configuration.IsMixedBuildsEnabled() {
runMixedModeBuild(configuration, ctx, extraNinjaDeps) runMixedModeBuild(configuration, ctx, extraNinjaDeps)
} else if configuration.BuildMode == android.ApiBp2build {
return runApiBp2build(configuration, extraNinjaDeps)
} else { } else {
var stopBefore bootstrap.StopBefore var stopBefore bootstrap.StopBefore
if configuration.BuildMode == android.GenerateModuleGraph { if configuration.BuildMode == android.GenerateModuleGraph {
@ -476,6 +573,16 @@ func getExistingBazelRelatedFiles(topDir string) ([]string, error) {
return files, nil 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 // Run Soong in the bp2build mode. This creates a standalone context that registers
// an alternate pipeline of mutators and singletons specifically for generating // an alternate pipeline of mutators and singletons specifically for generating
// Bazel BUILD files instead of Ninja files. // Bazel BUILD files instead of Ninja files.
@ -524,13 +631,7 @@ func runBp2Build(configuration android.Config, extraNinjaDeps []string) {
generatedRoot := shared.JoinPath(configuration.SoongOutDir(), "bp2build") generatedRoot := shared.JoinPath(configuration.SoongOutDir(), "bp2build")
workspaceRoot := shared.JoinPath(configuration.SoongOutDir(), "workspace") workspaceRoot := shared.JoinPath(configuration.SoongOutDir(), "workspace")
excludes := []string{ excludes := bazelArtifacts()
"bazel-bin",
"bazel-genfiles",
"bazel-out",
"bazel-testlogs",
"bazel-" + filepath.Base(topDir),
}
if outDir[0] != '/' { if outDir[0] != '/' {
excludes = append(excludes, outDir) excludes = append(excludes, outDir)

View file

@ -23,8 +23,9 @@ import (
"android/soong/bp2build" "android/soong/bp2build"
) )
func createBazelQueryView(ctx *bp2build.CodegenContext, bazelQueryViewDir string) error { // A helper function to generate a Read-only Bazel workspace in outDir
os.RemoveAll(bazelQueryViewDir) func createBazelWorkspace(ctx *bp2build.CodegenContext, outDir string) error {
os.RemoveAll(outDir)
ruleShims := bp2build.CreateRuleShims(android.ModuleTypeFactories()) ruleShims := bp2build.CreateRuleShims(android.ModuleTypeFactories())
res, err := bp2build.GenerateBazelTargets(ctx, true) res, err := bp2build.GenerateBazelTargets(ctx, true)
@ -33,9 +34,9 @@ func createBazelQueryView(ctx *bp2build.CodegenContext, bazelQueryViewDir string
} }
filesToWrite := bp2build.CreateBazelFiles(ctx.Config(), ruleShims, res.BuildDirToTargets(), filesToWrite := bp2build.CreateBazelFiles(ctx.Config(), ruleShims, res.BuildDirToTargets(),
bp2build.QueryView) ctx.Mode())
for _, f := range filesToWrite { for _, f := range filesToWrite {
if err := writeReadOnlyFile(bazelQueryViewDir, f); err != nil { if err := writeReadOnlyFile(outDir, f); err != nil {
return err return err
} }
} }

View file

@ -71,6 +71,7 @@ type configImpl struct {
checkbuild bool checkbuild bool
dist bool dist bool
jsonModuleGraph bool jsonModuleGraph bool
apiBp2build bool // Generate BUILD files for Soong modules that contribute APIs
bp2build bool bp2build bool
queryview bool queryview bool
reportMkMetrics bool // Collect and report mk2bp migration progress metrics. reportMkMetrics bool // Collect and report mk2bp migration progress metrics.
@ -756,6 +757,8 @@ func (c *configImpl) parseArgs(ctx Context, args []string) {
c.jsonModuleGraph = true c.jsonModuleGraph = true
} else if arg == "bp2build" { } else if arg == "bp2build" {
c.bp2build = true c.bp2build = true
} else if arg == "api_bp2build" {
c.apiBp2build = true
} else if arg == "queryview" { } else if arg == "queryview" {
c.queryview = true c.queryview = true
} else if arg == "soong_docs" { } else if arg == "soong_docs" {
@ -833,7 +836,7 @@ func (c *configImpl) SoongBuildInvocationNeeded() bool {
return true return true
} }
if !c.JsonModuleGraph() && !c.Bp2Build() && !c.Queryview() && !c.SoongDocs() { if !c.JsonModuleGraph() && !c.Bp2Build() && !c.Queryview() && !c.SoongDocs() && !c.ApiBp2build() {
// Command line was empty, the default Ninja target is built // Command line was empty, the default Ninja target is built
return true return true
} }
@ -916,6 +919,10 @@ func (c *configImpl) QueryviewMarkerFile() string {
return shared.JoinPath(c.SoongOutDir(), "queryview.marker") return shared.JoinPath(c.SoongOutDir(), "queryview.marker")
} }
func (c *configImpl) ApiBp2buildMarkerFile() string {
return shared.JoinPath(c.SoongOutDir(), "api_bp2build.marker")
}
func (c *configImpl) ModuleGraphFile() string { func (c *configImpl) ModuleGraphFile() string {
return shared.JoinPath(c.SoongOutDir(), "module-graph.json") return shared.JoinPath(c.SoongOutDir(), "module-graph.json")
} }
@ -957,6 +964,10 @@ func (c *configImpl) Bp2Build() bool {
return c.bp2build return c.bp2build
} }
func (c *configImpl) ApiBp2build() bool {
return c.apiBp2build
}
func (c *configImpl) Queryview() bool { func (c *configImpl) Queryview() bool {
return c.queryview return c.queryview
} }

View file

@ -45,6 +45,7 @@ const (
bp2buildTag = "bp2build" bp2buildTag = "bp2build"
jsonModuleGraphTag = "modulegraph" jsonModuleGraphTag = "modulegraph"
queryviewTag = "queryview" queryviewTag = "queryview"
apiBp2buildTag = "api_bp2build"
soongDocsTag = "soong_docs" soongDocsTag = "soong_docs"
// bootstrapEpoch is used to determine if an incremental build is incompatible with the current // bootstrapEpoch is used to determine if an incremental build is incompatible with the current
@ -237,6 +238,7 @@ func bootstrapGlobFileList(config Config) []string {
config.NamedGlobFile(bp2buildTag), config.NamedGlobFile(bp2buildTag),
config.NamedGlobFile(jsonModuleGraphTag), config.NamedGlobFile(jsonModuleGraphTag),
config.NamedGlobFile(queryviewTag), config.NamedGlobFile(queryviewTag),
config.NamedGlobFile(apiBp2buildTag),
config.NamedGlobFile(soongDocsTag), config.NamedGlobFile(soongDocsTag),
} }
} }
@ -307,6 +309,19 @@ func bootstrapBlueprint(ctx Context, config Config) {
fmt.Sprintf("generating the Soong module graph as a Bazel workspace at %s", queryviewDir), fmt.Sprintf("generating the Soong module graph as a Bazel workspace at %s", queryviewDir),
) )
// The BUILD files will be generated in out/soong/.api_bp2build (no symlinks to src files)
// The final workspace will be generated in out/soong/api_bp2build
apiBp2buildDir := filepath.Join(config.SoongOutDir(), ".api_bp2build")
apiBp2buildInvocation := primaryBuilderInvocation(
config,
apiBp2buildTag,
config.ApiBp2buildMarkerFile(),
[]string{
"--bazel_api_bp2build_dir", apiBp2buildDir,
},
fmt.Sprintf("generating BUILD files for API contributions at %s", apiBp2buildDir),
)
soongDocsInvocation := primaryBuilderInvocation( soongDocsInvocation := primaryBuilderInvocation(
config, config,
soongDocsTag, soongDocsTag,
@ -345,6 +360,7 @@ func bootstrapBlueprint(ctx Context, config Config) {
bp2buildInvocation, bp2buildInvocation,
jsonModuleGraphInvocation, jsonModuleGraphInvocation,
queryviewInvocation, queryviewInvocation,
apiBp2buildInvocation,
soongDocsInvocation}, soongDocsInvocation},
} }
@ -417,6 +433,10 @@ func runSoong(ctx Context, config Config) {
checkEnvironmentFile(soongBuildEnv, config.UsedEnvFile(queryviewTag)) checkEnvironmentFile(soongBuildEnv, config.UsedEnvFile(queryviewTag))
} }
if config.ApiBp2build() {
checkEnvironmentFile(soongBuildEnv, config.UsedEnvFile(apiBp2buildTag))
}
if config.SoongDocs() { if config.SoongDocs() {
checkEnvironmentFile(soongBuildEnv, config.UsedEnvFile(soongDocsTag)) checkEnvironmentFile(soongBuildEnv, config.UsedEnvFile(soongDocsTag))
} }
@ -480,6 +500,10 @@ func runSoong(ctx Context, config Config) {
targets = append(targets, config.QueryviewMarkerFile()) targets = append(targets, config.QueryviewMarkerFile())
} }
if config.ApiBp2build() {
targets = append(targets, config.ApiBp2buildMarkerFile())
}
if config.SoongDocs() { if config.SoongDocs() {
targets = append(targets, config.SoongDocsHtml()) targets = append(targets, config.SoongDocsHtml())
} }