Enable load() statement in the WORKSPACE file.
RELNOTES[NEW]: Skylark macros are now enabled in WORKSPACE file.
Design document at https://docs.google.com/document/d/1jKbNXOVp2T1zJD_iRnVr8k5D0xZKgO8blMVDlXOksJg/preview
Fixes #337
--
MOS_MIGRATED_REVID=108860301
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 579e060..a9ad155 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
@@ -688,6 +688,16 @@
}
/**
+ * Derive a LegacyBuilder from a normal Builder.
+ */
+ LegacyBuilder(Builder builder) {
+ super(builder.pkg);
+ if (builder.getFilename() != null) {
+ setFilename(builder.getFilename());
+ }
+ }
+
+ /**
* Sets the globber used for this package's glob expansions.
*/
LegacyBuilder setGlobber(Globber globber) {
@@ -717,8 +727,8 @@
}
}
- public static Builder newExternalPackageBuilder(Path workspacePath, String runfilesPrefix) {
- Builder b = new Builder(new Package(EXTERNAL_PACKAGE_IDENTIFIER, runfilesPrefix));
+ public static LegacyBuilder newExternalPackageBuilder(Path workspacePath, String runfilesPrefix) {
+ LegacyBuilder b = new LegacyBuilder(EXTERNAL_PACKAGE_IDENTIFIER, runfilesPrefix);
b.setFilename(workspacePath);
b.setMakeEnv(new MakeEnvironment.Builder());
return b;
@@ -799,6 +809,11 @@
return pkg.getPackageIdentifier();
}
+ /** Determine if we are in the WORKSPACE file or not */
+ public boolean isWorkspace() {
+ return pkg.getPackageIdentifier().equals(EXTERNAL_PACKAGE_IDENTIFIER);
+ }
+
/**
* Sets the name of this package's BUILD file.
*/
diff --git a/src/main/java/com/google/devtools/build/lib/packages/PackageFactory.java b/src/main/java/com/google/devtools/build/lib/packages/PackageFactory.java
index 987b572..6093a40 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/PackageFactory.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/PackageFactory.java
@@ -390,6 +390,7 @@
this.environmentExtensions = ImmutableList.copyOf(environmentExtensions);
this.packageArguments = createPackageArguments();
this.nativeModule = newNativeModule();
+ this.workspaceNativeModule = WorkspaceFactory.newNativeModule(ruleClassProvider);
}
/**
@@ -1223,10 +1224,11 @@
}
private final ClassObject nativeModule;
+ private final ClassObject workspaceNativeModule;
/** @return the Skylark struct to bind to "native" */
- public ClassObject getNativeModule() {
- return nativeModule;
+ public ClassObject getNativeModule(boolean workspace) {
+ return workspace ? workspaceNativeModule : nativeModule;
}
/**
diff --git a/src/main/java/com/google/devtools/build/lib/packages/RuleFactory.java b/src/main/java/com/google/devtools/build/lib/packages/RuleFactory.java
index d15994a..684289f 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/RuleFactory.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/RuleFactory.java
@@ -101,8 +101,7 @@
} catch (LabelSyntaxException e) {
throw new InvalidRuleException("illegal rule name: " + name + ": " + e.getMessage());
}
- boolean inWorkspaceFile =
- location.getPath() != null && location.getPath().getBaseName().contains("WORKSPACE");
+ boolean inWorkspaceFile = pkgBuilder.isWorkspace();
if (ruleClass.getWorkspaceOnly() && !inWorkspaceFile) {
throw new RuleFactory.InvalidRuleException(
ruleClass + " must be in the WORKSPACE file " + "(used by " + label + ")");
diff --git a/src/main/java/com/google/devtools/build/lib/packages/WorkspaceFactory.java b/src/main/java/com/google/devtools/build/lib/packages/WorkspaceFactory.java
index 4527f5e..4a089c3 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/WorkspaceFactory.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/WorkspaceFactory.java
@@ -17,24 +17,29 @@
import static com.google.devtools.build.lib.syntax.Runtime.NONE;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
import com.google.devtools.build.lib.cmdline.LabelValidator;
import com.google.devtools.build.lib.events.Event;
-import com.google.devtools.build.lib.events.Location;
import com.google.devtools.build.lib.events.StoredEventHandler;
import com.google.devtools.build.lib.packages.Package.Builder;
+import com.google.devtools.build.lib.packages.Package.LegacyBuilder;
import com.google.devtools.build.lib.packages.PackageFactory.EnvironmentExtension;
import com.google.devtools.build.lib.syntax.BaseFunction;
import com.google.devtools.build.lib.syntax.BuildFileAST;
import com.google.devtools.build.lib.syntax.BuiltinFunction;
+import com.google.devtools.build.lib.syntax.ClassObject;
import com.google.devtools.build.lib.syntax.Environment;
+import com.google.devtools.build.lib.syntax.Environment.Extension;
import com.google.devtools.build.lib.syntax.EvalException;
import com.google.devtools.build.lib.syntax.FuncallExpression;
import com.google.devtools.build.lib.syntax.FunctionSignature;
import com.google.devtools.build.lib.syntax.Mutability;
import com.google.devtools.build.lib.syntax.ParserInputSource;
+import com.google.devtools.build.lib.syntax.Runtime;
import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.lib.vfs.PathFragment;
import java.io.File;
import java.util.Map;
@@ -45,8 +50,17 @@
* Parser for WORKSPACE files. Fills in an ExternalPackage.Builder
*/
public class WorkspaceFactory {
- private final Builder builder;
- private final Environment environment;
+ public static final String BIND = "bind";
+
+ private final LegacyBuilder builder;
+ private final StoredEventHandler localReporter;
+
+ private final Path installDir;
+ private final Path workspaceDir;
+ private final Mutability mutability;
+
+ private final ImmutableMap<String, BaseFunction> workspaceFunctions;
+ private final ImmutableList<EnvironmentExtension> environmentExtensions;
/**
* @param builder a builder for the Workspace
@@ -54,8 +68,10 @@
* @param mutability the Mutability for the current evaluation context
*/
public WorkspaceFactory(
- Builder builder, RuleClassProvider ruleClassProvider,
- ImmutableList<EnvironmentExtension> environmentExtensions, Mutability mutability) {
+ LegacyBuilder builder,
+ RuleClassProvider ruleClassProvider,
+ ImmutableList<EnvironmentExtension> environmentExtensions,
+ Mutability mutability) {
this(builder, ruleClassProvider, environmentExtensions, mutability, null, null);
}
@@ -69,62 +85,106 @@
* @param workspaceDir the workspace directory
*/
public WorkspaceFactory(
- Builder builder,
+ LegacyBuilder builder,
RuleClassProvider ruleClassProvider,
ImmutableList<EnvironmentExtension> environmentExtensions,
Mutability mutability,
@Nullable Path installDir,
@Nullable Path workspaceDir) {
this.builder = builder;
- this.environment = createWorkspaceEnv(builder, ruleClassProvider, environmentExtensions,
- mutability, installDir, workspaceDir);
+ this.localReporter = new StoredEventHandler();
+ this.mutability = mutability;
+ this.installDir = installDir;
+ this.workspaceDir = workspaceDir;
+ this.environmentExtensions = environmentExtensions;
+ this.workspaceFunctions = createWorkspaceFunctions(ruleClassProvider);
}
- public void parse(ParserInputSource source)
- throws InterruptedException {
- StoredEventHandler localReporter = new StoredEventHandler();
- BuildFileAST buildFileAST;
+ // State while parsing the WORKSPACE file.
+ // We store them so we can pause the parsing and load skylark imports from the
+ // WorkspaceFileFunction. Loading skylark imports require access to the .skyframe package
+ // which this package cannot depends on.
+ private BuildFileAST buildFileAST = null;
+ private Environment.Builder environmentBuilder = null;
+ private ParserInputSource source = null;
+
+ // Called by com.google.devtools.build.workspace.Resolver from //src/tools/generate_workspace.
+ public void parse(ParserInputSource source) throws InterruptedException {
+ // This method is split in 2 so WorkspaceFileFunction can call the two parts separately and
+ // do the Skylark load imports in between.
+ parseBuildFile(source);
+ execute();
+ }
+
+ public void parseBuildFile(ParserInputSource source) {
+ this.source = source;
buildFileAST = BuildFileAST.parseBuildFile(source, localReporter, false);
if (buildFileAST.containsErrors()) {
+ environmentBuilder = null;
localReporter.handle(Event.error("WORKSPACE file could not be parsed"));
} else {
- if (!buildFileAST.exec(environment, localReporter)) {
+ environmentBuilder =
+ Environment.builder(mutability)
+ .setGlobals(Environment.BUILD)
+ .setEventHandler(localReporter);
+ }
+ }
+
+ public void execute() throws InterruptedException {
+ if (environmentBuilder != null) {
+ Environment workspaceEnv = environmentBuilder.setLoadingPhase().build();
+ addWorkspaceFunctions(workspaceEnv);
+ if (!buildFileAST.exec(workspaceEnv, localReporter)) {
localReporter.handle(Event.error("Error evaluating WORKSPACE file " + source.getPath()));
}
}
+ environmentBuilder = null;
+ buildFileAST = null;
+ source = null;
builder.addEvents(localReporter.getEvents());
if (localReporter.hasErrors()) {
builder.setContainsErrors();
}
+ localReporter.clear();
+ }
+
+ public BuildFileAST getBuildFileAST() {
+ return buildFileAST;
+ }
+
+ public void setImportedExtensions(Map<PathFragment, Extension> importedExtensions) {
+ environmentBuilder.setImportedExtensions(importedExtensions);
}
// TODO(bazel-team): use @SkylarkSignature annotations on a BuiltinFunction.Factory
// for signature + documentation of this and other functions in this file.
- private static BuiltinFunction newWorkspaceNameFunction(final Builder builder) {
- return new BuiltinFunction("workspace",
- FunctionSignature.namedOnly("name"), BuiltinFunction.USE_LOC) {
- public Object invoke(String name, Location loc) throws EvalException {
+ private static BuiltinFunction newWorkspaceNameFunction() {
+ return new BuiltinFunction(
+ "workspace", FunctionSignature.namedOnly("name"), BuiltinFunction.USE_AST_ENV) {
+ public Object invoke(String name, FuncallExpression ast, Environment env)
+ throws EvalException {
String errorMessage = LabelValidator.validateTargetName(name);
if (errorMessage != null) {
- throw new EvalException(loc, errorMessage);
+ throw new EvalException(ast.getLocation(), errorMessage);
}
- builder.setWorkspaceName(name);
+
+ PackageFactory.getContext(env, ast).pkgBuilder.setWorkspaceName(name);
return NONE;
}
};
}
- private static BuiltinFunction newBindFunction(
- final RuleFactory ruleFactory, final Builder builder) {
+ private static BuiltinFunction newBindFunction(final RuleFactory ruleFactory) {
return new BuiltinFunction(
- "bind", FunctionSignature.namedOnly(1, "name", "actual"), BuiltinFunction.USE_LOC) {
- public Object invoke(String name, String actual, Location loc)
+ "bind", FunctionSignature.namedOnly(1, "name", "actual"), BuiltinFunction.USE_AST_ENV) {
+ public Object invoke(String name, String actual, FuncallExpression ast, Environment env)
throws EvalException, InterruptedException {
Label nameLabel = null;
try {
nameLabel = Label.parseAbsolute("//external:" + name);
try {
+ LegacyBuilder builder = PackageFactory.getContext(env, ast).pkgBuilder;
RuleClass ruleClass = ruleFactory.getRuleClass("bind");
builder
.externalPackageData()
@@ -133,16 +193,16 @@
ruleClass,
nameLabel,
actual == null ? null : Label.parseAbsolute(actual),
- loc);
+ ast.getLocation());
} catch (
RuleFactory.InvalidRuleException | Package.NameConflictException
| LabelSyntaxException
e) {
- throw new EvalException(loc, e.getMessage());
+ throw new EvalException(ast.getLocation(), e.getMessage());
}
} catch (LabelSyntaxException e) {
- throw new EvalException(loc, e.getMessage());
+ throw new EvalException(ast.getLocation(), e.getMessage());
}
return NONE;
}
@@ -154,12 +214,13 @@
* specified package context.
*/
private static BuiltinFunction newRuleFunction(
- final RuleFactory ruleFactory, final Builder builder, final String ruleClassName) {
+ final RuleFactory ruleFactory, final String ruleClassName) {
return new BuiltinFunction(
ruleClassName, FunctionSignature.KWARGS, BuiltinFunction.USE_AST_ENV) {
public Object invoke(Map<String, Object> kwargs, FuncallExpression ast, Environment env)
throws EvalException, InterruptedException {
try {
+ Builder builder = PackageFactory.getContext(env, ast).pkgBuilder;
RuleClass ruleClass = ruleFactory.getRuleClass(ruleClassName);
RuleClass bindRuleClass = ruleFactory.getRuleClass("bind");
builder
@@ -175,22 +236,25 @@
};
}
- private Environment createWorkspaceEnv(
- Builder builder,
- RuleClassProvider ruleClassProvider,
- ImmutableList<EnvironmentExtension> environmentExtensions,
- Mutability mutability,
- Path installDir,
- Path workspaceDir) {
- Environment workspaceEnv = Environment.builder(mutability)
- .setGlobals(Environment.BUILD)
- .setLoadingPhase()
- .build();
+ private static ImmutableMap<String, BaseFunction> createWorkspaceFunctions(
+ RuleClassProvider ruleClassProvider) {
+ ImmutableMap.Builder<String, BaseFunction> mapBuilder = ImmutableMap.builder();
RuleFactory ruleFactory = new RuleFactory(ruleClassProvider);
+ mapBuilder.put(BIND, newBindFunction(ruleFactory));
+ for (String ruleClass : ruleFactory.getRuleClassNames()) {
+ if (!ruleClass.equals(BIND)) {
+ BaseFunction ruleFunction = newRuleFunction(ruleFactory, ruleClass);
+ mapBuilder.put(ruleClass, ruleFunction);
+ }
+ }
+ return mapBuilder.build();
+ }
+
+ private void addWorkspaceFunctions(Environment workspaceEnv) {
try {
- for (String ruleClass : ruleFactory.getRuleClassNames()) {
- BaseFunction ruleFunction = newRuleFunction(ruleFactory, builder, ruleClass);
- workspaceEnv.update(ruleClass, ruleFunction);
+ workspaceEnv.update("workspace", newWorkspaceNameFunction());
+ for (Map.Entry<String, BaseFunction> function : workspaceFunctions.entrySet()) {
+ workspaceEnv.update(function.getKey(), function.getValue());
}
if (installDir != null) {
workspaceEnv.update("__embedded_dir__", installDir.getPathString());
@@ -200,16 +264,32 @@
}
File jreDirectory = new File(System.getProperty("java.home"));
workspaceEnv.update("DEFAULT_SERVER_JAVABASE", jreDirectory.getParentFile().toString());
- workspaceEnv.update("bind", newBindFunction(ruleFactory, builder));
- workspaceEnv.update("workspace", newWorkspaceNameFunction(builder));
for (EnvironmentExtension extension : environmentExtensions) {
extension.updateWorkspace(workspaceEnv);
}
-
- return workspaceEnv;
+ workspaceEnv.setupDynamic(
+ PackageFactory.PKG_CONTEXT,
+ new PackageFactory.PackageContext(builder, null, localReporter));
} catch (EvalException e) {
throw new AssertionError(e);
}
}
+
+ private static ClassObject newNativeModule(
+ ImmutableMap<String, BaseFunction> workspaceFunctions) {
+ ImmutableMap.Builder<String, Object> builder = new ImmutableMap.Builder<>();
+ for (String nativeFunction : Runtime.getFunctionNames(SkylarkNativeModule.class)) {
+ builder.put(nativeFunction, Runtime.getFunction(SkylarkNativeModule.class, nativeFunction));
+ }
+ for (Map.Entry<String, BaseFunction> function : workspaceFunctions.entrySet()) {
+ builder.put(function.getKey(), function.getValue());
+ }
+
+ return new ClassObject.SkylarkClassObject(builder.build(), "no native function or rule '%s'");
+ }
+
+ public static ClassObject newNativeModule(RuleClassProvider ruleClassProvider) {
+ return newNativeModule(createWorkspaceFunctions(ruleClassProvider));
+ }
}