Add test for null build with Ninja execution.
Also, fix symlink forest creation to not delete special not-symlinked
directories under the exec root, since they should be treated as special
output directories.
Closes #10719.
PiperOrigin-RevId: 293606157
diff --git a/src/main/java/com/google/devtools/build/lib/buildtool/SymlinkForest.java b/src/main/java/com/google/devtools/build/lib/buildtool/SymlinkForest.java
index bfd5d92..eda2aaa 100644
--- a/src/main/java/com/google/devtools/build/lib/buildtool/SymlinkForest.java
+++ b/src/main/java/com/google/devtools/build/lib/buildtool/SymlinkForest.java
@@ -102,14 +102,16 @@
}
/**
- * Delete all dir trees under a given 'dir' that don't start with a given 'prefix'. Does not
- * follow any symbolic links.
+ * Delete all dir trees under a given 'dir' that don't start with a given 'prefix', and is not
+ * special case of not symlinked to exec root directories (those directories are special case of
+ * output roots, so they must be kept before commands). Does not follow any symbolic links.
*/
@VisibleForTesting
@ThreadSafety.ThreadSafe
- static void deleteTreesBelowNotPrefixed(Path dir, String prefix) throws IOException {
+ void deleteTreesBelowNotPrefixed(Path dir, String prefix) throws IOException {
for (Path p : dir.getDirectoryEntries()) {
- if (!p.getBaseName().startsWith(prefix)) {
+ if (!p.getBaseName().startsWith(prefix)
+ && !notSymlinkedInExecrootDirectories.contains(p.getBaseName())) {
p.deleteTree();
}
}
diff --git a/src/test/java/com/google/devtools/build/lib/blackbox/tests/BUILD b/src/test/java/com/google/devtools/build/lib/blackbox/tests/BUILD
index 40d58a5..4d98089 100644
--- a/src/test/java/com/google/devtools/build/lib/blackbox/tests/BUILD
+++ b/src/test/java/com/google/devtools/build/lib/blackbox/tests/BUILD
@@ -53,7 +53,7 @@
java_test(
name = "NinjaBlackBoxTest",
- size = "small",
+ timeout = "moderate",
srcs = ["NinjaBlackBoxTest.java"],
tags = ["black_box_test"],
deps = [
diff --git a/src/test/java/com/google/devtools/build/lib/blackbox/tests/NinjaBlackBoxTest.java b/src/test/java/com/google/devtools/build/lib/blackbox/tests/NinjaBlackBoxTest.java
index 2ff1af6..59d38d6 100644
--- a/src/test/java/com/google/devtools/build/lib/blackbox/tests/NinjaBlackBoxTest.java
+++ b/src/test/java/com/google/devtools/build/lib/blackbox/tests/NinjaBlackBoxTest.java
@@ -21,6 +21,7 @@
import com.google.devtools.build.lib.blackbox.junit.AbstractBlackBoxTest;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.util.List;
import org.junit.Test;
/** Integration test for Ninja execution functionality. */
@@ -201,4 +202,42 @@
.doesNotContain(
"INFO: Analyzed target //:graph (0 packages loaded, 0 targets configured).");
}
+
+ @Test
+ public void testNullBuild() throws Exception {
+ context().write(".bazelignore", "build_config");
+ context()
+ .write(
+ WORKSPACE,
+ "workspace(name = 'test')",
+ "dont_symlink_directories_in_execroot(paths = ['build_config'])");
+ // Print nanoseconds fraction of the current time into the output file.
+ context()
+ .write(
+ "build_config/build.ninja",
+ "rule echo_time",
+ " command = date +%N >> ${out}",
+ "build nano.txt: echo_time");
+ context()
+ .write(
+ "BUILD",
+ "ninja_graph(name = 'graph', ",
+ "output_root = 'build_config',",
+ " working_directory = 'build_config',",
+ " main = 'build_config/build.ninja',",
+ " output_groups = {'main': ['nano.txt']})");
+
+ BuilderRunner bazel = context().bazel().withFlags("--experimental_ninja_actions");
+ assertConfigured(bazel.build("//:graph"));
+ Path path = context().resolveExecRootPath(bazel, "build_config/nano.txt");
+ assertThat(path.toFile().exists()).isTrue();
+ List<String> text = Files.readAllLines(path);
+ assertThat(text).isNotEmpty();
+ long lastModified = path.toFile().lastModified();
+
+ // Should be null build, as nothing changed.
+ assertNothingConfigured(bazel.build("//:graph"));
+ assertThat(Files.readAllLines(path)).containsExactly(text.get(0));
+ assertThat(path.toFile().lastModified()).isEqualTo(lastModified);
+ }
}
diff --git a/src/test/java/com/google/devtools/build/lib/buildtool/SymlinkForestTest.java b/src/test/java/com/google/devtools/build/lib/buildtool/SymlinkForestTest.java
index 9792ccc..49278d9 100644
--- a/src/test/java/com/google/devtools/build/lib/buildtool/SymlinkForestTest.java
+++ b/src/test/java/com/google/devtools/build/lib/buildtool/SymlinkForestTest.java
@@ -36,6 +36,7 @@
import com.google.devtools.build.lib.vfs.Symlinks;
import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem;
import java.io.IOException;
+import java.nio.charset.StandardCharsets;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -129,7 +130,7 @@
@Test
public void testDeleteTreesBelowNotPrefixed() throws IOException {
createTestDirectoryTree();
- SymlinkForest.deleteTreesBelowNotPrefixed(topDir, "file-");
+ new SymlinkForest(ImmutableMap.of(), topDir, "").deleteTreesBelowNotPrefixed(topDir, "file-");
assertThat(file1.exists()).isTrue();
assertThat(file2.exists()).isTrue();
assertThat(aDir.exists()).isFalse();
@@ -427,6 +428,51 @@
}
@Test
+ public void testNotSymlinkedDirectoriesNotDeletedBetweenCommands() throws Exception {
+ Root outputBase = Root.fromPath(fileSystem.getPath("/ob"));
+ Root mainRepo = Root.fromPath(fileSystem.getPath("/my_repo"));
+ Path linkRoot = outputBase.getRelative("execroot/ws_name");
+
+ linkRoot.createDirectoryAndParents();
+ mainRepo.asPath().createDirectoryAndParents();
+ mainRepo.getRelative("build").createDirectoryAndParents();
+
+ ImmutableMap<PackageIdentifier, Root> packageRootMap =
+ ImmutableMap.<PackageIdentifier, Root>builder()
+ .put(createMainPkg(mainRepo, "dir1/pkg"), mainRepo)
+ // Empty package will cause every top-level files to be linked, except external/
+ .put(createMainPkg(mainRepo, ""), mainRepo)
+ .build();
+
+ SymlinkForest symlinkForest =
+ new SymlinkForest(
+ packageRootMap, linkRoot, TestConstants.PRODUCT_NAME, ImmutableSortedSet.of("build"));
+ symlinkForest.plantSymlinkForest();
+
+ assertLinksTo(linkRoot, mainRepo, "dir1");
+ assertThat(linkRoot.getChild("build").exists()).isFalse();
+
+ // Create some file in 'build' directory under exec root.
+ Path notSymlinkedDir = linkRoot.getChild("build");
+ notSymlinkedDir.createDirectoryAndParents();
+
+ byte[] bytes = "text".getBytes(StandardCharsets.ISO_8859_1);
+ Path childPath = notSymlinkedDir.getChild("child.txt");
+ FileSystemUtils.writeContent(childPath, bytes);
+
+ symlinkForest.plantSymlinkForest();
+
+ assertLinksTo(linkRoot, mainRepo, "dir1");
+ // Exists because it was explicitly created.
+ assertThat(linkRoot.getChild("build").exists()).isTrue();
+ // The presence of the manually added file indicates that SymlinkForest did not delete
+ // the directory it's in.
+ assertThat(childPath.exists()).isTrue();
+ assertThat(FileSystemUtils.readContent(childPath, StandardCharsets.ISO_8859_1))
+ .isEqualTo("text");
+ }
+
+ @Test
public void testNotSymlinkedDirectoriesInExecRootPartialMainRepo1() throws Exception {
Root outputBase = Root.fromPath(fileSystem.getPath("/ob"));
Root mainRepo = Root.fromPath(fileSystem.getPath("/my_repo"));