Add depfile handling for bazel_handler.

Test: go test soong tests
Test: TODO mixed build change header, mixed build
Change-Id: I7c51faf2d5b1a8717cbab6bb0b3eb75c307fcd85
This commit is contained in:
Liz Kammer 2021-03-25 16:42:37 -04:00
parent dca349a782
commit de116856fb
3 changed files with 123 additions and 4 deletions

View File

@ -746,6 +746,10 @@ func (c *bazelSingleton) GenerateBuildActions(ctx SingletonContext) {
cmd.Implicit(PathForBazelOut(ctx, inputPath)) cmd.Implicit(PathForBazelOut(ctx, inputPath))
} }
if depfile := buildStatement.Depfile; depfile != nil {
cmd.ImplicitDepFile(PathForBazelOut(ctx, *depfile))
}
// This is required to silence warnings pertaining to unexpected timestamps. Particularly, // This is required to silence warnings pertaining to unexpected timestamps. Particularly,
// some Bazel builtins (such as files in the bazel_tools directory) have far-future // some Bazel builtins (such as files in the bazel_tools directory) have far-future
// timestamps. Without restat, Ninja would emit warnings that the input files of a // timestamps. Without restat, Ninja would emit warnings that the input files of a

View File

@ -74,6 +74,7 @@ type actionGraphContainer struct {
// with a Bazel action from Bazel's action graph. // with a Bazel action from Bazel's action graph.
type BuildStatement struct { type BuildStatement struct {
Command string Command string
Depfile *string
OutputPaths []string OutputPaths []string
InputPaths []string InputPaths []string
Env []KeyValuePair Env []KeyValuePair
@ -133,12 +134,22 @@ func AqueryBuildStatements(aqueryJsonProto []byte) ([]BuildStatement, error) {
continue continue
} }
outputPaths := []string{} outputPaths := []string{}
var depfile *string
for _, outputId := range actionEntry.OutputIds { for _, outputId := range actionEntry.OutputIds {
outputPath, exists := artifactIdToPath[outputId] outputPath, exists := artifactIdToPath[outputId]
if !exists { if !exists {
return nil, fmt.Errorf("undefined outputId %d", outputId) return nil, fmt.Errorf("undefined outputId %d", outputId)
} }
outputPaths = append(outputPaths, outputPath) ext := filepath.Ext(outputPath)
if ext == ".d" {
if depfile != nil {
return nil, fmt.Errorf("found multiple potential depfiles %q, %q", *depfile, outputPath)
} else {
depfile = &outputPath
}
} else {
outputPaths = append(outputPaths, outputPath)
}
} }
inputPaths := []string{} inputPaths := []string{}
for _, inputDepSetId := range actionEntry.InputDepSetIds { for _, inputDepSetId := range actionEntry.InputDepSetIds {
@ -161,12 +172,13 @@ func AqueryBuildStatements(aqueryJsonProto []byte) ([]BuildStatement, error) {
} }
buildStatement := BuildStatement{ buildStatement := BuildStatement{
Command: strings.Join(proptools.ShellEscapeList(actionEntry.Arguments), " "), Command: strings.Join(proptools.ShellEscapeList(actionEntry.Arguments), " "),
Depfile: depfile,
OutputPaths: outputPaths, OutputPaths: outputPaths,
InputPaths: inputPaths, InputPaths: inputPaths,
Env: actionEntry.EnvironmentVariables, Env: actionEntry.EnvironmentVariables,
Mnemonic: actionEntry.Mnemonic} Mnemonic: actionEntry.Mnemonic}
if len(actionEntry.Arguments) < 1 { if len(actionEntry.Arguments) < 1 {
return nil, fmt.Errorf("received action with no command: [%s]", buildStatement) return nil, fmt.Errorf("received action with no command: [%v]", buildStatement)
continue continue
} }
buildStatements = append(buildStatements, buildStatement) buildStatements = append(buildStatements, buildStatement)

View File

@ -393,6 +393,109 @@ func TestInvalidPathFragmentId(t *testing.T) {
assertError(t, err, "undefined path fragment id 3") assertError(t, err, "undefined path fragment id 3")
} }
func TestDepfiles(t *testing.T) {
const inputString = `
{
"artifacts": [{
"id": 1,
"pathFragmentId": 1
}, {
"id": 2,
"pathFragmentId": 2
}, {
"id": 3,
"pathFragmentId": 3
}],
"actions": [{
"targetId": 1,
"actionKey": "x",
"mnemonic": "x",
"arguments": ["touch", "foo"],
"inputDepSetIds": [1],
"outputIds": [2, 3],
"primaryOutputId": 2
}],
"depSetOfFiles": [{
"id": 1,
"directArtifactIds": [1, 2, 3]
}],
"pathFragments": [{
"id": 1,
"label": "one"
}, {
"id": 2,
"label": "two"
}, {
"id": 3,
"label": "two.d"
}]
}`
actual, err := AqueryBuildStatements([]byte(inputString))
if err != nil {
t.Errorf("Unexpected error %q", err)
}
if expected := 1; len(actual) != expected {
t.Fatalf("Expected %d build statements, got %d", expected, len(actual))
}
bs := actual[0]
expectedDepfile := "two.d"
if bs.Depfile == nil {
t.Errorf("Expected depfile %q, but there was none found", expectedDepfile)
} else if *bs.Depfile != expectedDepfile {
t.Errorf("Expected depfile %q, but got %q", expectedDepfile, *bs.Depfile)
}
}
func TestMultipleDepfiles(t *testing.T) {
const inputString = `
{
"artifacts": [{
"id": 1,
"pathFragmentId": 1
}, {
"id": 2,
"pathFragmentId": 2
}, {
"id": 3,
"pathFragmentId": 3
}, {
"id": 4,
"pathFragmentId": 4
}],
"actions": [{
"targetId": 1,
"actionKey": "x",
"mnemonic": "x",
"arguments": ["touch", "foo"],
"inputDepSetIds": [1],
"outputIds": [2,3,4],
"primaryOutputId": 2
}],
"depSetOfFiles": [{
"id": 1,
"directArtifactIds": [1, 2, 3, 4]
}],
"pathFragments": [{
"id": 1,
"label": "one"
}, {
"id": 2,
"label": "two"
}, {
"id": 3,
"label": "two.d"
}, {
"id": 4,
"label": "other.d"
}]
}`
_, err := AqueryBuildStatements([]byte(inputString))
assertError(t, err, `found multiple potential depfiles "two.d", "other.d"`)
}
func TestTransitiveInputDepsets(t *testing.T) { func TestTransitiveInputDepsets(t *testing.T) {
// The input aquery for this test comes from a proof-of-concept starlark rule which registers // The input aquery for this test comes from a proof-of-concept starlark rule which registers
// a single action with many inputs given via a deep depset. // a single action with many inputs given via a deep depset.
@ -627,7 +730,7 @@ func assertError(t *testing.T, err error, expected string) {
// Build statement equivalence is determined using buildStatementEquals. // Build statement equivalence is determined using buildStatementEquals.
func assertBuildStatements(t *testing.T, expected []BuildStatement, actual []BuildStatement) { func assertBuildStatements(t *testing.T, expected []BuildStatement, actual []BuildStatement) {
if len(expected) != len(actual) { if len(expected) != len(actual) {
t.Errorf("expected %d build statements, but got %d,\n expected: %s,\n actual: %s", t.Errorf("expected %d build statements, but got %d,\n expected: %v,\n actual: %v",
len(expected), len(actual), expected, actual) len(expected), len(actual), expected, actual)
return return
} }
@ -638,7 +741,7 @@ ACTUAL_LOOP:
continue ACTUAL_LOOP continue ACTUAL_LOOP
} }
} }
t.Errorf("unexpected build statement %s.\n expected: %s", t.Errorf("unexpected build statement %v.\n expected: %v",
actualStatement, expected) actualStatement, expected)
return return
} }