From 94c519bcc555195d061e1a63f0e4235795bec5be Mon Sep 17 00:00:00 2001 From: kshyanashree <109167932+kshyanashree@users.noreply.github.com> Date: Fri, 10 Mar 2023 03:34:34 -0800 Subject: [PATCH] Skip empty directories instead of throwing in prefetcher. (#17718) While non-empty tree artifacts in the inputs to an action are expanded into the files they contain and omitted from the input mapping, empty tree artifacts are still present, as they signal the need to create the directory. Thus, the check added in 763f966 is incorrect. I'm explicitly skipping the empty tree artifacts in prefetchFiles() as otherwise they get skipped as a result of FileArtifactValue#isRemote() returning false for the FileArtifactValue associated with an empty tree artifact (even if it was produced remotely!), which is extremely subtle. Closes #17183. PiperOrigin-RevId: 501207095 Change-Id: Ib52727d6fdc6b7a291a61fba33914e57531fb1f4 Co-authored-by: Tiago Quelhas --- .../remote/AbstractActionInputPrefetcher.java | 6 +++ ...ildWithoutTheBytesIntegrationTestBase.java | 52 +++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/src/main/java/com/google/devtools/build/lib/remote/AbstractActionInputPrefetcher.java b/src/main/java/com/google/devtools/build/lib/remote/AbstractActionInputPrefetcher.java index 3c325684ee2ad5..e88a00e7fcc39d 100644 --- a/src/main/java/com/google/devtools/build/lib/remote/AbstractActionInputPrefetcher.java +++ b/src/main/java/com/google/devtools/build/lib/remote/AbstractActionInputPrefetcher.java @@ -197,10 +197,16 @@ protected ListenableFuture prefetchFiles( Map> trees = new HashMap<>(); List files = new ArrayList<>(); for (ActionInput input : inputs) { + // Source artifacts don't need to be fetched. if (input instanceof Artifact && ((Artifact) input).isSourceArtifact()) { continue; } + // Skip empty tree artifacts (non-empty tree artifacts should have already been expanded). + if (input.isDirectory()) { + continue; + } + if (input instanceof TreeFileArtifact) { TreeFileArtifact treeFile = (TreeFileArtifact) input; SpecialArtifact treeArtifact = treeFile.getParent(); diff --git a/src/test/java/com/google/devtools/build/lib/remote/BuildWithoutTheBytesIntegrationTestBase.java b/src/test/java/com/google/devtools/build/lib/remote/BuildWithoutTheBytesIntegrationTestBase.java index 215df976fd1316..02529d8c9de2dc 100644 --- a/src/test/java/com/google/devtools/build/lib/remote/BuildWithoutTheBytesIntegrationTestBase.java +++ b/src/test/java/com/google/devtools/build/lib/remote/BuildWithoutTheBytesIntegrationTestBase.java @@ -520,6 +520,58 @@ public void treeOutputsFromLocalFileSystem_works() throws Exception { assertValidOutputFile("out/foobar.txt", "file-1\nbar\n"); } + @Test + public void emptyTreeConsumedByLocalAction() throws Exception { + // Disable remote execution so that the empty tree artifact is prefetched. + addOptions("--modify_execution_info=Genrule=+no-remote-exec"); + setDownloadToplevel(); + writeOutputDirRule(); + write( + "BUILD", + "load(':output_dir.bzl', 'output_dir')", + "output_dir(", + " name = 'foo',", + " manifest = ':manifest',", + ")", + "genrule(", + " name = 'foobar',", + " srcs = [':foo'],", + " outs = ['foobar.txt'],", + " cmd = 'touch $@',", + ")"); + write("manifest"); // no files + + buildTarget("//:foobar"); + waitDownloads(); + } + + @Test + public void multiplePackagePaths_buildsSuccessfully() throws Exception { + write( + "../a/src/BUILD", + "genrule(", + " name = 'foo',", + " srcs = [],", + " outs = ['out/foo.txt'],", + " cmd = 'echo foo > $@',", + ")"); + write( + "BUILD", + "genrule(", + " name = 'foobar',", + " srcs = ['//src:foo'],", + " outs = ['out/foobar.txt'],", + " cmd = 'cat $(location //src:foo) > $@ && echo bar >> $@',", + ")"); + addOptions("--package_path=%workspace%:%workspace%/../a"); + setDownloadToplevel(); + + buildTarget("//:foobar"); + waitDownloads(); + + assertValidOutputFile("out/foobar.txt", "foo\nbar\n"); + } + @Test public void incrementalBuild_deleteOutputsInUnwritableParentDirectory() throws Exception { write(