Allow BUILD files directly under the build root
This makes the empty package name legal (//:foo). If the empty package is used,
this symlinks everything under the build root to the exec root. This includes
directories.
--
MOS_MIGRATED_REVID=87960882
diff --git a/docs/build-ref.html b/docs/build-ref.html
index fe808ac..315603f 100644
--- a/docs/build-ref.html
+++ b/docs/build-ref.html
@@ -546,6 +546,11 @@
'<code>_</code>', and '<code>/</code>'.
</p>
+<p>
+ The empty string is also a valid package name (e.g., //:foo). Generally
+ projects should attempt to use more descriptively named packages, though.
+</p>
+
<p>
Package names may not contain the substring <code>//</code>, nor
diff --git a/src/main/java/com/google/devtools/build/lib/cmdline/LabelValidator.java b/src/main/java/com/google/devtools/build/lib/cmdline/LabelValidator.java
index 0df134d..0458469 100644
--- a/src/main/java/com/google/devtools/build/lib/cmdline/LabelValidator.java
+++ b/src/main/java/com/google/devtools/build/lib/cmdline/LabelValidator.java
@@ -64,11 +64,12 @@
public static String validatePackageName(String packageName) {
int len = packageName.length();
if (len == 0) {
- return "empty package name";
+ // Empty package name (//:foo).
+ return null;
}
if (packageName.charAt(0) == '/') {
- return "package names may not start with '/'";
+ return "package names may not start with '/'";
}
// Fast path for packages with '.' in their name
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/PackageLookupFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/PackageLookupFunction.java
index f800d36..9ba197b 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/PackageLookupFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/PackageLookupFunction.java
@@ -54,12 +54,6 @@
return computeExternalPackageLookupValue(skyKey, env);
}
PathFragment pkg = packageKey.getPackageFragment();
-
- // This represents a package lookup at the package root.
- if (pkg.equals(PathFragment.EMPTY_FRAGMENT)) {
- return PackageLookupValue.invalidPackageName("The empty package name is invalid");
- }
-
String pkgName = pkg.getPathString();
String packageNameErrorMsg = LabelValidator.validatePackageName(pkgName);
if (packageNameErrorMsg != null) {
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/FileSystemUtils.java b/src/main/java/com/google/devtools/build/lib/vfs/FileSystemUtils.java
index c3d7787..796d828 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/FileSystemUtils.java
+++ b/src/main/java/com/google/devtools/build/lib/vfs/FileSystemUtils.java
@@ -23,6 +23,7 @@
import com.google.common.io.ByteSink;
import com.google.common.io.ByteSource;
import com.google.common.io.ByteStreams;
+import com.google.devtools.build.lib.Constants;
import com.google.devtools.build.lib.concurrent.ThreadSafety.ConditionallyThreadSafe;
import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
@@ -679,6 +680,8 @@
*/
public static void plantLinkForest(ImmutableMap<PathFragment, Path> packageRootMap, Path linkRoot)
throws IOException {
+ Path emptyPackagePath = null;
+
// Create a sorted map of all dirs (packages and their ancestors) to sets of their roots.
// Packages come from exactly one root, but their shared ancestors may come from more.
// The map is maintained sorted lexicographically, so parents are before their children.
@@ -686,6 +689,9 @@
for (Map.Entry<PathFragment, Path> entry : packageRootMap.entrySet()) {
PathFragment pkgDir = entry.getKey();
Path pkgRoot = entry.getValue();
+ if (pkgDir.segmentCount() == 0) {
+ emptyPackagePath = entry.getValue();
+ }
for (int i = 1; i <= pkgDir.segmentCount(); i++) {
PathFragment dir = pkgDir.subFragment(0, i);
Set<Path> roots = dirRootsMap.get(dir);
@@ -764,6 +770,19 @@
}
}
}
+
+ if (emptyPackagePath != null) {
+ // For the top-level directory, generate symlinks to everything in the directory instead of
+ // the directory itself.
+ for (Path target : emptyPackagePath.getDirectoryEntries()) {
+ String baseName = target.getBaseName();
+ // Create any links that don't exist yet and don't start with bazel-.
+ if (!baseName.startsWith(Constants.PRODUCT_NAME + "-")
+ && !linkRoot.getRelative(baseName).exists()) {
+ linkRoot.getRelative(baseName).createSymbolicLink(target);
+ }
+ }
+ }
}
/****************************************************************************
diff --git a/src/test/java/com/google/devtools/build/lib/syntax/LabelTest.java b/src/test/java/com/google/devtools/build/lib/syntax/LabelTest.java
index 4aac0de..cc4134f 100644
--- a/src/test/java/com/google/devtools/build/lib/syntax/LabelTest.java
+++ b/src/test/java/com/google/devtools/build/lib/syntax/LabelTest.java
@@ -53,6 +53,11 @@
assertEquals("foo/bar", l.getPackageName());
assertEquals("bar", l.getName());
}
+ {
+ Label l = Label.parseAbsolute("//:bar");
+ assertEquals("", l.getPackageName());
+ assertEquals("bar", l.getName());
+ }
}
private static String parseCommandLine(String label, String prefix) throws SyntaxException {
@@ -234,8 +239,6 @@
"//foo/./baz:baz");
assertSyntaxError(BAD_PACKAGE_CHARS,
"//./bar/baz:baz");
- assertSyntaxError(BAD_PACKAGE_CHARS,
- "//.:foo");
assertSyntaxError("target names may not contain '.' as a path segment",
"//foo:bar/./baz");
assertSyntaxError("target names may not contain '.' as a path segment",
@@ -257,7 +260,6 @@
public void testSomeOtherBadLabels() throws Exception {
assertSyntaxError("package names may not end with '/'",
"//foo/:bar");
- assertSyntaxError("empty package name", "//:foo");
assertSyntaxError("package names may not start with '/'", "///p:foo");
assertSyntaxError("package names may not contain '//' path separators",
"//a//b:foo");