diff options
Diffstat (limited to 'src/main/java/com')
8 files changed, 292 insertions, 168 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/BUILD b/src/main/java/com/google/devtools/build/lib/BUILD index 3c43f0874b..270c8a8340 100644 --- a/src/main/java/com/google/devtools/build/lib/BUILD +++ b/src/main/java/com/google/devtools/build/lib/BUILD @@ -910,6 +910,7 @@ java_library( "rules/java/proto/GeneratedExtensionRegistryProvider.java", ], deps = [ + ":commandline_item", "//src/main/java/com/google/devtools/build/lib:build-base", "//src/main/java/com/google/devtools/build/lib:build-info", "//src/main/java/com/google/devtools/build/lib:events", diff --git a/src/main/java/com/google/devtools/build/lib/analysis/actions/CommandLineItem.java b/src/main/java/com/google/devtools/build/lib/analysis/actions/CommandLineItem.java index c228226e19..f1418d9c10 100644 --- a/src/main/java/com/google/devtools/build/lib/analysis/actions/CommandLineItem.java +++ b/src/main/java/com/google/devtools/build/lib/analysis/actions/CommandLineItem.java @@ -24,6 +24,43 @@ public interface CommandLineItem { String expandToCommandLine(T object); } + /** + * Use this map function when parametrizing over a limited set of values. + * + * <p>The user promises that the number of distinct instances constructed is closer to O(rule + * class count) than O(rule count). + * + * <p>Without this, {@link + * com.google.devtools.build.lib.collect.nestedset.NestedSetFingerprintCache} will refuse to cache + * your {@link MapFn} computations. + */ + abstract class ParametrizedMapFn<T> implements MapFn<T> { + @Override + public abstract boolean equals(Object obj); + + @Override + public abstract int hashCode(); + + /** + * This method controls the max number of distinct instances allowed. If the system sees any + * more than this, it will throw. + * + * <p>Override and set this to something low. You want this to represent the small number of + * preallocated static instances used in this blaze instance. 3 is an OK number, 100 is a bad + * number. + */ + public abstract int maxInstancesAllowed(); + } + + /** + * Use this map function when your map function needs to capture per-rule information. + * + * <p>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<T> extends MapFn<T> {} + /** 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 b48d4a2298..083ef06269 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 @@ -15,9 +15,14 @@ package com.google.devtools.build.lib.collect.nestedset; import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.HashMultiset; +import com.google.common.collect.Multiset; import com.google.devtools.build.lib.analysis.actions.CommandLineItem; +import com.google.devtools.build.lib.analysis.actions.CommandLineItem.MapFn; import com.google.devtools.build.lib.util.Fingerprint; +import java.util.HashSet; import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; /** Computes fingerprints for nested sets, reusing sub-computations from children. */ @@ -26,7 +31,10 @@ public class NestedSetFingerprintCache { private static final int EMPTY_SET_DIGEST = 104_395_303; /** Memoize the subresults. We have to have one cache per type of command item map function. */ - private Map<CommandLineItem.MapFn<?>, DigestMap> mapFnToFingerprints = createMap(); + private Map<CommandLineItem.MapFn<?>, DigestMap> mapFnToDigestMap = createMap(); + + private final Set<Class<?>> seenMapFns = new HashSet<>(); + private final Multiset<Class<?>> seenParametrizedMapFns = HashMultiset.create(); public <T> void addNestedSetToFingerprint(Fingerprint fingerprint, NestedSet<T> nestedSet) { addNestedSetToFingerprint(CommandLineItem.MapFn.DEFAULT, fingerprint, nestedSet); @@ -34,20 +42,32 @@ public class NestedSetFingerprintCache { public <T> void addNestedSetToFingerprint( CommandLineItem.MapFn<? super T> mapFn, Fingerprint fingerprint, NestedSet<T> nestedSet) { + 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); return; } - DigestMap digestMap = - mapFnToFingerprints.computeIfAbsent(mapFn, k -> new DigestMap(DIGEST_SIZE, 1024)); + DigestMap digestMap = mapFnToDigestMap.computeIfAbsent(mapFn, this::newDigestMap); fingerprint.addInt(nestedSet.getOrder().ordinal()); Object children = nestedSet.rawChildren(); addToFingerprint(mapFn, fingerprint, digestMap, children); } + private <T> void addNestedSetToFingerprintSlow( + MapFn<? super T> mapFn, Fingerprint fingerprint, NestedSet<T> nestedSet) { + for (T object : nestedSet) { + fingerprint.addString(mapFn.expandToCommandLine(object)); + } + } + public void clear() { - mapFnToFingerprints = createMap(); + mapFnToDigestMap = createMap(); + seenMapFns.clear(); + seenParametrizedMapFns.clear(); } @SuppressWarnings("unchecked") @@ -78,4 +98,29 @@ public class NestedSetFingerprintCache { private static Map<CommandLineItem.MapFn<?>, DigestMap> createMap() { return new ConcurrentHashMap<>(); } + + private DigestMap newDigestMap(CommandLineItem.MapFn<?> mapFn) { + Class<?> mapFnClass = mapFn.getClass(); + if (mapFn instanceof CommandLineItem.ParametrizedMapFn) { + int occurrences = seenParametrizedMapFns.add(mapFnClass, 1) + 1; + if (occurrences > ((CommandLineItem.ParametrizedMapFn) mapFn).maxInstancesAllowed()) { + throw new IllegalArgumentException( + String.format( + "Too many instances of CommandLineItem.ParametrizedMapFn '%s' detected. " + + "Please construct fewer instances or use CommandLineItem.CapturingMapFn.", + mapFnClass.getName())); + } + } else { + if (!seenMapFns.add(mapFnClass)) { + throw new IllegalArgumentException( + String.format( + "Illegal mapFn implementation: '%s'. " + + "CommandLineItem.MapFn instances must be singletons. " + + "Please see CommandLineItem.ParametrizedMapFn or " + + "CommandLineItem.CapturingMapFn for alternatives.", + mapFnClass.getName())); + } + } + return new DigestMap(DIGEST_SIZE, 1024); + } } diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResourceMergingActionBuilder.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResourceMergingActionBuilder.java index 6dbab850f3..71612341af 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResourceMergingActionBuilder.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResourceMergingActionBuilder.java @@ -25,7 +25,8 @@ import com.google.devtools.build.lib.analysis.actions.ParamFileInfo; import com.google.devtools.build.lib.analysis.actions.SpawnAction; import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget.Mode; import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; -import com.google.devtools.build.lib.rules.android.ResourceContainerConverter.Builder.SeparatorType; +import com.google.devtools.build.lib.rules.android.ResourceContainerConverter.ToArg; +import com.google.devtools.build.lib.rules.android.ResourceContainerConverter.ToArg.Includes; import com.google.devtools.build.lib.util.OS; import java.util.ArrayList; import java.util.List; @@ -41,18 +42,18 @@ public class AndroidResourceMergingActionBuilder { private static final ResourceContainerConverter.ToArg RESOURCE_CONTAINER_TO_ARG = ResourceContainerConverter.builder() - .includeResourceRoots() - .includeLabel() - .includeSymbolsBin() - .withSeparator(SeparatorType.SEMICOLON_AMPERSAND) + .include(Includes.ResourceRoots) + .include(Includes.Label) + .include(Includes.SymbolsBin) + .withSeparator(ToArg.SeparatorType.SEMICOLON_AMPERSAND) .toArgConverter(); private static final ResourceContainerConverter.ToArg RESOURCE_CONTAINER_TO_ARG_FOR_COMPILED = ResourceContainerConverter.builder() - .includeResourceRoots() - .includeLabel() - .includeCompiledSymbols() - .withSeparator(SeparatorType.SEMICOLON_AMPERSAND) + .include(Includes.ResourceRoots) + .include(Includes.Label) + .include(Includes.CompiledSymbols) + .withSeparator(ToArg.SeparatorType.SEMICOLON_AMPERSAND) .toArgConverter(); private final RuleContext ruleContext; diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResourcesProcessorBuilder.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResourcesProcessorBuilder.java index 16ebb20876..2f97a4b2a5 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResourcesProcessorBuilder.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResourcesProcessorBuilder.java @@ -27,7 +27,8 @@ import com.google.devtools.build.lib.analysis.actions.SpawnAction; import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget.Mode; import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; import com.google.devtools.build.lib.rules.android.AndroidConfiguration.AndroidAaptVersion; -import com.google.devtools.build.lib.rules.android.ResourceContainerConverter.Builder.SeparatorType; +import com.google.devtools.build.lib.rules.android.ResourceContainerConverter.ToArg; +import com.google.devtools.build.lib.rules.android.ResourceContainerConverter.ToArg.Includes; import com.google.devtools.build.lib.util.OS; import java.util.ArrayList; import java.util.Collections; @@ -38,37 +39,37 @@ public class AndroidResourcesProcessorBuilder { private static final ResourceContainerConverter.ToArg AAPT2_RESOURCE_DEP_TO_ARG = ResourceContainerConverter.builder() - .includeResourceRoots() - .includeManifest() - .includeAapt2RTxt() - .includeSymbolsBin() - .includeCompiledSymbols() - .withSeparator(SeparatorType.COLON_COMMA) + .include(Includes.ResourceRoots) + .include(Includes.Manifest) + .include(Includes.Aapt2RTxt) + .include(Includes.SymbolsBin) + .include(Includes.CompiledSymbols) + .withSeparator(ToArg.SeparatorType.COLON_COMMA) .toArgConverter(); private static final ResourceContainerConverter.ToArg AAPT2_RESOURCE_DEP_TO_ARG_NO_PARSE = ResourceContainerConverter.builder() - .includeResourceRoots() - .includeManifest() - .includeAapt2RTxt() - .includeCompiledSymbols() - .withSeparator(SeparatorType.COLON_COMMA) + .include(Includes.ResourceRoots) + .include(Includes.Manifest) + .include(Includes.Aapt2RTxt) + .include(Includes.CompiledSymbols) + .withSeparator(ToArg.SeparatorType.COLON_COMMA) .toArgConverter(); private static final ResourceContainerConverter.ToArg RESOURCE_CONTAINER_TO_ARG = ResourceContainerConverter.builder() - .includeResourceRoots() - .includeManifest() - .withSeparator(SeparatorType.COLON_COMMA) + .include(Includes.ResourceRoots) + .include(Includes.Manifest) + .withSeparator(ToArg.SeparatorType.COLON_COMMA) .toArgConverter(); private static final ResourceContainerConverter.ToArg RESOURCE_DEP_TO_ARG = ResourceContainerConverter.builder() - .includeResourceRoots() - .includeManifest() - .includeRTxt() - .includeSymbolsBin() - .withSeparator(SeparatorType.COLON_COMMA) + .include(Includes.ResourceRoots) + .include(Includes.Manifest) + .include(Includes.RTxt) + .include(Includes.SymbolsBin) + .withSeparator(ToArg.SeparatorType.COLON_COMMA) .toArgConverter(); private ResourceContainer primary; diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/ResourceContainerConverter.java b/src/main/java/com/google/devtools/build/lib/rules/android/ResourceContainerConverter.java index fd9ba71a94..e653fb6015 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/ResourceContainerConverter.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/ResourceContainerConverter.java @@ -17,13 +17,18 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; import com.google.common.base.Functions; import com.google.common.base.Joiner; +import com.google.common.base.Objects; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterators; +import com.google.common.collect.Sets; import com.google.devtools.build.lib.analysis.actions.CommandLineItem; import com.google.devtools.build.lib.analysis.actions.CustomCommandLine; import com.google.devtools.build.lib.analysis.actions.CustomCommandLine.VectorArg; import com.google.devtools.build.lib.rules.android.ResourceContainer.ResourceType; +import com.google.devtools.build.lib.rules.android.ResourceContainerConverter.ToArg.Includes; +import java.util.HashSet; +import java.util.Set; /** * Factory for functions to convert a {@link ResourceContainer} to a commandline argument, or a @@ -37,152 +42,150 @@ public class ResourceContainerConverter { return new Builder(); } - interface ToArg extends CommandLineItem.MapFn<ResourceContainer> { - - String listSeparator(); - } - - static class Builder { - - private boolean includeResourceRoots; - private boolean includeLabel; - private boolean includeManifest; - private boolean includeRTxt; - private boolean includeSymbolsBin; - private boolean includeCompiledSymbols; - private boolean includeStaticLibrary; - private boolean includeAapt2RTxt; - private SeparatorType separatorType; - private Joiner argJoiner; - private Function<String, String> escaper = Functions.identity(); + static class ToArg extends CommandLineItem.ParametrizedMapFn<ResourceContainer> { + + private final Set<Includes> includes; + private final SeparatorType separatorType; + private final Joiner argJoiner; + private final Function<String, String> escaper; + + enum Includes { + ResourceRoots, + Label, + Manifest, + RTxt, + SymbolsBin, + CompiledSymbols, + StaticLibrary, + Aapt2RTxt + } enum SeparatorType { COLON_COMMA, SEMICOLON_AMPERSAND } - Builder() {} + ToArg(Builder builder) { + this.includes = Sets.immutableEnumSet(builder.includes); + this.separatorType = builder.separatorType; - Builder includeAapt2RTxt() { - includeAapt2RTxt = true; - return this; + switch (separatorType) { + case COLON_COMMA: + argJoiner = Joiner.on(":"); + // We currently use ":" to separate components of an argument and "," to separate + // arguments in a list of arguments. Those characters require escaping if used in a label + // (part of the set of allowed characters in a label). + if (includes.contains(Includes.Label)) { + escaper = (String input) -> input.replace(":", "\\:").replace(",", "\\,"); + } else { + escaper = Functions.identity(); + } + break; + case SEMICOLON_AMPERSAND: + argJoiner = Joiner.on(";"); + escaper = Functions.identity(); + break; + default: + throw new IllegalStateException("Unknown separator type " + separatorType); + } } - Builder includeStaticLibrary() { - includeStaticLibrary = true; - return this; + @Override + public String expandToCommandLine(ResourceContainer container) { + ImmutableList.Builder<String> cmdPieces = ImmutableList.builder(); + if (includes.contains(Includes.ResourceRoots)) { + cmdPieces.add(convertRoots(container, ResourceType.RESOURCES)); + cmdPieces.add(convertRoots(container, ResourceType.ASSETS)); + } + if (includes.contains(Includes.Label)) { + cmdPieces.add(escaper.apply(container.getLabel().toString())); + } + if (includes.contains(Includes.Manifest)) { + cmdPieces.add(container.getManifest().getExecPathString()); + } + if (includes.contains(Includes.RTxt)) { + cmdPieces.add(container.getRTxt() == null ? "" : container.getRTxt().getExecPathString()); + } + if (includes.contains(Includes.Aapt2RTxt)) { + cmdPieces.add( + container.getAapt2RTxt() == null ? "" : container.getAapt2RTxt().getExecPathString()); + } + if (includes.contains(Includes.StaticLibrary)) { + cmdPieces.add( + container.getStaticLibrary() == null + ? "" + : container.getStaticLibrary().getExecPathString()); + } + if (includes.contains(Includes.CompiledSymbols)) { + cmdPieces.add( + container.getCompiledSymbols() == null + ? "" + : container.getCompiledSymbols().getExecPathString()); + } + if (includes.contains(Includes.SymbolsBin)) { + cmdPieces.add( + container.getSymbols() == null ? "" : container.getSymbols().getExecPathString()); + } + return argJoiner.join(cmdPieces.build()); } - Builder includeResourceRoots() { - includeResourceRoots = true; - return this; + String listSeparator() { + switch (separatorType) { + case COLON_COMMA: + return ","; + case SEMICOLON_AMPERSAND: + return "&"; + default: + Preconditions.checkState(false, "Unknown separator type " + separatorType); + return null; + } } - Builder includeLabel() { - includeLabel = true; - return this; + @Override + public int maxInstancesAllowed() { + // This is the max number of resource converters we expect to statically + // construct for any given blaze instance. + // Do not increase recklessly. + return 10; } - Builder includeManifest() { - includeManifest = true; - return this; + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ToArg toArg = (ToArg) o; + return includes.equals(toArg.includes) && separatorType == toArg.separatorType; } - Builder includeRTxt() { - includeRTxt = true; - return this; + @Override + public int hashCode() { + return Objects.hashCode(includes, separatorType); } + } - Builder includeSymbolsBin() { - includeSymbolsBin = true; - return this; - } + static class Builder { + + private final Set<Includes> includes = new HashSet<>(); + private ToArg.SeparatorType separatorType; - Builder includeCompiledSymbols() { - includeCompiledSymbols = true; + Builder() {} + + Builder include(Includes include) { + includes.add(include); return this; } - Builder withSeparator(SeparatorType type) { + Builder withSeparator(ToArg.SeparatorType type) { separatorType = type; return this; } ToArg toArgConverter() { - switch (separatorType) { - case COLON_COMMA: - argJoiner = Joiner.on(":"); - // We currently use ":" to separate components of an argument and "," to separate - // arguments in a list of arguments. Those characters require escaping if used in a label - // (part of the set of allowed characters in a label). - if (includeLabel) { - escaper = (String input) -> input.replace(":", "\\:").replace(",", "\\,"); - } - break; - case SEMICOLON_AMPERSAND: - argJoiner = Joiner.on(";"); - break; - default: - Preconditions.checkState(false, "Unknown separator type " + separatorType); - break; - } - - return new ToArg() { - @Override - public String expandToCommandLine(ResourceContainer container) { - ImmutableList.Builder<String> cmdPieces = ImmutableList.builder(); - if (includeResourceRoots) { - cmdPieces.add(convertRoots(container, ResourceType.RESOURCES)); - cmdPieces.add(convertRoots(container, ResourceType.ASSETS)); - } - if (includeLabel) { - cmdPieces.add(escaper.apply(container.getLabel().toString())); - } - if (includeManifest) { - cmdPieces.add(container.getManifest().getExecPathString()); - } - if (includeRTxt) { - cmdPieces.add( - container.getRTxt() == null ? "" : container.getRTxt().getExecPathString()); - } - if (includeAapt2RTxt) { - cmdPieces.add( - container.getAapt2RTxt() == null - ? "" - : container.getAapt2RTxt().getExecPathString()); - } - if (includeStaticLibrary) { - cmdPieces.add( - container.getStaticLibrary() == null - ? "" - : container.getStaticLibrary().getExecPathString()); - } - if (includeCompiledSymbols) { - cmdPieces.add( - container.getCompiledSymbols() == null - ? "" - : container.getCompiledSymbols().getExecPathString()); - } - if (includeSymbolsBin) { - cmdPieces.add( - container.getSymbols() == null ? "" : container.getSymbols().getExecPathString()); - } - return argJoiner.join(cmdPieces.build()); - } - - @Override - public String listSeparator() { - switch (separatorType) { - case COLON_COMMA: - return ","; - case SEMICOLON_AMPERSAND: - return "&"; - default: - Preconditions.checkState(false, "Unknown separator type " + separatorType); - return null; - } - } - }; + return new ToArg(this); } } diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/RobolectricResourceSymbolsActionBuilder.java b/src/main/java/com/google/devtools/build/lib/rules/android/RobolectricResourceSymbolsActionBuilder.java index ea84eeede7..fe0568defc 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/RobolectricResourceSymbolsActionBuilder.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/RobolectricResourceSymbolsActionBuilder.java @@ -25,8 +25,8 @@ import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTa import com.google.devtools.build.lib.collect.nestedset.NestedSet; import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; import com.google.devtools.build.lib.rules.android.AndroidConfiguration.AndroidAaptVersion; -import com.google.devtools.build.lib.rules.android.ResourceContainerConverter.Builder.SeparatorType; import com.google.devtools.build.lib.rules.android.ResourceContainerConverter.ToArg; +import com.google.devtools.build.lib.rules.android.ResourceContainerConverter.ToArg.Includes; import com.google.devtools.build.lib.util.OS; /** @@ -39,20 +39,20 @@ public class RobolectricResourceSymbolsActionBuilder { private static final ResourceContainerConverter.ToArg RESOURCE_CONTAINER_TO_ARG = ResourceContainerConverter.builder() - .includeResourceRoots() - .includeManifest() - .includeRTxt() - .includeSymbolsBin() - .withSeparator(SeparatorType.COLON_COMMA) + .include(Includes.ResourceRoots) + .include(Includes.Manifest) + .include(Includes.RTxt) + .include(Includes.SymbolsBin) + .withSeparator(ToArg.SeparatorType.COLON_COMMA) .toArgConverter(); private static final ResourceContainerConverter.ToArg RESOURCE_CONTAINER_TO_AAPT2_ARG = ResourceContainerConverter.builder() - .includeResourceRoots() - .includeManifest() - .includeAapt2RTxt() - .includeSymbolsBin() - .withSeparator(SeparatorType.COLON_COMMA) + .include(Includes.ResourceRoots) + .include(Includes.Manifest) + .include(Includes.Aapt2RTxt) + .include(Includes.SymbolsBin) + .withSeparator(ToArg.SeparatorType.COLON_COMMA) .toArgConverter(); private Artifact classJarOut; diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/SingleJarActionBuilder.java b/src/main/java/com/google/devtools/build/lib/rules/java/SingleJarActionBuilder.java index 85759c25b4..29470d125f 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/java/SingleJarActionBuilder.java +++ b/src/main/java/com/google/devtools/build/lib/rules/java/SingleJarActionBuilder.java @@ -15,6 +15,7 @@ package com.google.devtools.build.lib.rules.java; import static java.util.Objects.requireNonNull; +import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableCollection; import com.google.common.collect.ImmutableList; import com.google.devtools.build.lib.actions.Artifact; @@ -22,6 +23,7 @@ import com.google.devtools.build.lib.actions.ExecutionRequirements; import com.google.devtools.build.lib.actions.ParameterFile.ParameterFileType; import com.google.devtools.build.lib.analysis.RuleContext; import com.google.devtools.build.lib.analysis.actions.CommandLine; +import com.google.devtools.build.lib.analysis.actions.CommandLineItem; import com.google.devtools.build.lib.analysis.actions.CustomCommandLine; import com.google.devtools.build.lib.analysis.actions.CustomCommandLine.VectorArg; import com.google.devtools.build.lib.analysis.actions.ParamFileInfo; @@ -160,16 +162,50 @@ public final class SingleJarActionBuilder { args.addExecPaths("--sources", resourceJars); if (!resources.isEmpty()) { args.add("--resources"); - args.addAll(VectorArg.of(resources).mapped(resource -> getResourceArg(semantics, resource))); + args.addAll(VectorArg.of(resources).mapped(new ResourceArgMapFn(semantics))); } return args.build(); } - private static String getResourceArg(JavaSemantics semantics, Artifact resource) { - return String.format( - "%s:%s", - resource.getExecPathString(), - semantics.getDefaultJavaResourcePath(resource.getRootRelativePath())); + private static class ResourceArgMapFn extends CommandLineItem.ParametrizedMapFn<Artifact> { + private final JavaSemantics semantics; + + ResourceArgMapFn(JavaSemantics semantics) { + this.semantics = Preconditions.checkNotNull(semantics); + } + + @Override + public String expandToCommandLine(Artifact resource) { + String execPath = resource.getExecPathString(); + String resourcePath = + semantics.getDefaultJavaResourcePath(resource.getRootRelativePath()).getPathString(); + StringBuilder sb = new StringBuilder(execPath.length() + resourcePath.length() + 1); + sb.append(execPath).append(":").append(resourcePath); + return sb.toString(); + } + + @Override + public int maxInstancesAllowed() { + // Expect only one semantics object. + return 1; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ResourceArgMapFn that = (ResourceArgMapFn) o; + return semantics.equals(that.semantics); + } + + @Override + public int hashCode() { + return semantics.hashCode(); + } } } |