From 5af0bd3e4838dbe5680d9adc290095b8240d49d3 Mon Sep 17 00:00:00 2001 From: Spandan Das Date: Wed, 28 Sep 2022 20:43:08 +0000 Subject: [PATCH] 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 --- android/bazel.go | 23 ++++++ android/config.go | 3 + android/mutator.go | 19 ++++- android/register.go | 10 +++ android/testing.go | 6 ++ bp2build/build_conversion.go | 9 +++ bp2build/build_conversion_test.go | 24 ++++++ bp2build/bzl_conversion_test.go | 3 + bp2build/conversion.go | 2 +- bp2build/testing.go | 41 +++++++++- cmd/soong_build/main.go | 127 +++++++++++++++++++++++++++--- cmd/soong_build/queryview.go | 9 ++- ui/build/config.go | 13 ++- ui/build/soong.go | 24 ++++++ 14 files changed, 288 insertions(+), 25 deletions(-) diff --git a/android/bazel.go b/android/bazel.go index dd1de7bf0..7b227bddd 100644 --- a/android/bazel.go +++ b/android/bazel.go @@ -134,6 +134,11 @@ type Bazelable interface { 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 // to be "handled by Bazel" in a mixed build. type MixedBuildBuildable interface { @@ -415,6 +420,13 @@ func (b *BazelModuleBase) shouldConvertWithBp2build(ctx bazelOtherModuleContext, 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 packagePath := ctx.OtherModuleDir(module) @@ -510,6 +522,17 @@ func convertWithBp2build(ctx TopDownMutatorContext) { 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 // 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. diff --git a/android/config.go b/android/config.go index ee432a21f..e86fc2783 100644 --- a/android/config.go +++ b/android/config.go @@ -83,6 +83,9 @@ const ( // express build semantics. GenerateQueryView + // Generate BUILD files for API contributions to API surfaces + ApiBp2build + // Create a JSON representation of the module graph and exit. GenerateModuleGraph diff --git a/android/mutator.go b/android/mutator.go index 9e4aa59ad..83d4e6644 100644 --- a/android/mutator.go +++ b/android/mutator.go @@ -31,22 +31,33 @@ import ( // RegisterMutatorsForBazelConversion is a alternate registration pipeline for bp2build. Exported for testing. 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 := ®isterMutatorsContext{ bazelConversionMode: true, } - bp2buildMutators := append([]RegisterMutatorFunc{ + allMutators := append([]RegisterMutatorFunc{ RegisterNamespaceMutator, RegisterDefaultsPreArchMutators, // TODO(b/165114590): this is required to resolve deps that are only prebuilts, but we should // evaluate the impact on conversion. RegisterPrebuiltsPreArchMutators, }, - preArchMutators...) - bp2buildMutators = append(bp2buildMutators, registerBp2buildConversionMutator) + bp2buildMutators...) // Register bp2build mutators - for _, f := range bp2buildMutators { + for _, f := range allMutators { f(mctx) } diff --git a/android/register.go b/android/register.go index d4ce5f166..6c69cc5fd 100644 --- a/android/register.go +++ b/android/register.go @@ -180,6 +180,16 @@ func (ctx *Context) RegisterForBazelConversion() { 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 // generating build.ninja and other files for Kati, from Android.bp files. func (ctx *Context) Register() { diff --git a/android/testing.go b/android/testing.go index 7b74c89a2..401865905 100644 --- a/android/testing.go +++ b/android/testing.go @@ -461,6 +461,12 @@ func (ctx *TestContext) RegisterForBazelConversion() { 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) { // This function adapts the old style ParseFileList calls that are spread throughout the tests // to the new style that takes a config. diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go index 36c3a4885..82ce115f6 100644 --- a/bp2build/build_conversion.go +++ b/bp2build/build_conversion.go @@ -163,6 +163,9 @@ const ( // This mode is used for discovering and introspecting the existing Soong // module graph. QueryView + + // ApiBp2build - generate BUILD files for API contribution targets + ApiBp2build ) type unconvertedDepsMode int @@ -181,6 +184,8 @@ func (mode CodegenMode) String() string { return "Bp2Build" case QueryView: return "QueryView" + case ApiBp2build: + return "ApiBp2build" default: return fmt.Sprintf("%d", mode) } @@ -327,6 +332,10 @@ func GenerateBazelTargets(ctx *CodegenContext, generateFilegroups bool) (convers errs = append(errs, err) } targets = append(targets, t) + case ApiBp2build: + if aModule, ok := m.(android.Module); ok && aModule.IsConvertedByBp2build() { + targets, errs = generateBazelTargets(bpCtx, aModule) + } default: errs = append(errs, fmt.Errorf("Unknown code-generation mode: %s", ctx.Mode())) return diff --git a/bp2build/build_conversion_test.go b/bp2build/build_conversion_test.go index d513d04d9..c168ecbe7 100644 --- a/bp2build/build_conversion_test.go +++ b/bp2build/build_conversion_test.go @@ -1848,3 +1848,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", + }) +} diff --git a/bp2build/bzl_conversion_test.go b/bp2build/bzl_conversion_test.go index 28d2c759b..a8e557deb 100644 --- a/bp2build/bzl_conversion_test.go +++ b/bp2build/bzl_conversion_test.go @@ -85,6 +85,7 @@ custom = rule( "soong_module_name": attr.string(mandatory = True), "soong_module_variant": attr.string(), "soong_module_deps": attr.label_list(providers = [SoongModuleInfo]), + "api": attr.string(), "arch_paths": attr.string_list(), "arch_paths_exclude": attr.string_list(), # bazel_module start @@ -119,6 +120,7 @@ custom_defaults = rule( "soong_module_name": attr.string(mandatory = True), "soong_module_variant": attr.string(), "soong_module_deps": attr.label_list(providers = [SoongModuleInfo]), + "api": attr.string(), "arch_paths": attr.string_list(), "arch_paths_exclude": attr.string_list(), "bool_prop": attr.bool(), @@ -149,6 +151,7 @@ custom_test_ = rule( "soong_module_name": attr.string(mandatory = True), "soong_module_variant": attr.string(), "soong_module_deps": attr.label_list(providers = [SoongModuleInfo]), + "api": attr.string(), "arch_paths": attr.string_list(), "arch_paths_exclude": attr.string_list(), "bool_prop": attr.bool(), diff --git a/bp2build/conversion.go b/bp2build/conversion.go index 522c10e3a..4d8b8a450 100644 --- a/bp2build/conversion.go +++ b/bp2build/conversion.go @@ -97,7 +97,7 @@ func createBuildFiles(buildToTargets map[string]BazelTargets, mode CodegenMode) targets.sort() var content string - if mode == Bp2Build { + if mode == Bp2Build || mode == ApiBp2build { content = `# READ THIS FIRST: # 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. diff --git a/bp2build/testing.go b/bp2build/testing.go index c2c1b197d..31aa8304a 100644 --- a/bp2build/testing.go +++ b/bp2build/testing.go @@ -24,6 +24,8 @@ import ( "strings" "testing" + "github.com/google/blueprint/proptools" + "android/soong/android" "android/soong/android/allowlists" "android/soong/bazel" @@ -88,6 +90,22 @@ type Bp2buildTestCase struct { } 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() dir := "." 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) ctx := android.NewTestContext(config) - registerModuleTypes(ctx) + setup(ctx) ctx.RegisterModuleType(tc.ModuleTypeUnderTest, tc.ModuleTypeUnderTestFactory) // 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.RegisterForBazelConversion() _, parseErrs := ctx.ParseFileList(dir, toParse) if errored(t, tc, parseErrs) { @@ -198,6 +215,8 @@ type customProps struct { // Prop used to indicate this conversion should be 1 module -> multiple targets One_to_many_prop *bool + + Api *string // File describing the APIs of this module } type customModule struct { @@ -320,6 +339,7 @@ type customBazelModuleAttributes struct { String_ptr_prop *string String_list_prop []string Arch_paths bazel.LabelListAttribute + Api bazel.LabelAttribute } 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) } +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 // module to target. func customBp2buildOneToMany(ctx android.TopDownMutatorContext, m *customModule) { diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go index 0b8cc8864..770ad0cfe 100644 --- a/cmd/soong_build/main.go +++ b/cmd/soong_build/main.go @@ -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) diff --git a/cmd/soong_build/queryview.go b/cmd/soong_build/queryview.go index 983dbf0fc..cd1d6fb5d 100644 --- a/cmd/soong_build/queryview.go +++ b/cmd/soong_build/queryview.go @@ -23,8 +23,9 @@ import ( "android/soong/bp2build" ) -func createBazelQueryView(ctx *bp2build.CodegenContext, bazelQueryViewDir string) error { - os.RemoveAll(bazelQueryViewDir) +// A helper function to generate a Read-only Bazel workspace in outDir +func createBazelWorkspace(ctx *bp2build.CodegenContext, outDir string) error { + os.RemoveAll(outDir) ruleShims := bp2build.CreateRuleShims(android.ModuleTypeFactories()) 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(), - bp2build.QueryView) + ctx.Mode()) for _, f := range filesToWrite { - if err := writeReadOnlyFile(bazelQueryViewDir, f); err != nil { + if err := writeReadOnlyFile(outDir, f); err != nil { return err } } diff --git a/ui/build/config.go b/ui/build/config.go index 14a99d080..206066066 100644 --- a/ui/build/config.go +++ b/ui/build/config.go @@ -71,6 +71,7 @@ type configImpl struct { checkbuild bool dist bool jsonModuleGraph bool + apiBp2build bool // Generate BUILD files for Soong modules that contribute APIs bp2build bool queryview bool reportMkMetrics bool // Collect and report mk2bp migration progress metrics. @@ -756,6 +757,8 @@ func (c *configImpl) parseArgs(ctx Context, args []string) { c.jsonModuleGraph = true } else if arg == "bp2build" { c.bp2build = true + } else if arg == "api_bp2build" { + c.apiBp2build = true } else if arg == "queryview" { c.queryview = true } else if arg == "soong_docs" { @@ -833,7 +836,7 @@ func (c *configImpl) SoongBuildInvocationNeeded() bool { 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 return true } @@ -916,6 +919,10 @@ func (c *configImpl) QueryviewMarkerFile() string { 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 { return shared.JoinPath(c.SoongOutDir(), "module-graph.json") } @@ -957,6 +964,10 @@ func (c *configImpl) Bp2Build() bool { return c.bp2build } +func (c *configImpl) ApiBp2build() bool { + return c.apiBp2build +} + func (c *configImpl) Queryview() bool { return c.queryview } diff --git a/ui/build/soong.go b/ui/build/soong.go index ac00fe61f..e0d67cc84 100644 --- a/ui/build/soong.go +++ b/ui/build/soong.go @@ -45,6 +45,7 @@ const ( bp2buildTag = "bp2build" jsonModuleGraphTag = "modulegraph" queryviewTag = "queryview" + apiBp2buildTag = "api_bp2build" soongDocsTag = "soong_docs" // 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(jsonModuleGraphTag), config.NamedGlobFile(queryviewTag), + config.NamedGlobFile(apiBp2buildTag), 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), ) + // 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( config, soongDocsTag, @@ -345,6 +360,7 @@ func bootstrapBlueprint(ctx Context, config Config) { bp2buildInvocation, jsonModuleGraphInvocation, queryviewInvocation, + apiBp2buildInvocation, soongDocsInvocation}, } @@ -417,6 +433,10 @@ func runSoong(ctx Context, config Config) { checkEnvironmentFile(soongBuildEnv, config.UsedEnvFile(queryviewTag)) } + if config.ApiBp2build() { + checkEnvironmentFile(soongBuildEnv, config.UsedEnvFile(apiBp2buildTag)) + } + if config.SoongDocs() { checkEnvironmentFile(soongBuildEnv, config.UsedEnvFile(soongDocsTag)) } @@ -480,6 +500,10 @@ func runSoong(ctx Context, config Config) { targets = append(targets, config.QueryviewMarkerFile()) } + if config.ApiBp2build() { + targets = append(targets, config.ApiBp2buildMarkerFile()) + } + if config.SoongDocs() { targets = append(targets, config.SoongDocsHtml()) }