Enforce Starlark nested set depth limit in runfiles.merge() and merge_all()
We want to prevent overly deep nested sets which can cause a stack overflow.
Since we are now accessing --nested_set_depth_limit from additional places
(Runfiles as well as Depset), we want to move the option from
NestedSetOptionsModule to BuildLanguageOptions (where, arguably, it ought to
have been from the start). As a nice side effect, since the depth limit no
longer lives in global static state, depth limit tests are now thread-safe
regardless of sharding.
RELNOTES: runfiles.merge() and merge_all() now respect --nested_set_depth_limit.
If you hit the depth limit because you were calling merge() in a loop, use
merge_all() on a sequence of runfiles objects instead.
PiperOrigin-RevId: 364631293
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/Runfiles.java b/src/main/java/com/google/devtools/build/lib/analysis/Runfiles.java
index d8afce2..eca8b41 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/Runfiles.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/Runfiles.java
@@ -33,6 +33,7 @@
import com.google.devtools.build.lib.events.EventHandler;
import com.google.devtools.build.lib.events.EventKind;
import com.google.devtools.build.lib.packages.BuildType;
+import com.google.devtools.build.lib.packages.semantics.BuildLanguageOptions;
import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec.VisibleForSerialization;
import com.google.devtools.build.lib.starlarkbuildapi.RunfilesApi;
@@ -51,6 +52,9 @@
import net.starlark.java.eval.EvalException;
import net.starlark.java.eval.Printer;
import net.starlark.java.eval.Sequence;
+import net.starlark.java.eval.Starlark;
+import net.starlark.java.eval.StarlarkSemantics;
+import net.starlark.java.eval.StarlarkThread;
import net.starlark.java.syntax.Location;
/**
@@ -1064,8 +1068,34 @@
}
}
+ private static void verifyNestedSetDepthLimitHelper(
+ NestedSet<?> nestedSet, String name, int limit) throws EvalException {
+ if (nestedSet.getApproxDepth() > limit) {
+ throw Starlark.errorf(
+ "%s depset depth %d exceeds limit (%d)", name, nestedSet.getApproxDepth(), limit);
+ }
+ }
+
+ /**
+ * Checks that the depth of a Runfiles object's nested sets (artifacts, symlinks, root symlinks,
+ * extra middlemen) does not exceed Starlark's depset depth limit, as specified by {@code
+ * --nested_set_depth_limit}.
+ *
+ * @param semantics Starlark semantics providing {@code --nested_set_depth_limit}
+ * @return this object, in the fluent style
+ * @throws EvalException if a nested set in the Runfiles object exceeds the depth limit
+ */
+ private Runfiles verifyNestedSetDepthLimit(StarlarkSemantics semantics) throws EvalException {
+ int limit = semantics.get(BuildLanguageOptions.NESTED_SET_DEPTH_LIMIT);
+ verifyNestedSetDepthLimitHelper(artifacts, "artifacts", limit);
+ verifyNestedSetDepthLimitHelper(symlinks, "symlinks", limit);
+ verifyNestedSetDepthLimitHelper(rootSymlinks, "root symlinks", limit);
+ verifyNestedSetDepthLimitHelper(extraMiddlemen, "extra middlemen", limit);
+ return this;
+ }
+
@Override
- public Runfiles merge(RunfilesApi other) {
+ public Runfiles merge(RunfilesApi other, StarlarkThread thread) throws EvalException {
Runfiles o = (Runfiles) other;
if (isEmpty()) {
// This is not just a memory / performance optimization. The Builder requires a valid suffix,
@@ -1075,11 +1105,15 @@
} else if (o.isEmpty()) {
return this;
}
- return new Runfiles.Builder(suffix, false).merge(this).merge(o).build();
+ return new Runfiles.Builder(suffix, false)
+ .merge(this)
+ .merge(o)
+ .build()
+ .verifyNestedSetDepthLimit(thread.getSemantics());
}
@Override
- public Runfiles mergeAll(Sequence<?> sequence) throws EvalException {
+ public Runfiles mergeAll(Sequence<?> sequence, StarlarkThread thread) throws EvalException {
// The delayed initialization of the Builder is not just a memory / performance optimization.
// The Builder requires a valid suffix, but the {@code Runfiles.EMPTY} singleton has an invalid
// one, which must not be used to construct a Runfiles.Builder.
@@ -1109,7 +1143,7 @@
if (uniqueNonEmptyMergee != null) {
return uniqueNonEmptyMergee;
} else if (builder != null) {
- return builder.build();
+ return builder.build().verifyNestedSetDepthLimit(thread.getSemantics());
} else {
return EMPTY;
}
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/BUILD b/src/main/java/com/google/devtools/build/lib/bazel/BUILD
index 4cd99a6..d3255ff 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/bazel/BUILD
@@ -135,7 +135,6 @@
"//src/main/java/com/google/devtools/build/lib/bazel/rules",
"//src/main/java/com/google/devtools/build/lib/bazel/rules/ninja/actions",
"//src/main/java/com/google/devtools/build/lib/buildeventservice",
- "//src/main/java/com/google/devtools/build/lib/collect/nestedset:options",
"//src/main/java/com/google/devtools/build/lib/dynamic",
"//src/main/java/com/google/devtools/build/lib/metrics:memory-use-recorder",
"//src/main/java/com/google/devtools/build/lib/metrics:metrics_module",
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/Bazel.java b/src/main/java/com/google/devtools/build/lib/bazel/Bazel.java
index 807d74d..f64dd97 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/Bazel.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/Bazel.java
@@ -53,7 +53,6 @@
StarlarkRepositoryDebugModule.class,
com.google.devtools.build.lib.bazel.debug.WorkspaceRuleModule.class,
com.google.devtools.build.lib.bazel.coverage.BazelCoverageReportModule.class,
- com.google.devtools.build.lib.collect.nestedset.NestedSetOptionsModule.class,
com.google.devtools.build.lib.starlarkdebug.module.StarlarkDebuggerModule.class,
com.google.devtools.build.lib.bazel.repository.RepositoryResolvedModule.class,
com.google.devtools.build.lib.bazel.repository.CacheHitReportingModule.class,
diff --git a/src/main/java/com/google/devtools/build/lib/collect/nestedset/BUILD b/src/main/java/com/google/devtools/build/lib/collect/nestedset/BUILD
index 1a40e07..6b02d36 100644
--- a/src/main/java/com/google/devtools/build/lib/collect/nestedset/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/collect/nestedset/BUILD
@@ -43,17 +43,6 @@
)
java_library(
- name = "options",
- srcs = ["NestedSetOptionsModule.java"],
- deps = [
- ":nestedset",
- "//src/main/java/com/google/devtools/build/lib:runtime",
- "//src/main/java/com/google/devtools/common/options",
- "//third_party:guava",
- ],
-)
-
-java_library(
name = "testutils",
testonly = 1,
srcs = ["NestedSetCodecTestUtils.java"],
diff --git a/src/main/java/com/google/devtools/build/lib/collect/nestedset/Depset.java b/src/main/java/com/google/devtools/build/lib/collect/nestedset/Depset.java
index 7bd10c9..f940ebc 100644
--- a/src/main/java/com/google/devtools/build/lib/collect/nestedset/Depset.java
+++ b/src/main/java/com/google/devtools/build/lib/collect/nestedset/Depset.java
@@ -20,7 +20,6 @@
import com.google.devtools.build.lib.packages.semantics.BuildLanguageOptions;
import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
import java.util.List;
-import java.util.concurrent.atomic.AtomicInteger;
import javax.annotation.Nullable;
import net.starlark.java.annot.StarlarkAnnotations;
import net.starlark.java.annot.StarlarkBuiltin;
@@ -565,7 +564,7 @@
// check depth limit
int depth = result.getSet().getApproxDepth();
- int limit = depthLimit.get();
+ int limit = semantics.get(BuildLanguageOptions.NESTED_SET_DEPTH_LIMIT);
if (depth > limit) {
throw Starlark.errorf("depset depth %d exceeds limit (%d)", depth, limit);
}
@@ -606,21 +605,6 @@
return o instanceof Sequence && ((Sequence) o).isEmpty();
}
- /**
- * Sets the maximum depth for nested sets constructed by the Starlark {@code depset} function (as
- * set by {@code --nested_set_depth_limit}).
- *
- * @return whether the new limit differs from the old
- */
- public static boolean setDepthLimit(int newLimit) {
- int oldValue = depthLimit.getAndSet(newLimit);
- return oldValue != newLimit;
- }
-
- // The effective default value comes from the --nested_set_depth_limit
- // flag in NestedSetOptionsModule, which overrides this.
- private static final AtomicInteger depthLimit = new AtomicInteger(3500);
-
// Delegate equality to the underlying NestedSet. Otherwise, it's possible to create multiple
// Depset instances wrapping the same NestedSet that aren't equal to each other.
diff --git a/src/main/java/com/google/devtools/build/lib/collect/nestedset/NestedSet.java b/src/main/java/com/google/devtools/build/lib/collect/nestedset/NestedSet.java
index d128dac..06ae545 100644
--- a/src/main/java/com/google/devtools/build/lib/collect/nestedset/NestedSet.java
+++ b/src/main/java/com/google/devtools/build/lib/collect/nestedset/NestedSet.java
@@ -369,7 +369,7 @@
* <p>This function may return an overapproximation of the true depth if the NestedSet was derived
* from the result of calling {@link #getNonLeaves} or {@link #splitIfExceedsMaximumSize}.
*/
- int getApproxDepth() {
+ public int getApproxDepth() {
return this.depthAndOrder >>> 2;
}
diff --git a/src/main/java/com/google/devtools/build/lib/collect/nestedset/NestedSetOptionsModule.java b/src/main/java/com/google/devtools/build/lib/collect/nestedset/NestedSetOptionsModule.java
deleted file mode 100644
index 851d0f4..0000000
--- a/src/main/java/com/google/devtools/build/lib/collect/nestedset/NestedSetOptionsModule.java
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright 2019 The Bazel Authors. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.devtools.build.lib.collect.nestedset;
-
-import com.google.common.collect.ImmutableList;
-import com.google.devtools.build.lib.runtime.BlazeModule;
-import com.google.devtools.build.lib.runtime.CommandEnvironment;
-import com.google.devtools.common.options.Option;
-import com.google.devtools.common.options.OptionDocumentationCategory;
-import com.google.devtools.common.options.OptionEffectTag;
-import com.google.devtools.common.options.OptionsBase;
-
-/** A {@link BlazeModule} handling options pertaining to {@link NestedSet}. */
-public class NestedSetOptionsModule extends BlazeModule {
-
- /** Command line options controlling the behavior of {@link NestedSet}. */
- public static final class Options extends OptionsBase {
- @Option(
- name = "nested_set_depth_limit",
- defaultValue = "3500",
- documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
- effectTags = {OptionEffectTag.LOADING_AND_ANALYSIS},
- help =
- "The maximum depth of the graph internal to a depset (also known as NestedSet), above"
- + " which the depset() constructor will fail.")
- public int nestedSetDepthLimit;
- }
-
- @Override
- public void beforeCommand(CommandEnvironment env) {
- Options options = env.getOptions().getOptions(Options.class);
- boolean changed = Depset.setDepthLimit(options.nestedSetDepthLimit);
- if (changed) {
- env.getSkyframeExecutor().resetEvaluator();
- }
- }
-
- @Override
- public Iterable<Class<? extends OptionsBase>> getCommonCommandOptions() {
- return ImmutableList.of(Options.class);
- }
-}
diff --git a/src/main/java/com/google/devtools/build/lib/packages/semantics/BuildLanguageOptions.java b/src/main/java/com/google/devtools/build/lib/packages/semantics/BuildLanguageOptions.java
index c5316e8..8941b1ad 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/semantics/BuildLanguageOptions.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/semantics/BuildLanguageOptions.java
@@ -605,6 +605,16 @@
public long maxComputationSteps;
@Option(
+ name = "nested_set_depth_limit",
+ defaultValue = "3500",
+ documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
+ effectTags = {OptionEffectTag.LOADING_AND_ANALYSIS},
+ help =
+ "The maximum depth of the graph internal to a depset (also known as NestedSet), above"
+ + " which the depset() constructor will fail.")
+ public int nestedSetDepthLimit;
+
+ @Option(
name = "experimental_shadowed_action",
defaultValue = "false",
documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
@@ -685,6 +695,7 @@
.setBool(INCOMPATIBLE_RESTRICT_STRING_ESCAPES, incompatibleRestrictStringEscapes)
.setBool(INCOMPATIBLE_LINKOPTS_TO_LINKLIBS, incompatibleLinkoptsToLinklibs)
.set(MAX_COMPUTATION_STEPS, maxComputationSteps)
+ .set(NESTED_SET_DEPTH_LIMIT, nestedSetDepthLimit)
.setBool(EXPERIMENTAL_SHADOWED_ACTION, experimentalShadowedAction)
.build();
return INTERNER.intern(semantics);
@@ -771,4 +782,6 @@
"experimental_cc_starlark_api_enabled_packages", ImmutableList.of());
public static final StarlarkSemantics.Key<Long> MAX_COMPUTATION_STEPS =
new StarlarkSemantics.Key<>("max_computation_steps", 0L);
+ public static final StarlarkSemantics.Key<Integer> NESTED_SET_DEPTH_LIMIT =
+ new StarlarkSemantics.Key<>("nested_set_depth_limit", 3500);
}
diff --git a/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/RunfilesApi.java b/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/RunfilesApi.java
index df92dea..4d43026 100644
--- a/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/RunfilesApi.java
+++ b/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/RunfilesApi.java
@@ -22,6 +22,7 @@
import net.starlark.java.annot.StarlarkMethod;
import net.starlark.java.eval.EvalException;
import net.starlark.java.eval.Sequence;
+import net.starlark.java.eval.StarlarkThread;
import net.starlark.java.eval.StarlarkValue;
/** An interface for a set of runfiles. */
@@ -69,8 +70,9 @@
positional = true,
named = false,
doc = "The runfiles object to merge into this."),
- })
- RunfilesApi merge(RunfilesApi other);
+ },
+ useStarlarkThread = true)
+ RunfilesApi merge(RunfilesApi other, StarlarkThread thread) throws EvalException;
@StarlarkMethod(
name = "merge_all",
@@ -86,6 +88,7 @@
positional = true,
named = false,
doc = "The sequence of runfiles objects to merge into this."),
- })
- RunfilesApi mergeAll(Sequence<?> sequence) throws EvalException;
+ },
+ useStarlarkThread = true)
+ RunfilesApi mergeAll(Sequence<?> sequence, StarlarkThread thread) throws EvalException;
}
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/BUILD b/src/test/java/com/google/devtools/build/lib/analysis/BUILD
index 555f696..e031705 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/BUILD
+++ b/src/test/java/com/google/devtools/build/lib/analysis/BUILD
@@ -118,6 +118,7 @@
"//src/main/java/com/google/devtools/build/lib/events",
"//src/main/java/com/google/devtools/build/lib/exec:bin_tools",
"//src/main/java/com/google/devtools/build/lib/packages",
+ "//src/main/java/com/google/devtools/build/lib/packages/semantics",
"//src/main/java/com/google/devtools/build/lib/pkgcache",
"//src/main/java/com/google/devtools/build/lib/rules/android",
"//src/main/java/com/google/devtools/build/lib/rules/cpp",
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/RunfilesTest.java b/src/test/java/com/google/devtools/build/lib/analysis/RunfilesTest.java
index 69d6620..840a16d 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/RunfilesTest.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/RunfilesTest.java
@@ -15,6 +15,7 @@
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
+import static org.junit.Assert.assertThrows;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@@ -28,15 +29,21 @@
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.cmdline.LabelConstants;
import com.google.devtools.build.lib.events.EventKind;
+import com.google.devtools.build.lib.packages.semantics.BuildLanguageOptions;
import com.google.devtools.build.lib.testutil.FoundationTestCase;
import com.google.devtools.build.lib.vfs.PathFragment;
import com.google.devtools.build.lib.vfs.Root;
import com.google.devtools.build.skyframe.SkyFunctionName;
+import com.google.devtools.common.options.Options;
+import com.google.devtools.common.options.OptionsParsingException;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.annotation.Nullable;
+import net.starlark.java.eval.EvalException;
+import net.starlark.java.eval.Mutability;
import net.starlark.java.eval.StarlarkList;
+import net.starlark.java.eval.StarlarkThread;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -55,6 +62,13 @@
assertThat(Iterables.getOnlyElement(eventCollector).getKind()).isEqualTo(EventKind.WARNING);
}
+ private static StarlarkThread newStarlarkThread(String... options)
+ throws OptionsParsingException {
+ return new StarlarkThread(
+ Mutability.create("test"),
+ Options.parse(BuildLanguageOptions.class, options).getOptions().toStarlarkSemantics());
+ }
+
@Test
public void testFilterListForObscuringSymlinksCatchesBadObscurer() throws Exception {
Map<PathFragment, Artifact> obscuringMap = new HashMap<>();
@@ -463,7 +477,7 @@
}
@Test
- public void testMergeWithSymlinks() {
+ public void testMergeWithSymlinks() throws Exception {
ArtifactRoot root = ArtifactRoot.asSourceRoot(Root.fromPath(scratch.resolve("/workspace")));
Artifact artifactA = ActionsTestUtil.createArtifact(root, "a/target");
Artifact artifactB = ActionsTestUtil.createArtifact(root, "b/target");
@@ -475,8 +489,9 @@
Runfiles runfilesB = new Runfiles.Builder("TESTING")
.addSymlink(sympathB, artifactB)
.build();
+ StarlarkThread thread = newStarlarkThread();
- Runfiles runfilesC = runfilesA.merge(runfilesB);
+ Runfiles runfilesC = runfilesA.merge(runfilesB, thread);
assertThat(runfilesC.getSymlinksAsMap(null).get(sympathA)).isEqualTo(artifactA);
assertThat(runfilesC.getSymlinksAsMap(null).get(sympathB)).isEqualTo(artifactB);
}
@@ -493,19 +508,23 @@
Runfiles runfilesA = new Runfiles.Builder("TESTING").addSymlink(sympathA, artifactA).build();
Runfiles runfilesB = new Runfiles.Builder("TESTING").addSymlink(sympathB, artifactB).build();
Runfiles runfilesC = new Runfiles.Builder("TESTING").addSymlink(sympathC, artifactC).build();
+ StarlarkThread thread = newStarlarkThread();
- Runfiles runfilesMerged = runfilesA.mergeAll(StarlarkList.immutableOf(runfilesB, runfilesC));
+ Runfiles runfilesMerged =
+ runfilesA.mergeAll(StarlarkList.immutableOf(runfilesB, runfilesC), thread);
assertThat(runfilesMerged.getSymlinksAsMap(null))
.containsExactly(sympathA, artifactA, sympathB, artifactB, sympathC, artifactC);
}
@Test
- public void testMergeEmptyWithNonEmpty() {
+ public void testMergeEmptyWithNonEmpty() throws Exception {
ArtifactRoot root = ArtifactRoot.asSourceRoot(Root.fromPath(scratch.resolve("/workspace")));
Artifact artifactA = ActionsTestUtil.createArtifact(root, "a/target");
Runfiles runfilesB = new Runfiles.Builder("TESTING").addArtifact(artifactA).build();
- assertThat(Runfiles.EMPTY.merge(runfilesB)).isSameInstanceAs(runfilesB);
- assertThat(runfilesB.merge(Runfiles.EMPTY)).isSameInstanceAs(runfilesB);
+ StarlarkThread thread = newStarlarkThread();
+
+ assertThat(Runfiles.EMPTY.merge(runfilesB, thread)).isSameInstanceAs(runfilesB);
+ assertThat(runfilesB.merge(Runfiles.EMPTY, thread)).isSameInstanceAs(runfilesB);
}
@Test
@@ -513,27 +532,70 @@
ArtifactRoot root = ArtifactRoot.asSourceRoot(Root.fromPath(scratch.resolve("/workspace")));
Artifact artifact = ActionsTestUtil.createArtifact(root, "target");
Runfiles nonEmpty = new Runfiles.Builder("TESTING").addArtifact(artifact).build();
+ StarlarkThread thread = newStarlarkThread();
- assertThat(Runfiles.EMPTY.mergeAll(StarlarkList.immutableOf(nonEmpty)))
+ assertThat(Runfiles.EMPTY.mergeAll(StarlarkList.immutableOf(nonEmpty), thread))
.isSameInstanceAs(nonEmpty);
assertThat(
Runfiles.EMPTY.mergeAll(
- StarlarkList.immutableOf(Runfiles.EMPTY, nonEmpty, Runfiles.EMPTY)))
+ StarlarkList.immutableOf(Runfiles.EMPTY, nonEmpty, Runfiles.EMPTY), thread))
.isSameInstanceAs(nonEmpty);
- assertThat(nonEmpty.mergeAll(StarlarkList.immutableOf(Runfiles.EMPTY, Runfiles.EMPTY)))
+ assertThat(nonEmpty.mergeAll(StarlarkList.immutableOf(Runfiles.EMPTY, Runfiles.EMPTY), thread))
.isSameInstanceAs(nonEmpty);
- assertThat(nonEmpty.mergeAll(StarlarkList.immutableOf())).isSameInstanceAs(nonEmpty);
+ assertThat(nonEmpty.mergeAll(StarlarkList.immutableOf(), thread)).isSameInstanceAs(nonEmpty);
}
@Test
public void mergeAll_emptyWithEmpty() throws Exception {
- assertThat(Runfiles.EMPTY.mergeAll(StarlarkList.immutableOf()))
+ StarlarkThread thread = newStarlarkThread();
+ assertThat(Runfiles.EMPTY.mergeAll(StarlarkList.immutableOf(), thread))
.isSameInstanceAs(Runfiles.EMPTY);
- assertThat(Runfiles.EMPTY.mergeAll(StarlarkList.immutableOf(Runfiles.EMPTY, Runfiles.EMPTY)))
+ assertThat(
+ Runfiles.EMPTY.mergeAll(
+ StarlarkList.immutableOf(Runfiles.EMPTY, Runfiles.EMPTY), thread))
.isSameInstanceAs(Runfiles.EMPTY);
}
@Test
+ public void merge_exceedsDepthLimit_throwsException() throws Exception {
+ ArtifactRoot root = ArtifactRoot.asSourceRoot(Root.fromPath(scratch.resolve("/workspace")));
+ Artifact artifactA = ActionsTestUtil.createArtifact(root, "a/target");
+ Artifact artifactB = ActionsTestUtil.createArtifact(root, "b/target");
+ Artifact artifactC = ActionsTestUtil.createArtifact(root, "c/target");
+ Runfiles runfilesA = new Runfiles.Builder("TESTING").addArtifact(artifactA).build();
+ Runfiles runfilesB = new Runfiles.Builder("TESTING").addArtifact(artifactB).build();
+ Runfiles runfilesC = new Runfiles.Builder("TESTING").addArtifact(artifactC).build();
+ StarlarkThread thread = newStarlarkThread("--nested_set_depth_limit=2");
+
+ Runfiles mergeAB = runfilesA.merge(runfilesB, thread);
+ EvalException expected =
+ assertThrows(EvalException.class, () -> mergeAB.merge(runfilesC, thread));
+ assertThat(expected).hasMessageThat().contains("artifacts depset depth 3 exceeds limit (2)");
+ }
+
+ @Test
+ public void mergeAll_exceedsDepthLimit_throwsException() throws Exception {
+ ArtifactRoot root = ArtifactRoot.asSourceRoot(Root.fromPath(scratch.resolve("/workspace")));
+ Artifact artifactA = ActionsTestUtil.createArtifact(root, "a/target");
+ Artifact artifactB = ActionsTestUtil.createArtifact(root, "b/target");
+ Artifact artifactC = ActionsTestUtil.createArtifact(root, "c/target");
+ PathFragment sympathA = PathFragment.create("a/symlink");
+ PathFragment sympathB = PathFragment.create("b/symlink");
+ PathFragment sympathC = PathFragment.create("c/symlink");
+ Runfiles runfilesA = new Runfiles.Builder("TESTING").addSymlink(sympathA, artifactA).build();
+ Runfiles runfilesB = new Runfiles.Builder("TESTING").addSymlink(sympathB, artifactB).build();
+ Runfiles runfilesC = new Runfiles.Builder("TESTING").addSymlink(sympathC, artifactC).build();
+ StarlarkThread thread = newStarlarkThread("--nested_set_depth_limit=2");
+
+ Runfiles mergeAllAB = runfilesA.mergeAll(StarlarkList.immutableOf(runfilesB), thread);
+ EvalException expected =
+ assertThrows(
+ EvalException.class,
+ () -> mergeAllAB.mergeAll(StarlarkList.immutableOf(runfilesC), thread));
+ assertThat(expected).hasMessageThat().contains("symlinks depset depth 3 exceeds limit (2)");
+ }
+
+ @Test
public void testOnlyExtraMiddlemenNotConsideredEmpty() {
ArtifactRoot root =
ArtifactRoot.asDerivedRoot(
diff --git a/src/test/java/com/google/devtools/build/lib/collect/nestedset/DepsetTest.java b/src/test/java/com/google/devtools/build/lib/collect/nestedset/DepsetTest.java
index b39db0a..92637fe 100644
--- a/src/test/java/com/google/devtools/build/lib/collect/nestedset/DepsetTest.java
+++ b/src/test/java/com/google/devtools/build/lib/collect/nestedset/DepsetTest.java
@@ -461,8 +461,7 @@
.testEval("create_depset(3000)", "None") // succeeds
.testIfErrorContains("depset depth 3501 exceeds limit (3500)", "create_depset(4000)");
- Depset.setDepthLimit(100);
- ev.new Scenario()
+ ev.new Scenario("--nested_set_depth_limit=100")
.setUp(
"def create_depset(depth):",
" x = depset([0])",