Ignore external/ directory in users' source tree when creating execroot symlink tree.

Fixes https://github.com/bazelbuild/bazel/issues/3857#issuecomment-510538152

RELNOTES: None
PiperOrigin-RevId: 257786258
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 aca8cfe..0f04ceb 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
@@ -110,8 +110,10 @@
     for (Path target : mainRepoRoot.getDirectoryEntries()) {
       String baseName = target.getBaseName();
       Path execPath = execroot.getRelative(baseName);
-      // Create any links that don't start with bazel-.
-      if (!baseName.startsWith(prefix)) {
+      // Create any links that don't start with bazel-, and ignore external/ directory if
+      // user has it in the source tree because it conflicts with external repository location.
+      if (!baseName.startsWith(prefix)
+          && !baseName.equals(LabelConstants.EXTERNAL_PATH_PREFIX.getBaseName())) {
         execPath.createSymbolicLink(target);
       }
     }
@@ -284,7 +286,13 @@
         if (pkgId.getPackageFragment().equals(PathFragment.EMPTY_FRAGMENT)) {
           shouldLinkAllTopLevelItems = true;
         } else {
-          Path execrootLink = execroot.getRelative(pkgId.getPackageFragment().getSegment(0));
+          String baseName = pkgId.getPackageFragment().getSegment(0);
+          // ignore external/ directory if user has it in the source tree
+          // because it conflicts with external repository location.
+          if (baseName.equals(LabelConstants.EXTERNAL_PATH_PREFIX.getBaseName())) {
+            continue;
+          }
+          Path execrootLink = execroot.getRelative(baseName);
           Path sourcePath = entry.getValue().getRelative(pkgId.getSourceRoot().getSegment(0));
           mainRepoLinks.putIfAbsent(execrootLink, sourcePath);
         }
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 17124c6..4be5fa1 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
@@ -310,6 +310,65 @@
   }
 
   @Test
+  public void testTestExternalDirInMainRepoIsIgnored1() throws Exception {
+    // Test external/ is ignored even when packages like "//external/foo" is specified.
+    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();
+
+    ImmutableMap<PackageIdentifier, Root> packageRootMap =
+        ImmutableMap.<PackageIdentifier, Root>builder()
+            .put(createMainPkg(mainRepo, "dir1/pkg/foo"), mainRepo)
+            .put(createMainPkg(mainRepo, "dir2/pkg"), mainRepo)
+            .put(createMainPkg(mainRepo, "dir3"), mainRepo)
+            // external/ should not be linked even we have "//external/foo" package
+            .put(createMainPkg(mainRepo, "external/foo"), mainRepo)
+            .put(createExternalPkg(outputBase, "X", "dir_x/pkg"), outputBase)
+            .build();
+
+    new SymlinkForest(packageRootMap, linkRoot, TestConstants.PRODUCT_NAME).plantSymlinkForest();
+
+    assertLinksTo(linkRoot, mainRepo, "dir1");
+    assertLinksTo(linkRoot, mainRepo, "dir2");
+    assertLinksTo(linkRoot, mainRepo, "dir3");
+    assertLinksTo(linkRoot, outputBase, LabelConstants.EXTERNAL_PATH_PREFIX + "/X");
+    assertThat(outputBase.getRelative("external/foo").exists()).isFalse();
+  }
+
+  @Test
+  public void testTestExternalDirInMainRepoIsIgnored2() throws Exception {
+    // Test external/ is ignored when root package "//:" is specified.
+    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("dir3").createDirectoryAndParents();
+    mainRepo.getRelative("external/foo").createDirectoryAndParents();
+
+    ImmutableMap<PackageIdentifier, Root> packageRootMap =
+        ImmutableMap.<PackageIdentifier, Root>builder()
+            .put(createMainPkg(mainRepo, "dir1/pkg/foo"), mainRepo)
+            .put(createMainPkg(mainRepo, "dir2/pkg"), mainRepo)
+            // Empty package will cause every top-level files to be linked, except external/
+            .put(createMainPkg(mainRepo, ""), mainRepo)
+            .put(createExternalPkg(outputBase, "X", "dir_x/pkg"), outputBase)
+            .build();
+
+    new SymlinkForest(packageRootMap, linkRoot, TestConstants.PRODUCT_NAME).plantSymlinkForest();
+
+    assertLinksTo(linkRoot, mainRepo, "dir1");
+    assertLinksTo(linkRoot, mainRepo, "dir2");
+    assertLinksTo(linkRoot, mainRepo, "dir3");
+    assertLinksTo(linkRoot, outputBase, LabelConstants.EXTERNAL_PATH_PREFIX + "/X");
+    assertThat(outputBase.getRelative("external/foo").exists()).isFalse();
+  }
+
+  @Test
   public void testExternalPackage() throws Exception {
     Path linkRoot = fileSystem.getPath("/linkRoot");
     linkRoot.createDirectoryAndParents();