blob: ca6e8ebef8fd34dbdfb1ee77712e3882f1f44eda [file] [log] [blame]
// 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.runtime;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.devtools.build.lib.events.EventKind;
import com.google.devtools.common.options.Converter;
import com.google.devtools.common.options.Converters.CommaSeparatedOptionListConverter;
import com.google.devtools.common.options.Converters.RangeConverter;
import com.google.devtools.common.options.EnumConverter;
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.OptionMetadataTag;
import com.google.devtools.common.options.OptionsBase;
import com.google.devtools.common.options.OptionsParsingException;
import java.util.HashSet;
import java.util.List;
/** Command-line UI options. */
public class UiOptions extends OptionsBase {
/** Enum to select whether color output is enabled or not. */
public enum UseColor {
YES,
NO,
AUTO
}
/** Enum to select whether curses output is enabled or not. */
public enum UseCurses {
YES,
NO,
AUTO
}
/** Converter for {@link EventKind} filters * */
public static class EventFiltersConverter
extends Converter.Contextless<EventFiltersConverter.EventKindFilters> {
/** Container for an EventKind input filter. */
@AutoValue
public abstract static class EventKindFilters {
public abstract ImmutableSet<EventKind> getFilteredEventKinds();
public abstract ImmutableSet<EventKind> getUnfilteredEventKinds();
public static EventKindFilters from(
ImmutableSet<EventKind> filtered, ImmutableSet<EventKind> unfiltered) {
return new AutoValue_UiOptions_EventFiltersConverter_EventKindFilters(filtered, unfiltered);
}
}
private final CommaSeparatedOptionListConverter commaSeparatedListConverter;
private final EnumConverter<EventKind> eventKindConverter;
public EventFiltersConverter() {
this.commaSeparatedListConverter = new CommaSeparatedOptionListConverter();
this.eventKindConverter = new EnumConverter<>(EventKind.class, "event kind") {};
}
@Override
public EventKindFilters convert(String input) throws OptionsParsingException {
if (input.isEmpty()) {
// This method is not called to convert the default value
// Empty list means that the user wants to filter all events
return EventKindFilters.from(EventKind.ALL_EVENTS, ImmutableSet.of());
}
ImmutableList<String> filters =
commaSeparatedListConverter.convert(input, /* conversionContext= */ null);
HashSet<EventKind> filteredEventKinds = new HashSet<>();
HashSet<EventKind> unfilteredEventKinds = new HashSet<>();
for (String filter : filters) {
if (!filter.startsWith("+") && !filter.startsWith("-")) {
filteredEventKinds.addAll(EventKind.ALL_EVENTS);
unfilteredEventKinds.clear();
}
if (!filter.isEmpty()) {
EventKind kind =
eventKindConverter.convert(
filter.replaceFirst("^[+-]", ""), /* conversionContext= */ null);
if (filter.startsWith("-")) {
filteredEventKinds.add(kind);
unfilteredEventKinds.remove(kind);
} else {
unfilteredEventKinds.add(kind);
filteredEventKinds.remove(kind);
}
}
}
return EventKindFilters.from(
ImmutableSet.copyOf(filteredEventKinds), ImmutableSet.copyOf(unfilteredEventKinds));
}
@Override
public String getTypeDescription() {
return "Convert list of comma separated event kind to list of filters";
}
}
/** Converter for {@link UseColor}. */
public static class UseColorConverter extends EnumConverter<UseColor> {
public UseColorConverter() {
super(UseColor.class, "--color setting");
}
}
/** Converter for {@link UseCurses}. */
public static class UseCursesConverter extends EnumConverter<UseCurses> {
public UseCursesConverter() {
super(UseCurses.class, "--curses setting");
}
}
@Option(
name = "show_progress",
defaultValue = "true",
documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
effectTags = {OptionEffectTag.UNKNOWN},
help = "Display progress messages during a build.")
public boolean showProgress;
@Option(
name = "show_progress_rate_limit",
defaultValue = "0.2", // A nice middle ground; snappy but not too spammy in logs.
documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
effectTags = {OptionEffectTag.UNKNOWN},
help = "Minimum number of seconds between progress messages in the output.")
public double showProgressRateLimit;
@Option(
name = "color",
defaultValue = "auto",
converter = UseColorConverter.class,
documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
effectTags = {OptionEffectTag.UNKNOWN},
help = "Use terminal controls to colorize output.")
public UseColor useColorEnum;
@Option(
name = "curses",
defaultValue = "auto",
converter = UseCursesConverter.class,
documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
effectTags = {OptionEffectTag.UNKNOWN},
help = "Use terminal cursor controls to minimize scrolling output.")
public UseCurses useCursesEnum;
@Option(
name = "terminal_columns",
defaultValue = "80",
metadataTags = {OptionMetadataTag.HIDDEN},
documentationCategory = OptionDocumentationCategory.UNDOCUMENTED,
effectTags = {OptionEffectTag.UNKNOWN},
help = "A system-generated parameter which specifies the terminal width in columns.")
public int terminalColumns;
@Option(
name = "isatty",
defaultValue = "false",
metadataTags = {OptionMetadataTag.HIDDEN},
documentationCategory = OptionDocumentationCategory.UNDOCUMENTED,
effectTags = {OptionEffectTag.UNKNOWN},
help =
"A system-generated parameter which is used to notify the "
+ "server whether this client is running in a terminal. "
+ "If this is set to false, then '--color=auto' will be treated as '--color=no'. "
+ "If this is set to true, then '--color=auto' will be treated as '--color=yes'.")
public boolean isATty;
// This lives here (as opposed to the more logical BuildRequest.Options)
// because the client passes it to the server *always*. We don't want the
// client to have to figure out when it should or shouldn't to send it.
@Option(
name = "emacs",
defaultValue = "false",
documentationCategory = OptionDocumentationCategory.UNDOCUMENTED,
effectTags = {OptionEffectTag.UNKNOWN},
help =
"A system-generated parameter which is true iff EMACS=t or INSIDE_EMACS is set "
+ "in the environment of the client. This option controls certain display "
+ "features.")
public boolean runningInEmacs;
@Option(
name = "show_timestamps",
defaultValue = "false",
documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
effectTags = {OptionEffectTag.UNKNOWN},
help = "Include timestamps in messages")
public boolean showTimestamp;
@Option(
name = "progress_in_terminal_title",
defaultValue = "false",
documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
effectTags = {OptionEffectTag.UNKNOWN},
help =
"Show the command progress in the terminal title. "
+ "Useful to see what bazel is doing when having multiple terminal tabs.")
public boolean progressInTermTitle;
@Option(
name = "attempt_to_print_relative_paths",
oldName = "experimental_ui_attempt_to_print_relative_paths",
defaultValue = "false",
documentationCategory = OptionDocumentationCategory.LOGGING,
effectTags = {OptionEffectTag.TERMINAL_OUTPUT},
help =
"When printing the location part of messages, attempt to use a path relative to the "
+ "workspace directory or one of the directories specified by --package_path.")
public boolean attemptToPrintRelativePaths;
@Option(
name = "experimental_ui_debug_all_events",
defaultValue = "false",
metadataTags = {OptionMetadataTag.HIDDEN},
documentationCategory = OptionDocumentationCategory.UNDOCUMENTED,
effectTags = {OptionEffectTag.UNKNOWN},
help = "Report all events known to the Bazel UI.")
public boolean experimentalUiDebugAllEvents;
@Option(
name = "ui_event_filters",
converter = EventFiltersConverter.class,
defaultValue = "null",
documentationCategory = OptionDocumentationCategory.LOGGING,
effectTags = {OptionEffectTag.TERMINAL_OUTPUT},
help =
"Specifies which events to show in the UI. It is possible to add or remove events "
+ "to the default ones using leading +/-, or override the default "
+ "set completely with direct assignment. The set of supported event kinds "
+ "include INFO, DEBUG, ERROR and more.",
allowMultiple = true)
public List<EventFiltersConverter.EventKindFilters> eventKindFilters;
@Option(
name = "ui_actions_shown",
oldName = "experimental_ui_actions_shown",
defaultValue = "8",
documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
effectTags = {OptionEffectTag.TERMINAL_OUTPUT},
help =
"Number of concurrent actions shown in the detailed progress bar; each "
+ "action is shown on a separate line. The progress bar always shows "
+ "at least one one, all numbers less than 1 are mapped to 1.")
public int uiActionsShown;
@Option(
name = "experimental_ui_max_stdouterr_bytes",
documentationCategory = OptionDocumentationCategory.EXECUTION_STRATEGY,
effectTags = {OptionEffectTag.EXECUTION},
defaultValue = "1048576",
converter = MaxStdoutErrBytesConverter.class,
help =
"The maximum size of the stdout / stderr files that will be printed to the console. "
+ "-1 implies no limit.")
public int maxStdoutErrBytes;
public boolean useColor() {
return useColorEnum == UseColor.YES || (useColorEnum == UseColor.AUTO && isATty);
}
public boolean useCursorControl() {
return useCursesEnum == UseCurses.YES || (useCursesEnum == UseCurses.AUTO && isATty);
}
public ImmutableSet<EventKind> getFilteredEventKinds() {
HashSet<EventKind> filtered = new HashSet<>();
for (EventFiltersConverter.EventKindFilters filters : eventKindFilters) {
filtered.addAll(filters.getFilteredEventKinds());
filtered.removeAll(filters.getUnfilteredEventKinds());
}
return ImmutableSet.copyOf(filtered);
}
/** A converter for --experimental_ui_max_stdouterr_bytes. */
public static class MaxStdoutErrBytesConverter extends RangeConverter {
/**
* The maximum value of the flag must be limited to ensure conversions to UTF-8 do not trigger
* integer overflows. In JDK9+, if the message buffer contains a byte whose high bit is set, a
* UTF-8 decoding path is taken that allocates a new byte[] buffer twice as large as the message
* byte[] buffer.
*/
private static final int MAX_VALUE = (Integer.MAX_VALUE - 8) >> 1;
public MaxStdoutErrBytesConverter() {
super(-1, (Integer.MAX_VALUE - 8) >> 1);
}
@Override
public Integer convert(String input) throws OptionsParsingException {
Integer value = super.convert(input);
return value >= 0 ? value : MAX_VALUE;
}
}
}