Enable Ninja parser to parse validation inputs.
RELNOTES: None
PiperOrigin-RevId: 320089902
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/ninja/lexer/NinjaLexer.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/ninja/lexer/NinjaLexer.java
index fe57c2b..fb77ba0 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/ninja/lexer/NinjaLexer.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/ninja/lexer/NinjaLexer.java
@@ -115,6 +115,9 @@
if (step.tryReadDoublePipe()) {
return push(NinjaToken.PIPE2);
}
+ if (step.tryReadPipeAt()) {
+ return push(NinjaToken.PIPE_AT);
+ }
return push(NinjaToken.PIPE);
case '$':
if (step.trySkipEscapedNewline()) {
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/ninja/lexer/NinjaLexerStep.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/ninja/lexer/NinjaLexerStep.java
index 025a517..a697448 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/ninja/lexer/NinjaLexerStep.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/ninja/lexer/NinjaLexerStep.java
@@ -246,6 +246,15 @@
return false;
}
+ public boolean tryReadPipeAt() {
+ Preconditions.checkState('|' == fragment.byteAt(position));
+ if (checkForward(1, '@')) {
+ end = position + 2;
+ return true;
+ }
+ return false;
+ }
+
public void readText() {
int i = position;
for (; i < fragment.length(); i++) {
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/ninja/lexer/NinjaToken.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/ninja/lexer/NinjaToken.java
index d068595..b812bef 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/ninja/lexer/NinjaToken.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/ninja/lexer/NinjaToken.java
@@ -36,6 +36,7 @@
EQUALS("="),
PIPE("|"),
PIPE2("||"),
+ PIPE_AT("|@"),
INDENT("indent"),
NEWLINE("newline"),
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/ninja/parser/NinjaParser.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/ninja/parser/NinjaParser.java
index a1349ed..c23bed9 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/ninja/parser/NinjaParser.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/ninja/parser/NinjaParser.java
@@ -135,6 +135,7 @@
case NEWLINE:
case PIPE:
case PIPE2:
+ case PIPE_AT:
case TEXT:
case VARIABLE:
throw new UnsupportedOperationException(token.name() + UNSUPPORTED_TOKEN_MESSAGE);
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/ninja/parser/NinjaParserStep.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/ninja/parser/NinjaParserStep.java
index e7ad10e..3913748 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/ninja/parser/NinjaParserStep.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/ninja/parser/NinjaParserStep.java
@@ -233,11 +233,13 @@
}
private enum NinjaTargetParsingPart {
+
OUTPUTS(OutputKind.EXPLICIT, true),
IMPLICIT_OUTPUTS(OutputKind.IMPLICIT, true),
INPUTS(InputKind.EXPLICIT, false),
IMPLICIT_INPUTS(InputKind.IMPLICIT, false),
ORDER_ONLY_INPUTS(InputKind.ORDER_ONLY, false),
+ VALIDATION_INPUTS(InputKind.VALIDATION, false),
RULE_NAME(null, false),
VARIABLES(null, false);
@@ -265,24 +267,42 @@
private static final ImmutableSortedMap<
NinjaTargetParsingPart, ImmutableSortedMap<NinjaToken, NinjaTargetParsingPart>>
TARGET_PARTS_TRANSITIONS_MAP =
- ImmutableSortedMap.of(
- NinjaTargetParsingPart.OUTPUTS,
+ ImmutableSortedMap
+ .<NinjaTargetParsingPart, ImmutableSortedMap<NinjaToken, NinjaTargetParsingPart>>
+ naturalOrder()
+ .put(
+ NinjaTargetParsingPart.OUTPUTS,
ImmutableSortedMap.of(
NinjaToken.PIPE, NinjaTargetParsingPart.IMPLICIT_OUTPUTS,
- NinjaToken.COLON, NinjaTargetParsingPart.RULE_NAME),
- NinjaTargetParsingPart.IMPLICIT_OUTPUTS,
- ImmutableSortedMap.of(NinjaToken.COLON, NinjaTargetParsingPart.RULE_NAME),
- NinjaTargetParsingPart.INPUTS,
+ NinjaToken.COLON, NinjaTargetParsingPart.RULE_NAME))
+ .put(
+ NinjaTargetParsingPart.IMPLICIT_OUTPUTS,
+ ImmutableSortedMap.of(NinjaToken.COLON, NinjaTargetParsingPart.RULE_NAME))
+ // Because there is no specific token separating the rule name from the inputs
+ // (besides a space), there is no entry for transitioning to INPUTS, and transitioning
+ // is instead handled in parseTargetDependenciesPart().
+ .put(
+ NinjaTargetParsingPart.INPUTS,
ImmutableSortedMap.of(
NinjaToken.PIPE, NinjaTargetParsingPart.IMPLICIT_INPUTS,
NinjaToken.PIPE2, NinjaTargetParsingPart.ORDER_ONLY_INPUTS,
- NinjaToken.NEWLINE, NinjaTargetParsingPart.VARIABLES),
- NinjaTargetParsingPart.IMPLICIT_INPUTS,
+ NinjaToken.PIPE_AT, NinjaTargetParsingPart.VALIDATION_INPUTS,
+ NinjaToken.NEWLINE, NinjaTargetParsingPart.VARIABLES))
+ .put(
+ NinjaTargetParsingPart.IMPLICIT_INPUTS,
ImmutableSortedMap.of(
NinjaToken.PIPE2, NinjaTargetParsingPart.ORDER_ONLY_INPUTS,
- NinjaToken.NEWLINE, NinjaTargetParsingPart.VARIABLES),
- NinjaTargetParsingPart.ORDER_ONLY_INPUTS,
- ImmutableSortedMap.of(NinjaToken.NEWLINE, NinjaTargetParsingPart.VARIABLES));
+ NinjaToken.PIPE_AT, NinjaTargetParsingPart.VALIDATION_INPUTS,
+ NinjaToken.NEWLINE, NinjaTargetParsingPart.VARIABLES))
+ .put(
+ NinjaTargetParsingPart.ORDER_ONLY_INPUTS,
+ ImmutableSortedMap.of(
+ NinjaToken.PIPE_AT, NinjaTargetParsingPart.VALIDATION_INPUTS,
+ NinjaToken.NEWLINE, NinjaTargetParsingPart.VARIABLES))
+ .put(
+ NinjaTargetParsingPart.VALIDATION_INPUTS,
+ ImmutableSortedMap.of(NinjaToken.NEWLINE, NinjaTargetParsingPart.VARIABLES))
+ .build();
/**
* Parses Ninja target using {@link NinjaScope} of the file, where it is defined, to expand
@@ -362,29 +382,36 @@
*/
private Map<InputOutputKind, List<NinjaVariableValue>> parseTargetDependenciesPart(
NinjaTarget.Builder builder) throws GenericParsingException {
+
Map<InputOutputKind, List<NinjaVariableValue>> pathValuesMap = Maps.newHashMap();
boolean ruleNameParsed = false;
NinjaTargetParsingPart parsingPart = NinjaTargetParsingPart.OUTPUTS;
+
while (lexer.hasNextToken() && !NinjaTargetParsingPart.VARIABLES.equals(parsingPart)) {
+
if (NinjaTargetParsingPart.RULE_NAME.equals(parsingPart)) {
ruleNameParsed = true;
builder.setRuleName(asString(parseExpected(NinjaToken.IDENTIFIER)));
parsingPart = NinjaTargetParsingPart.INPUTS;
continue;
}
+
List<NinjaVariableValue> paths = parsePaths();
if (paths.isEmpty() && !NinjaTargetParsingPart.INPUTS.equals(parsingPart)) {
throw new GenericParsingException("Expected paths sequence");
}
+
if (!paths.isEmpty()) {
pathValuesMap.put(Preconditions.checkNotNull(parsingPart.getInputOutputKind()), paths);
}
+
if (!lexer.hasNextToken()) {
if (parsingPart.isTransitionRequired()) {
throw new GenericParsingException("Unexpected end of target");
}
break;
}
+
NinjaToken lexicalSeparator = lexer.nextToken();
parsingPart =
Preconditions.checkNotNull(TARGET_PARTS_TRANSITIONS_MAP.get(parsingPart))
@@ -394,11 +421,13 @@
throw new GenericParsingException("Unexpected token: " + lexicalSeparator);
}
}
+
if (!ruleNameParsed) {
throw new GenericParsingException("Expected rule name");
}
Preconditions.checkState(
!lexer.hasNextToken() || NinjaTargetParsingPart.VARIABLES.equals(parsingPart));
+
return pathValuesMap;
}
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/ninja/parser/NinjaTarget.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/ninja/parser/NinjaTarget.java
index b8a3a27..c667954 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/ninja/parser/NinjaTarget.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/ninja/parser/NinjaTarget.java
@@ -165,7 +165,8 @@
public enum InputKind implements InputOutputKind {
EXPLICIT,
IMPLICIT,
- ORDER_ONLY
+ ORDER_ONLY,
+ VALIDATION,
}
/** Enum with possible kinds of outputs. */
@@ -242,6 +243,10 @@
return inputs.get(InputKind.ORDER_ONLY);
}
+ public Collection<PathFragment> getValidationInputs() {
+ return inputs.get(InputKind.VALIDATION);
+ }
+
public long getOffset() {
return offset;
}
diff --git a/src/test/java/com/google/devtools/build/lib/bazel/rules/ninja/NinjaParserStepTest.java b/src/test/java/com/google/devtools/build/lib/bazel/rules/ninja/NinjaParserStepTest.java
index ab3d232..2ec7c33 100644
--- a/src/test/java/com/google/devtools/build/lib/bazel/rules/ninja/NinjaParserStepTest.java
+++ b/src/test/java/com/google/devtools/build/lib/bazel/rules/ninja/NinjaParserStepTest.java
@@ -327,7 +327,7 @@
assertThat(target.getExplicitInputs()).containsExactly(PathFragment.create("input"));
NinjaTarget target1 =
- createParser("build o1 o2 | io1 io2: command i1 i2 | ii1 ii2 || ooi1 ooi2")
+ createParser("build o1 o2 | io1 io2: command i1 i2 | ii1 ii2 || ooi1 ooi2 |@ vi1 vi2")
.parseNinjaTarget(scope, LINE_NUM_AFTER_RULE_DEFS);
assertThat(target1.getRuleName()).isEqualTo("command");
assertThat(target1.getOutputs())
@@ -340,6 +340,8 @@
.containsExactly(PathFragment.create("ii1"), PathFragment.create("ii2"));
assertThat(target1.getOrderOnlyInputs())
.containsExactly(PathFragment.create("ooi1"), PathFragment.create("ooi2"));
+ assertThat(target1.getValidationInputs())
+ .containsExactly(PathFragment.create("vi1"), PathFragment.create("vi2"));
NinjaTarget target2 =
createParser("build output: phony").parseNinjaTarget(scope, LINE_NUM_AFTER_RULE_DEFS);
@@ -362,6 +364,7 @@
testNinjaTargetParsingError("build xxx || yyy: command", "Unexpected token: PIPE2");
testNinjaTargetParsingError("build xxx: command :", "Unexpected token: COLON");
testNinjaTargetParsingError("build xxx: command | || a", "Expected paths sequence");
+ testNinjaTargetParsingError("build xxx: command | |@ a", "Expected paths sequence");
}
@Test