Refactor mixed builds to only take one pass

This large refactoring has both immense performance implications and
improves mixed builds complexity / usability. Summary:

1. Queueing calls to Bazel is done in a new mutator instead of a full
   soong_build pass. Normal soong_build flow is interrupted (via a
   functional hook in blueprint) to invoke bazel and parse its response.
2. Implementing mixed build support for additional modules is as simple
   as implementing MixedBuildsBuildable. In this interface, define the
   request that must be queued to Bazel, and then subsequently define
   how to handle the returned bazel cquery metadata.
3. Mixed builds consists of only a single pass. This greatly
   improves mixed build performance.

Result:
  A 33% runtime improvement on soong analysis phase with mixed builds.

Caveats:
  C++ BazelHandler handling still remains a bit of a mess; I did what
  I could within this CL's scope, but this may require additional cleanup.

Test: Treehugger
Test: Verified that aosp_arm ninja file is bit-for-bit identical with or
without this change.

Change-Id: I412d9c94d429105f4ebfafc84100d546069e6621
This commit is contained in:
Chris Parsons
2022-05-10 13:50:12 -04:00
parent 53c6c67cbb
commit f874e46153
14 changed files with 383 additions and 285 deletions

View File

@@ -115,6 +115,27 @@ type Bazelable interface {
SetBaseModuleType(baseModuleType string) SetBaseModuleType(baseModuleType string)
} }
// MixedBuildBuildable is an interface that module types should implement in order
// to be "handled by Bazel" in a mixed build.
type MixedBuildBuildable interface {
// IsMixedBuildSupported returns true if and only if this module should be
// "handled by Bazel" in a mixed build.
// This "escape hatch" allows modules with corner-case scenarios to opt out
// of being built with Bazel.
IsMixedBuildSupported(ctx BaseModuleContext) bool
// QueueBazelCall invokes request-queueing functions on the BazelContext
// so that these requests are handled when Bazel's cquery is invoked.
QueueBazelCall(ctx BaseModuleContext)
// ProcessBazelQueryResponse uses Bazel information (obtained from the BazelContext)
// to set module fields and providers to propagate this module's metadata upstream.
// This effectively "bridges the gap" between Bazel and Soong in a mixed build.
// Soong modules depending on this module should be oblivious to the fact that
// this module was handled by Bazel.
ProcessBazelQueryResponse(ctx ModuleContext)
}
// BazelModule is a lightweight wrapper interface around Module for Bazel-convertible modules. // BazelModule is a lightweight wrapper interface around Module for Bazel-convertible modules.
type BazelModule interface { type BazelModule interface {
Module Module
@@ -342,7 +363,7 @@ func shouldKeepExistingBuildFileForDir(allowlist bp2BuildConversionAllowlist, di
// converted or handcrafted Bazel target. As a side effect, calling this // converted or handcrafted Bazel target. As a side effect, calling this
// method will also log whether this module is mixed build enabled for // method will also log whether this module is mixed build enabled for
// metrics reporting. // metrics reporting.
func MixedBuildsEnabled(ctx ModuleContext) bool { func MixedBuildsEnabled(ctx BaseModuleContext) bool {
mixedBuildEnabled := mixedBuildPossible(ctx) mixedBuildEnabled := mixedBuildPossible(ctx)
ctx.Config().LogMixedBuild(ctx, mixedBuildEnabled) ctx.Config().LogMixedBuild(ctx, mixedBuildEnabled)
return mixedBuildEnabled return mixedBuildEnabled
@@ -350,7 +371,7 @@ func MixedBuildsEnabled(ctx ModuleContext) bool {
// mixedBuildPossible returns true if a module is ready to be replaced by a // mixedBuildPossible returns true if a module is ready to be replaced by a
// converted or handcrafted Bazel target. // converted or handcrafted Bazel target.
func mixedBuildPossible(ctx ModuleContext) bool { func mixedBuildPossible(ctx BaseModuleContext) bool {
if ctx.Os() == Windows { if ctx.Os() == Windows {
// Windows toolchains are not currently supported. // Windows toolchains are not currently supported.
return false return false

View File

@@ -33,6 +33,26 @@ import (
"android/soong/bazel" "android/soong/bazel"
) )
func init() {
RegisterMixedBuildsMutator(InitRegistrationContext)
}
func RegisterMixedBuildsMutator(ctx RegistrationContext) {
ctx.PostDepsMutators(func(ctx RegisterMutatorsContext) {
ctx.BottomUp("mixed_builds_prep", mixedBuildsPrepareMutator).Parallel()
})
}
func mixedBuildsPrepareMutator(ctx BottomUpMutatorContext) {
if m := ctx.Module(); m.Enabled() {
if mixedBuildMod, ok := m.(MixedBuildBuildable); ok {
if mixedBuildMod.IsMixedBuildSupported(ctx) && MixedBuildsEnabled(ctx) {
mixedBuildMod.QueueBazelCall(ctx)
}
}
}
}
type cqueryRequest interface { type cqueryRequest interface {
// Name returns a string name for this request type. Such request type names must be unique, // Name returns a string name for this request type. Such request type names must be unique,
// and must only consist of alphanumeric characters. // and must only consist of alphanumeric characters.
@@ -62,33 +82,32 @@ type cqueryKey struct {
configKey configKey configKey configKey
} }
// bazelHandler is the interface for a helper object related to deferring to Bazel for // BazelContext is a context object useful for interacting with Bazel during
// processing a module (during Bazel mixed builds). Individual module types should define // the course of a build. Use of Bazel to evaluate part of the build graph
// their own bazel handler if they support deferring to Bazel. // is referred to as a "mixed build". (Some modules are managed by Soong,
type BazelHandler interface { // some are managed by Bazel). To facilitate interop between these build
// Issue query to Bazel to retrieve information about Bazel's view of the current module. // subgraphs, Soong may make requests to Bazel and evaluate their responses
// If Bazel returns this information, set module properties on the current module to reflect // so that Soong modules may accurately depend on Bazel targets.
// the returned information.
// Returns true if information was available from Bazel, false if bazel invocation still needs to occur.
GenerateBazelBuildActions(ctx ModuleContext, label string) bool
}
type BazelContext interface { type BazelContext interface {
// The methods below involve queuing cquery requests to be later invoked // Add a cquery request to the bazel request queue. All queued requests
// by bazel. If any of these methods return (_, false), then the request // will be sent to Bazel on a subsequent invocation of InvokeBazel.
// has been queued to be run later. QueueBazelRequest(label string, requestType cqueryRequest, cfgKey configKey)
// ** Cquery Results Retrieval Functions
// The below functions pertain to retrieving cquery results from a prior
// InvokeBazel function call and parsing the results.
// Returns result files built by building the given bazel target label. // Returns result files built by building the given bazel target label.
GetOutputFiles(label string, cfgKey configKey) ([]string, bool) GetOutputFiles(label string, cfgKey configKey) ([]string, error)
// TODO(cparsons): Other cquery-related methods should be added here.
// Returns the results of GetOutputFiles and GetCcObjectFiles in a single query (in that order). // Returns the results of GetOutputFiles and GetCcObjectFiles in a single query (in that order).
GetCcInfo(label string, cfgKey configKey) (cquery.CcInfo, bool, error) GetCcInfo(label string, cfgKey configKey) (cquery.CcInfo, error)
// Returns the executable binary resultant from building together the python sources // Returns the executable binary resultant from building together the python sources
GetPythonBinary(label string, cfgKey configKey) (string, bool) // TODO(b/232976601): Remove.
GetPythonBinary(label string, cfgKey configKey) (string, error)
// ** End cquery methods // ** end Cquery Results Retrieval Functions
// Issues commands to Bazel to receive results for all cquery requests // Issues commands to Bazel to receive results for all cquery requests
// queued in the BazelContext. // queued in the BazelContext.
@@ -153,19 +172,23 @@ type MockBazelContext struct {
LabelToPythonBinary map[string]string LabelToPythonBinary map[string]string
} }
func (m MockBazelContext) GetOutputFiles(label string, cfgKey configKey) ([]string, bool) { func (m MockBazelContext) QueueBazelRequest(label string, requestType cqueryRequest, cfgKey configKey) {
result, ok := m.LabelToOutputFiles[label] panic("unimplemented")
return result, ok
} }
func (m MockBazelContext) GetCcInfo(label string, cfgKey configKey) (cquery.CcInfo, bool, error) { func (m MockBazelContext) GetOutputFiles(label string, cfgKey configKey) ([]string, error) {
result, ok := m.LabelToCcInfo[label] result, _ := m.LabelToOutputFiles[label]
return result, ok, nil return result, nil
} }
func (m MockBazelContext) GetPythonBinary(label string, cfgKey configKey) (string, bool) { func (m MockBazelContext) GetCcInfo(label string, cfgKey configKey) (cquery.CcInfo, error) {
result, ok := m.LabelToPythonBinary[label] result, _ := m.LabelToCcInfo[label]
return result, ok return result, nil
}
func (m MockBazelContext) GetPythonBinary(label string, cfgKey configKey) (string, error) {
result, _ := m.LabelToPythonBinary[label]
return result, nil
} }
func (m MockBazelContext) InvokeBazel() error { func (m MockBazelContext) InvokeBazel() error {
@@ -188,46 +211,53 @@ func (m MockBazelContext) AqueryDepsets() []bazel.AqueryDepset {
var _ BazelContext = MockBazelContext{} var _ BazelContext = MockBazelContext{}
func (bazelCtx *bazelContext) GetOutputFiles(label string, cfgKey configKey) ([]string, bool) { func (bazelCtx *bazelContext) QueueBazelRequest(label string, requestType cqueryRequest, cfgKey configKey) {
rawString, ok := bazelCtx.cquery(label, cquery.GetOutputFiles, cfgKey) key := cqueryKey{label, requestType, cfgKey}
var ret []string bazelCtx.requestMutex.Lock()
if ok { defer bazelCtx.requestMutex.Unlock()
bazelCtx.requests[key] = true
}
func (bazelCtx *bazelContext) GetOutputFiles(label string, cfgKey configKey) ([]string, error) {
key := cqueryKey{label, cquery.GetOutputFiles, cfgKey}
if rawString, ok := bazelCtx.results[key]; ok {
bazelOutput := strings.TrimSpace(rawString) bazelOutput := strings.TrimSpace(rawString)
ret = cquery.GetOutputFiles.ParseResult(bazelOutput) return cquery.GetOutputFiles.ParseResult(bazelOutput), nil
} }
return ret, ok return nil, fmt.Errorf("no bazel response found for %v", key)
} }
func (bazelCtx *bazelContext) GetCcInfo(label string, cfgKey configKey) (cquery.CcInfo, bool, error) { func (bazelCtx *bazelContext) GetCcInfo(label string, cfgKey configKey) (cquery.CcInfo, error) {
result, ok := bazelCtx.cquery(label, cquery.GetCcInfo, cfgKey) key := cqueryKey{label, cquery.GetCcInfo, cfgKey}
if !ok { if rawString, ok := bazelCtx.results[key]; ok {
return cquery.CcInfo{}, ok, nil
}
bazelOutput := strings.TrimSpace(result)
ret, err := cquery.GetCcInfo.ParseResult(bazelOutput)
return ret, ok, err
}
func (bazelCtx *bazelContext) GetPythonBinary(label string, cfgKey configKey) (string, bool) {
rawString, ok := bazelCtx.cquery(label, cquery.GetPythonBinary, cfgKey)
var ret string
if ok {
bazelOutput := strings.TrimSpace(rawString) bazelOutput := strings.TrimSpace(rawString)
ret = cquery.GetPythonBinary.ParseResult(bazelOutput) return cquery.GetCcInfo.ParseResult(bazelOutput)
} }
return ret, ok return cquery.CcInfo{}, fmt.Errorf("no bazel response found for %v", key)
} }
func (n noopBazelContext) GetOutputFiles(label string, cfgKey configKey) ([]string, bool) { func (bazelCtx *bazelContext) GetPythonBinary(label string, cfgKey configKey) (string, error) {
key := cqueryKey{label, cquery.GetPythonBinary, cfgKey}
if rawString, ok := bazelCtx.results[key]; ok {
bazelOutput := strings.TrimSpace(rawString)
return cquery.GetPythonBinary.ParseResult(bazelOutput), nil
}
return "", fmt.Errorf("no bazel response found for %v", key)
}
func (n noopBazelContext) QueueBazelRequest(label string, requestType cqueryRequest, cfgKey configKey) {
panic("unimplemented") panic("unimplemented")
} }
func (n noopBazelContext) GetCcInfo(label string, cfgKey configKey) (cquery.CcInfo, bool, error) { func (n noopBazelContext) GetOutputFiles(label string, cfgKey configKey) ([]string, error) {
panic("unimplemented") panic("unimplemented")
} }
func (n noopBazelContext) GetPythonBinary(label string, cfgKey configKey) (string, bool) { func (n noopBazelContext) GetCcInfo(label string, cfgKey configKey) (cquery.CcInfo, error) {
panic("unimplemented")
}
func (n noopBazelContext) GetPythonBinary(label string, cfgKey configKey) (string, error) {
panic("unimplemented") panic("unimplemented")
} }
@@ -314,24 +344,6 @@ func (context *bazelContext) BazelEnabled() bool {
return true return true
} }
// Adds a cquery request to the Bazel request queue, to be later invoked, or
// returns the result of the given request if the request was already made.
// If the given request was already made (and the results are available), then
// returns (result, true). If the request is queued but no results are available,
// then returns ("", false).
func (context *bazelContext) cquery(label string, requestType cqueryRequest,
cfgKey configKey) (string, bool) {
key := cqueryKey{label, requestType, cfgKey}
if result, ok := context.results[key]; ok {
return result, true
} else {
context.requestMutex.Lock()
defer context.requestMutex.Unlock()
context.requests[key] = true
return "", false
}
}
func pwdPrefix() string { func pwdPrefix() string {
// Darwin doesn't have /proc // Darwin doesn't have /proc
if runtime.GOOS != "darwin" { if runtime.GOOS != "darwin" {
@@ -916,7 +928,7 @@ func getConfigString(key cqueryKey) string {
return arch + "|" + os return arch + "|" + os
} }
func GetConfigKey(ctx ModuleContext) configKey { func GetConfigKey(ctx BaseModuleContext) configKey {
return configKey{ return configKey{
// use string because Arch is not a valid key in go // use string because Arch is not a valid key in go
arch: ctx.Arch().String(), arch: ctx.Arch().String(),

View File

@@ -5,6 +5,8 @@ import (
"path/filepath" "path/filepath"
"reflect" "reflect"
"testing" "testing"
"android/soong/bazel/cquery"
) )
func TestRequestResultsAfterInvokeBazel(t *testing.T) { func TestRequestResultsAfterInvokeBazel(t *testing.T) {
@@ -13,17 +15,14 @@ func TestRequestResultsAfterInvokeBazel(t *testing.T) {
bazelContext, _ := testBazelContext(t, map[bazelCommand]string{ bazelContext, _ := testBazelContext(t, map[bazelCommand]string{
bazelCommand{command: "cquery", expression: "deps(@soong_injection//mixed_builds:buildroot, 2)"}: `//foo:bar|arm64_armv8-a|android>>out/foo/bar.txt`, bazelCommand{command: "cquery", expression: "deps(@soong_injection//mixed_builds:buildroot, 2)"}: `//foo:bar|arm64_armv8-a|android>>out/foo/bar.txt`,
}) })
g, ok := bazelContext.GetOutputFiles(label, cfg) bazelContext.QueueBazelRequest(label, cquery.GetOutputFiles, cfg)
if ok {
t.Errorf("Did not expect cquery results prior to running InvokeBazel(), but got %s", g)
}
err := bazelContext.InvokeBazel() err := bazelContext.InvokeBazel()
if err != nil { if err != nil {
t.Fatalf("Did not expect error invoking Bazel, but got %s", err) t.Fatalf("Did not expect error invoking Bazel, but got %s", err)
} }
g, ok = bazelContext.GetOutputFiles(label, cfg) g, err := bazelContext.GetOutputFiles(label, cfg)
if !ok { if err != nil {
t.Errorf("Expected cquery results after running InvokeBazel(), but got none") t.Errorf("Expected cquery results after running InvokeBazel(), but got err %v", err)
} else if w := []string{"out/foo/bar.txt"}; !reflect.DeepEqual(w, g) { } else if w := []string{"out/foo/bar.txt"}; !reflect.DeepEqual(w, g) {
t.Errorf("Expected output %s, got %s", w, g) t.Errorf("Expected output %s, got %s", w, g)
} }

View File

@@ -2047,7 +2047,7 @@ func (c *config) UseHostMusl() bool {
return Bool(c.productVariables.HostMusl) return Bool(c.productVariables.HostMusl)
} }
func (c *config) LogMixedBuild(ctx ModuleContext, useBazel bool) { func (c *config) LogMixedBuild(ctx BaseModuleContext, useBazel bool) {
moduleName := ctx.Module().Name() moduleName := ctx.Module().Name()
c.mixedBuildsLock.Lock() c.mixedBuildsLock.Lock()
defer c.mixedBuildsLock.Unlock() defer c.mixedBuildsLock.Unlock()

View File

@@ -18,6 +18,7 @@ import (
"strings" "strings"
"android/soong/bazel" "android/soong/bazel"
"android/soong/bazel/cquery"
"github.com/google/blueprint" "github.com/google/blueprint"
) )
@@ -101,6 +102,7 @@ type fileGroup struct {
srcs Paths srcs Paths
} }
var _ MixedBuildBuildable = (*fileGroup)(nil)
var _ SourceFileProducer = (*fileGroup)(nil) var _ SourceFileProducer = (*fileGroup)(nil)
// filegroup contains a list of files that are referenced by other modules // filegroup contains a list of files that are referenced by other modules
@@ -114,42 +116,11 @@ func FileGroupFactory() Module {
return module return module
} }
func (fg *fileGroup) maybeGenerateBazelBuildActions(ctx ModuleContext) {
if !MixedBuildsEnabled(ctx) {
return
}
archVariant := ctx.Arch().String()
osVariant := ctx.Os()
if len(fg.Srcs()) == 1 && fg.Srcs()[0].Base() == fg.Name() {
// This will be a regular file target, not filegroup, in Bazel.
// See FilegroupBp2Build for more information.
archVariant = Common.String()
osVariant = CommonOS
}
bazelCtx := ctx.Config().BazelContext
filePaths, ok := bazelCtx.GetOutputFiles(fg.GetBazelLabel(ctx, fg), configKey{archVariant, osVariant})
if !ok {
return
}
bazelOuts := make(Paths, 0, len(filePaths))
for _, p := range filePaths {
src := PathForBazelOut(ctx, p)
bazelOuts = append(bazelOuts, src)
}
fg.srcs = bazelOuts
}
func (fg *fileGroup) GenerateAndroidBuildActions(ctx ModuleContext) { func (fg *fileGroup) GenerateAndroidBuildActions(ctx ModuleContext) {
fg.srcs = PathsForModuleSrcExcludes(ctx, fg.properties.Srcs, fg.properties.Exclude_srcs) fg.srcs = PathsForModuleSrcExcludes(ctx, fg.properties.Srcs, fg.properties.Exclude_srcs)
if fg.properties.Path != nil { if fg.properties.Path != nil {
fg.srcs = PathsWithModuleSrcSubDir(ctx, fg.srcs, String(fg.properties.Path)) fg.srcs = PathsWithModuleSrcSubDir(ctx, fg.srcs, String(fg.properties.Path))
} }
fg.maybeGenerateBazelBuildActions(ctx)
} }
func (fg *fileGroup) Srcs() Paths { func (fg *fileGroup) Srcs() Paths {
@@ -161,3 +132,38 @@ func (fg *fileGroup) MakeVars(ctx MakeVarsModuleContext) {
ctx.StrictRaw(makeVar, strings.Join(fg.srcs.Strings(), " ")) ctx.StrictRaw(makeVar, strings.Join(fg.srcs.Strings(), " "))
} }
} }
func (fg *fileGroup) QueueBazelCall(ctx BaseModuleContext) {
bazelCtx := ctx.Config().BazelContext
bazelCtx.QueueBazelRequest(
fg.GetBazelLabel(ctx, fg),
cquery.GetOutputFiles,
configKey{Common.String(), CommonOS})
}
func (fg *fileGroup) IsMixedBuildSupported(ctx BaseModuleContext) bool {
return true
}
func (fg *fileGroup) ProcessBazelQueryResponse(ctx ModuleContext) {
fg.srcs = PathsForModuleSrcExcludes(ctx, fg.properties.Srcs, fg.properties.Exclude_srcs)
if fg.properties.Path != nil {
fg.srcs = PathsWithModuleSrcSubDir(ctx, fg.srcs, String(fg.properties.Path))
}
bazelCtx := ctx.Config().BazelContext
filePaths, err := bazelCtx.GetOutputFiles(fg.GetBazelLabel(ctx, fg), configKey{Common.String(), CommonOS})
if err != nil {
ctx.ModuleErrorf(err.Error())
return
}
bazelOuts := make(Paths, 0, len(filePaths))
for _, p := range filePaths {
src := PathForBazelOut(ctx, p)
bazelOuts = append(bazelOuts, src)
}
fg.srcs = bazelOuts
}

View File

@@ -2275,7 +2275,11 @@ func (m *ModuleBase) GenerateBuildActions(blueprintCtx blueprint.ModuleContext)
return return
} }
m.module.GenerateAndroidBuildActions(ctx) if mixedBuildMod, handled := m.isHandledByBazel(ctx); handled {
mixedBuildMod.ProcessBazelQueryResponse(ctx)
} else {
m.module.GenerateAndroidBuildActions(ctx)
}
if ctx.Failed() { if ctx.Failed() {
return return
} }
@@ -2331,6 +2335,18 @@ func (m *ModuleBase) GenerateBuildActions(blueprintCtx blueprint.ModuleContext)
m.variables = ctx.variables m.variables = ctx.variables
} }
func (m *ModuleBase) isHandledByBazel(ctx ModuleContext) (MixedBuildBuildable, bool) {
if !ctx.Config().BazelContext.BazelEnabled() {
return nil, false
}
if mixedBuildMod, ok := m.module.(MixedBuildBuildable); ok {
if mixedBuildMod.IsMixedBuildSupported(ctx) && MixedBuildsEnabled(ctx) {
return mixedBuildMod, true
}
}
return nil, false
}
// Check the supplied dist structure to make sure that it is valid. // Check the supplied dist structure to make sure that it is valid.
// //
// property - the base property, e.g. dist or dists[1], which is combined with the // property - the base property, e.g. dist or dists[1], which is combined with the

View File

@@ -17,6 +17,7 @@ package cc
import ( import (
"path/filepath" "path/filepath"
"android/soong/bazel/cquery"
"github.com/google/blueprint" "github.com/google/blueprint"
"github.com/google/blueprint/proptools" "github.com/google/blueprint/proptools"
@@ -562,25 +563,32 @@ func (binary *binaryDecorator) verifyHostBionicLinker(ctx ModuleContext, in, lin
} }
type ccBinaryBazelHandler struct { type ccBinaryBazelHandler struct {
android.BazelHandler BazelHandler
module *Module module *Module
} }
func (handler *ccBinaryBazelHandler) GenerateBazelBuildActions(ctx android.ModuleContext, label string) bool { func (handler *ccBinaryBazelHandler) QueueBazelCall(ctx android.BaseModuleContext, label string) {
bazelCtx := ctx.Config().BazelContext bazelCtx := ctx.Config().BazelContext
filePaths, ok := bazelCtx.GetOutputFiles(label, android.GetConfigKey(ctx)) bazelCtx.QueueBazelRequest(label, cquery.GetOutputFiles, android.GetConfigKey(ctx))
if ok { }
if len(filePaths) != 1 {
ctx.ModuleErrorf("expected exactly one output file for '%s', but got %s", label, filePaths) func (handler *ccBinaryBazelHandler) ProcessBazelQueryResponse(ctx android.ModuleContext, label string) {
return false bazelCtx := ctx.Config().BazelContext
} filePaths, err := bazelCtx.GetOutputFiles(label, android.GetConfigKey(ctx))
outputFilePath := android.PathForBazelOut(ctx, filePaths[0]) if err != nil {
handler.module.outputFile = android.OptionalPathForPath(outputFilePath) ctx.ModuleErrorf(err.Error())
// TODO(b/220164721): We need to decide if we should return the stripped as the unstripped. return
handler.module.linker.(*binaryDecorator).unstrippedOutputFile = outputFilePath
} }
return ok
if len(filePaths) != 1 {
ctx.ModuleErrorf("expected exactly one output file for '%s', but got %s", label, filePaths)
return
}
outputFilePath := android.PathForBazelOut(ctx, filePaths[0])
handler.module.outputFile = android.OptionalPathForPath(outputFilePath)
// TODO(b/220164721): We need to decide if we should return the stripped as the unstripped.
handler.module.linker.(*binaryDecorator).unstrippedOutputFile = outputFilePath
} }
func binaryBp2build(ctx android.TopDownMutatorContext, m *Module, typ string) { func binaryBp2build(ctx android.TopDownMutatorContext, m *Module, typ string) {

View File

@@ -772,6 +772,19 @@ func IsTestPerSrcDepTag(depTag blueprint.DependencyTag) bool {
return ok && ccDepTag == testPerSrcDepTag return ok && ccDepTag == testPerSrcDepTag
} }
// bazelHandler is the interface for a helper object related to deferring to Bazel for
// processing a cc module (during Bazel mixed builds). Individual module types should define
// their own bazel handler if they support being handled by Bazel.
type BazelHandler interface {
// QueueBazelCall invokes request-queueing functions on the BazelContext
//so that these requests are handled when Bazel's cquery is invoked.
QueueBazelCall(ctx android.BaseModuleContext, label string)
// ProcessBazelQueryResponse uses information retrieved from Bazel to set properties
// on the current module with given label.
ProcessBazelQueryResponse(ctx android.ModuleContext, label string)
}
// Module contains the properties and members used by all C/C++ module types, and implements // Module contains the properties and members used by all C/C++ module types, and implements
// the blueprint.Module interface. It delegates to compiler, linker, and installer interfaces // the blueprint.Module interface. It delegates to compiler, linker, and installer interfaces
// to construct the output file. Behavior can be customized with a Customizer, or "decorator", // to construct the output file. Behavior can be customized with a Customizer, or "decorator",
@@ -811,7 +824,7 @@ type Module struct {
compiler compiler compiler compiler
linker linker linker linker
installer installer installer installer
bazelHandler android.BazelHandler bazelHandler BazelHandler
features []feature features []feature
stl *stl stl *stl
@@ -1773,31 +1786,51 @@ func GetSubnameProperty(actx android.ModuleContext, c LinkableInterface) string
return subName return subName
} }
// Returns true if Bazel was successfully used for the analysis of this module. var _ android.MixedBuildBuildable = (*Module)(nil)
func (c *Module) maybeGenerateBazelActions(actx android.ModuleContext) bool {
var bazelModuleLabel string func (c *Module) getBazelModuleLabel(ctx android.BaseModuleContext) string {
if c.typ() == fullLibrary && c.static() { if c.typ() == fullLibrary && c.static() {
// cc_library is a special case in bp2build; two targets are generated -- one for each // cc_library is a special case in bp2build; two targets are generated -- one for each
// of the shared and static variants. The shared variant keeps the module name, but the // of the shared and static variants. The shared variant keeps the module name, but the
// static variant uses a different suffixed name. // static variant uses a different suffixed name.
bazelModuleLabel = bazelLabelForStaticModule(actx, c) return bazelLabelForStaticModule(ctx, c)
} else { }
bazelModuleLabel = c.GetBazelLabel(actx, c) return c.GetBazelLabel(ctx, c)
}
func (c *Module) QueueBazelCall(ctx android.BaseModuleContext) {
c.bazelHandler.QueueBazelCall(ctx, c.getBazelModuleLabel(ctx))
}
func (c *Module) IsMixedBuildSupported(ctx android.BaseModuleContext) bool {
return c.bazelHandler != nil
}
func (c *Module) ProcessBazelQueryResponse(ctx android.ModuleContext) {
bazelModuleLabel := c.getBazelModuleLabel(ctx)
c.bazelHandler.ProcessBazelQueryResponse(ctx, bazelModuleLabel)
c.Properties.SubName = GetSubnameProperty(ctx, c)
apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
if !apexInfo.IsForPlatform() {
c.hideApexVariantFromMake = true
} }
bazelActionsUsed := false c.makeLinkType = GetMakeLinkType(ctx, c)
// Mixed builds mode is disabled for modules outside of device OS.
// TODO(b/200841190): Support non-device OS in mixed builds. mctx := &moduleContext{
if android.MixedBuildsEnabled(actx) && c.bazelHandler != nil { ModuleContext: ctx,
bazelActionsUsed = c.bazelHandler.GenerateBazelBuildActions(actx, bazelModuleLabel) moduleContextImpl: moduleContextImpl{
mod: c,
},
} }
return bazelActionsUsed mctx.ctx = mctx
c.maybeInstall(mctx, apexInfo)
} }
func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) { func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) {
// TODO(cparsons): Any logic in this method occurring prior to querying Bazel should be
// requested from Bazel instead.
// Handle the case of a test module split by `test_per_src` mutator. // Handle the case of a test module split by `test_per_src` mutator.
// //
// The `test_per_src` mutator adds an extra variation named "", depending on all the other // The `test_per_src` mutator adds an extra variation named "", depending on all the other
@@ -1824,11 +1857,6 @@ func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) {
} }
ctx.ctx = ctx ctx.ctx = ctx
if c.maybeGenerateBazelActions(actx) {
c.maybeInstall(ctx, apexInfo)
return
}
deps := c.depsToPaths(ctx) deps := c.depsToPaths(ctx)
if ctx.Failed() { if ctx.Failed() {
return return

View File

@@ -642,18 +642,18 @@ type libraryDecorator struct {
} }
type ccLibraryBazelHandler struct { type ccLibraryBazelHandler struct {
android.BazelHandler BazelHandler
module *Module module *Module
} }
// generateStaticBazelBuildActions constructs the StaticLibraryInfo Soong // generateStaticBazelBuildActions constructs the StaticLibraryInfo Soong
// provider from a Bazel shared library's CcInfo provider. // provider from a Bazel shared library's CcInfo provider.
func (handler *ccLibraryBazelHandler) generateStaticBazelBuildActions(ctx android.ModuleContext, label string, ccInfo cquery.CcInfo) bool { func (handler *ccLibraryBazelHandler) generateStaticBazelBuildActions(ctx android.ModuleContext, label string, ccInfo cquery.CcInfo) {
rootStaticArchives := ccInfo.RootStaticArchives rootStaticArchives := ccInfo.RootStaticArchives
if len(rootStaticArchives) != 1 { if len(rootStaticArchives) != 1 {
ctx.ModuleErrorf("expected exactly one root archive file for '%s', but got %s", label, rootStaticArchives) ctx.ModuleErrorf("expected exactly one root archive file for '%s', but got %s", label, rootStaticArchives)
return false return
} }
outputFilePath := android.PathForBazelOut(ctx, rootStaticArchives[0]) outputFilePath := android.PathForBazelOut(ctx, rootStaticArchives[0])
handler.module.outputFile = android.OptionalPathForPath(outputFilePath) handler.module.outputFile = android.OptionalPathForPath(outputFilePath)
@@ -679,17 +679,17 @@ func (handler *ccLibraryBazelHandler) generateStaticBazelBuildActions(ctx androi
Build(), Build(),
}) })
return true return
} }
// generateSharedBazelBuildActions constructs the SharedLibraryInfo Soong // generateSharedBazelBuildActions constructs the SharedLibraryInfo Soong
// provider from a Bazel shared library's CcInfo provider. // provider from a Bazel shared library's CcInfo provider.
func (handler *ccLibraryBazelHandler) generateSharedBazelBuildActions(ctx android.ModuleContext, label string, ccInfo cquery.CcInfo) bool { func (handler *ccLibraryBazelHandler) generateSharedBazelBuildActions(ctx android.ModuleContext, label string, ccInfo cquery.CcInfo) {
rootDynamicLibraries := ccInfo.RootDynamicLibraries rootDynamicLibraries := ccInfo.RootDynamicLibraries
if len(rootDynamicLibraries) != 1 { if len(rootDynamicLibraries) != 1 {
ctx.ModuleErrorf("expected exactly one root dynamic library file for '%s', but got %s", label, rootDynamicLibraries) ctx.ModuleErrorf("expected exactly one root dynamic library file for '%s', but got %s", label, rootDynamicLibraries)
return false return
} }
outputFilePath := android.PathForBazelOut(ctx, rootDynamicLibraries[0]) outputFilePath := android.PathForBazelOut(ctx, rootDynamicLibraries[0])
handler.module.outputFile = android.OptionalPathForPath(outputFilePath) handler.module.outputFile = android.OptionalPathForPath(outputFilePath)
@@ -709,30 +709,27 @@ func (handler *ccLibraryBazelHandler) generateSharedBazelBuildActions(ctx androi
// TODO(b/190524881): Include transitive static libraries in this provider to support // TODO(b/190524881): Include transitive static libraries in this provider to support
// static libraries with deps. The provider key for this is TransitiveStaticLibrariesForOrdering. // static libraries with deps. The provider key for this is TransitiveStaticLibrariesForOrdering.
}) })
return true
} }
func (handler *ccLibraryBazelHandler) GenerateBazelBuildActions(ctx android.ModuleContext, label string) bool { func (handler *ccLibraryBazelHandler) QueueBazelCall(ctx android.BaseModuleContext, label string) {
bazelCtx := ctx.Config().BazelContext bazelCtx := ctx.Config().BazelContext
ccInfo, ok, err := bazelCtx.GetCcInfo(label, android.GetConfigKey(ctx)) bazelCtx.QueueBazelRequest(label, cquery.GetCcInfo, android.GetConfigKey(ctx))
}
func (handler *ccLibraryBazelHandler) ProcessBazelQueryResponse(ctx android.ModuleContext, label string) {
bazelCtx := ctx.Config().BazelContext
ccInfo, err := bazelCtx.GetCcInfo(label, android.GetConfigKey(ctx))
if err != nil { if err != nil {
ctx.ModuleErrorf("Error getting Bazel CcInfo: %s", err) ctx.ModuleErrorf("Error getting Bazel CcInfo: %s", err)
return false return
}
if !ok {
return ok
} }
if handler.module.static() { if handler.module.static() {
if ok := handler.generateStaticBazelBuildActions(ctx, label, ccInfo); !ok { handler.generateStaticBazelBuildActions(ctx, label, ccInfo)
return false
}
} else if handler.module.Shared() { } else if handler.module.Shared() {
if ok := handler.generateSharedBazelBuildActions(ctx, label, ccInfo); !ok { handler.generateSharedBazelBuildActions(ctx, label, ccInfo)
return false
}
} else { } else {
return false ctx.ModuleErrorf("Unhandled bazel case for %s (neither shared nor static!)", ctx.ModuleName())
} }
handler.module.linker.(*libraryDecorator).setFlagExporterInfoFromCcInfo(ctx, ccInfo) handler.module.linker.(*libraryDecorator).setFlagExporterInfoFromCcInfo(ctx, ccInfo)
@@ -746,7 +743,6 @@ func (handler *ccLibraryBazelHandler) GenerateBazelBuildActions(ctx android.Modu
// implementation. // implementation.
i.(*libraryDecorator).collectedSnapshotHeaders = android.Paths{} i.(*libraryDecorator).collectedSnapshotHeaders = android.Paths{}
} }
return ok
} }
func (library *libraryDecorator) setFlagExporterInfoFromCcInfo(ctx android.ModuleContext, ccInfo cquery.CcInfo) { func (library *libraryDecorator) setFlagExporterInfoFromCcInfo(ctx android.ModuleContext, ccInfo cquery.CcInfo) {

View File

@@ -17,6 +17,7 @@ package cc
import ( import (
"android/soong/android" "android/soong/android"
"android/soong/bazel" "android/soong/bazel"
"android/soong/bazel/cquery"
) )
func init() { func init() {
@@ -47,28 +48,30 @@ func RegisterLibraryHeadersBuildComponents(ctx android.RegistrationContext) {
ctx.RegisterModuleType("cc_prebuilt_library_headers", prebuiltLibraryHeaderFactory) ctx.RegisterModuleType("cc_prebuilt_library_headers", prebuiltLibraryHeaderFactory)
} }
type libraryHeaderBazelHander struct { type libraryHeaderBazelHandler struct {
android.BazelHandler BazelHandler
module *Module module *Module
library *libraryDecorator library *libraryDecorator
} }
func (h *libraryHeaderBazelHander) GenerateBazelBuildActions(ctx android.ModuleContext, label string) bool { func (handler *libraryHeaderBazelHandler) QueueBazelCall(ctx android.BaseModuleContext, label string) {
bazelCtx := ctx.Config().BazelContext bazelCtx := ctx.Config().BazelContext
ccInfo, ok, err := bazelCtx.GetCcInfo(label, android.GetConfigKey(ctx)) bazelCtx.QueueBazelRequest(label, cquery.GetCcInfo, android.GetConfigKey(ctx))
}
func (h *libraryHeaderBazelHandler) ProcessBazelQueryResponse(ctx android.ModuleContext, label string) {
bazelCtx := ctx.Config().BazelContext
ccInfo, err := bazelCtx.GetCcInfo(label, android.GetConfigKey(ctx))
if err != nil { if err != nil {
ctx.ModuleErrorf("Error getting Bazel CcInfo: %s", err) ctx.ModuleErrorf(err.Error())
return false return
}
if !ok {
return false
} }
outputPaths := ccInfo.OutputFiles outputPaths := ccInfo.OutputFiles
if len(outputPaths) != 1 { if len(outputPaths) != 1 {
ctx.ModuleErrorf("expected exactly one output file for %q, but got %q", label, outputPaths) ctx.ModuleErrorf("expected exactly one output file for %q, but got %q", label, outputPaths)
return false return
} }
outputPath := android.PathForBazelOut(ctx, outputPaths[0]) outputPath := android.PathForBazelOut(ctx, outputPaths[0])
@@ -83,8 +86,6 @@ func (h *libraryHeaderBazelHander) GenerateBazelBuildActions(ctx android.ModuleC
// validation will fail. For now, set this to an empty list. // validation will fail. For now, set this to an empty list.
// TODO(cparsons): More closely mirror the collectHeadersForSnapshot implementation. // TODO(cparsons): More closely mirror the collectHeadersForSnapshot implementation.
h.library.collectedSnapshotHeaders = android.Paths{} h.library.collectedSnapshotHeaders = android.Paths{}
return true
} }
// cc_library_headers contains a set of c/c++ headers which are imported by // cc_library_headers contains a set of c/c++ headers which are imported by
@@ -96,7 +97,7 @@ func LibraryHeaderFactory() android.Module {
library.HeaderOnly() library.HeaderOnly()
module.sdkMemberTypes = []android.SdkMemberType{headersLibrarySdkMemberType} module.sdkMemberTypes = []android.SdkMemberType{headersLibrarySdkMemberType}
module.bazelable = true module.bazelable = true
module.bazelHandler = &libraryHeaderBazelHander{module: module, library: library} module.bazelHandler = &libraryHeaderBazelHandler{module: module, library: library}
return module.Init() return module.Init()
} }

View File

@@ -19,6 +19,7 @@ import (
"android/soong/android" "android/soong/android"
"android/soong/bazel" "android/soong/bazel"
"android/soong/bazel/cquery"
) )
// //
@@ -46,23 +47,30 @@ type objectLinker struct {
} }
type objectBazelHandler struct { type objectBazelHandler struct {
android.BazelHandler BazelHandler
module *Module module *Module
} }
func (handler *objectBazelHandler) GenerateBazelBuildActions(ctx android.ModuleContext, label string) bool { func (handler *objectBazelHandler) QueueBazelCall(ctx android.BaseModuleContext, label string) {
bazelCtx := ctx.Config().BazelContext bazelCtx := ctx.Config().BazelContext
objPaths, ok := bazelCtx.GetOutputFiles(label, android.GetConfigKey(ctx)) bazelCtx.QueueBazelRequest(label, cquery.GetOutputFiles, android.GetConfigKey(ctx))
if ok { }
if len(objPaths) != 1 {
ctx.ModuleErrorf("expected exactly one object file for '%s', but got %s", label, objPaths)
return false
}
handler.module.outputFile = android.OptionalPathForPath(android.PathForBazelOut(ctx, objPaths[0])) func (handler *objectBazelHandler) ProcessBazelQueryResponse(ctx android.ModuleContext, label string) {
bazelCtx := ctx.Config().BazelContext
objPaths, err := bazelCtx.GetOutputFiles(label, android.GetConfigKey(ctx))
if err != nil {
ctx.ModuleErrorf(err.Error())
return
} }
return ok
if len(objPaths) != 1 {
ctx.ModuleErrorf("expected exactly one object file for '%s', but got %s", label, objPaths)
return
}
handler.module.outputFile = android.OptionalPathForPath(android.PathForBazelOut(ctx, objPaths[0]))
} }
type ObjectLinkerProperties struct { type ObjectLinkerProperties struct {

View File

@@ -20,6 +20,7 @@ import (
"android/soong/android" "android/soong/android"
"android/soong/bazel" "android/soong/bazel"
"android/soong/bazel/cquery"
) )
func init() { func init() {
@@ -406,25 +407,28 @@ type prebuiltObjectLinker struct {
} }
type prebuiltStaticLibraryBazelHandler struct { type prebuiltStaticLibraryBazelHandler struct {
android.BazelHandler BazelHandler
module *Module module *Module
library *libraryDecorator library *libraryDecorator
} }
func (h *prebuiltStaticLibraryBazelHandler) GenerateBazelBuildActions(ctx android.ModuleContext, label string) bool { func (h *prebuiltStaticLibraryBazelHandler) QueueBazelCall(ctx android.BaseModuleContext, label string) {
bazelCtx := ctx.Config().BazelContext bazelCtx := ctx.Config().BazelContext
ccInfo, ok, err := bazelCtx.GetCcInfo(label, android.GetConfigKey(ctx)) bazelCtx.QueueBazelRequest(label, cquery.GetCcInfo, android.GetConfigKey(ctx))
}
func (h *prebuiltStaticLibraryBazelHandler) ProcessBazelQueryResponse(ctx android.ModuleContext, label string) {
bazelCtx := ctx.Config().BazelContext
ccInfo, err := bazelCtx.GetCcInfo(label, android.GetConfigKey(ctx))
if err != nil { if err != nil {
ctx.ModuleErrorf("Error getting Bazel CcInfo: %s", err) ctx.ModuleErrorf(err.Error())
} return
if !ok {
return false
} }
staticLibs := ccInfo.CcStaticLibraryFiles staticLibs := ccInfo.CcStaticLibraryFiles
if len(staticLibs) > 1 { if len(staticLibs) > 1 {
ctx.ModuleErrorf("expected 1 static library from bazel target %q, got %s", label, staticLibs) ctx.ModuleErrorf("expected 1 static library from bazel target %q, got %s", label, staticLibs)
return false return
} }
// TODO(b/184543518): cc_prebuilt_library_static may have properties for re-exporting flags // TODO(b/184543518): cc_prebuilt_library_static may have properties for re-exporting flags
@@ -439,7 +443,7 @@ func (h *prebuiltStaticLibraryBazelHandler) GenerateBazelBuildActions(ctx androi
if len(staticLibs) == 0 { if len(staticLibs) == 0 {
h.module.outputFile = android.OptionalPath{} h.module.outputFile = android.OptionalPath{}
return true return
} }
out := android.PathForBazelOut(ctx, staticLibs[0]) out := android.PathForBazelOut(ctx, staticLibs[0])
@@ -451,30 +455,31 @@ func (h *prebuiltStaticLibraryBazelHandler) GenerateBazelBuildActions(ctx androi
TransitiveStaticLibrariesForOrdering: depSet, TransitiveStaticLibrariesForOrdering: depSet,
}) })
return true
} }
type prebuiltSharedLibraryBazelHandler struct { type prebuiltSharedLibraryBazelHandler struct {
android.BazelHandler BazelHandler
module *Module module *Module
library *libraryDecorator library *libraryDecorator
} }
func (h *prebuiltSharedLibraryBazelHandler) GenerateBazelBuildActions(ctx android.ModuleContext, label string) bool { func (h *prebuiltSharedLibraryBazelHandler) QueueBazelCall(ctx android.BaseModuleContext, label string) {
bazelCtx := ctx.Config().BazelContext bazelCtx := ctx.Config().BazelContext
ccInfo, ok, err := bazelCtx.GetCcInfo(label, android.GetConfigKey(ctx)) bazelCtx.QueueBazelRequest(label, cquery.GetCcInfo, android.GetConfigKey(ctx))
}
func (h *prebuiltSharedLibraryBazelHandler) ProcessBazelQueryResponse(ctx android.ModuleContext, label string) {
bazelCtx := ctx.Config().BazelContext
ccInfo, err := bazelCtx.GetCcInfo(label, android.GetConfigKey(ctx))
if err != nil { if err != nil {
ctx.ModuleErrorf("Error getting Bazel CcInfo for %s: %s", label, err) ctx.ModuleErrorf(err.Error())
} return
if !ok {
return false
} }
sharedLibs := ccInfo.CcSharedLibraryFiles sharedLibs := ccInfo.CcSharedLibraryFiles
if len(sharedLibs) != 1 { if len(sharedLibs) != 1 {
ctx.ModuleErrorf("expected 1 shared library from bazel target %s, got %q", label, sharedLibs) ctx.ModuleErrorf("expected 1 shared library from bazel target %s, got %q", label, sharedLibs)
return false return
} }
// TODO(b/184543518): cc_prebuilt_library_shared may have properties for re-exporting flags // TODO(b/184543518): cc_prebuilt_library_shared may have properties for re-exporting flags
@@ -489,7 +494,7 @@ func (h *prebuiltSharedLibraryBazelHandler) GenerateBazelBuildActions(ctx androi
if len(sharedLibs) == 0 { if len(sharedLibs) == 0 {
h.module.outputFile = android.OptionalPath{} h.module.outputFile = android.OptionalPath{}
return true return
} }
out := android.PathForBazelOut(ctx, sharedLibs[0]) out := android.PathForBazelOut(ctx, sharedLibs[0])
@@ -514,8 +519,6 @@ func (h *prebuiltSharedLibraryBazelHandler) GenerateBazelBuildActions(ctx androi
h.library.setFlagExporterInfoFromCcInfo(ctx, ccInfo) h.library.setFlagExporterInfoFromCcInfo(ctx, ccInfo)
h.module.maybeUnhideFromMake() h.module.maybeUnhideFromMake()
return true
} }
func (p *prebuiltObjectLinker) prebuilt() *android.Prebuilt { func (p *prebuiltObjectLinker) prebuilt() *android.Prebuilt {

View File

@@ -129,44 +129,26 @@ func newConfig(availableEnv map[string]string) android.Config {
return configuration return configuration
} }
// Bazel-enabled mode. Soong runs in two passes. // Bazel-enabled mode. Attaches a mutator to queue Bazel requests, adds a
// First pass: Analyze the build tree, but only store all bazel commands // BeforePrepareBuildActionsHook to invoke Bazel, and then uses Bazel metadata
// needed to correctly evaluate the tree in the second pass. // for modules that should be handled by Bazel.
// TODO(cparsons): Don't output any ninja file, as the second pass will overwrite func runMixedModeBuild(configuration android.Config, ctx *android.Context, extraNinjaDeps []string) {
// the incorrect results from the first pass, and file I/O is expensive. ctx.EventHandler.Begin("mixed_build")
func runMixedModeBuild(configuration android.Config, firstCtx *android.Context, extraNinjaDeps []string) { defer ctx.EventHandler.End("mixed_build")
firstCtx.EventHandler.Begin("mixed_build")
defer firstCtx.EventHandler.End("mixed_build")
firstCtx.EventHandler.Begin("prepare") bazelHook := func() error {
bootstrap.RunBlueprint(cmdlineArgs, bootstrap.StopBeforeWriteNinja, firstCtx.Context, configuration) ctx.EventHandler.Begin("bazel")
firstCtx.EventHandler.End("prepare") defer ctx.EventHandler.End("bazel")
return configuration.BazelContext.InvokeBazel()
firstCtx.EventHandler.Begin("bazel")
// Invoke bazel commands and save results for second pass.
if err := configuration.BazelContext.InvokeBazel(); err != nil {
fmt.Fprintf(os.Stderr, "%s", err)
os.Exit(1)
} }
// Second pass: Full analysis, using the bazel command results. Output ninja file. ctx.SetBeforePrepareBuildActionsHook(bazelHook)
secondConfig, err := android.ConfigForAdditionalRun(configuration)
if err != nil {
fmt.Fprintf(os.Stderr, "%s", err)
os.Exit(1)
}
firstCtx.EventHandler.End("bazel")
secondCtx := newContext(secondConfig) ninjaDeps := bootstrap.RunBlueprint(cmdlineArgs, bootstrap.DoEverything, ctx.Context, configuration)
secondCtx.EventHandler = firstCtx.EventHandler
secondCtx.EventHandler.Begin("analyze")
ninjaDeps := bootstrap.RunBlueprint(cmdlineArgs, bootstrap.DoEverything, secondCtx.Context, secondConfig)
ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
secondCtx.EventHandler.End("analyze")
globListFiles := writeBuildGlobsNinjaFile(secondCtx, configuration.SoongOutDir(), configuration) globListFiles := writeBuildGlobsNinjaFile(ctx, configuration.SoongOutDir(), configuration)
ninjaDeps = append(ninjaDeps, globListFiles...) ninjaDeps = append(ninjaDeps, globListFiles...)
writeDepFile(cmdlineArgs.OutFile, *secondCtx.EventHandler, ninjaDeps) writeDepFile(cmdlineArgs.OutFile, *ctx.EventHandler, ninjaDeps)
} }
// Run the code-generation phase to convert BazelTargetModules to BUILD files. // Run the code-generation phase to convert BazelTargetModules to BUILD files.

View File

@@ -25,6 +25,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"android/soong/bazel/cquery"
"github.com/google/blueprint" "github.com/google/blueprint"
"github.com/google/blueprint/bootstrap" "github.com/google/blueprint/bootstrap"
"github.com/google/blueprint/proptools" "github.com/google/blueprint/proptools"
@@ -189,6 +190,8 @@ type Module struct {
modulePaths []string modulePaths []string
} }
var _ android.MixedBuildBuildable = (*Module)(nil)
type taskFunc func(ctx android.ModuleContext, rawCommand string, srcFiles android.Paths) []generateTask type taskFunc func(ctx android.ModuleContext, rawCommand string, srcFiles android.Paths) []generateTask
type generateTask struct { type generateTask struct {
@@ -249,27 +252,36 @@ func toolDepsMutator(ctx android.BottomUpMutatorContext) {
} }
} }
// Returns true if information was available from Bazel, false if bazel invocation still needs to occur. func (g *Module) ProcessBazelQueryResponse(ctx android.ModuleContext) {
func (c *Module) GenerateBazelBuildActions(ctx android.ModuleContext, label string) bool { g.generateCommonBuildActions(ctx)
label := g.GetBazelLabel(ctx, g)
bazelCtx := ctx.Config().BazelContext bazelCtx := ctx.Config().BazelContext
filePaths, ok := bazelCtx.GetOutputFiles(label, android.GetConfigKey(ctx)) filePaths, err := bazelCtx.GetOutputFiles(label, android.GetConfigKey(ctx))
if ok { if err != nil {
var bazelOutputFiles android.Paths ctx.ModuleErrorf(err.Error())
exportIncludeDirs := map[string]bool{} return
for _, bazelOutputFile := range filePaths { }
bazelOutputFiles = append(bazelOutputFiles, android.PathForBazelOut(ctx, bazelOutputFile))
exportIncludeDirs[filepath.Dir(bazelOutputFile)] = true var bazelOutputFiles android.Paths
} exportIncludeDirs := map[string]bool{}
c.outputFiles = bazelOutputFiles for _, bazelOutputFile := range filePaths {
c.outputDeps = bazelOutputFiles bazelOutputFiles = append(bazelOutputFiles, android.PathForBazelOut(ctx, bazelOutputFile))
for includePath, _ := range exportIncludeDirs { exportIncludeDirs[filepath.Dir(bazelOutputFile)] = true
c.exportedIncludeDirs = append(c.exportedIncludeDirs, android.PathForBazelOut(ctx, includePath)) }
} g.outputFiles = bazelOutputFiles
g.outputDeps = bazelOutputFiles
for includePath, _ := range exportIncludeDirs {
g.exportedIncludeDirs = append(g.exportedIncludeDirs, android.PathForBazelOut(ctx, includePath))
} }
return ok
} }
func (g *Module) GenerateAndroidBuildActions(ctx android.ModuleContext) { // generateCommonBuildActions contains build action generation logic
// common to both the mixed build case and the legacy case of genrule processing.
// To fully support genrule in mixed builds, the contents of this function should
// approach zero; there should be no genrule action registration done directly
// by Soong logic in the mixed-build case.
func (g *Module) generateCommonBuildActions(ctx android.ModuleContext) {
g.subName = ctx.ModuleSubDir() g.subName = ctx.ModuleSubDir()
// Collect the module directory for IDE info in java/jdeps.go. // Collect the module directory for IDE info in java/jdeps.go.
@@ -575,31 +587,37 @@ func (g *Module) GenerateAndroidBuildActions(ctx android.ModuleContext) {
} }
g.outputFiles = outputFiles.Paths() g.outputFiles = outputFiles.Paths()
}
bazelModuleLabel := g.GetBazelLabel(ctx, g) func (g *Module) GenerateAndroidBuildActions(ctx android.ModuleContext) {
bazelActionsUsed := false g.generateCommonBuildActions(ctx)
if android.MixedBuildsEnabled(ctx) {
bazelActionsUsed = g.GenerateBazelBuildActions(ctx, bazelModuleLabel) // For <= 6 outputs, just embed those directly in the users. Right now, that covers >90% of
} // the genrules on AOSP. That will make things simpler to look at the graph in the common
if !bazelActionsUsed { // case. For larger sets of outputs, inject a phony target in between to limit ninja file
// For <= 6 outputs, just embed those directly in the users. Right now, that covers >90% of // growth.
// the genrules on AOSP. That will make things simpler to look at the graph in the common if len(g.outputFiles) <= 6 {
// case. For larger sets of outputs, inject a phony target in between to limit ninja file g.outputDeps = g.outputFiles
// growth. } else {
if len(g.outputFiles) <= 6 { phonyFile := android.PathForModuleGen(ctx, "genrule-phony")
g.outputDeps = g.outputFiles ctx.Build(pctx, android.BuildParams{
} else { Rule: blueprint.Phony,
phonyFile := android.PathForModuleGen(ctx, "genrule-phony") Output: phonyFile,
ctx.Build(pctx, android.BuildParams{ Inputs: g.outputFiles,
Rule: blueprint.Phony, })
Output: phonyFile, g.outputDeps = android.Paths{phonyFile}
Inputs: g.outputFiles,
})
g.outputDeps = android.Paths{phonyFile}
}
} }
} }
func (g *Module) QueueBazelCall(ctx android.BaseModuleContext) {
bazelCtx := ctx.Config().BazelContext
bazelCtx.QueueBazelRequest(g.GetBazelLabel(ctx, g), cquery.GetOutputFiles, android.GetConfigKey(ctx))
}
func (g *Module) IsMixedBuildSupported(ctx android.BaseModuleContext) bool {
return true
}
// Collect information for opening IDE project files in java/jdeps.go. // Collect information for opening IDE project files in java/jdeps.go.
func (g *Module) IDEInfo(dpInfo *android.IdeInfo) { func (g *Module) IDEInfo(dpInfo *android.IdeInfo) {
dpInfo.Srcs = append(dpInfo.Srcs, g.Srcs().Strings()...) dpInfo.Srcs = append(dpInfo.Srcs, g.Srcs().Strings()...)