Support WORKSPACE.bazel file
Similar to BUILD.bazel file, Bazel now supports WORKSPACE.bazel as the workspace file. If both WORKSPACE and WORKSPACE.bazel exist, the latter one takes priority.
By supporting WORKSPACE.bazel file, users are more conscious about which files are Bazel related files.
Fixes https://github.com/bazelbuild/bazel/issues/3885
Closes #10175.
PiperOrigin-RevId: 279730425
diff --git a/site/docs/build-ref.html b/site/docs/build-ref.html
index 5a0991c..fd8f608 100644
--- a/site/docs/build-ref.html
+++ b/site/docs/build-ref.html
@@ -63,6 +63,9 @@
at a subdirectory containing a <code>WORKSPACE</code> file (as they form
another workspace).</p>
+<p>Bazel also supports <code>WORKSPACE.bazel</code> file as an alias of <code>WORKSPACE</code> file.
+ If both files exist, <code>WORKSPACE.bazel</code> will take the priority.</p>
+
<h3 id="repositories">Repositories</h3>
<p>Code is organized in <em>repositories</em>. The directory containing
the <code>WORKSPACE</code> file is the root of the main repository, also
diff --git a/site/docs/external.md b/site/docs/external.md
index 51dc612..3a27554 100644
--- a/site/docs/external.md
+++ b/site/docs/external.md
@@ -8,7 +8,7 @@
Bazel can depend on targets from other projects. Dependencies from these other
projects are called _external dependencies_.
-The `WORKSPACE` file in the [workspace directory](build-ref.html#workspace)
+The `WORKSPACE` file (or `WORKSPACE.bazel` file) in the [workspace directory](build-ref.html#workspace)
tells Bazel how to get other projects' sources. These other projects can
contain one or more `BUILD` files with their own targets. `BUILD` files within
the main project can depend on these external targets by using their name from
@@ -45,6 +45,10 @@
Encyclopedia's [Workspace Rules](be/workspace.html) and the documentation
for [Embedded Starlark Repository Rules](repo/index.html).
+Like in the [workspace directory](build-ref.html#workspace), Bazel also supports `WORKSPACE.bazel`
+file as an alias of `WORKSPACE` in external dependencies. If both files exist, `WORKSPACE.bazel`
+will take the priority.
+
<a name="types"></a>
## Supported types of external dependencies
diff --git a/src/main/cpp/workspace_layout.cc b/src/main/cpp/workspace_layout.cc
index d781993..c526762 100644
--- a/src/main/cpp/workspace_layout.cc
+++ b/src/main/cpp/workspace_layout.cc
@@ -27,6 +27,7 @@
using std::string;
using std::vector;
+static const char kWorkspaceDotBazelMarker[] = "WORKSPACE.bazel";
static const char kWorkspaceMarker[] = "WORKSPACE";
string WorkspaceLayout::GetOutputRoot() const {
@@ -35,7 +36,9 @@
bool WorkspaceLayout::InWorkspace(const string &workspace) const {
return blaze_util::PathExists(
- blaze_util::JoinPath(workspace, kWorkspaceMarker));
+ blaze_util::JoinPath(workspace, kWorkspaceDotBazelMarker)) ||
+ blaze_util::PathExists(
+ blaze_util::JoinPath(workspace, kWorkspaceMarker));
}
string WorkspaceLayout::GetWorkspace(const string &cwd) const {
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/repository/skylark/SkylarkRepositoryFunction.java b/src/main/java/com/google/devtools/build/lib/bazel/repository/skylark/SkylarkRepositoryFunction.java
index 7d9de76..7a59327 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/repository/skylark/SkylarkRepositoryFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/repository/skylark/SkylarkRepositoryFunction.java
@@ -23,7 +23,6 @@
import com.google.devtools.build.lib.analysis.RuleDefinition;
import com.google.devtools.build.lib.bazel.repository.RepositoryResolvedEvent;
import com.google.devtools.build.lib.bazel.repository.downloader.HttpDownloader;
-import com.google.devtools.build.lib.cmdline.LabelConstants;
import com.google.devtools.build.lib.events.Event;
import com.google.devtools.build.lib.packages.BazelStarlarkContext;
import com.google.devtools.build.lib.packages.Rule;
@@ -33,6 +32,7 @@
import com.google.devtools.build.lib.rules.repository.RepositoryDirectoryValue;
import com.google.devtools.build.lib.rules.repository.RepositoryFunction;
import com.google.devtools.build.lib.rules.repository.ResolvedHashesValue;
+import com.google.devtools.build.lib.rules.repository.WorkspaceFileHelper;
import com.google.devtools.build.lib.skyframe.BlacklistedPackagePrefixesValue;
import com.google.devtools.build.lib.skyframe.PrecomputedValue;
import com.google.devtools.build.lib.syntax.BaseFunction;
@@ -232,7 +232,7 @@
new IOException(rule + " must create a directory"), Transience.TRANSIENT);
}
- if (!outputDirectory.getRelative(LabelConstants.WORKSPACE_FILE_NAME).exists()) {
+ if (!WorkspaceFileHelper.doesWorkspaceFileExistUnder(outputDirectory)) {
createWorkspaceFile(outputDirectory, rule.getTargetKind(), rule.getName());
}
diff --git a/src/main/java/com/google/devtools/build/lib/cmdline/LabelConstants.java b/src/main/java/com/google/devtools/build/lib/cmdline/LabelConstants.java
index b6c3d72..042b049 100644
--- a/src/main/java/com/google/devtools/build/lib/cmdline/LabelConstants.java
+++ b/src/main/java/com/google/devtools/build/lib/cmdline/LabelConstants.java
@@ -22,5 +22,7 @@
PackageIdentifier.createInMainRepo(EXTERNAL_PACKAGE_NAME);
public static final PathFragment EXTERNAL_PATH_PREFIX = PathFragment.create("external");
public static final PathFragment WORKSPACE_FILE_NAME = PathFragment.create("WORKSPACE");
+ public static final PathFragment WORKSPACE_DOT_BAZEL_FILE_NAME =
+ PathFragment.create("WORKSPACE.bazel");
public static final String DEFAULT_REPOSITORY_DIRECTORY = "__main__";
}
diff --git a/src/main/java/com/google/devtools/build/lib/packages/BuildFileName.java b/src/main/java/com/google/devtools/build/lib/packages/BuildFileName.java
index a229429..627ff36 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/BuildFileName.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/BuildFileName.java
@@ -24,6 +24,12 @@
return getFilenameFragment();
}
},
+ WORKSPACE_DOT_BAZEL("WORKSPACE.bazel") {
+ @Override
+ public PathFragment getBuildFileFragment(PackageIdentifier packageIdentifier) {
+ return getFilenameFragment();
+ }
+ },
BUILD("BUILD") {
@Override
public PathFragment getBuildFileFragment(PackageIdentifier packageIdentifier) {
diff --git a/src/main/java/com/google/devtools/build/lib/packages/Package.java b/src/main/java/com/google/devtools/build/lib/packages/Package.java
index 8785c0a..9d6e6a4 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/Package.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/Package.java
@@ -381,9 +381,11 @@
this.packageDirectory = filename.asPath().getParentDirectory();
this.sourceRoot = getSourceRoot(filename, packageIdentifier.getSourceRoot());
+ String baseName = filename.getRootRelativePath().getBaseName();
if ((sourceRoot.asPath() == null
|| !sourceRoot.getRelative(packageIdentifier.getSourceRoot()).equals(packageDirectory))
- && !filename.getRootRelativePath().getBaseName().equals("WORKSPACE")) {
+ && !(baseName.equals(LabelConstants.WORKSPACE_DOT_BAZEL_FILE_NAME.getPathString())
+ || baseName.equals(LabelConstants.WORKSPACE_FILE_NAME.getPathString()))) {
throw new IllegalArgumentException(
"Invalid BUILD file name for package '"
+ packageIdentifier
diff --git a/src/main/java/com/google/devtools/build/lib/pkgcache/PathPackageLocator.java b/src/main/java/com/google/devtools/build/lib/pkgcache/PathPackageLocator.java
index c8b18c6..8940eea 100644
--- a/src/main/java/com/google/devtools/build/lib/pkgcache/PathPackageLocator.java
+++ b/src/main/java/com/google/devtools/build/lib/pkgcache/PathPackageLocator.java
@@ -250,6 +250,10 @@
AtomicReference<? extends UnixGlob.FilesystemCalls> cache = UnixGlob.DEFAULT_SYSCALLS_REF;
// TODO(bazel-team): correctness in the presence of changes to the location of the WORKSPACE
// file.
+ Path workspaceFile = getFilePath(LabelConstants.WORKSPACE_DOT_BAZEL_FILE_NAME, cache);
+ if (workspaceFile != null) {
+ return workspaceFile;
+ }
return getFilePath(LabelConstants.WORKSPACE_FILE_NAME, cache);
}
diff --git a/src/main/java/com/google/devtools/build/lib/query2/SkyQueryEnvironment.java b/src/main/java/com/google/devtools/build/lib/query2/SkyQueryEnvironment.java
index d8388a9..b42dd40 100644
--- a/src/main/java/com/google/devtools/build/lib/query2/SkyQueryEnvironment.java
+++ b/src/main/java/com/google/devtools/build/lib/query2/SkyQueryEnvironment.java
@@ -86,6 +86,7 @@
import com.google.devtools.build.lib.query2.engine.ThreadSafeOutputFormatterCallback;
import com.google.devtools.build.lib.query2.engine.Uniquifier;
import com.google.devtools.build.lib.query2.query.BlazeTargetAccessor;
+import com.google.devtools.build.lib.rules.repository.WorkspaceFileHelper;
import com.google.devtools.build.lib.skyframe.BlacklistedPackagePrefixesValue;
import com.google.devtools.build.lib.skyframe.ContainingPackageLookupFunction;
import com.google.devtools.build.lib.skyframe.GraphBackedRecursivePackageProvider;
@@ -1110,7 +1111,7 @@
private static Iterable<SkyKey> getPkgLookupKeysForFile(PathFragment originalFileFragment,
PathFragment currentPathFragment) {
if (originalFileFragment.equals(currentPathFragment)
- && originalFileFragment.equals(LabelConstants.WORKSPACE_FILE_NAME)) {
+ && WorkspaceFileHelper.matchWorkspaceFileName(originalFileFragment)) {
// TODO(mschaller): this should not be checked at runtime. These are constants!
Preconditions.checkState(
LabelConstants.WORKSPACE_FILE_NAME
diff --git a/src/main/java/com/google/devtools/build/lib/rules/repository/LocalRepositoryFunction.java b/src/main/java/com/google/devtools/build/lib/rules/repository/LocalRepositoryFunction.java
index 7740ca8..ffd6d22 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/repository/LocalRepositoryFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/repository/LocalRepositoryFunction.java
@@ -19,7 +19,6 @@
import com.google.devtools.build.lib.analysis.BlazeDirectories;
import com.google.devtools.build.lib.analysis.RuleDefinition;
import com.google.devtools.build.lib.events.ExtendedEventHandler.ResolvedEvent;
-import com.google.devtools.build.lib.packages.BuildFileName;
import com.google.devtools.build.lib.packages.Rule;
import com.google.devtools.build.lib.syntax.Printer;
import com.google.devtools.build.lib.vfs.Path;
@@ -117,13 +116,19 @@
@Nullable
protected static FileValue getWorkspaceFile(RootedPath directory, Environment env)
throws RepositoryFunctionException, InterruptedException {
- RootedPath workspaceRootedFile =
- RootedPath.toRootedPath(
- directory.getRoot(),
- directory
- .getRootRelativePath()
- .getRelative(BuildFileName.WORKSPACE.getFilenameFragment()));
-
+ RootedPath workspaceRootedFile;
+ try {
+ workspaceRootedFile = WorkspaceFileHelper.getWorkspaceRootedFile(directory, env);
+ if (workspaceRootedFile == null) {
+ return null;
+ }
+ } catch (IOException e) {
+ throw new RepositoryFunctionException(
+ new IOException(
+ "Could not determine workspace file (\"WORKSPACE.bazel\" or \"WORKSPACE\"): "
+ + e.getMessage()),
+ Transience.PERSISTENT);
+ }
SkyKey workspaceFileKey = FileValue.key(workspaceRootedFile);
FileValue value;
try {
diff --git a/src/main/java/com/google/devtools/build/lib/rules/repository/RepositoryFunction.java b/src/main/java/com/google/devtools/build/lib/rules/repository/RepositoryFunction.java
index 35c4276..d40692a 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/repository/RepositoryFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/repository/RepositoryFunction.java
@@ -31,7 +31,6 @@
import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
import com.google.devtools.build.lib.events.Location;
import com.google.devtools.build.lib.packages.BuildFileContainsErrorsException;
-import com.google.devtools.build.lib.packages.BuildFileName;
import com.google.devtools.build.lib.packages.NoSuchPackageException;
import com.google.devtools.build.lib.packages.Rule;
import com.google.devtools.build.lib.packages.Type;
@@ -417,7 +416,7 @@
Path repositoryDirectory, String ruleKind, String ruleName)
throws RepositoryFunctionException {
try {
- Path workspaceFile = repositoryDirectory.getRelative("WORKSPACE");
+ Path workspaceFile = repositoryDirectory.getRelative(LabelConstants.WORKSPACE_FILE_NAME);
FileSystemUtils.writeContent(workspaceFile, Charset.forName("UTF-8"),
String.format("# DO NOT EDIT: automatically generated WORKSPACE file for %s\n"
+ "workspace(name = \"%s\")\n", ruleKind, ruleName));
@@ -581,7 +580,7 @@
if (isDirectory || repositoryPath.segmentCount() > 1) {
if (!isDirectory
&& rule.getRuleClass().equals(LocalRepositoryRule.NAME)
- && repositoryPath.endsWith(BuildFileName.WORKSPACE.getFilenameFragment())) {
+ && WorkspaceFileHelper.endsWithWorkspaceFileName(repositoryPath)) {
// Ignore this, there is a dependency from LocalRepositoryFunction->WORKSPACE file already
return;
}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/repository/RepositoryLoaderFunction.java b/src/main/java/com/google/devtools/build/lib/rules/repository/RepositoryLoaderFunction.java
index a0c9a16..75f2c71 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/repository/RepositoryLoaderFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/repository/RepositoryLoaderFunction.java
@@ -18,13 +18,14 @@
import com.google.devtools.build.lib.cmdline.RepositoryName;
import com.google.devtools.build.lib.packages.WorkspaceFileValue;
import com.google.devtools.build.lib.skyframe.RepositoryValue;
-import com.google.devtools.build.lib.vfs.PathFragment;
import com.google.devtools.build.lib.vfs.Root;
import com.google.devtools.build.lib.vfs.RootedPath;
import com.google.devtools.build.skyframe.SkyFunction;
import com.google.devtools.build.skyframe.SkyFunctionException;
+import com.google.devtools.build.skyframe.SkyFunctionException.Transience;
import com.google.devtools.build.skyframe.SkyKey;
import com.google.devtools.build.skyframe.SkyValue;
+import java.io.IOException;
import javax.annotation.Nullable;
/** Creates a local or remote repository. */
@@ -46,21 +47,33 @@
if (!repository.repositoryExists()) {
return RepositoryValue.notFound(nameFromRule);
}
-
- SkyKey workspaceKey =
- WorkspaceFileValue.key(
- RootedPath.toRootedPath(
- Root.fromPath(repository.getPath()), PathFragment.create("WORKSPACE")));
+ RootedPath workspaceFilePath;
+ try {
+ workspaceFilePath =
+ WorkspaceFileHelper.getWorkspaceRootedFile(Root.fromPath(repository.getPath()), env);
+ if (workspaceFilePath == null) {
+ return null;
+ }
+ } catch (IOException e) {
+ throw new RepositoryLoaderFunctionException(
+ new IOException(
+ "Could not determine workspace file (\"WORKSPACE.bazel\" or \"WORKSPACE\"): "
+ + e.getMessage()),
+ Transience.PERSISTENT);
+ }
+ SkyKey workspaceKey = WorkspaceFileValue.key(workspaceFilePath);
WorkspaceFileValue workspacePackage = (WorkspaceFileValue) env.getValue(workspaceKey);
if (workspacePackage == null) {
return null;
}
- RepositoryName workspaceName;
try {
String workspaceNameStr = workspacePackage.getPackage().getWorkspaceName();
- workspaceName = workspaceNameStr.isEmpty()
- ? RepositoryName.create("") : RepositoryName.create("@" + workspaceNameStr);
+ if (workspaceNameStr.isEmpty()) {
+ RepositoryName.create("");
+ } else {
+ RepositoryName.create("@" + workspaceNameStr);
+ }
} catch (LabelSyntaxException e) {
throw new IllegalStateException(e);
}
@@ -73,4 +86,13 @@
public String extractTag(SkyKey skyKey) {
return null;
}
+
+ /** An exception thrown by RepositoryLoaderFunction */
+ public static class RepositoryLoaderFunctionException extends SkyFunctionException {
+
+ /** Error reading or writing to the filesystem. */
+ public RepositoryLoaderFunctionException(IOException cause, Transience transience) {
+ super(cause, transience);
+ }
+ }
}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/repository/WorkspaceAttributeMapper.java b/src/main/java/com/google/devtools/build/lib/rules/repository/WorkspaceAttributeMapper.java
index 6acccc2..7936b69 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/repository/WorkspaceAttributeMapper.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/repository/WorkspaceAttributeMapper.java
@@ -17,7 +17,6 @@
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.base.Preconditions;
-import com.google.devtools.build.lib.cmdline.LabelConstants;
import com.google.devtools.build.lib.packages.AggregatingAttributeMapper;
import com.google.devtools.build.lib.packages.BuildType.SelectorList;
import com.google.devtools.build.lib.packages.Rule;
@@ -63,10 +62,7 @@
Object value = rule.getAttributeContainer().getAttr(checkNotNull(attributeName));
if (value instanceof SelectorList) {
String message;
- if (rule.getLocation()
- .getPath()
- .getBaseName()
- .equals(LabelConstants.WORKSPACE_FILE_NAME.getPathString())) {
+ if (WorkspaceFileHelper.matchWorkspaceFileName(rule.getLocation().getPath().getBaseName())) {
message = "select() cannot be used in WORKSPACE files";
} else {
message = "select() cannot be used in macros called from WORKSPACE files";
diff --git a/src/main/java/com/google/devtools/build/lib/rules/repository/WorkspaceFileHelper.java b/src/main/java/com/google/devtools/build/lib/rules/repository/WorkspaceFileHelper.java
new file mode 100644
index 0000000..b8987c6
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/rules/repository/WorkspaceFileHelper.java
@@ -0,0 +1,91 @@
+// Copyright 2018 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.lib.rules.repository;
+
+import com.google.devtools.build.lib.actions.FileValue;
+import com.google.devtools.build.lib.cmdline.LabelConstants;
+import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.lib.vfs.PathFragment;
+import com.google.devtools.build.lib.vfs.Root;
+import com.google.devtools.build.lib.vfs.RootedPath;
+import com.google.devtools.build.skyframe.SkyFunction.Environment;
+import com.google.devtools.build.skyframe.SkyKey;
+import java.io.IOException;
+
+/** A class to help dealing with WORKSPACE.bazel and WORKSAPCE file */
+public class WorkspaceFileHelper {
+
+ public static RootedPath getWorkspaceRootedFile(Root directory, Environment env)
+ throws IOException, InterruptedException {
+ return getWorkspaceRootedFile(
+ RootedPath.toRootedPath(directory, PathFragment.EMPTY_FRAGMENT), env);
+ }
+
+ /**
+ * Get a RootedPath of the WORKSPACE file we should use for a given directory. This function
+ * returns a RootedPath to <directory>/WORKSPACE.bazel file if it exists and it's a regular file
+ * or file symlink, otherwise, return a RootedPath to <directory>/WORKSPACE. The caller of this
+ * function cannot assume the path returned exists, because in the second case we don't check the
+ * FileValue of `WORKSPACE` file.
+ *
+ * @param directory The directory that could contain WORKSPACE.bazel or WORKSPACE file.
+ * @param env Skyframe env
+ * @return A RootedPath to the WORKSPACE file we should use for the given directory.
+ */
+ public static RootedPath getWorkspaceRootedFile(RootedPath directory, Environment env)
+ throws IOException, InterruptedException {
+ RootedPath workspaceRootedFile =
+ RootedPath.toRootedPath(
+ directory.getRoot(),
+ directory
+ .getRootRelativePath()
+ .getRelative(LabelConstants.WORKSPACE_DOT_BAZEL_FILE_NAME));
+
+ SkyKey workspaceFileKey = FileValue.key(workspaceRootedFile);
+ FileValue value;
+ value = (FileValue) env.getValueOrThrow(workspaceFileKey, IOException.class);
+ if (value == null) {
+ return null;
+ }
+
+ if (value.isFile() && !value.isSpecialFile()) {
+ return workspaceRootedFile;
+ }
+
+ return RootedPath.toRootedPath(
+ directory.getRoot(),
+ directory.getRootRelativePath().getRelative(LabelConstants.WORKSPACE_FILE_NAME));
+ }
+
+ public static boolean doesWorkspaceFileExistUnder(Path directory) {
+ return directory.getRelative(LabelConstants.WORKSPACE_DOT_BAZEL_FILE_NAME).exists()
+ || directory.getRelative(LabelConstants.WORKSPACE_FILE_NAME).exists();
+ }
+
+ public static boolean matchWorkspaceFileName(String name) {
+ return matchWorkspaceFileName(PathFragment.create(name));
+ }
+
+ public static boolean matchWorkspaceFileName(PathFragment name) {
+ return name.equals(LabelConstants.WORKSPACE_DOT_BAZEL_FILE_NAME)
+ || name.equals(LabelConstants.WORKSPACE_FILE_NAME);
+ }
+
+ public static boolean endsWithWorkspaceFileName(PathFragment pathFragment) {
+ return pathFragment.endsWith(LabelConstants.WORKSPACE_DOT_BAZEL_FILE_NAME)
+ || pathFragment.endsWith(LabelConstants.WORKSPACE_FILE_NAME);
+ }
+
+ private WorkspaceFileHelper() {}
+}
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/LocalRepositoryLookupFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/LocalRepositoryLookupFunction.java
index c993425..94b54d5 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/LocalRepositoryLookupFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/LocalRepositoryLookupFunction.java
@@ -23,7 +23,6 @@
import com.google.devtools.build.lib.cmdline.RepositoryName;
import com.google.devtools.build.lib.events.Event;
import com.google.devtools.build.lib.packages.AggregatingAttributeMapper;
-import com.google.devtools.build.lib.packages.BuildFileName;
import com.google.devtools.build.lib.packages.BuildFileNotFoundException;
import com.google.devtools.build.lib.packages.ErrorDeterminingRepositoryException;
import com.google.devtools.build.lib.packages.Package;
@@ -32,6 +31,7 @@
import com.google.devtools.build.lib.packages.Type;
import com.google.devtools.build.lib.packages.WorkspaceFileValue;
import com.google.devtools.build.lib.rules.repository.LocalRepositoryRule;
+import com.google.devtools.build.lib.rules.repository.WorkspaceFileHelper;
import com.google.devtools.build.lib.skyframe.PackageFunction.PackageFunctionException;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.PathFragment;
@@ -91,12 +91,10 @@
private Optional<Boolean> maybeGetWorkspaceFileExistence(Environment env, RootedPath directory)
throws InterruptedException, LocalRepositoryLookupFunctionException {
try {
- RootedPath workspaceRootedFile =
- RootedPath.toRootedPath(
- directory.getRoot(),
- directory
- .getRootRelativePath()
- .getRelative(BuildFileName.WORKSPACE.getFilenameFragment()));
+ RootedPath workspaceRootedFile = WorkspaceFileHelper.getWorkspaceRootedFile(directory, env);
+ if (workspaceRootedFile == null) {
+ return Optional.absent();
+ }
FileValue workspaceFileValue =
(FileValue) env.getValueOrThrow(FileValue.key(workspaceRootedFile), IOException.class);
if (workspaceFileValue == null) {
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/PackageFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/PackageFunction.java
index a738236..40bd2f5 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/PackageFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/PackageFunction.java
@@ -53,6 +53,7 @@
import com.google.devtools.build.lib.profiler.Profiler;
import com.google.devtools.build.lib.profiler.ProfilerTask;
import com.google.devtools.build.lib.profiler.SilentCloseable;
+import com.google.devtools.build.lib.rules.repository.WorkspaceFileHelper;
import com.google.devtools.build.lib.skyframe.GlobValue.InvalidGlobPatternException;
import com.google.devtools.build.lib.skyframe.SkylarkImportLookupFunction.SkylarkImportFailedException;
import com.google.devtools.build.lib.skyframe.SkylarkImportLookupValue.SkylarkImportLookupKey;
@@ -291,8 +292,20 @@
if (starlarkSemantics == null) {
return null;
}
- RootedPath workspacePath =
- RootedPath.toRootedPath(packageLookupPath, LabelConstants.WORKSPACE_FILE_NAME);
+ RootedPath workspacePath;
+ try {
+ workspacePath = WorkspaceFileHelper.getWorkspaceRootedFile(packageLookupPath, env);
+ if (workspacePath == null) {
+ return null;
+ }
+ } catch (IOException e) {
+ throw new PackageFunctionException(
+ new NoSuchPackageException(
+ LabelConstants.EXTERNAL_PACKAGE_IDENTIFIER,
+ "Could not determine workspace file (\"WORKSPACE.bazel\" or \"WORKSPACE\"): "
+ + e.getMessage()),
+ Transience.PERSISTENT);
+ }
SkyKey workspaceKey = ExternalPackageFunction.key(workspacePath);
PackageValue workspace = null;
try {
@@ -565,7 +578,9 @@
// Load imported modules in parallel.
List<SkylarkImportLookupKey> importLookupKeys =
Lists.newArrayListWithExpectedSize(loadMap.size());
- boolean inWorkspace = buildFilePath.getRootRelativePath().getBaseName().endsWith("WORKSPACE");
+
+ boolean inWorkspace =
+ WorkspaceFileHelper.endsWithWorkspaceFileName(buildFilePath.getRootRelativePath());
for (Label importLabel : loadMap.values()) {
int originalChunk =
getOriginalWorkspaceChunk(env, buildFilePath, workspaceChunk, importLabel);
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 48fb9e9..d1a20d3 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
@@ -295,17 +295,29 @@
private PackageLookupValue computeWorkspacePackageLookupValue(
Environment env, ImmutableList<Root> packagePathEntries)
throws PackageLookupFunctionException, InterruptedException {
- PackageLookupValue result =
+ PackageLookupValue resultForWorkspaceDotBazel =
+ getPackageLookupValue(
+ env,
+ packagePathEntries,
+ LabelConstants.EXTERNAL_PACKAGE_IDENTIFIER,
+ BuildFileName.WORKSPACE_DOT_BAZEL);
+ if (resultForWorkspaceDotBazel == null) {
+ return null;
+ }
+ if (resultForWorkspaceDotBazel.packageExists()) {
+ return resultForWorkspaceDotBazel;
+ }
+ PackageLookupValue resultForWorkspace =
getPackageLookupValue(
env,
packagePathEntries,
LabelConstants.EXTERNAL_PACKAGE_IDENTIFIER,
BuildFileName.WORKSPACE);
- if (result == null) {
+ if (resultForWorkspace == null) {
return null;
}
- if (result.packageExists()) {
- return result;
+ if (resultForWorkspace.packageExists()) {
+ return resultForWorkspace;
}
// Fall back on the last package path entry if there were any and nothing else worked.
// TODO(kchodorow): get rid of this, the semantics are wrong (successful package lookup should
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/FileFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/FileFunctionTest.java
index e5a0e36..19e8c37 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/FileFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/FileFunctionTest.java
@@ -407,6 +407,7 @@
assertThat(seenFiles)
.containsExactly(
rootedPath("WORKSPACE"),
+ rootedPath("WORKSPACE.bazel"),
rootedPath("a"),
rootedPath(""),
RootedPath.toRootedPath(root, PathFragment.create("/")),
diff --git a/src/test/py/bazel/BUILD b/src/test/py/bazel/BUILD
index 842a3e3..72e1837 100644
--- a/src/test/py/bazel/BUILD
+++ b/src/test/py/bazel/BUILD
@@ -1,8 +1,8 @@
-package(default_visibility = ["//visibility:private"])
-
load("@rules_cc//cc:defs.bzl", "cc_binary")
load("//tools/python:private/defs.bzl", "py_library", "py_test")
+package(default_visibility = ["//visibility:private"])
+
filegroup(
name = "srcs",
srcs = glob(["**"]),
@@ -220,6 +220,12 @@
deps = [":test_base"],
)
+py_test(
+ name = "bazel_workspace_test",
+ srcs = ["bazel_workspace_test.py"],
+ deps = [":test_base"],
+)
+
test_suite(
name = "windows_tests",
tags = [
diff --git a/src/test/py/bazel/bazel_workspace_test.py b/src/test/py/bazel/bazel_workspace_test.py
new file mode 100644
index 0000000..56237d1
--- /dev/null
+++ b/src/test/py/bazel/bazel_workspace_test.py
@@ -0,0 +1,83 @@
+# pylint: disable=g-bad-file-header
+# Copyright 2017 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+import unittest
+from src.test.py.bazel import test_base
+
+
+class BazelWorkspaceTest(test_base.TestBase):
+
+ def testWorkspaceDotBazelFileInMainRepo(self):
+ workspace_dot_bazel = self.ScratchFile("WORKSPACE.bazel")
+ self.ScratchFile("BUILD", [
+ "py_binary(",
+ " name = 'bin',",
+ " srcs = ['bin.py'],",
+ ")",
+ ])
+ self.ScratchFile("bin.py")
+ exit_code, _, stderr = self.RunBazel(["build", "//:bin"])
+ self.AssertExitCode(exit_code, 0, stderr)
+
+ # If WORKSPACE.bazel is deleted and no WORKSPACE exists,
+ # the build should fail.
+ os.remove(workspace_dot_bazel)
+ exit_code, _, stderr = self.RunBazel(["build", "//:bin"])
+ self.AssertExitCode(exit_code, 2, stderr)
+
+ def testWorkspaceDotBazelFileWithExternalRepo(self):
+ self.ScratchDir("A")
+ self.ScratchFile("A/WORKSPACE.bazel")
+ self.ScratchFile("A/BUILD", [
+ "py_library(",
+ " name = 'lib',",
+ " srcs = ['lib.py'],",
+ " visibility = ['//visibility:public'],",
+ ")",
+ ])
+ self.ScratchFile("A/lib.py")
+ work_dir = self.ScratchDir("B")
+ # Test WORKSPACE.bazel takes priority over WORKSPACE
+ self.ScratchFile("B/WORKSPACE")
+ workspace_dot_bazel = self.ScratchFile(
+ "B/WORKSPACE.bazel", ["local_repository(name = 'A', path='../A')"])
+ self.ScratchFile("B/bin.py")
+ self.ScratchFile("B/BUILD", [
+ "py_binary(",
+ " name = 'bin',",
+ " srcs = ['bin.py'],",
+ " deps = ['@A//:lib'],",
+ ")",
+ ])
+ exit_code, _, stderr = self.RunBazel(args=["build", ":bin"], cwd=work_dir)
+ self.AssertExitCode(exit_code, 0, stderr)
+
+ # Test WORKSPACE takes effect after deleting WORKSPACE.bazel
+ os.remove(workspace_dot_bazel)
+ exit_code, _, stderr = self.RunBazel(args=["build", ":bin"], cwd=work_dir)
+ self.AssertExitCode(exit_code, 1, stderr)
+ self.assertIn("no such package '@A//'", "".join(stderr))
+
+ # Test a WORKSPACE.bazel directory won't confuse Bazel
+ self.ScratchFile("B/WORKSPACE",
+ ["local_repository(name = 'A', path='../A')"])
+ self.ScratchDir("B/WORKSPACE.bazel")
+ exit_code, _, stderr = self.RunBazel(args=["build", ":bin"], cwd=work_dir)
+ self.AssertExitCode(exit_code, 0, stderr)
+
+
+if __name__ == "__main__":
+ unittest.main()