Skip to content

Commit 27b86c9

Browse files
committed
Consider invalid entries in the marker file reason for refetch
Especially due to #23127, older marker files may contain entries that don't even parse correctly. Instead of crashing Bazel, we should signal a refetch. Fixes #23322. Closes #23336. PiperOrigin-RevId: 666416942 Change-Id: Ie5507654f69825d93f9523d9c209d417fb3cdaf6
1 parent ab63737 commit 27b86c9

File tree

3 files changed

+134
-20
lines changed

3 files changed

+134
-20
lines changed

src/main/java/com/google/devtools/build/lib/rules/repository/RepoRecordedInput.java

+85-17
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import com.google.devtools.build.lib.actions.FileValue;
2727
import com.google.devtools.build.lib.analysis.BlazeDirectories;
2828
import com.google.devtools.build.lib.cmdline.LabelConstants;
29+
import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
2930
import com.google.devtools.build.lib.cmdline.LabelValidator;
3031
import com.google.devtools.build.lib.cmdline.RepositoryName;
3132
import com.google.devtools.build.lib.skyframe.ActionEnvironmentFunction;
@@ -76,25 +77,32 @@ public abstract static class Parser {
7677

7778
/**
7879
* Parses a recorded input from the post-colon substring that identifies the specific input: for
79-
* example, the {@code MY_ENV_VAR} part of {@code ENV:MY_ENV_VAR}.
80+
* example, the {@code MY_ENV_VAR} part of {@code ENV:MY_ENV_VAR}. Returns null if the parsed
81+
* part is invalid.
8082
*/
8183
public abstract RepoRecordedInput parse(String s);
8284
}
8385

8486
private static final Comparator<RepoRecordedInput> COMPARATOR =
85-
Comparator.comparing((RepoRecordedInput rri) -> rri.getParser().getPrefix())
86-
.thenComparing(RepoRecordedInput::toStringInternal);
87+
(o1, o2) ->
88+
o1 == o2
89+
? 0
90+
: Comparator.comparing((RepoRecordedInput rri) -> rri.getParser().getPrefix())
91+
.thenComparing(RepoRecordedInput::toStringInternal)
92+
.compare(o1, o2);
8793

8894
/**
8995
* Parses a recorded input from its string representation.
9096
*
9197
* @param s the string representation
92-
* @return The parsed recorded input object, or {@code null} if the string representation is
93-
* invalid
98+
* @return The parsed recorded input object, or {@link #NEVER_UP_TO_DATE} if the string
99+
* representation is invalid
94100
*/
95-
@Nullable
96101
public static RepoRecordedInput parse(String s) {
97102
List<String> parts = Splitter.on(':').limit(2).splitToList(s);
103+
if (parts.size() < 2) {
104+
return NEVER_UP_TO_DATE;
105+
}
98106
for (Parser parser :
99107
new Parser[] {
100108
File.PARSER, Dirents.PARSER, DirTree.PARSER, EnvVar.PARSER, RecordedRepoMapping.PARSER
@@ -103,7 +111,7 @@ public static RepoRecordedInput parse(String s) {
103111
return parser.parse(parts.get(1));
104112
}
105113
}
106-
return null;
114+
return NEVER_UP_TO_DATE;
107115
}
108116

109117
/**
@@ -172,6 +180,42 @@ public abstract boolean isUpToDate(
172180
Environment env, BlazeDirectories directories, @Nullable String oldValue)
173181
throws InterruptedException;
174182

183+
/** A sentinel "input" that's always out-of-date to signify parse failure. */
184+
public static final RepoRecordedInput NEVER_UP_TO_DATE =
185+
new RepoRecordedInput() {
186+
@Override
187+
public boolean equals(Object obj) {
188+
return this == obj;
189+
}
190+
191+
@Override
192+
public int hashCode() {
193+
return 12345678;
194+
}
195+
196+
@Override
197+
public String toStringInternal() {
198+
throw new UnsupportedOperationException("this sentinel input should never be serialized");
199+
}
200+
201+
@Override
202+
public Parser getParser() {
203+
throw new UnsupportedOperationException("this sentinel input should never be parsed");
204+
}
205+
206+
@Override
207+
public SkyKey getSkyKey(BlazeDirectories directories) {
208+
// Return a random SkyKey to satisfy the contract.
209+
return PrecomputedValue.STARLARK_SEMANTICS.getKey();
210+
}
211+
212+
@Override
213+
public boolean isUpToDate(
214+
Environment env, BlazeDirectories directories, @Nullable String oldValue) {
215+
return false;
216+
}
217+
};
218+
175219
/**
176220
* Represents a filesystem path stored in a way that is repo-cache-friendly. That is, if the path
177221
* happens to point inside the current Bazel workspace (in either the main repo or an external
@@ -213,12 +257,12 @@ public final String toString() {
213257
return repoName().map(repoName -> repoName + "//" + path()).orElse(path().toString());
214258
}
215259

216-
public static RepoCacheFriendlyPath parse(String s) {
260+
public static RepoCacheFriendlyPath parse(String s) throws LabelSyntaxException {
217261
if (LabelValidator.isAbsolute(s)) {
218262
int doubleSlash = s.indexOf("//");
219263
int skipAts = s.startsWith("@@") ? 2 : s.startsWith("@") ? 1 : 0;
220264
return createInsideWorkspace(
221-
RepositoryName.createUnvalidated(s.substring(skipAts, doubleSlash)),
265+
RepositoryName.create(s.substring(skipAts, doubleSlash)),
222266
PathFragment.create(s.substring(doubleSlash + 2)));
223267
}
224268
return createOutsideWorkspace(PathFragment.create(s));
@@ -264,7 +308,12 @@ public String getPrefix() {
264308

265309
@Override
266310
public RepoRecordedInput parse(String s) {
267-
return new File(RepoCacheFriendlyPath.parse(s));
311+
try {
312+
return new File(RepoCacheFriendlyPath.parse(s));
313+
} catch (LabelSyntaxException e) {
314+
// malformed inputs cause refetch
315+
return NEVER_UP_TO_DATE;
316+
}
268317
}
269318
};
270319

@@ -355,7 +404,12 @@ public String getPrefix() {
355404

356405
@Override
357406
public RepoRecordedInput parse(String s) {
358-
return new Dirents(RepoCacheFriendlyPath.parse(s));
407+
try {
408+
return new Dirents(RepoCacheFriendlyPath.parse(s));
409+
} catch (LabelSyntaxException e) {
410+
// malformed inputs cause refetch
411+
return NEVER_UP_TO_DATE;
412+
}
359413
}
360414
};
361415

@@ -439,7 +493,12 @@ public String getPrefix() {
439493

440494
@Override
441495
public RepoRecordedInput parse(String s) {
442-
return new DirTree(RepoCacheFriendlyPath.parse(s));
496+
try {
497+
return new DirTree(RepoCacheFriendlyPath.parse(s));
498+
} catch (LabelSyntaxException e) {
499+
// malformed inputs cause refetch
500+
return NEVER_UP_TO_DATE;
501+
}
443502
}
444503
};
445504

@@ -578,8 +637,12 @@ public String getPrefix() {
578637
@Override
579638
public RepoRecordedInput parse(String s) {
580639
List<String> parts = Splitter.on(',').limit(2).splitToList(s);
581-
return new RecordedRepoMapping(
582-
RepositoryName.createUnvalidated(parts.get(0)), parts.get(1));
640+
try {
641+
return new RecordedRepoMapping(RepositoryName.create(parts.get(0)), parts.get(1));
642+
} catch (LabelSyntaxException | IndexOutOfBoundsException e) {
643+
// malformed inputs cause refetch
644+
return NEVER_UP_TO_DATE;
645+
}
583646
}
584647
};
585648

@@ -635,9 +698,14 @@ public boolean isUpToDate(
635698
throws InterruptedException {
636699
RepositoryMappingValue repoMappingValue =
637700
(RepositoryMappingValue) env.getValue(getSkyKey(directories));
638-
return repoMappingValue != RepositoryMappingValue.NOT_FOUND_VALUE
639-
&& RepositoryName.createUnvalidated(oldValue)
640-
.equals(repoMappingValue.getRepositoryMapping().get(apparentName));
701+
try {
702+
return repoMappingValue != RepositoryMappingValue.NOT_FOUND_VALUE
703+
&& RepositoryName.create(oldValue)
704+
.equals(repoMappingValue.getRepositoryMapping().get(apparentName));
705+
} catch (LabelSyntaxException e) {
706+
// malformed old value causes refetch
707+
return false;
708+
}
641709
}
642710
}
643711
}

src/main/java/com/google/devtools/build/lib/rules/repository/RepositoryDelegatorFunction.java

+9-3
Original file line numberDiff line numberDiff line change
@@ -694,19 +694,25 @@ private static String readMarkerFile(
694694

695695
boolean firstLine = true;
696696
for (String line : lines) {
697+
if (line.isEmpty()) {
698+
continue;
699+
}
697700
if (firstLine) {
698701
markerRuleKey = line;
699702
firstLine = false;
700703
} else {
701704
int sChar = line.indexOf(' ');
702705
if (sChar > 0) {
703706
RepoRecordedInput input = RepoRecordedInput.parse(unescape(line.substring(0, sChar)));
704-
if (input == null) {
705-
// ignore invalid entries.
707+
if (!input.equals(RepoRecordedInput.NEVER_UP_TO_DATE)) {
708+
recordedInputValues.put(input, unescape(line.substring(sChar + 1)));
706709
continue;
707710
}
708-
recordedInputValues.put(input, unescape(line.substring(sChar + 1)));
709711
}
712+
// On parse failure, just forget everything else and mark the whole input out of date.
713+
recordedInputValues.clear();
714+
recordedInputValues.put(RepoRecordedInput.NEVER_UP_TO_DATE, "");
715+
break;
710716
}
711717
}
712718
return markerRuleKey;

src/test/shell/bazel/starlark_repository_test.sh

+40
Original file line numberDiff line numberDiff line change
@@ -2954,6 +2954,46 @@ EOF
29542954
expect_log "I see:"
29552955
}
29562956

2957+
function test_bad_marker_file_ignored() {
2958+
# when reading a file in another repo, we should watch it.
2959+
local outside_dir="${TEST_TMPDIR}/outside_dir"
2960+
mkdir -p "${outside_dir}"
2961+
echo nothing > ${outside_dir}/data.txt
2962+
2963+
create_new_workspace
2964+
cat > $(setup_module_dot_bazel) <<EOF
2965+
foo = use_repo_rule("//:r.bzl", "foo")
2966+
foo(name = "foo")
2967+
bar = use_repo_rule("//:r.bzl", "bar")
2968+
bar(name = "bar", data = "nothing")
2969+
EOF
2970+
touch BUILD
2971+
cat > r.bzl <<EOF
2972+
def _foo(rctx):
2973+
rctx.file("BUILD", "filegroup(name='foo')")
2974+
# intentionally grab a file that's not directly addressable by a label
2975+
otherfile = rctx.path(Label("@bar//subpkg:BUILD")).dirname.dirname.get_child("data.txt")
2976+
print("I see: " + rctx.read(otherfile))
2977+
foo=repository_rule(_foo)
2978+
def _bar(rctx):
2979+
rctx.file("subpkg/BUILD")
2980+
rctx.file("data.txt", rctx.attr.data)
2981+
bar=repository_rule(_bar, attrs={"data":attr.string()})
2982+
EOF
2983+
2984+
bazel build @foo >& $TEST_log || fail "expected bazel to succeed"
2985+
expect_log "I see: nothing"
2986+
2987+
local marker_file=$(bazel info output_base)/external/@+_repo_rules+foo.marker
2988+
# the marker file for this repo should contain a reference to "@@+_repo_rules+bar". Mangle that.
2989+
sed -i'' -e 's/@@+_repo_rules+bar/@@LOL@@LOL/g' ${marker_file}
2990+
2991+
# Running Bazel again shouldn't crash, and should result in a refetch.
2992+
bazel shutdown
2993+
bazel build @foo >& $TEST_log || fail "expected bazel to succeed"
2994+
expect_log "I see: nothing"
2995+
}
2996+
29572997
function test_file_watching_in_undefined_repo() {
29582998
create_new_workspace
29592999
cat > MODULE.bazel <<EOF

0 commit comments

Comments
 (0)