Handle simple symlinks in mixed builds
Bug: 180945121
Test: build/bazel/ci/mixed_libc.sh
Change-Id: I49fba569a41dcb8cd4c2e58560817443697f58f1
diff --git a/bazel/aquery_test.go b/bazel/aquery_test.go
index 7b40dcd..43e4155 100644
--- a/bazel/aquery_test.go
+++ b/bazel/aquery_test.go
@@ -805,17 +805,229 @@
}
}
+func TestSimpleSymlink(t *testing.T) {
+ const inputString = `
+{
+ "artifacts": [{
+ "id": 1,
+ "pathFragmentId": 3
+ }, {
+ "id": 2,
+ "pathFragmentId": 5
+ }],
+ "actions": [{
+ "targetId": 1,
+ "actionKey": "x",
+ "mnemonic": "Symlink",
+ "inputDepSetIds": [1],
+ "outputIds": [2],
+ "primaryOutputId": 2
+ }],
+ "depSetOfFiles": [{
+ "id": 1,
+ "directArtifactIds": [1]
+ }],
+ "pathFragments": [{
+ "id": 1,
+ "label": "one"
+ }, {
+ "id": 2,
+ "label": "file_subdir",
+ "parentId": 1
+ }, {
+ "id": 3,
+ "label": "file",
+ "parentId": 2
+ }, {
+ "id": 4,
+ "label": "symlink_subdir",
+ "parentId": 1
+ }, {
+ "id": 5,
+ "label": "symlink",
+ "parentId": 4
+ }]
+}`
+
+ actual, err := AqueryBuildStatements([]byte(inputString))
+
+ if err != nil {
+ t.Errorf("Unexpected error %q", err)
+ }
+
+ expectedBuildStatements := []BuildStatement{
+ BuildStatement{
+ Command: "mkdir -p one/symlink_subdir && " +
+ "rm -f one/symlink_subdir/symlink && " +
+ "ln -rsf one/file_subdir/file one/symlink_subdir/symlink",
+ InputPaths: []string{"one/file_subdir/file"},
+ OutputPaths: []string{"one/symlink_subdir/symlink"},
+ SymlinkPaths: []string{"one/symlink_subdir/symlink"},
+ Mnemonic: "Symlink",
+ },
+ }
+ assertBuildStatements(t, actual, expectedBuildStatements)
+}
+
+func TestSymlinkQuotesPaths(t *testing.T) {
+ const inputString = `
+{
+ "artifacts": [{
+ "id": 1,
+ "pathFragmentId": 3
+ }, {
+ "id": 2,
+ "pathFragmentId": 5
+ }],
+ "actions": [{
+ "targetId": 1,
+ "actionKey": "x",
+ "mnemonic": "SolibSymlink",
+ "inputDepSetIds": [1],
+ "outputIds": [2],
+ "primaryOutputId": 2
+ }],
+ "depSetOfFiles": [{
+ "id": 1,
+ "directArtifactIds": [1]
+ }],
+ "pathFragments": [{
+ "id": 1,
+ "label": "one"
+ }, {
+ "id": 2,
+ "label": "file subdir",
+ "parentId": 1
+ }, {
+ "id": 3,
+ "label": "file",
+ "parentId": 2
+ }, {
+ "id": 4,
+ "label": "symlink subdir",
+ "parentId": 1
+ }, {
+ "id": 5,
+ "label": "symlink",
+ "parentId": 4
+ }]
+}`
+
+ actual, err := AqueryBuildStatements([]byte(inputString))
+
+ if err != nil {
+ t.Errorf("Unexpected error %q", err)
+ }
+
+ expectedBuildStatements := []BuildStatement{
+ BuildStatement{
+ Command: "mkdir -p 'one/symlink subdir' && " +
+ "rm -f 'one/symlink subdir/symlink' && " +
+ "ln -rsf 'one/file subdir/file' 'one/symlink subdir/symlink'",
+ InputPaths: []string{"one/file subdir/file"},
+ OutputPaths: []string{"one/symlink subdir/symlink"},
+ SymlinkPaths: []string{"one/symlink subdir/symlink"},
+ Mnemonic: "SolibSymlink",
+ },
+ }
+ assertBuildStatements(t, actual, expectedBuildStatements)
+}
+
+func TestSymlinkMultipleInputs(t *testing.T) {
+ const inputString = `
+{
+ "artifacts": [{
+ "id": 1,
+ "pathFragmentId": 1
+ }, {
+ "id": 2,
+ "pathFragmentId": 2
+ }, {
+ "id": 3,
+ "pathFragmentId": 3
+ }],
+ "actions": [{
+ "targetId": 1,
+ "actionKey": "x",
+ "mnemonic": "Symlink",
+ "inputDepSetIds": [1],
+ "outputIds": [3],
+ "primaryOutputId": 3
+ }],
+ "depSetOfFiles": [{
+ "id": 1,
+ "directArtifactIds": [1,2]
+ }],
+ "pathFragments": [{
+ "id": 1,
+ "label": "file"
+ }, {
+ "id": 2,
+ "label": "other_file"
+ }, {
+ "id": 3,
+ "label": "symlink"
+ }]
+}`
+
+ _, err := AqueryBuildStatements([]byte(inputString))
+ assertError(t, err, `Expect 1 input and 1 output to symlink action, got: input ["file" "other_file"], output ["symlink"]`)
+}
+
+func TestSymlinkMultipleOutputs(t *testing.T) {
+ const inputString = `
+{
+ "artifacts": [{
+ "id": 1,
+ "pathFragmentId": 1
+ }, {
+ "id": 2,
+ "pathFragmentId": 2
+ }, {
+ "id": 3,
+ "pathFragmentId": 3
+ }],
+ "actions": [{
+ "targetId": 1,
+ "actionKey": "x",
+ "mnemonic": "Symlink",
+ "inputDepSetIds": [1],
+ "outputIds": [2,3],
+ "primaryOutputId": 2
+ }],
+ "depSetOfFiles": [{
+ "id": 1,
+ "directArtifactIds": [1]
+ }],
+ "pathFragments": [{
+ "id": 1,
+ "label": "file"
+ }, {
+ "id": 2,
+ "label": "symlink"
+ }, {
+ "id": 3,
+ "label": "other_symlink"
+ }]
+}`
+
+ _, err := AqueryBuildStatements([]byte(inputString))
+ assertError(t, err, `Expect 1 input and 1 output to symlink action, got: input ["file"], output ["symlink" "other_symlink"]`)
+}
+
func assertError(t *testing.T, err error, expected string) {
+ t.Helper()
if err == nil {
t.Errorf("expected error '%s', but got no error", expected)
} else if err.Error() != expected {
- t.Errorf("expected error '%s', but got: %s", expected, err.Error())
+ t.Errorf("expected error:\n\t'%s', but got:\n\t'%s'", expected, err.Error())
}
}
// Asserts that the given actual build statements match the given expected build statements.
// Build statement equivalence is determined using buildStatementEquals.
func assertBuildStatements(t *testing.T, expected []BuildStatement, actual []BuildStatement) {
+ t.Helper()
if len(expected) != len(actual) {
t.Errorf("expected %d build statements, but got %d,\n expected: %v,\n actual: %v",
len(expected), len(actual), expected, actual)
@@ -852,6 +1064,12 @@
if !reflect.DeepEqual(stringSet(first.OutputPaths), stringSet(second.OutputPaths)) {
return false
}
+ if !reflect.DeepEqual(stringSet(first.SymlinkPaths), stringSet(second.SymlinkPaths)) {
+ return false
+ }
+ if first.Depfile != second.Depfile {
+ return false
+ }
return true
}