aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java')
-rw-r--r--src/main/java/com/google/devtools/build/lib/packages/PackageFactory.java2
-rw-r--r--src/main/java/com/google/devtools/build/lib/packages/WorkspaceFactory.java2
-rw-r--r--src/main/java/com/google/devtools/build/lib/skyframe/ASTFileLookupFunction.java8
-rw-r--r--src/main/java/com/google/devtools/build/lib/skyframe/PackageFunction.java35
-rw-r--r--src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java3
-rw-r--r--src/main/java/com/google/devtools/build/lib/syntax/BuildFileAST.java100
-rw-r--r--src/main/java/com/google/devtools/build/lib/syntax/Environment.java40
-rw-r--r--src/main/java/com/google/devtools/build/lib/syntax/Parser.java129
8 files changed, 263 insertions, 56 deletions
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 c192738a22..2b09e7b803 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
@@ -989,7 +989,7 @@ public final class PackageFactory {
// Logged messages are used as a testability hook tracing the parsing progress
LOG.fine("Starting to parse " + packageId);
BuildFileAST buildFileAST = BuildFileAST.parseBuildFile(
- preprocessingResult.result, preludeStatements, localReporter, false);
+ preprocessingResult.result, preludeStatements, localReporter, locator, false);
LOG.fine("Finished parsing of " + packageId);
MakeEnvironment.Builder makeEnv = new MakeEnvironment.Builder();
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 22df6d780e..a19b36bd18 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
@@ -75,7 +75,7 @@ public class WorkspaceFactory {
throws InterruptedException {
StoredEventHandler localReporter = new StoredEventHandler();
BuildFileAST buildFileAST;
- buildFileAST = BuildFileAST.parseBuildFile(source, localReporter, false);
+ buildFileAST = BuildFileAST.parseBuildFile(source, localReporter, null, false);
if (buildFileAST.containsErrors()) {
localReporter.handle(Event.error("WORKSPACE file could not be parsed"));
} else {
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ASTFileLookupFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/ASTFileLookupFunction.java
index a7cd395b33..9bc6bf226c 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/ASTFileLookupFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/ASTFileLookupFunction.java
@@ -16,6 +16,7 @@ package com.google.devtools.build.lib.skyframe;
import com.google.common.collect.ImmutableList;
import com.google.devtools.build.lib.cmdline.PackageIdentifier;
+import com.google.devtools.build.lib.packages.CachingPackageLocator;
import com.google.devtools.build.lib.packages.RuleClassProvider;
import com.google.devtools.build.lib.pkgcache.PathPackageLocator;
import com.google.devtools.build.lib.syntax.BuildFileAST;
@@ -111,10 +112,13 @@ public class ASTFileLookupFunction implements SkyFunction {
private final AtomicReference<PathPackageLocator> pkgLocator;
private final RuleClassProvider ruleClassProvider;
+ private final CachingPackageLocator packageManager;
public ASTFileLookupFunction(AtomicReference<PathPackageLocator> pkgLocator,
+ CachingPackageLocator packageManager,
RuleClassProvider ruleClassProvider) {
this.pkgLocator = pkgLocator;
+ this.packageManager = packageManager;
this.ruleClassProvider = ruleClassProvider;
}
@@ -139,7 +143,7 @@ public class ASTFileLookupFunction implements SkyFunction {
if (parseAsSkylark) {
try (Mutability mutability = Mutability.create("validate")) {
ast = BuildFileAST.parseSkylarkFile(path, fileSize, env.getListener(),
- new ValidationEnvironment(
+ packageManager, new ValidationEnvironment(
ruleClassProvider.createSkylarkRuleClassEnvironment(
mutability,
env.getListener(),
@@ -149,7 +153,7 @@ public class ASTFileLookupFunction implements SkyFunction {
.setupDynamic(Runtime.PKG_NAME, Runtime.NONE)));
}
} else {
- ast = BuildFileAST.parseBuildFile(path, fileSize, env.getListener(), false);
+ ast = BuildFileAST.parseBuildFile(path, fileSize, env.getListener(), packageManager, false);
}
} catch (IOException e) {
throw new ASTLookupFunctionException(new ErrorReadingSkylarkExtensionException(
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 427e758b29..eb580be9ba 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
@@ -21,6 +21,7 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
+import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
import com.google.devtools.build.lib.cmdline.PackageIdentifier;
import com.google.devtools.build.lib.cmdline.PackageIdentifier.RepositoryName;
import com.google.devtools.build.lib.events.Event;
@@ -524,6 +525,32 @@ public class PackageFunction implements SkyFunction {
return new PackageValue(pkg);
}
+ /**
+ * Returns true if includes referencing a different repository have already been computed.
+ */
+ private boolean fetchIncludeRepositoryDeps(Environment env, BuildFileAST ast) {
+ boolean ok = true;
+ for (String include : ast.getIncludes()) {
+ Label label;
+ try {
+ label = Label.parseAbsolute(include);
+ } catch (LabelSyntaxException e) {
+ // Ignore. This will be reported when the BUILD file is actually evaluated.
+ continue;
+ }
+ if (!label.getPackageIdentifier().getRepository().isDefault()) {
+ // If this is the default repository, the include refers to the same repository, whose
+ // RepositoryValue is already a dependency of this PackageValue.
+ if (env.getValue(RepositoryValue.key(
+ label.getPackageIdentifier().getRepository())) == null) {
+ ok = false;
+ }
+ }
+ }
+
+ return ok;
+ }
+
// TODO(bazel-team): this should take the AST so we don't parse the file twice.
@Nullable
private SkylarkImportResult discoverSkylarkImports(
@@ -540,16 +567,24 @@ public class PackageFunction implements SkyFunction {
inputSource,
preludeStatements,
eventHandler,
+ /* package locator */ null,
/* parse python */ false);
SkylarkImportResult importResult;
+ boolean includeRepositoriesFetched;
if (eventHandler.hasErrors()) {
importResult =
new SkylarkImportResult(
ImmutableMap.<PathFragment, Extension>of(),
ImmutableList.<Label>of());
+ includeRepositoriesFetched = true;
} else {
importResult =
fetchImportsFromBuildFile(buildFilePath, buildFileFragment, packageId, buildFileAST, env);
+ includeRepositoriesFetched = fetchIncludeRepositoryDeps(env, buildFileAST);
+ }
+
+ if (!includeRepositoriesFetched) {
+ return null;
}
return importResult;
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java
index c16ac7c8ef..252df16a3b 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java
@@ -312,7 +312,8 @@ public abstract class SkyframeExecutor implements WalkableGraphFactory {
map.put(SkyFunctions.DIRECTORY_LISTING, new DirectoryListingFunction());
map.put(SkyFunctions.PACKAGE_LOOKUP, new PackageLookupFunction(deletedPackages));
map.put(SkyFunctions.CONTAINING_PACKAGE_LOOKUP, new ContainingPackageLookupFunction());
- map.put(SkyFunctions.AST_FILE_LOOKUP, new ASTFileLookupFunction(pkgLocator, ruleClassProvider));
+ map.put(SkyFunctions.AST_FILE_LOOKUP, new ASTFileLookupFunction(
+ pkgLocator, packageManager, ruleClassProvider));
map.put(SkyFunctions.SKYLARK_IMPORTS_LOOKUP, new SkylarkImportLookupFunction(
ruleClassProvider, pkgFactory));
map.put(SkyFunctions.GLOB, newGlobFunction());
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/BuildFileAST.java b/src/main/java/com/google/devtools/build/lib/syntax/BuildFileAST.java
index e6d00ef0bc..60e84f94a5 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/BuildFileAST.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/BuildFileAST.java
@@ -15,10 +15,12 @@ package com.google.devtools.build.lib.syntax;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
import com.google.common.hash.HashCode;
import com.google.devtools.build.lib.events.Event;
import com.google.devtools.build.lib.events.EventHandler;
import com.google.devtools.build.lib.events.Location;
+import com.google.devtools.build.lib.packages.CachingPackageLocator;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.PathFragment;
@@ -38,6 +40,8 @@ public class BuildFileAST extends ASTNode {
private ImmutableMap<Location, PathFragment> loads;
+ private ImmutableSet<String> includes;
+
/**
* Whether any errors were encountered during scanning or parsing.
*/
@@ -45,11 +49,11 @@ public class BuildFileAST extends ASTNode {
private final String contentHashCode;
- private BuildFileAST(List<Statement> preludeStatements, Parser.ParseResult result) {
- this(preludeStatements, result, null);
+ private BuildFileAST(Lexer lexer, List<Statement> preludeStatements, Parser.ParseResult result) {
+ this(lexer, preludeStatements, result, null);
}
- private BuildFileAST(List<Statement> preludeStatements,
+ private BuildFileAST(Lexer lexer, List<Statement> preludeStatements,
Parser.ParseResult result, String contentHashCode) {
this.stmts = ImmutableList.<Statement>builder()
.addAll(preludeStatements)
@@ -58,7 +62,42 @@ public class BuildFileAST extends ASTNode {
this.comments = ImmutableList.copyOf(result.comments);
this.containsErrors = result.containsErrors;
this.contentHashCode = contentHashCode;
- setLocation(result.location);
+ if (!result.statements.isEmpty()) {
+ setLocation(lexer.createLocation(
+ result.statements.get(0).getLocation().getStartOffset(),
+ result.statements.get(result.statements.size() - 1).getLocation().getEndOffset()));
+ } else {
+ setLocation(Location.fromPathFragment(lexer.getFilename()));
+ }
+ }
+
+ private ImmutableSet<String> fetchIncludes(List<Statement> stmts) {
+ ImmutableSet.Builder<String> result = new ImmutableSet.Builder<>();
+ for (Statement stmt : stmts) {
+ if (!(stmt instanceof ExpressionStatement)) {
+ continue;
+ }
+
+ ExpressionStatement expr = (ExpressionStatement) stmt;
+ if (!(expr.getExpression() instanceof FuncallExpression)) {
+ continue;
+ }
+
+ FuncallExpression funcall = (FuncallExpression) expr.getExpression();
+ if (!funcall.getFunction().getName().equals("include")
+ || funcall.getArguments().size() != 1) {
+ continue;
+ }
+
+ Expression arg = funcall.getArguments().get(0).value;
+ if (!(arg instanceof StringLiteral)) {
+ continue;
+ }
+
+ result.add(((StringLiteral) arg).getValue());
+ }
+
+ return result.build();
}
/** Collects paths from all load statements */
@@ -106,6 +145,14 @@ public class BuildFileAST extends ASTNode {
return loads;
}
+ public synchronized ImmutableSet<String> getIncludes() {
+ if (includes == null) {
+ includes = fetchIncludes(stmts);
+ }
+
+ return includes;
+ }
+
/**
* Executes this build file in a given Environment.
*
@@ -164,17 +211,17 @@ public class BuildFileAST extends ASTNode {
* @throws IOException if the file cannot not be read.
*/
public static BuildFileAST parseBuildFile(Path buildFile, EventHandler eventHandler,
- boolean parsePython)
+ CachingPackageLocator locator, boolean parsePython)
throws IOException {
- return parseBuildFile(buildFile, buildFile.getFileSize(), eventHandler, parsePython);
+ return parseBuildFile(buildFile, buildFile.getFileSize(), eventHandler, locator, parsePython);
}
public static BuildFileAST parseBuildFile(Path buildFile, long fileSize,
EventHandler eventHandler,
- boolean parsePython)
+ CachingPackageLocator locator, boolean parsePython)
throws IOException {
ParserInputSource inputSource = ParserInputSource.create(buildFile, fileSize);
- return parseBuildFile(inputSource, eventHandler, parsePython);
+ return parseBuildFile(inputSource, eventHandler, locator, parsePython);
}
/**
@@ -184,15 +231,27 @@ public class BuildFileAST extends ASTNode {
public static BuildFileAST parseBuildFile(ParserInputSource input,
List<Statement> preludeStatements,
EventHandler eventHandler,
+ CachingPackageLocator locator,
boolean parsePython) {
- Parser.ParseResult result = Parser.parseFile(input, eventHandler, parsePython);
- return new BuildFileAST(preludeStatements, result);
+ Lexer lexer = new Lexer(input, eventHandler, parsePython);
+ Parser.ParseResult result = Parser.parseFile(lexer, eventHandler, locator, parsePython);
+ return new BuildFileAST(lexer, preludeStatements, result);
}
public static BuildFileAST parseBuildFile(ParserInputSource input, EventHandler eventHandler,
- boolean parsePython) {
- Parser.ParseResult result = Parser.parseFile(input, eventHandler, parsePython);
- return new BuildFileAST(ImmutableList.<Statement>of(), result);
+ CachingPackageLocator locator, boolean parsePython) {
+ Lexer lexer = new Lexer(input, eventHandler, parsePython);
+ Parser.ParseResult result = Parser.parseFile(lexer, eventHandler, locator, parsePython);
+ return new BuildFileAST(lexer, ImmutableList.<Statement>of(), result);
+ }
+
+ /**
+ * Parse the specified build file, returning its AST. All errors during
+ * scanning or parsing will be reported to the reporter.
+ */
+ public static BuildFileAST parseBuildFile(Lexer lexer, EventHandler eventHandler) {
+ Parser.ParseResult result = Parser.parseFile(lexer, eventHandler, null, false);
+ return new BuildFileAST(lexer, ImmutableList.<Statement>of(), result);
}
/**
@@ -202,17 +261,20 @@ public class BuildFileAST extends ASTNode {
* @throws IOException if the file cannot not be read.
*/
public static BuildFileAST parseSkylarkFile(Path file, EventHandler eventHandler,
- ValidationEnvironment validationEnvironment) throws IOException {
- return parseSkylarkFile(file, file.getFileSize(), eventHandler,
+ CachingPackageLocator locator, ValidationEnvironment validationEnvironment)
+ throws IOException {
+ return parseSkylarkFile(file, file.getFileSize(), eventHandler, locator,
validationEnvironment);
}
public static BuildFileAST parseSkylarkFile(Path file, long fileSize, EventHandler eventHandler,
- ValidationEnvironment validationEnvironment) throws IOException {
+ CachingPackageLocator locator, ValidationEnvironment validationEnvironment)
+ throws IOException {
ParserInputSource input = ParserInputSource.create(file, fileSize);
+ Lexer lexer = new Lexer(input, eventHandler, false);
Parser.ParseResult result =
- Parser.parseFileForSkylark(input, eventHandler, validationEnvironment);
- return new BuildFileAST(ImmutableList.<Statement>of(), result,
+ Parser.parseFileForSkylark(lexer, eventHandler, locator, validationEnvironment);
+ return new BuildFileAST(lexer, ImmutableList.<Statement>of(), result,
HashCode.fromBytes(file.getMD5Digest()).toString());
}
@@ -223,7 +285,7 @@ public class BuildFileAST extends ASTNode {
*/
public static boolean checkSyntax(ParserInputSource input,
EventHandler eventHandler, boolean parsePython) {
- return !parseBuildFile(input, eventHandler, parsePython).containsErrors();
+ return !parseBuildFile(input, eventHandler, null, parsePython).containsErrors();
}
/**
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/Environment.java b/src/main/java/com/google/devtools/build/lib/syntax/Environment.java
index 266c2c79aa..89360a765a 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/Environment.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/Environment.java
@@ -19,14 +19,17 @@ import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
+import com.google.devtools.build.lib.cmdline.PackageIdentifier;
import com.google.devtools.build.lib.events.Event;
import com.google.devtools.build.lib.events.EventHandler;
import com.google.devtools.build.lib.events.EventKind;
import com.google.devtools.build.lib.events.Location;
+import com.google.devtools.build.lib.packages.CachingPackageLocator;
import com.google.devtools.build.lib.syntax.Mutability.Freezable;
import com.google.devtools.build.lib.syntax.Mutability.MutabilityException;
import com.google.devtools.build.lib.util.Fingerprint;
import com.google.devtools.build.lib.util.Pair;
+import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.PathFragment;
import java.io.Serializable;
@@ -908,16 +911,45 @@ public final class Environment implements Freezable {
}
};
+ /** Mock package locator class */
+ private static final class EmptyPackageLocator implements CachingPackageLocator {
+ @Override
+ public Path getBuildFileForPackage(PackageIdentifier packageName) {
+ return null;
+ }
+ }
+
+ /** A mock package locator */
+ @VisibleForTesting
+ static final CachingPackageLocator EMPTY_PACKAGE_LOCATOR = new EmptyPackageLocator();
+
+ /**
+ * Creates a Lexer without a supporting file.
+ * @param input a list of lines of code
+ */
+ @VisibleForTesting
+ Lexer createLexer(String... input) {
+ return new Lexer(ParserInputSource.create(Joiner.on("\n").join(input), null),
+ eventHandler);
+ }
+
/**
* Parses some String input without a supporting file, returning statements and comments.
* @param input a list of lines of code
*/
@VisibleForTesting
- Parser.ParseResult parseFileWithComments(String... inputLines) {
- ParserInputSource input = ParserInputSource.create(Joiner.on("\n").join(inputLines), null);
+ Parser.ParseResult parseFileWithComments(String... input) {
return isSkylark
- ? Parser.parseFileForSkylark(input, eventHandler, new ValidationEnvironment(this))
- : Parser.parseFile(input, eventHandler, /*parsePython=*/false);
+ ? Parser.parseFileForSkylark(
+ createLexer(input),
+ eventHandler,
+ EMPTY_PACKAGE_LOCATOR,
+ new ValidationEnvironment(this))
+ : Parser.parseFile(
+ createLexer(input),
+ eventHandler,
+ EMPTY_PACKAGE_LOCATOR,
+ /*parsePython=*/false);
}
/**
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/Parser.java b/src/main/java/com/google/devtools/build/lib/syntax/Parser.java
index d685dcf5fe..2734e4ff6f 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/Parser.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/Parser.java
@@ -23,14 +23,19 @@ import com.google.common.base.Preconditions;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
+import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
import com.google.devtools.build.lib.events.Event;
import com.google.devtools.build.lib.events.EventHandler;
import com.google.devtools.build.lib.events.Location;
+import com.google.devtools.build.lib.packages.CachingPackageLocator;
import com.google.devtools.build.lib.profiler.Profiler;
import com.google.devtools.build.lib.profiler.ProfilerTask;
import com.google.devtools.build.lib.syntax.DictionaryLiteral.DictionaryEntryLiteral;
import com.google.devtools.build.lib.syntax.IfStatement.ConditionalStatements;
+import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.lib.vfs.PathFragment;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
@@ -59,19 +64,14 @@ class Parser {
/** The comments from the parsed file. */
public final List<Comment> comments;
- /** Represents every statement in the file. */
- public final Location location;
-
/** Whether the file contained any errors. */
public final boolean containsErrors;
- public ParseResult(List<Statement> statements, List<Comment> comments, Location location,
- boolean containsErrors) {
+ public ParseResult(List<Statement> statements, List<Comment> comments, boolean containsErrors) {
// No need to copy here; when the object is created, the parser instance is just about to go
// out of scope and be garbage collected.
this.statements = Preconditions.checkNotNull(statements);
this.comments = Preconditions.checkNotNull(comments);
- this.location = location;
this.containsErrors = containsErrors;
}
}
@@ -180,23 +180,28 @@ class Parser {
private int errorsCount;
private boolean recoveryMode; // stop reporting errors until next statement
- private Parser(Lexer lexer, EventHandler eventHandler, ParsingMode parsingMode) {
+ private CachingPackageLocator locator;
+
+ private List<PathFragment> includedFiles;
+
+ private Parser(
+ Lexer lexer,
+ EventHandler eventHandler,
+ CachingPackageLocator locator,
+ ParsingMode parsingMode) {
this.lexer = lexer;
this.eventHandler = eventHandler;
this.parsingMode = parsingMode;
this.tokens = lexer.getTokens().iterator();
this.comments = new ArrayList<>();
+ this.locator = locator;
+ this.includedFiles = new ArrayList<>();
+ this.includedFiles.add(lexer.getFilename());
nextToken();
}
- private static Location locationFromStatements(Lexer lexer, List<Statement> statements) {
- if (!statements.isEmpty()) {
- return lexer.createLocation(
- statements.get(0).getLocation().getStartOffset(),
- statements.get(statements.size() - 1).getLocation().getEndOffset());
- } else {
- return Location.fromPathFragment(lexer.getFilename());
- }
+ private Parser(Lexer lexer, EventHandler eventHandler, CachingPackageLocator locator) {
+ this(lexer, eventHandler, locator, BUILD);
}
/**
@@ -204,13 +209,12 @@ class Parser {
* encountered during parsing are reported via "reporter".
*/
public static ParseResult parseFile(
- ParserInputSource input, EventHandler eventHandler, boolean parsePython) {
- Lexer lexer = new Lexer(input, eventHandler, parsePython);
+ Lexer lexer, EventHandler eventHandler, CachingPackageLocator locator, boolean parsePython) {
ParsingMode parsingMode = parsePython ? PYTHON : BUILD;
- Parser parser = new Parser(lexer, eventHandler, parsingMode);
+ Parser parser = new Parser(lexer, eventHandler, locator, parsingMode);
List<Statement> statements = parser.parseFileInput();
- return new ParseResult(statements, parser.comments, locationFromStatements(lexer, statements),
- parser.errorsCount > 0 || lexer.containsErrors());
+ return new ParseResult(
+ statements, parser.comments, parser.errorsCount > 0 || lexer.containsErrors());
}
/**
@@ -219,11 +223,11 @@ class Parser {
* that are not part of the core BUILD language.
*/
public static ParseResult parseFileForSkylark(
- ParserInputSource input,
+ Lexer lexer,
EventHandler eventHandler,
+ CachingPackageLocator locator,
@Nullable ValidationEnvironment validationEnvironment) {
- Lexer lexer = new Lexer(input, eventHandler, false);
- Parser parser = new Parser(lexer, eventHandler, SKYLARK);
+ Parser parser = new Parser(lexer, eventHandler, locator, SKYLARK);
List<Statement> statements = parser.parseFileInput();
boolean hasSemanticalErrors = false;
try {
@@ -237,7 +241,7 @@ class Parser {
}
hasSemanticalErrors = true;
}
- return new ParseResult(statements, parser.comments, locationFromStatements(lexer, statements),
+ return new ParseResult(statements, parser.comments,
parser.errorsCount > 0 || lexer.containsErrors() || hasSemanticalErrors);
}
@@ -247,8 +251,7 @@ class Parser {
* by newline tokens.
*/
@VisibleForTesting
- public static Expression parseExpression(ParserInputSource input, EventHandler eventHandler) {
- Lexer lexer = new Lexer(input, eventHandler, false);
+ public static Expression parseExpression(Lexer lexer, EventHandler eventHandler) {
Parser parser = new Parser(lexer, eventHandler, null);
Expression result = parser.parseExpression();
while (parser.token.kind == TokenKind.NEWLINE) {
@@ -258,6 +261,10 @@ class Parser {
return result;
}
+ private void addIncludedFiles(List<PathFragment> files) {
+ this.includedFiles.addAll(files);
+ }
+
private void reportError(Location location, String message) {
errorsCount++;
// Limit the number of reported errors to avoid spamming output.
@@ -594,6 +601,60 @@ class Parser {
return setLocation(new DictionaryEntryLiteral(key, value), start, value);
}
+ private ExpressionStatement mocksubincludeExpression(
+ String labelName, String file, Location location) {
+ List<Argument.Passed> args = new ArrayList<>();
+ args.add(setLocation(new Argument.Positional(
+ new StringLiteral(labelName, '"')), location));
+ args.add(setLocation(new Argument.Positional(
+ new StringLiteral(file, '"')), location));
+ Identifier mockIdent = setLocation(new Identifier("mocksubinclude"), location);
+ Expression funCall = new FuncallExpression(null, mockIdent, args);
+ return setLocation(new ExpressionStatement(funCall), location);
+ }
+
+ // parse a file from an include call
+ private void include(String labelName, List<Statement> list, Location location) {
+ if (locator == null) {
+ return;
+ }
+
+ try {
+ Label label = Label.parseAbsolute(labelName);
+ // Note that this doesn't really work if the label belongs to a different repository, because
+ // there is no guarantee that its RepositoryValue has been evaluated. In an ideal world, we
+ // could put a Skyframe dependency the appropriate PackageLookupValue, but we can't do that
+ // because package loading is not completely Skyframized.
+ Path packagePath = locator.getBuildFileForPackage(label.getPackageIdentifier());
+ if (packagePath == null) {
+ reportError(location, "Package '" + label.getPackageIdentifier() + "' not found");
+ list.add(mocksubincludeExpression(labelName, "", location));
+ return;
+ }
+ Path path = packagePath.getParentDirectory();
+ Path file = path.getRelative(label.getName());
+
+ if (this.includedFiles.contains(file.asFragment())) {
+ reportError(location, "Recursive inclusion of file '" + path + "'");
+ return;
+ }
+ ParserInputSource inputSource = ParserInputSource.create(file);
+
+ // Insert call to the mocksubinclude function to get the dependencies right.
+ list.add(mocksubincludeExpression(labelName, file.toString(), location));
+
+ Lexer lexer = new Lexer(inputSource, eventHandler, parsingMode == PYTHON);
+ Parser parser = new Parser(lexer, eventHandler, locator, parsingMode);
+ parser.addIncludedFiles(this.includedFiles);
+ list.addAll(parser.parseFileInput());
+ } catch (LabelSyntaxException e) {
+ reportError(location, "Invalid label '" + labelName + "'");
+ } catch (IOException e) {
+ reportError(location, "Include of '" + labelName + "' failed: " + e.getMessage());
+ list.add(mocksubincludeExpression(labelName, "", location));
+ }
+ }
+
/**
* Parse a String literal value, e.g. "str".
*/
@@ -1023,7 +1084,7 @@ class Parser {
parseTopLevelStatement(list);
}
}
- Profiler.instance().logSimpleTask(startTime, ProfilerTask.SKYLARK_PARSER, "");
+ Profiler.instance().logSimpleTask(startTime, ProfilerTask.SKYLARK_PARSER, includedFiles);
return list;
}
@@ -1113,12 +1174,24 @@ class Parser {
private void parseTopLevelStatement(List<Statement> list) {
// In Python grammar, there is no "top-level statement" and imports are
// considered as "small statements". We are a bit stricter than Python here.
+ int start = token.left;
+
// Check if there is an include
if (token.kind == TokenKind.IDENTIFIER) {
Token identToken = token;
Identifier ident = parseIdent();
- if (ident.getName().equals("load") && token.kind == TokenKind.LPAREN) {
+ if (ident.getName().equals("include")
+ && token.kind == TokenKind.LPAREN
+ && parsingMode == BUILD) {
+ expect(TokenKind.LPAREN);
+ if (token.kind == TokenKind.STRING) {
+ include((String) token.value, list, lexer.createLocation(start, token.right));
+ }
+ expect(TokenKind.STRING);
+ expect(TokenKind.RPAREN);
+ return;
+ } else if (ident.getName().equals("load") && token.kind == TokenKind.LPAREN) {
expect(TokenKind.LPAREN);
parseLoad(list);
return;