Skip to content

Commit 77f8603

Browse files
tjgqcopybara-github
authored andcommitted
Add a flag to disallow materializing an output file as a directory.
PiperOrigin-RevId: 539591651 Change-Id: I25580ffd0b51d5db1f755c53523211d5cd377eb2
1 parent 5100d87 commit 77f8603

File tree

4 files changed

+71
-11
lines changed

4 files changed

+71
-11
lines changed

src/main/java/com/google/devtools/build/lib/analysis/config/CoreOptions.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,17 @@ public class CoreOptions extends FragmentOptions implements Cloneable {
175175
"Check for action prefix file path conflicts, regardless of action-specific overrides.")
176176
public boolean strictConflictChecks;
177177

178+
@Option(
179+
name = "incompatible_disallow_unsound_directory_outputs",
180+
defaultValue = "false",
181+
documentationCategory = OptionDocumentationCategory.UNDOCUMENTED,
182+
metadataTags = OptionMetadataTag.INCOMPATIBLE_CHANGE,
183+
effectTags = {OptionEffectTag.BAZEL_INTERNAL_CONFIGURATION},
184+
help =
185+
"If set, it is an error for an action to materialize an output file as a directory. Does"
186+
+ " not affect source directories.")
187+
public boolean disallowUnsoundDirectoryOutputs;
188+
178189
// This option is only used during execution. However, it is a required input to the analysis
179190
// phase, as otherwise flipping this flag would not invalidate already-executed actions.
180191
@Option(
@@ -997,6 +1008,7 @@ public FragmentOptions getExec() {
9971008
exec.useAutoExecGroups = useAutoExecGroups;
9981009
exec.experimentalWritableOutputs = experimentalWritableOutputs;
9991010
exec.strictConflictChecks = strictConflictChecks;
1011+
exec.disallowUnsoundDirectoryOutputs = disallowUnsoundDirectoryOutputs;
10001012

10011013
// === Runfiles ===
10021014
exec.buildRunfilesManifests = buildRunfilesManifests;

src/main/java/com/google/devtools/build/lib/skyframe/SkyframeActionExecutor.java

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@
8383
import com.google.devtools.build.lib.collect.nestedset.NestedSet;
8484
import com.google.devtools.build.lib.events.Event;
8585
import com.google.devtools.build.lib.events.EventHandler;
86+
import com.google.devtools.build.lib.events.EventKind;
8687
import com.google.devtools.build.lib.events.ExtendedEventHandler;
8788
import com.google.devtools.build.lib.events.Reporter;
8889
import com.google.devtools.build.lib.profiler.Profiler;
@@ -1504,7 +1505,7 @@ private static void flushActionFileSystem(
15041505
* Validates that all action outputs were created or intentionally omitted. This can result in
15051506
* chmod calls on the output files; see {@link ActionMetadataHandler}.
15061507
*
1507-
* @return false if some outputs are missing, true - otherwise.
1508+
* @return false if some outputs are missing or invalid, true - otherwise.
15081509
*/
15091510
private boolean checkOutputs(
15101511
Action action,
@@ -1522,7 +1523,9 @@ private boolean checkOutputs(
15221523
try {
15231524
FileArtifactValue metadata = outputMetadataStore.getOutputMetadata(output);
15241525

1525-
checkForUnsoundDirectoryOutput(action, output, metadata);
1526+
if (!checkForUnsoundDirectoryOutput(action, output, metadata)) {
1527+
return false;
1528+
}
15261529

15271530
addOutputToMetrics(
15281531
output,
@@ -1634,19 +1637,26 @@ private void checkForUnsoundDirectoryInputs(Action action, InputMetadataProvider
16341637
}
16351638
}
16361639

1637-
private void checkForUnsoundDirectoryOutput(
1640+
private boolean checkForUnsoundDirectoryOutput(
16381641
Action action, Artifact output, FileArtifactValue metadata) {
1642+
boolean success = true;
16391643
if (!output.isDirectory() && !output.isSymlink() && metadata.getType().isDirectory()) {
1644+
boolean asError = options.getOptions(CoreOptions.class).disallowUnsoundDirectoryOutputs;
16401645
String ownerString = action.getOwner().getLabel().toString();
16411646
reporter.handle(
1642-
Event.warn(
1647+
Event.of(
1648+
asError ? EventKind.ERROR : EventKind.WARNING,
16431649
action.getOwner().getLocation(),
16441650
String.format(
16451651
"output '%s' of %s is a directory; "
16461652
+ "dependency checking of directories is unsound",
16471653
output.prettyPrint(), ownerString))
16481654
.withTag(ownerString));
1655+
if (asError) {
1656+
success = false;
1657+
}
16491658
}
1659+
return success;
16501660
}
16511661

16521662
/**

src/test/java/com/google/devtools/build/lib/buildtool/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ java_test(
199199
"no_windows",
200200
],
201201
deps = [
202+
"//src/main/java/com/google/devtools/build/lib/actions",
202203
"//src/test/java/com/google/devtools/build/lib/buildtool/util",
203204
"//third_party:junit4",
204205
],

src/test/java/com/google/devtools/build/lib/buildtool/DirectoryArtifactWarningTest.java

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
// limitations under the License.
1414
package com.google.devtools.build.lib.buildtool;
1515

16+
import static org.junit.Assert.assertThrows;
17+
18+
import com.google.devtools.build.lib.actions.BuildFailedException;
1619
import com.google.devtools.build.lib.buildtool.util.BuildIntegrationTestCase;
1720
import org.junit.Test;
1821
import org.junit.runner.RunWith;
@@ -24,23 +27,39 @@
2427
@RunWith(JUnit4.class)
2528
public class DirectoryArtifactWarningTest extends BuildIntegrationTestCase {
2629

27-
@Test
28-
public void testOutputArtifactDirectoryWarning_forGenrule() throws Exception {
30+
private void setupGenruleWithOutputArtifactDirectory() throws Exception {
2931
write(
3032
"x/BUILD",
3133
"genrule(name = 'x',",
3234
" outs = ['dir'],",
3335
" cmd = 'mkdir $(location dir)',",
3436
" srcs = [])");
37+
}
38+
39+
@Test
40+
public void testOutputArtifactDirectoryWarning_forGenrule() throws Exception {
41+
setupGenruleWithOutputArtifactDirectory();
3542

3643
buildTarget("//x");
3744

38-
events.assertContainsWarning("output 'x/dir' of //x:x is a directory; "
39-
+ "dependency checking of directories is unsound");
45+
events.assertContainsWarning(
46+
"output 'x/dir' of //x:x is a directory; "
47+
+ "dependency checking of directories is unsound");
4048
}
4149

4250
@Test
43-
public void testOutputArtifactDirectoryWarning_forStarlarkRule() throws Exception {
51+
public void testOutputArtifactDirectoryError_forGenrule() throws Exception {
52+
setupGenruleWithOutputArtifactDirectory();
53+
54+
addOptions("--incompatible_disallow_unsound_directory_outputs");
55+
assertThrows(BuildFailedException.class, () -> buildTarget("//x"));
56+
57+
events.assertContainsError(
58+
"output 'x/dir' of //x:x is a directory; "
59+
+ "dependency checking of directories is unsound");
60+
}
61+
62+
private void setupStarlarkRuleWithOutputArtifactDirectory() throws Exception {
4463
write(
4564
"x/defs.bzl",
4665
"def _impl(ctx):",
@@ -56,6 +75,11 @@ public void testOutputArtifactDirectoryWarning_forStarlarkRule() throws Exceptio
5675
" },",
5776
")");
5877
write("x/BUILD", "load('defs.bzl', 'my_rule')", "my_rule(name = 'x', out = 'dir')");
78+
}
79+
80+
@Test
81+
public void testOutputArtifactDirectoryWarning_forStarlarkRule() throws Exception {
82+
setupStarlarkRuleWithOutputArtifactDirectory();
5983

6084
buildTarget("//x");
6185

@@ -64,6 +88,18 @@ public void testOutputArtifactDirectoryWarning_forStarlarkRule() throws Exceptio
6488
+ "dependency checking of directories is unsound");
6589
}
6690

91+
@Test
92+
public void testOutputArtifactDirectoryError_forStarlarkRule() throws Exception {
93+
setupStarlarkRuleWithOutputArtifactDirectory();
94+
95+
addOptions("--incompatible_disallow_unsound_directory_outputs");
96+
assertThrows(BuildFailedException.class, () -> buildTarget("//x"));
97+
98+
events.assertContainsError(
99+
"output 'x/dir' of //x:x is a directory; "
100+
+ "dependency checking of directories is unsound");
101+
}
102+
67103
@Test
68104
public void testInputArtifactDirectoryWarning_forGenrule() throws Exception {
69105
write(
@@ -76,8 +112,9 @@ public void testInputArtifactDirectoryWarning_forGenrule() throws Exception {
76112

77113
buildTarget("//x");
78114

79-
events.assertContainsWarning("input 'x/dir' to //x:x is a directory; "
80-
+ "dependency checking of directories is unsound");
115+
events.assertContainsWarning(
116+
"input 'x/dir' to //x:x is a directory; "
117+
+ "dependency checking of directories is unsound");
81118
}
82119

83120
@Test

0 commit comments

Comments
 (0)