Merge "Collect more metrics for aquery handling"
This commit is contained in:
@@ -32,6 +32,7 @@ import (
|
|||||||
"android/soong/starlark_fmt"
|
"android/soong/starlark_fmt"
|
||||||
|
|
||||||
"github.com/google/blueprint"
|
"github.com/google/blueprint"
|
||||||
|
"github.com/google/blueprint/metrics"
|
||||||
|
|
||||||
"android/soong/bazel"
|
"android/soong/bazel"
|
||||||
)
|
)
|
||||||
@@ -135,6 +136,10 @@ func (c cqueryKey) String() string {
|
|||||||
return fmt.Sprintf("cquery(%s,%s,%s)", c.label, c.requestType.Name(), c.configKey)
|
return fmt.Sprintf("cquery(%s,%s,%s)", c.label, c.requestType.Name(), c.configKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type invokeBazelContext interface {
|
||||||
|
GetEventHandler() *metrics.EventHandler
|
||||||
|
}
|
||||||
|
|
||||||
// BazelContext is a context object useful for interacting with Bazel during
|
// BazelContext is a context object useful for interacting with Bazel during
|
||||||
// the course of a build. Use of Bazel to evaluate part of the build graph
|
// the course of a build. Use of Bazel to evaluate part of the build graph
|
||||||
// is referred to as a "mixed build". (Some modules are managed by Soong,
|
// is referred to as a "mixed build". (Some modules are managed by Soong,
|
||||||
@@ -171,7 +176,7 @@ type BazelContext interface {
|
|||||||
// 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. The ctx argument is optional and is only
|
// queued in the BazelContext. The ctx argument is optional and is only
|
||||||
// used for performance data collection
|
// used for performance data collection
|
||||||
InvokeBazel(config Config, ctx *Context) error
|
InvokeBazel(config Config, ctx invokeBazelContext) error
|
||||||
|
|
||||||
// Returns true if Bazel handling is enabled for the module with the given name.
|
// Returns true if Bazel handling is enabled for the module with the given name.
|
||||||
// Note that this only implies "bazel mixed build" allowlisting. The caller
|
// Note that this only implies "bazel mixed build" allowlisting. The caller
|
||||||
@@ -191,7 +196,7 @@ type BazelContext interface {
|
|||||||
|
|
||||||
type bazelRunner interface {
|
type bazelRunner interface {
|
||||||
createBazelCommand(config Config, paths *bazelPaths, runName bazel.RunName, command bazelCommand, extraFlags ...string) *exec.Cmd
|
createBazelCommand(config Config, paths *bazelPaths, runName bazel.RunName, command bazelCommand, extraFlags ...string) *exec.Cmd
|
||||||
issueBazelCommand(bazelCmd *exec.Cmd) (output string, errorMessage string, error error)
|
issueBazelCommand(bazelCmd *exec.Cmd, eventHandler *metrics.EventHandler) (output string, errorMessage string, error error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type bazelPaths struct {
|
type bazelPaths struct {
|
||||||
@@ -299,7 +304,7 @@ func (m MockBazelContext) GetCcUnstrippedInfo(label string, _ configKey) (cquery
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m MockBazelContext) InvokeBazel(_ Config, _ *Context) error {
|
func (m MockBazelContext) InvokeBazel(_ Config, _ invokeBazelContext) error {
|
||||||
panic("unimplemented")
|
panic("unimplemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -417,7 +422,7 @@ func (n noopBazelContext) GetCcUnstrippedInfo(_ string, _ configKey) (cquery.CcU
|
|||||||
panic("implement me")
|
panic("implement me")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n noopBazelContext) InvokeBazel(_ Config, _ *Context) error {
|
func (n noopBazelContext) InvokeBazel(_ Config, _ invokeBazelContext) error {
|
||||||
panic("unimplemented")
|
panic("unimplemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -595,7 +600,7 @@ func (r *mockBazelRunner) createBazelCommand(_ Config, _ *bazelPaths, _ bazel.Ru
|
|||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *mockBazelRunner) issueBazelCommand(bazelCmd *exec.Cmd) (string, string, error) {
|
func (r *mockBazelRunner) issueBazelCommand(bazelCmd *exec.Cmd, _ *metrics.EventHandler) (string, string, error) {
|
||||||
if command, ok := r.tokens[bazelCmd]; ok {
|
if command, ok := r.tokens[bazelCmd]; ok {
|
||||||
return r.bazelCommandResults[command], "", nil
|
return r.bazelCommandResults[command], "", nil
|
||||||
}
|
}
|
||||||
@@ -608,7 +613,9 @@ type builtinBazelRunner struct{}
|
|||||||
// Returns (stdout, stderr, error). The first and second return values are strings
|
// Returns (stdout, stderr, error). The first and second return values are strings
|
||||||
// containing the stdout and stderr of the run command, and an error is returned if
|
// containing the stdout and stderr of the run command, and an error is returned if
|
||||||
// the invocation returned an error code.
|
// the invocation returned an error code.
|
||||||
func (r *builtinBazelRunner) issueBazelCommand(bazelCmd *exec.Cmd) (string, string, error) {
|
func (r *builtinBazelRunner) issueBazelCommand(bazelCmd *exec.Cmd, eventHandler *metrics.EventHandler) (string, string, error) {
|
||||||
|
eventHandler.Begin("bazel command")
|
||||||
|
defer eventHandler.End("bazel command")
|
||||||
stderr := &bytes.Buffer{}
|
stderr := &bytes.Buffer{}
|
||||||
bazelCmd.Stderr = stderr
|
bazelCmd.Stderr = stderr
|
||||||
if output, err := bazelCmd.Output(); err != nil {
|
if output, err := bazelCmd.Output(); err != nil {
|
||||||
@@ -985,11 +992,10 @@ var (
|
|||||||
|
|
||||||
// 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.
|
||||||
func (context *mixedBuildBazelContext) InvokeBazel(config Config, ctx *Context) error {
|
func (context *mixedBuildBazelContext) InvokeBazel(config Config, ctx invokeBazelContext) error {
|
||||||
if ctx != nil {
|
eventHandler := ctx.GetEventHandler()
|
||||||
ctx.EventHandler.Begin("bazel")
|
eventHandler.Begin("bazel")
|
||||||
defer ctx.EventHandler.End("bazel")
|
defer eventHandler.End("bazel")
|
||||||
}
|
|
||||||
|
|
||||||
if metricsDir := context.paths.BazelMetricsDir(); metricsDir != "" {
|
if metricsDir := context.paths.BazelMetricsDir(); metricsDir != "" {
|
||||||
if err := os.MkdirAll(metricsDir, 0777); err != nil {
|
if err := os.MkdirAll(metricsDir, 0777); err != nil {
|
||||||
@@ -1012,11 +1018,10 @@ func (context *mixedBuildBazelContext) InvokeBazel(config Config, ctx *Context)
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (context *mixedBuildBazelContext) runCquery(config Config, ctx *Context) error {
|
func (context *mixedBuildBazelContext) runCquery(config Config, ctx invokeBazelContext) error {
|
||||||
if ctx != nil {
|
eventHandler := ctx.GetEventHandler()
|
||||||
ctx.EventHandler.Begin("cquery")
|
eventHandler.Begin("cquery")
|
||||||
defer ctx.EventHandler.End("cquery")
|
defer eventHandler.End("cquery")
|
||||||
}
|
|
||||||
soongInjectionPath := absolutePath(context.paths.injectedFilesDir())
|
soongInjectionPath := absolutePath(context.paths.injectedFilesDir())
|
||||||
mixedBuildsPath := filepath.Join(soongInjectionPath, "mixed_builds")
|
mixedBuildsPath := filepath.Join(soongInjectionPath, "mixed_builds")
|
||||||
if _, err := os.Stat(mixedBuildsPath); os.IsNotExist(err) {
|
if _, err := os.Stat(mixedBuildsPath); os.IsNotExist(err) {
|
||||||
@@ -1041,7 +1046,7 @@ func (context *mixedBuildBazelContext) runCquery(config Config, ctx *Context) er
|
|||||||
|
|
||||||
cqueryCommandWithFlag := context.createBazelCommand(config, context.paths, bazel.CqueryBuildRootRunName, cqueryCmd,
|
cqueryCommandWithFlag := context.createBazelCommand(config, context.paths, bazel.CqueryBuildRootRunName, cqueryCmd,
|
||||||
"--output=starlark", "--starlark:file="+absolutePath(cqueryFileRelpath))
|
"--output=starlark", "--starlark:file="+absolutePath(cqueryFileRelpath))
|
||||||
cqueryOutput, cqueryErrorMessage, cqueryErr := context.issueBazelCommand(cqueryCommandWithFlag)
|
cqueryOutput, cqueryErrorMessage, cqueryErr := context.issueBazelCommand(cqueryCommandWithFlag, eventHandler)
|
||||||
if cqueryErr != nil {
|
if cqueryErr != nil {
|
||||||
return cqueryErr
|
return cqueryErr
|
||||||
}
|
}
|
||||||
@@ -1075,11 +1080,10 @@ func writeFileBytesIfChanged(path string, contents []byte, perm os.FileMode) err
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (context *mixedBuildBazelContext) runAquery(config Config, ctx *Context) error {
|
func (context *mixedBuildBazelContext) runAquery(config Config, ctx invokeBazelContext) error {
|
||||||
if ctx != nil {
|
eventHandler := ctx.GetEventHandler()
|
||||||
ctx.EventHandler.Begin("aquery")
|
eventHandler.Begin("aquery")
|
||||||
defer ctx.EventHandler.End("aquery")
|
defer eventHandler.End("aquery")
|
||||||
}
|
|
||||||
// Issue an aquery command to retrieve action information about the bazel build tree.
|
// Issue an aquery command to retrieve action information about the bazel build tree.
|
||||||
//
|
//
|
||||||
// Use jsonproto instead of proto; actual proto parsing would require a dependency on Bazel's
|
// Use jsonproto instead of proto; actual proto parsing would require a dependency on Bazel's
|
||||||
@@ -1105,23 +1109,22 @@ func (context *mixedBuildBazelContext) runAquery(config Config, ctx *Context) er
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
aqueryOutput, _, err := context.issueBazelCommand(context.createBazelCommand(config, context.paths, bazel.AqueryBuildRootRunName, aqueryCmd,
|
aqueryOutput, _, err := context.issueBazelCommand(context.createBazelCommand(config, context.paths, bazel.AqueryBuildRootRunName, aqueryCmd,
|
||||||
extraFlags...))
|
extraFlags...), eventHandler)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
context.buildStatements, context.depsets, err = bazel.AqueryBuildStatements([]byte(aqueryOutput))
|
context.buildStatements, context.depsets, err = bazel.AqueryBuildStatements([]byte(aqueryOutput), eventHandler)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (context *mixedBuildBazelContext) generateBazelSymlinks(config Config, ctx *Context) error {
|
func (context *mixedBuildBazelContext) generateBazelSymlinks(config Config, ctx invokeBazelContext) error {
|
||||||
if ctx != nil {
|
eventHandler := ctx.GetEventHandler()
|
||||||
ctx.EventHandler.Begin("symlinks")
|
eventHandler.Begin("symlinks")
|
||||||
defer ctx.EventHandler.End("symlinks")
|
defer eventHandler.End("symlinks")
|
||||||
}
|
|
||||||
// Issue a build command of the phony root to generate symlink forests for dependencies of the
|
// Issue a build command of the phony root to generate symlink forests for dependencies of the
|
||||||
// Bazel build. This is necessary because aquery invocations do not generate this symlink forest,
|
// Bazel build. This is necessary because aquery invocations do not generate this symlink forest,
|
||||||
// but some of symlinks may be required to resolve source dependencies of the build.
|
// but some of symlinks may be required to resolve source dependencies of the build.
|
||||||
_, _, err := context.issueBazelCommand(context.createBazelCommand(config, context.paths, bazel.BazelBuildPhonyRootRunName, buildCmd))
|
_, _, err := context.issueBazelCommand(context.createBazelCommand(config, context.paths, bazel.BazelBuildPhonyRootRunName, buildCmd), eventHandler)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -11,11 +11,18 @@ import (
|
|||||||
"android/soong/bazel/cquery"
|
"android/soong/bazel/cquery"
|
||||||
analysis_v2_proto "prebuilts/bazel/common/proto/analysis_v2"
|
analysis_v2_proto "prebuilts/bazel/common/proto/analysis_v2"
|
||||||
|
|
||||||
|
"github.com/google/blueprint/metrics"
|
||||||
"google.golang.org/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
var testConfig = TestConfig("out", nil, "", nil)
|
var testConfig = TestConfig("out", nil, "", nil)
|
||||||
|
|
||||||
|
type testInvokeBazelContext struct{}
|
||||||
|
|
||||||
|
func (t *testInvokeBazelContext) GetEventHandler() *metrics.EventHandler {
|
||||||
|
return &metrics.EventHandler{}
|
||||||
|
}
|
||||||
|
|
||||||
func TestRequestResultsAfterInvokeBazel(t *testing.T) {
|
func TestRequestResultsAfterInvokeBazel(t *testing.T) {
|
||||||
label := "@//foo:bar"
|
label := "@//foo:bar"
|
||||||
cfg := configKey{"arm64_armv8-a", Android}
|
cfg := configKey{"arm64_armv8-a", Android}
|
||||||
@@ -23,7 +30,7 @@ func TestRequestResultsAfterInvokeBazel(t *testing.T) {
|
|||||||
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`,
|
||||||
})
|
})
|
||||||
bazelContext.QueueBazelRequest(label, cquery.GetOutputFiles, cfg)
|
bazelContext.QueueBazelRequest(label, cquery.GetOutputFiles, cfg)
|
||||||
err := bazelContext.InvokeBazel(testConfig, nil)
|
err := bazelContext.InvokeBazel(testConfig, &testInvokeBazelContext{})
|
||||||
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)
|
||||||
}
|
}
|
||||||
@@ -37,7 +44,7 @@ func TestRequestResultsAfterInvokeBazel(t *testing.T) {
|
|||||||
|
|
||||||
func TestInvokeBazelWritesBazelFiles(t *testing.T) {
|
func TestInvokeBazelWritesBazelFiles(t *testing.T) {
|
||||||
bazelContext, baseDir := testBazelContext(t, map[bazelCommand]string{})
|
bazelContext, baseDir := testBazelContext(t, map[bazelCommand]string{})
|
||||||
err := bazelContext.InvokeBazel(testConfig, nil)
|
err := bazelContext.InvokeBazel(testConfig, &testInvokeBazelContext{})
|
||||||
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)
|
||||||
}
|
}
|
||||||
@@ -118,7 +125,7 @@ func TestInvokeBazelPopulatesBuildStatements(t *testing.T) {
|
|||||||
bazelContext, _ := testBazelContext(t, map[bazelCommand]string{
|
bazelContext, _ := testBazelContext(t, map[bazelCommand]string{
|
||||||
bazelCommand{command: "aquery", expression: "deps(@soong_injection//mixed_builds:buildroot)"}: string(data)})
|
bazelCommand{command: "aquery", expression: "deps(@soong_injection//mixed_builds:buildroot)"}: string(data)})
|
||||||
|
|
||||||
err = bazelContext.InvokeBazel(testConfig, nil)
|
err = bazelContext.InvokeBazel(testConfig, &testInvokeBazelContext{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("testCase #%d: did not expect error invoking Bazel, but got %s", i+1, err)
|
t.Fatalf("testCase #%d: did not expect error invoking Bazel, but got %s", i+1, err)
|
||||||
}
|
}
|
||||||
@@ -197,7 +204,7 @@ func TestBazelRequestsSorted(t *testing.T) {
|
|||||||
func verifyExtraFlags(t *testing.T, config Config, expected string) string {
|
func verifyExtraFlags(t *testing.T, config Config, expected string) string {
|
||||||
bazelContext, _ := testBazelContext(t, map[bazelCommand]string{})
|
bazelContext, _ := testBazelContext(t, map[bazelCommand]string{})
|
||||||
|
|
||||||
err := bazelContext.InvokeBazel(config, nil)
|
err := bazelContext.InvokeBazel(config, &testInvokeBazelContext{})
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
134
bazel/aquery.go
134
bazel/aquery.go
@@ -23,9 +23,11 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
analysis_v2_proto "prebuilts/bazel/common/proto/analysis_v2"
|
||||||
|
|
||||||
|
"github.com/google/blueprint/metrics"
|
||||||
"github.com/google/blueprint/proptools"
|
"github.com/google/blueprint/proptools"
|
||||||
"google.golang.org/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
analysis_v2_proto "prebuilts/bazel/common/proto/analysis_v2"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type artifactId int
|
type artifactId int
|
||||||
@@ -313,7 +315,7 @@ func (a *aqueryArtifactHandler) artifactPathsFromDepsetHash(depsetHash string) (
|
|||||||
// action graph, as described by the given action graph json proto.
|
// action graph, as described by the given action graph json proto.
|
||||||
// BuildStatements are one-to-one with actions in the given action graph, and AqueryDepsets
|
// BuildStatements are one-to-one with actions in the given action graph, and AqueryDepsets
|
||||||
// are one-to-one with Bazel's depSetOfFiles objects.
|
// are one-to-one with Bazel's depSetOfFiles objects.
|
||||||
func AqueryBuildStatements(aqueryJsonProto []byte) ([]BuildStatement, []AqueryDepset, error) {
|
func AqueryBuildStatements(aqueryJsonProto []byte, eventHandler *metrics.EventHandler) ([]BuildStatement, []AqueryDepset, error) {
|
||||||
aqueryProto := &analysis_v2_proto.ActionGraphContainer{}
|
aqueryProto := &analysis_v2_proto.ActionGraphContainer{}
|
||||||
err := proto.Unmarshal(aqueryJsonProto, aqueryProto)
|
err := proto.Unmarshal(aqueryJsonProto, aqueryProto)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -387,74 +389,92 @@ func AqueryBuildStatements(aqueryJsonProto []byte) ([]BuildStatement, []AqueryDe
|
|||||||
ParentId: pathFragmentId(protoPathFragments.ParentId)})
|
ParentId: pathFragmentId(protoPathFragments.ParentId)})
|
||||||
|
|
||||||
}
|
}
|
||||||
aqueryHandler, err := newAqueryHandler(aqueryResult)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var buildStatements []BuildStatement
|
|
||||||
for _, actionEntry := range aqueryResult.Actions {
|
|
||||||
if shouldSkipAction(actionEntry) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
var buildStatement BuildStatement
|
|
||||||
if actionEntry.isSymlinkAction() {
|
|
||||||
buildStatement, err = aqueryHandler.symlinkActionBuildStatement(actionEntry)
|
|
||||||
} else if actionEntry.isTemplateExpandAction() && len(actionEntry.Arguments) < 1 {
|
|
||||||
buildStatement, err = aqueryHandler.templateExpandActionBuildStatement(actionEntry)
|
|
||||||
} else if actionEntry.isFileWriteAction() {
|
|
||||||
buildStatement, err = aqueryHandler.fileWriteActionBuildStatement(actionEntry)
|
|
||||||
} else if actionEntry.isSymlinkTreeAction() {
|
|
||||||
buildStatement, err = aqueryHandler.symlinkTreeActionBuildStatement(actionEntry)
|
|
||||||
} else if len(actionEntry.Arguments) < 1 {
|
|
||||||
return nil, nil, fmt.Errorf("received action with no command: [%s]", actionEntry.Mnemonic)
|
|
||||||
} else {
|
|
||||||
buildStatement, err = aqueryHandler.normalActionBuildStatement(actionEntry)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
var aqueryHandler *aqueryArtifactHandler
|
||||||
|
{
|
||||||
|
eventHandler.Begin("init_handler")
|
||||||
|
defer eventHandler.End("init_handler")
|
||||||
|
aqueryHandler, err = newAqueryHandler(aqueryResult)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
buildStatements = append(buildStatements, buildStatement)
|
}
|
||||||
|
|
||||||
|
var buildStatements []BuildStatement
|
||||||
|
{
|
||||||
|
eventHandler.Begin("build_statements")
|
||||||
|
defer eventHandler.End("build_statements")
|
||||||
|
for _, actionEntry := range aqueryResult.Actions {
|
||||||
|
if shouldSkipAction(actionEntry) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var buildStatement BuildStatement
|
||||||
|
if actionEntry.isSymlinkAction() {
|
||||||
|
buildStatement, err = aqueryHandler.symlinkActionBuildStatement(actionEntry)
|
||||||
|
} else if actionEntry.isTemplateExpandAction() && len(actionEntry.Arguments) < 1 {
|
||||||
|
buildStatement, err = aqueryHandler.templateExpandActionBuildStatement(actionEntry)
|
||||||
|
} else if actionEntry.isFileWriteAction() {
|
||||||
|
buildStatement, err = aqueryHandler.fileWriteActionBuildStatement(actionEntry)
|
||||||
|
} else if actionEntry.isSymlinkTreeAction() {
|
||||||
|
buildStatement, err = aqueryHandler.symlinkTreeActionBuildStatement(actionEntry)
|
||||||
|
} else if len(actionEntry.Arguments) < 1 {
|
||||||
|
err = fmt.Errorf("received action with no command: [%s]", actionEntry.Mnemonic)
|
||||||
|
} else {
|
||||||
|
buildStatement, err = aqueryHandler.normalActionBuildStatement(actionEntry)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
buildStatements = append(buildStatements, buildStatement)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
depsetsByHash := map[string]AqueryDepset{}
|
depsetsByHash := map[string]AqueryDepset{}
|
||||||
var depsets []AqueryDepset
|
var depsets []AqueryDepset
|
||||||
for _, aqueryDepset := range aqueryHandler.depsetIdToAqueryDepset {
|
{
|
||||||
if prevEntry, hasKey := depsetsByHash[aqueryDepset.ContentHash]; hasKey {
|
eventHandler.Begin("depsets")
|
||||||
// Two depsets collide on hash. Ensure that their contents are identical.
|
defer eventHandler.End("depsets")
|
||||||
if !reflect.DeepEqual(aqueryDepset, prevEntry) {
|
for _, aqueryDepset := range aqueryHandler.depsetIdToAqueryDepset {
|
||||||
return nil, nil, fmt.Errorf("two different depsets have the same hash: %v, %v", prevEntry, aqueryDepset)
|
if prevEntry, hasKey := depsetsByHash[aqueryDepset.ContentHash]; hasKey {
|
||||||
|
// Two depsets collide on hash. Ensure that their contents are identical.
|
||||||
|
if !reflect.DeepEqual(aqueryDepset, prevEntry) {
|
||||||
|
return nil, nil, fmt.Errorf("two different depsets have the same hash: %v, %v", prevEntry, aqueryDepset)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
depsetsByHash[aqueryDepset.ContentHash] = aqueryDepset
|
||||||
|
depsets = append(depsets, aqueryDepset)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
depsetsByHash[aqueryDepset.ContentHash] = aqueryDepset
|
|
||||||
depsets = append(depsets, aqueryDepset)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build Statements and depsets must be sorted by their content hash to
|
eventHandler.Do("build_statement_sort", func() {
|
||||||
// preserve determinism between builds (this will result in consistent ninja file
|
// Build Statements and depsets must be sorted by their content hash to
|
||||||
// output). Note they are not sorted by their original IDs nor their Bazel ordering,
|
// preserve determinism between builds (this will result in consistent ninja file
|
||||||
// as Bazel gives nondeterministic ordering / identifiers in aquery responses.
|
// output). Note they are not sorted by their original IDs nor their Bazel ordering,
|
||||||
sort.Slice(buildStatements, func(i, j int) bool {
|
// as Bazel gives nondeterministic ordering / identifiers in aquery responses.
|
||||||
// For build statements, compare output lists. In Bazel, each output file
|
sort.Slice(buildStatements, func(i, j int) bool {
|
||||||
// may only have one action which generates it, so this will provide
|
// For build statements, compare output lists. In Bazel, each output file
|
||||||
// a deterministic ordering.
|
// may only have one action which generates it, so this will provide
|
||||||
outputs_i := buildStatements[i].OutputPaths
|
// a deterministic ordering.
|
||||||
outputs_j := buildStatements[j].OutputPaths
|
outputs_i := buildStatements[i].OutputPaths
|
||||||
if len(outputs_i) != len(outputs_j) {
|
outputs_j := buildStatements[j].OutputPaths
|
||||||
return len(outputs_i) < len(outputs_j)
|
if len(outputs_i) != len(outputs_j) {
|
||||||
}
|
return len(outputs_i) < len(outputs_j)
|
||||||
if len(outputs_i) == 0 {
|
}
|
||||||
// No outputs for these actions, so compare commands.
|
if len(outputs_i) == 0 {
|
||||||
return buildStatements[i].Command < buildStatements[j].Command
|
// No outputs for these actions, so compare commands.
|
||||||
}
|
return buildStatements[i].Command < buildStatements[j].Command
|
||||||
// There may be multiple outputs, but the output ordering is deterministic.
|
}
|
||||||
return outputs_i[0] < outputs_j[0]
|
// There may be multiple outputs, but the output ordering is deterministic.
|
||||||
|
return outputs_i[0] < outputs_j[0]
|
||||||
|
})
|
||||||
})
|
})
|
||||||
sort.Slice(depsets, func(i, j int) bool {
|
eventHandler.Do("depset_sort", func() {
|
||||||
return depsets[i].ContentHash < depsets[j].ContentHash
|
sort.Slice(depsets, func(i, j int) bool {
|
||||||
|
return depsets[i].ContentHash < depsets[j].ContentHash
|
||||||
|
})
|
||||||
})
|
})
|
||||||
return buildStatements, depsets, nil
|
return buildStatements, depsets, nil
|
||||||
}
|
}
|
||||||
|
@@ -21,8 +21,10 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"google.golang.org/protobuf/proto"
|
|
||||||
analysis_v2_proto "prebuilts/bazel/common/proto/analysis_v2"
|
analysis_v2_proto "prebuilts/bazel/common/proto/analysis_v2"
|
||||||
|
|
||||||
|
"github.com/google/blueprint/metrics"
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAqueryMultiArchGenrule(t *testing.T) {
|
func TestAqueryMultiArchGenrule(t *testing.T) {
|
||||||
@@ -136,7 +138,7 @@ func TestAqueryMultiArchGenrule(t *testing.T) {
|
|||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
actualbuildStatements, actualDepsets, _ := AqueryBuildStatements(data)
|
actualbuildStatements, actualDepsets, _ := AqueryBuildStatements(data, &metrics.EventHandler{})
|
||||||
var expectedBuildStatements []BuildStatement
|
var expectedBuildStatements []BuildStatement
|
||||||
for _, arch := range []string{"arm", "arm64", "x86", "x86_64"} {
|
for _, arch := range []string{"arm", "arm64", "x86", "x86_64"} {
|
||||||
expectedBuildStatements = append(expectedBuildStatements,
|
expectedBuildStatements = append(expectedBuildStatements,
|
||||||
@@ -195,7 +197,7 @@ func TestInvalidOutputId(t *testing.T) {
|
|||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_, _, err = AqueryBuildStatements(data)
|
_, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
|
||||||
assertError(t, err, "undefined outputId 3")
|
assertError(t, err, "undefined outputId 3")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,7 +228,7 @@ func TestInvalidInputDepsetIdFromAction(t *testing.T) {
|
|||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_, _, err = AqueryBuildStatements(data)
|
_, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
|
||||||
assertError(t, err, "undefined (not even empty) input depsetId 2")
|
assertError(t, err, "undefined (not even empty) input depsetId 2")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,7 +259,7 @@ func TestInvalidInputDepsetIdFromDepset(t *testing.T) {
|
|||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_, _, err = AqueryBuildStatements(data)
|
_, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
|
||||||
assertError(t, err, "undefined input depsetId 42 (referenced by depsetId 1)")
|
assertError(t, err, "undefined input depsetId 42 (referenced by depsetId 1)")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -288,7 +290,7 @@ func TestInvalidInputArtifactId(t *testing.T) {
|
|||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_, _, err = AqueryBuildStatements(data)
|
_, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
|
||||||
assertError(t, err, "undefined input artifactId 3")
|
assertError(t, err, "undefined input artifactId 3")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -319,7 +321,7 @@ func TestInvalidPathFragmentId(t *testing.T) {
|
|||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_, _, err = AqueryBuildStatements(data)
|
_, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
|
||||||
assertError(t, err, "undefined path fragment id 3")
|
assertError(t, err, "undefined path fragment id 3")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -352,7 +354,7 @@ func TestDepfiles(t *testing.T) {
|
|||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
actual, _, err := AqueryBuildStatements(data)
|
actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Unexpected error %q", err)
|
t.Errorf("Unexpected error %q", err)
|
||||||
}
|
}
|
||||||
@@ -402,7 +404,7 @@ func TestMultipleDepfiles(t *testing.T) {
|
|||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_, _, err = AqueryBuildStatements(data)
|
_, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
|
||||||
assertError(t, err, `found multiple potential depfiles "two.d", "other.d"`)
|
assertError(t, err, `found multiple potential depfiles "two.d", "other.d"`)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -483,7 +485,7 @@ func TestTransitiveInputDepsets(t *testing.T) {
|
|||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
actualbuildStatements, actualDepsets, _ := AqueryBuildStatements(data)
|
actualbuildStatements, actualDepsets, _ := AqueryBuildStatements(data, &metrics.EventHandler{})
|
||||||
|
|
||||||
expectedBuildStatements := []BuildStatement{
|
expectedBuildStatements := []BuildStatement{
|
||||||
{
|
{
|
||||||
@@ -538,7 +540,7 @@ func TestSymlinkTree(t *testing.T) {
|
|||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
actual, _, err := AqueryBuildStatements(data)
|
actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Unexpected error %q", err)
|
t.Errorf("Unexpected error %q", err)
|
||||||
}
|
}
|
||||||
@@ -594,7 +596,7 @@ func TestBazelOutRemovalFromInputDepsets(t *testing.T) {
|
|||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
actualBuildStatements, actualDepsets, _ := AqueryBuildStatements(data)
|
actualBuildStatements, actualDepsets, _ := AqueryBuildStatements(data, &metrics.EventHandler{})
|
||||||
if len(actualDepsets) != 1 {
|
if len(actualDepsets) != 1 {
|
||||||
t.Errorf("expected 1 depset but found %#v", actualDepsets)
|
t.Errorf("expected 1 depset but found %#v", actualDepsets)
|
||||||
return
|
return
|
||||||
@@ -681,7 +683,7 @@ func TestBazelOutRemovalFromTransitiveInputDepsets(t *testing.T) {
|
|||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
actualBuildStatements, actualDepsets, _ := AqueryBuildStatements(data)
|
actualBuildStatements, actualDepsets, _ := AqueryBuildStatements(data, &metrics.EventHandler{})
|
||||||
if len(actualDepsets) != 0 {
|
if len(actualDepsets) != 0 {
|
||||||
t.Errorf("expected 0 depsets but found %#v", actualDepsets)
|
t.Errorf("expected 0 depsets but found %#v", actualDepsets)
|
||||||
return
|
return
|
||||||
@@ -748,7 +750,7 @@ func TestMiddlemenAction(t *testing.T) {
|
|||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
actualBuildStatements, actualDepsets, err := AqueryBuildStatements(data)
|
actualBuildStatements, actualDepsets, err := AqueryBuildStatements(data, &metrics.EventHandler{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Unexpected error %q", err)
|
t.Errorf("Unexpected error %q", err)
|
||||||
}
|
}
|
||||||
@@ -845,7 +847,7 @@ func TestSimpleSymlink(t *testing.T) {
|
|||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
actual, _, err := AqueryBuildStatements(data)
|
actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Unexpected error %q", err)
|
t.Errorf("Unexpected error %q", err)
|
||||||
@@ -894,7 +896,7 @@ func TestSymlinkQuotesPaths(t *testing.T) {
|
|||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
actual, _, err := AqueryBuildStatements(data)
|
actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Unexpected error %q", err)
|
t.Errorf("Unexpected error %q", err)
|
||||||
}
|
}
|
||||||
@@ -940,7 +942,7 @@ func TestSymlinkMultipleInputs(t *testing.T) {
|
|||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_, _, err = AqueryBuildStatements(data)
|
_, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
|
||||||
assertError(t, err, `Expect 1 input and 1 output to symlink action, got: input ["file" "other_file"], output ["symlink"]`)
|
assertError(t, err, `Expect 1 input and 1 output to symlink action, got: input ["file" "other_file"], output ["symlink"]`)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -971,7 +973,7 @@ func TestSymlinkMultipleOutputs(t *testing.T) {
|
|||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_, _, err = AqueryBuildStatements(data)
|
_, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
|
||||||
assertError(t, err, "undefined outputId 2")
|
assertError(t, err, "undefined outputId 2")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1004,7 +1006,7 @@ func TestTemplateExpandActionSubstitutions(t *testing.T) {
|
|||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
actual, _, err := AqueryBuildStatements(data)
|
actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Unexpected error %q", err)
|
t.Errorf("Unexpected error %q", err)
|
||||||
}
|
}
|
||||||
@@ -1046,7 +1048,7 @@ func TestTemplateExpandActionNoOutput(t *testing.T) {
|
|||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_, _, err = AqueryBuildStatements(data)
|
_, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
|
||||||
assertError(t, err, `Expect 1 output to template expand action, got: output []`)
|
assertError(t, err, `Expect 1 output to template expand action, got: output []`)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1074,7 +1076,7 @@ func TestFileWrite(t *testing.T) {
|
|||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
actual, _, err := AqueryBuildStatements(data)
|
actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Unexpected error %q", err)
|
t.Errorf("Unexpected error %q", err)
|
||||||
}
|
}
|
||||||
@@ -1111,7 +1113,7 @@ func TestSourceSymlinkManifest(t *testing.T) {
|
|||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
actual, _, err := AqueryBuildStatements(data)
|
actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Unexpected error %q", err)
|
t.Errorf("Unexpected error %q", err)
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user