Move CommandLine, CommandLineItem, and ParamFileInfo from lib.analysis.actions -> lib.actions. These are fundamental types that want to sit alongside types like Spawn. RELNOTES: None PiperOrigin-RevId: 185887971
diff --git a/src/main/java/com/google/devtools/build/lib/actions/ActionKeyContext.java b/src/main/java/com/google/devtools/build/lib/actions/ActionKeyContext.java index 053c120..21ddc43 100644 --- a/src/main/java/com/google/devtools/build/lib/actions/ActionKeyContext.java +++ b/src/main/java/com/google/devtools/build/lib/actions/ActionKeyContext.java
@@ -14,7 +14,6 @@ package com.google.devtools.build.lib.actions; -import com.google.devtools.build.lib.analysis.actions.CommandLineItem; import com.google.devtools.build.lib.collect.nestedset.NestedSet; import com.google.devtools.build.lib.collect.nestedset.NestedSetFingerprintCache; import com.google.devtools.build.lib.util.Fingerprint;
diff --git a/src/main/java/com/google/devtools/build/lib/actions/Artifact.java b/src/main/java/com/google/devtools/build/lib/actions/Artifact.java index a3da211..91b5313 100644 --- a/src/main/java/com/google/devtools/build/lib/actions/Artifact.java +++ b/src/main/java/com/google/devtools/build/lib/actions/Artifact.java
@@ -27,7 +27,6 @@ import com.google.common.collect.Iterables; import com.google.common.collect.Streams; import com.google.devtools.build.lib.actions.ActionAnalysisMetadata.MiddlemanType; -import com.google.devtools.build.lib.analysis.actions.CommandLineItem; import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; import com.google.devtools.build.lib.shell.ShellUtils;
diff --git a/src/main/java/com/google/devtools/build/lib/actions/BUILD b/src/main/java/com/google/devtools/build/lib/actions/BUILD index f936712..287dad9 100644 --- a/src/main/java/com/google/devtools/build/lib/actions/BUILD +++ b/src/main/java/com/google/devtools/build/lib/actions/BUILD
@@ -12,18 +12,21 @@ # Action graph and cache. java_library( name = "actions", - srcs = glob([ - "*.java", - "cache/*.java", - ]), + srcs = glob( + [ + "*.java", + "cache/*.java", + ], + exclude = ["CommandLineItem.java"], + ), deps = [ - "//src/main/java/com/google/devtools/build/lib:commandline_item", "//src/main/java/com/google/devtools/build/lib:events", "//src/main/java/com/google/devtools/build/lib:io", "//src/main/java/com/google/devtools/build/lib:packages-internal", "//src/main/java/com/google/devtools/build/lib:skylarkinterface", "//src/main/java/com/google/devtools/build/lib:unix", "//src/main/java/com/google/devtools/build/lib:util", + "//src/main/java/com/google/devtools/build/lib/actions:commandline_item", "//src/main/java/com/google/devtools/build/lib/analysis/platform", "//src/main/java/com/google/devtools/build/lib/buildeventstream", "//src/main/java/com/google/devtools/build/lib/buildeventstream/proto:build_event_stream_java_proto", @@ -48,3 +51,8 @@ "//third_party/protobuf:protobuf_java", ], ) + +java_library( + name = "commandline_item", + srcs = ["CommandLineItem.java"], +)
diff --git a/src/main/java/com/google/devtools/build/lib/actions/CommandLine.java b/src/main/java/com/google/devtools/build/lib/actions/CommandLine.java new file mode 100644 index 0000000..dcf1006 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/actions/CommandLine.java
@@ -0,0 +1,184 @@ +// Copyright 2014 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.actions; + +import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import com.google.devtools.build.lib.actions.Artifact.ArtifactExpander; +import com.google.devtools.build.lib.collect.CollectionUtils; +import com.google.devtools.build.lib.skyframe.serialization.ObjectCodec; +import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec; +import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec.Strategy; +import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec.VisibleForSerialization; +import com.google.devtools.build.lib.util.Fingerprint; + +/** A representation of a list of arguments. */ +@AutoCodec(strategy = Strategy.POLYMORPHIC) +public abstract class CommandLine { + public static final ObjectCodec<CommandLine> CODEC = new CommandLine_AutoCodec(); + + @AutoCodec + @VisibleForSerialization + static class EmptyCommandLine extends CommandLine { + public static final ObjectCodec<EmptyCommandLine> CODEC = + new CommandLine_EmptyCommandLine_AutoCodec(); + + @Override + public Iterable<String> arguments() throws CommandLineExpansionException { + return ImmutableList.of(); + } + } + + public static final CommandLine EMPTY = new EmptyCommandLine(); + + /** Returns the command line. */ + public abstract Iterable<String> arguments() throws CommandLineExpansionException; + + /** + * Returns the evaluated command line with enclosed artifacts expanded by {@code artifactExpander} + * at execution time. + * + * <p>By default, this method just delegates to {@link #arguments()}, without performing any + * artifact expansion. Subclasses should override this method if they contain TreeArtifacts and + * need to expand them for proper argument evaluation. + */ + public Iterable<String> arguments(ArtifactExpander artifactExpander) + throws CommandLineExpansionException { + return arguments(); + } + + public void addToFingerprint(ActionKeyContext actionKeyContext, Fingerprint fingerprint) + throws CommandLineExpansionException { + for (String s : arguments()) { + fingerprint.addString(s); + } + } + + @AutoCodec + @VisibleForSerialization + static class ArgumentCommandLine extends CommandLine { + public static final ObjectCodec<ArgumentCommandLine> CODEC = + new CommandLine_ArgumentCommandLine_AutoCodec(); + + private Iterable<String> args; + + ArgumentCommandLine(Iterable<String> args) { + this.args = args; + } + + @Override + public Iterable<String> arguments() throws CommandLineExpansionException { + return args; + } + } + + /** Returns a {@link CommandLine} backed by a copy of the given list of arguments. */ + public static CommandLine of(Iterable<String> arguments) { + final Iterable<String> immutableArguments = CollectionUtils.makeImmutable(arguments); + return new ArgumentCommandLine(immutableArguments); + } + + @AutoCodec + @VisibleForSerialization + static class ConcatenatedCommandLine extends CommandLine { + public static final ObjectCodec<ConcatenatedCommandLine> CODEC = + new CommandLine_ConcatenatedCommandLine_AutoCodec(); + + private ImmutableList<String> executableArgs; + private CommandLine commandLine; + + @VisibleForSerialization + ConcatenatedCommandLine(ImmutableList<String> executableArgs, CommandLine commandLine) { + this.executableArgs = executableArgs; + this.commandLine = commandLine; + } + + @Override + public Iterable<String> arguments() throws CommandLineExpansionException { + return Iterables.concat(executableArgs, commandLine.arguments()); + } + + @Override + public Iterable<String> arguments(ArtifactExpander artifactExpander) + throws CommandLineExpansionException { + return Iterables.concat(executableArgs, commandLine.arguments(artifactExpander)); + } + } + + /** + * Returns a {@link CommandLine} that is constructed by prepending the {@code executableArgs} to + * {@code commandLine}. + */ + public static CommandLine concat( + final ImmutableList<String> executableArgs, final CommandLine commandLine) { + if (executableArgs.isEmpty()) { + return commandLine; + } + return new ConcatenatedCommandLine(executableArgs, commandLine); + } + + @AutoCodec + @VisibleForSerialization + static class ReverseConcatenatedCommandLine extends CommandLine { + public static final ObjectCodec<ReverseConcatenatedCommandLine> CODEC = + new CommandLine_ReverseConcatenatedCommandLine_AutoCodec(); + + private ImmutableList<String> executableArgs; + private CommandLine commandLine; + + @VisibleForSerialization + ReverseConcatenatedCommandLine(ImmutableList<String> executableArgs, CommandLine commandLine) { + this.executableArgs = executableArgs; + this.commandLine = commandLine; + } + + @Override + public Iterable<String> arguments() throws CommandLineExpansionException { + return Iterables.concat(commandLine.arguments(), executableArgs); + } + + @Override + public Iterable<String> arguments(ArtifactExpander artifactExpander) + throws CommandLineExpansionException { + return Iterables.concat(commandLine.arguments(artifactExpander), executableArgs); + } + } + + /** + * Returns a {@link CommandLine} that is constructed by appending the {@code args} to {@code + * commandLine}. + */ + public static CommandLine concat( + final CommandLine commandLine, final ImmutableList<String> args) { + if (args.isEmpty()) { + return commandLine; + } + return new ReverseConcatenatedCommandLine(args, commandLine); + } + + /** + * This helps when debugging Blaze code that uses {@link CommandLine}s, as you can see their + * content directly in the variable inspector. + */ + @Override + public String toString() { + try { + return Joiner.on(' ').join(arguments()); + } catch (CommandLineExpansionException e) { + return "Error in expanding command line"; + } + } +}
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 new file mode 100644 index 0000000..3cd455f --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/actions/CommandLineItem.java
@@ -0,0 +1,84 @@ +// Copyright 2018 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.actions; + +import java.util.function.Consumer; + +/** An interface for an object that customizes how it is expanded into a command line. */ +public interface CommandLineItem { + /** + * A map function that allows caller customization how a type is expanded into the command line. + */ + interface MapFn<T> { + MapFn<Object> DEFAULT = + (Object object, Consumer<String> args) -> + args.accept(CommandLineItem.expandToCommandLine(object)); + + void expandToCommandLine(T object, Consumer<String> args); + } + + /** + * 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(); + + /** + * The default method of expanding types. + * + * <p>If the object is a {@link CommandLineItem} we use its {@link + * CommandLineItem#expandToCommandLine} method, else we call {@link Object#toString()}. + */ + static String expandToCommandLine(Object object) { + if (object instanceof CommandLineItem) { + return ((CommandLineItem) object).expandToCommandLine(); + } else { + return object.toString(); + } + } +}
diff --git a/src/main/java/com/google/devtools/build/lib/actions/ParamFileInfo.java b/src/main/java/com/google/devtools/build/lib/actions/ParamFileInfo.java new file mode 100644 index 0000000..27f4d45 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/actions/ParamFileInfo.java
@@ -0,0 +1,130 @@ +// Copyright 2014 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.actions; + +import static java.nio.charset.StandardCharsets.ISO_8859_1; + +import com.google.common.base.Preconditions; +import com.google.devtools.build.lib.actions.ParameterFile.ParameterFileType; +import java.nio.charset.Charset; +import java.util.Objects; +import javax.annotation.concurrent.Immutable; + +/** + * An object that encapsulates how a params file should be constructed: what is the filetype, + * what charset to use and what prefix (typically "@") to use. + */ +@Immutable +public final class ParamFileInfo { + private final ParameterFileType fileType; + private final Charset charset; + private final String flagFormatString; + private final boolean always; + + private ParamFileInfo( + ParameterFileType fileType, Charset charset, String flagFormatString, boolean always) { + this.fileType = Preconditions.checkNotNull(fileType); + this.charset = Preconditions.checkNotNull(charset); + this.flagFormatString = Preconditions.checkNotNull(flagFormatString); + this.always = always; + } + + /** + * Returns the file type. + */ + public ParameterFileType getFileType() { + return fileType; + } + + /** + * Returns the charset. + */ + public Charset getCharset() { + return charset; + } + + /** Returns the format string for the params filename on the command line (typically "@%s"). */ + public String getFlagFormatString() { + return flagFormatString; + } + + /** Returns true if a params file should always be used. */ + public boolean always() { + return always; + } + + @Override + public int hashCode() { + return Objects.hash(charset, flagFormatString, fileType, always); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof ParamFileInfo)) { + return false; + } + ParamFileInfo other = (ParamFileInfo) obj; + return fileType.equals(other.fileType) + && charset.equals(other.charset) + && flagFormatString.equals(other.flagFormatString) + && always == other.always; + } + + public static Builder builder(ParameterFileType parameterFileType) { + return new Builder(parameterFileType); + } + + /** Builder for a ParamFileInfo. */ + public static class Builder { + private final ParameterFileType fileType; + private Charset charset = ISO_8859_1; + private String flagFormatString = "@%s"; + private boolean always; + + private Builder(ParameterFileType fileType) { + this.fileType = fileType; + } + + /** Sets the encoding to write the parameter file with. */ + public Builder setCharset(Charset charset) { + this.charset = charset; + return this; + } + + /** + * Sets a format string to use for the flag that is passed to original command. + * + * <p>The format string must have a single "%s" that will be replaced by the execution path to + * the param file. + */ + public Builder setFlagFormatString(String flagFormatString) { + this.flagFormatString = flagFormatString; + return this; + } + + /** Set whether the parameter file is always used, regardless of parameter file length. */ + public Builder setUseAlways(boolean always) { + this.always = always; + return this; + } + + public ParamFileInfo build() { + return new ParamFileInfo(fileType, charset, flagFormatString, always); + } + } +}