diff --git a/src/main/java/com/google/devtools/build/lib/actions/CommandLineItem.java b/src/main/java/com/google/devtools/build/lib/actions/CommandLineItem.java index 7b983799f54bfe..5580b1cd026ca8 100644 --- a/src/main/java/com/google/devtools/build/lib/actions/CommandLineItem.java +++ b/src/main/java/com/google/devtools/build/lib/actions/CommandLineItem.java @@ -57,6 +57,15 @@ abstract class ParametrizedMapFn implements MapFn { public abstract int maxInstancesAllowed(); } + /** + * Use this map function when your map function needs to capture per-rule information. + * + *

Use of this class prevents sharing sub-computations over shared NestedSets, since the map + * function is per-target. This will make your action key computations become O(N^2). Please avoid + * if possible. + */ + interface CapturingMapFn extends MapFn {} + /** Expands the object into the command line as a string. */ String expandToCommandLine(); diff --git a/src/main/java/com/google/devtools/build/lib/collect/nestedset/NestedSetFingerprintCache.java b/src/main/java/com/google/devtools/build/lib/collect/nestedset/NestedSetFingerprintCache.java index 1611817797ad85..29c0284cb014f4 100644 --- a/src/main/java/com/google/devtools/build/lib/collect/nestedset/NestedSetFingerprintCache.java +++ b/src/main/java/com/google/devtools/build/lib/collect/nestedset/NestedSetFingerprintCache.java @@ -19,6 +19,7 @@ import com.google.common.collect.Multiset; import com.google.devtools.build.lib.actions.CommandLineExpansionException; import com.google.devtools.build.lib.actions.CommandLineItem; +import com.google.devtools.build.lib.actions.CommandLineItem.MapFn; import com.google.devtools.build.lib.util.Fingerprint; import com.google.devtools.build.lib.vfs.DigestHashFunction; import java.util.HashSet; @@ -44,6 +45,10 @@ public void addNestedSetToFingerprint(Fingerprint fingerprint, NestedSet public void addNestedSetToFingerprint( CommandLineItem.MapFn mapFn, Fingerprint fingerprint, NestedSet nestedSet) throws CommandLineExpansionException, InterruptedException { + if (mapFn instanceof CommandLineItem.CapturingMapFn) { + addNestedSetToFingerprintSlow(mapFn, fingerprint, nestedSet); + return; + } // Only top-level nested sets can be empty, so we can bail here if (nestedSet.isEmpty()) { fingerprint.addInt(EMPTY_SET_DIGEST); @@ -55,6 +60,14 @@ public void addNestedSetToFingerprint( addToFingerprint(mapFn, fingerprint, digestMap, children); } + private void addNestedSetToFingerprintSlow( + MapFn mapFn, Fingerprint fingerprint, NestedSet nestedSet) + throws CommandLineExpansionException, InterruptedException { + for (T object : nestedSet.toList()) { + addToFingerprint(mapFn, fingerprint, object); + } + } + public void clear() { mapFnToDigestMap = createMap(); seenMapFns.clear(); diff --git a/src/test/java/com/google/devtools/build/lib/collect/nestedset/NestedSetFingerprintCacheTest.java b/src/test/java/com/google/devtools/build/lib/collect/nestedset/NestedSetFingerprintCacheTest.java index 216b3c326be023..87f5275c8c4f97 100644 --- a/src/test/java/com/google/devtools/build/lib/collect/nestedset/NestedSetFingerprintCacheTest.java +++ b/src/test/java/com/google/devtools/build/lib/collect/nestedset/NestedSetFingerprintCacheTest.java @@ -21,6 +21,7 @@ import com.google.common.collect.Multiset; import com.google.devtools.build.lib.actions.CommandLineExpansionException; import com.google.devtools.build.lib.actions.CommandLineItem; +import com.google.devtools.build.lib.actions.CommandLineItem.CapturingMapFn; import com.google.devtools.build.lib.actions.CommandLineItem.MapFn; import com.google.devtools.build.lib.util.Fingerprint; import java.util.function.Consumer; @@ -141,6 +142,12 @@ public void testMultipleInstancesOfMapFnThrows() throws Exception { (s, args) -> args.accept(s + "_mapped"), new Fingerprint(), nestedSet); } + // Make sure a CapturingMapFn doesn't get denied + for (int i = 0; i < 2; ++i) { + cache.addNestedSetToFingerprint( + (CapturingMapFn) (s, args) -> args.accept(s + 1), new Fingerprint(), nestedSet); + } + // Make sure a ParametrizedMapFn doesn't get denied until it exceeds its instance count cache.addNestedSetToFingerprint(new IntParametrizedMapFn(1), new Fingerprint(), nestedSet); cache.addNestedSetToFingerprint(new IntParametrizedMapFn(2), new Fingerprint(), nestedSet);