aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorGravatar ccalvarin <ccalvarin@google.com>2017-09-26 18:11:53 -0400
committerGravatar John Cater <jcater@google.com>2017-09-27 10:01:20 -0400
commit77e3a5ca955a3834406a837dbd4607b0b432b2d8 (patch)
tree3a1abc2361cc5cde7f26588c1d331429e0fa89e8 /src
parenta703546e2e69888e4274d94cdd55767e02459d9a (diff)
Add new option categorization and tagging information to HelpCommand's output.
If setting flag --use_new_category_enum, group the options by the new categories in both the command line output and the "everything-as-html" output used for the generated docs at https://bazel.build/versions/master/docs/command-line-reference.html. In the html output, the effect and metadata tags are listed for each option, with links to their descriptions at the bottom of the page. The tags only appear in the terminal output in -l/--long/--help_verbosity=long, and only the names appear. This is still experimental as the majority of options do not yet use the new categorization system. The new output can be seen in command-line-reference.html format by adding the new flag to the "help everything-as-html" line in //src/main/java/com/google/devtools/build/lib:gen_command-line-reference. The html output is in the same order as before (by blaze rule, with inherited options not repeated), which means it still has duplicate options, that should ideally be fixed separately. I propose either dropping the high-level grouping and just grouping the options by documentation category, or potentially grouping them by optionsbase in some non-class-naming way, and listing the commands that they apply to, as more and more optionsbases are used by multiple commands without being inherited (for example, all BuildEventServiceOptions are listed 20 times). People probably use ctrl-f to navigate this page anyway. Once we know that we only list each option once, we can actually have links to the options, which will make it possible to have links in the expansion lists. Issue #3758 RELNOTES: added experimental --use_new_category_enum to the help command to output options grouped by the new type of category. PiperOrigin-RevId: 170116553
Diffstat (limited to 'src')
-rw-r--r--src/main/java/com/google/devtools/build/docgen/BuildEncyclopediaGenerator.java2
-rw-r--r--src/main/java/com/google/devtools/build/lib/runtime/BlazeCommandUtils.java74
-rw-r--r--src/main/java/com/google/devtools/build/lib/runtime/commands/HelpCommand.java204
-rw-r--r--src/main/java/com/google/devtools/common/options/OptionFilterDescriptions.java175
-rw-r--r--src/main/java/com/google/devtools/common/options/OptionsParser.java136
-rw-r--r--src/main/java/com/google/devtools/common/options/OptionsParserImpl.java5
-rw-r--r--src/main/java/com/google/devtools/common/options/OptionsUsage.java90
-rw-r--r--src/test/java/com/google/devtools/common/options/OptionFilterDescriptionsTest.java73
-rw-r--r--src/test/java/com/google/devtools/common/options/OptionsParserTest.java31
-rw-r--r--src/test/java/com/google/devtools/common/options/OptionsUsageTest.java417
-rw-r--r--src/test/java/com/google/devtools/common/options/TestOptions.java15
11 files changed, 1058 insertions, 164 deletions
diff --git a/src/main/java/com/google/devtools/build/docgen/BuildEncyclopediaGenerator.java b/src/main/java/com/google/devtools/build/docgen/BuildEncyclopediaGenerator.java
index 52f34478eb..6c6b4f35bd 100644
--- a/src/main/java/com/google/devtools/build/docgen/BuildEncyclopediaGenerator.java
+++ b/src/main/java/com/google/devtools/build/docgen/BuildEncyclopediaGenerator.java
@@ -32,7 +32,7 @@ public class BuildEncyclopediaGenerator {
+ "The product name (-n), rule class provider (-p) and at least one input_dir\n"
+ "(-i) must be specified.\n");
System.err.println(
- parser.describeOptions(
+ parser.describeOptionsWithDeprecatedCategories(
Collections.<String, String>emptyMap(), OptionsParser.HelpVerbosity.LONG));
}
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/BlazeCommandUtils.java b/src/main/java/com/google/devtools/build/lib/runtime/BlazeCommandUtils.java
index 5f57b735bd..07573fa3a8 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/BlazeCommandUtils.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/BlazeCommandUtils.java
@@ -21,7 +21,6 @@ import com.google.devtools.build.lib.syntax.SkylarkSemanticsOptions;
import com.google.devtools.build.lib.util.ResourceFileLoader;
import com.google.devtools.common.options.OptionsBase;
import com.google.devtools.common.options.OptionsParser;
-
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
@@ -114,21 +113,42 @@ public class BlazeCommandUtils {
* Returns the expansion of the specified help topic.
*
* @param topic the name of the help topic; used in %{command} expansion.
- * @param help the text template of the help message. Certain %{x} variables
- * will be expanded. A prefix of "resource:" means use the .jar
- * resource of that name.
- * @param categoryDescriptions a mapping from option category names to
- * descriptions, passed to {@link OptionsParser#describeOptions}.
- * @param helpVerbosity a tri-state verbosity option selecting between just
- * names, names and syntax, and full description.
+ * @param help the text template of the help message. Certain %{x} variables will be expanded. A
+ * prefix of "resource:" means use the .jar resource of that name.
+ * @param categoryDescriptions a mapping from option category names to descriptions, passed to
+ * {@link OptionsParser#describeOptionsWithDeprecatedCategories}.
+ * @param helpVerbosity a tri-state verbosity option selecting between just names, names and
+ * syntax, and full description.
* @param productName the product name
*/
- public static String expandHelpTopic(String topic, String help,
+ public static final String expandHelpTopic(
+ String topic,
+ String help,
Class<? extends BlazeCommand> commandClass,
Collection<Class<? extends OptionsBase>> options,
Map<String, String> categoryDescriptions,
OptionsParser.HelpVerbosity helpVerbosity,
String productName) {
+ return expandHelpTopic(
+ topic,
+ help,
+ commandClass,
+ options,
+ categoryDescriptions,
+ helpVerbosity,
+ productName,
+ false);
+ }
+
+ public static final String expandHelpTopic(
+ String topic,
+ String help,
+ Class<? extends BlazeCommand> commandClass,
+ Collection<Class<? extends OptionsBase>> options,
+ Map<String, String> categoryDescriptions,
+ OptionsParser.HelpVerbosity helpVerbosity,
+ String productName,
+ boolean useNewCategoryEnum) {
OptionsParser parser = OptionsParser.newOptionsParser(options);
String template;
@@ -137,8 +157,12 @@ public class BlazeCommandUtils {
try {
template = ResourceFileLoader.loadResource(commandClass, resourceName);
} catch (IOException e) {
- throw new IllegalStateException("failed to load help resource '" + resourceName
- + "' due to I/O error: " + e.getMessage(), e);
+ throw new IllegalStateException(
+ "failed to load help resource '"
+ + resourceName
+ + "' due to I/O error: "
+ + e.getMessage(),
+ e);
}
} else {
template = help;
@@ -148,10 +172,16 @@ public class BlazeCommandUtils {
throw new IllegalStateException("Help template for '" + topic + "' omits %{options}!");
}
- String optionStr =
- parser
- .describeOptions(categoryDescriptions, helpVerbosity)
- .replace("%{product}", productName);
+ String optionStr;
+ if (useNewCategoryEnum) {
+ optionStr =
+ parser.describeOptions(productName, helpVerbosity).replace("%{product}", productName);
+ } else {
+ optionStr =
+ parser
+ .describeOptionsWithDeprecatedCategories(categoryDescriptions, helpVerbosity)
+ .replace("%{product}", productName);
+ }
return template
.replace("%{product}", productName)
.replace("%{command}", topic)
@@ -166,10 +196,10 @@ public class BlazeCommandUtils {
/**
* The help page for this command.
*
- * @param categoryDescriptions a mapping from option category names to
- * descriptions, passed to {@link OptionsParser#describeOptions}.
- * @param verbosity a tri-state verbosity option selecting between just names,
- * names and syntax, and full description.
+ * @param categoryDescriptions a mapping from option category names to descriptions, passed to
+ * {@link OptionsParser#describeOptionsWithDeprecatedCategories}.
+ * @param verbosity a tri-state verbosity option selecting between just names, names and syntax,
+ * and full description.
*/
public static String getUsage(
Class<? extends BlazeCommand> commandClass,
@@ -177,7 +207,8 @@ public class BlazeCommandUtils {
OptionsParser.HelpVerbosity verbosity,
Iterable<BlazeModule> blazeModules,
ConfiguredRuleClassProvider ruleClassProvider,
- String productName) {
+ String productName,
+ boolean useNewCategoryEnum) {
Command commandAnnotation = commandClass.getAnnotation(Command.class);
return BlazeCommandUtils.expandHelpTopic(
commandAnnotation.name(),
@@ -186,6 +217,7 @@ public class BlazeCommandUtils {
BlazeCommandUtils.getOptions(commandClass, blazeModules, ruleClassProvider),
categoryDescriptions,
verbosity,
- productName);
+ productName,
+ useNewCategoryEnum);
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/commands/HelpCommand.java b/src/main/java/com/google/devtools/build/lib/runtime/commands/HelpCommand.java
index 8c3821bff6..8b1d9a8f03 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/commands/HelpCommand.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/commands/HelpCommand.java
@@ -43,6 +43,8 @@ import com.google.devtools.common.options.Option;
import com.google.devtools.common.options.OptionDefinition;
import com.google.devtools.common.options.OptionDocumentationCategory;
import com.google.devtools.common.options.OptionEffectTag;
+import com.google.devtools.common.options.OptionFilterDescriptions;
+import com.google.devtools.common.options.OptionMetadataTag;
import com.google.devtools.common.options.OptionsBase;
import com.google.devtools.common.options.OptionsParser;
import com.google.devtools.common.options.OptionsProvider;
@@ -113,15 +115,25 @@ public final class HelpCommand implements BlazeCommand {
help = "Show only the names of the options, not their types or meanings."
)
public Void showShortFormOptions;
+
+ @Option(
+ name = "use_new_category_enum",
+ defaultValue = "false",
+ documentationCategory = OptionDocumentationCategory.LOGGING,
+ effectTags = {OptionEffectTag.AFFECTS_OUTPUTS, OptionEffectTag.TERMINAL_OUTPUT},
+ metadataTags = {OptionMetadataTag.EXPERIMENTAL}
+ )
+ public boolean useNewCategoryEnum;
}
/**
- * Returns a map that maps option categories to descriptive help strings for categories that
- * are not part of the Bazel core.
+ * Returns a map that maps option categories to descriptive help strings for categories that are
+ * not part of the Bazel core.
*/
- private static ImmutableMap<String, String> getOptionCategories(BlazeRuntime runtime) {
+ @Deprecated
+ private static ImmutableMap<String, String> getDeprecatedOptionCategoriesDescriptions(
+ String name) {
ImmutableMap.Builder<String, String> optionCategoriesBuilder = ImmutableMap.builder();
- String name = runtime.getProductName();
optionCategoriesBuilder
.put("checking", String.format(
"Checking options, which control %s's error checking and/or warnings", name))
@@ -180,27 +192,40 @@ public final class HelpCommand implements BlazeCommand {
return ExitCode.COMMAND_LINE_ERROR;
}
String helpSubject = options.getResidue().get(0);
- if (helpSubject.equals("startup_options")) {
- emitBlazeVersionInfo(outErr, runtime.getProductName());
- emitStartupOptions(
- outErr, helpOptions.helpVerbosity, runtime, getOptionCategories(runtime));
- return ExitCode.SUCCESS;
- } else if (helpSubject.equals("target-syntax")) {
- emitBlazeVersionInfo(outErr, runtime.getProductName());
- emitTargetSyntaxHelp(outErr, getOptionCategories(runtime), runtime.getProductName());
- return ExitCode.SUCCESS;
- } else if (helpSubject.equals("info-keys")) {
- emitInfoKeysHelp(env, outErr);
- return ExitCode.SUCCESS;
- } else if (helpSubject.equals("completion")) {
- emitCompletionHelp(runtime, outErr);
- return ExitCode.SUCCESS;
- } else if (helpSubject.equals("flags-as-proto")) {
- emitFlagsAsProtoHelp(runtime, outErr);
- return ExitCode.SUCCESS;
- } else if (helpSubject.equals("everything-as-html")) {
- new HtmlEmitter(runtime).emit(outErr);
- return ExitCode.SUCCESS;
+ String productName = runtime.getProductName();
+ // Go through the custom subjects before going through Bazel commands.
+ switch (helpSubject) {
+ case "startup_options":
+ emitBlazeVersionInfo(outErr, runtime.getProductName());
+ emitStartupOptions(
+ outErr,
+ helpOptions.helpVerbosity,
+ runtime,
+ getDeprecatedOptionCategoriesDescriptions(productName),
+ helpOptions.useNewCategoryEnum);
+ return ExitCode.SUCCESS;
+ case "target-syntax":
+ emitBlazeVersionInfo(outErr, runtime.getProductName());
+ emitTargetSyntaxHelp(
+ outErr,
+ getDeprecatedOptionCategoriesDescriptions(productName),
+ productName,
+ helpOptions.useNewCategoryEnum);
+
+ return ExitCode.SUCCESS;
+ case "info-keys":
+ emitInfoKeysHelp(env, outErr);
+ return ExitCode.SUCCESS;
+ case "completion":
+ emitCompletionHelp(runtime, outErr);
+ return ExitCode.SUCCESS;
+ case "flags-as-proto":
+ emitFlagsAsProtoHelp(runtime, outErr);
+ return ExitCode.SUCCESS;
+ case "everything-as-html":
+ new HtmlEmitter(runtime, helpOptions.useNewCategoryEnum).emit(outErr);
+ return ExitCode.SUCCESS;
+ default: // fall out
}
BlazeCommand command = runtime.getCommandMap().get(helpSubject);
@@ -218,14 +243,17 @@ public final class HelpCommand implements BlazeCommand {
return ExitCode.COMMAND_LINE_ERROR;
}
}
- emitBlazeVersionInfo(outErr, runtime.getProductName());
- outErr.printOut(BlazeCommandUtils.getUsage(
- command.getClass(),
- getOptionCategories(runtime),
- helpOptions.helpVerbosity,
- runtime.getBlazeModules(),
- runtime.getRuleClassProvider(),
- runtime.getProductName()));
+ emitBlazeVersionInfo(outErr, productName);
+ outErr.printOut(
+ BlazeCommandUtils.getUsage(
+ command.getClass(),
+ getDeprecatedOptionCategoriesDescriptions(productName),
+ helpOptions.helpVerbosity,
+ runtime.getBlazeModules(),
+ runtime.getRuleClassProvider(),
+ productName,
+ helpOptions.useNewCategoryEnum));
+
return ExitCode.SUCCESS;
}
@@ -235,16 +263,22 @@ public final class HelpCommand implements BlazeCommand {
outErr.printOut(String.format("%80s\n", line));
}
- private void emitStartupOptions(OutErr outErr, OptionsParser.HelpVerbosity helpVerbosity,
- BlazeRuntime runtime, ImmutableMap<String, String> optionCategories) {
+ private void emitStartupOptions(
+ OutErr outErr,
+ OptionsParser.HelpVerbosity helpVerbosity,
+ BlazeRuntime runtime,
+ ImmutableMap<String, String> optionCategories,
+ boolean useNewCategoryEnum) {
outErr.printOut(
- BlazeCommandUtils.expandHelpTopic("startup_options",
+ BlazeCommandUtils.expandHelpTopic(
+ "startup_options",
"resource:startup_options.txt",
getClass(),
BlazeCommandUtils.getStartupOptions(runtime.getBlazeModules()),
optionCategories,
helpVerbosity,
- runtime.getProductName()));
+ runtime.getProductName(),
+ useNewCategoryEnum));
}
private void emitCompletionHelp(BlazeRuntime runtime, OutErr outErr) {
@@ -345,15 +379,21 @@ public final class HelpCommand implements BlazeCommand {
return ImmutableSortedMap.copyOf(runtime.getCommandMap());
}
- private void emitTargetSyntaxHelp(OutErr outErr, ImmutableMap<String, String> optionCategories,
- String productName) {
- outErr.printOut(BlazeCommandUtils.expandHelpTopic("target-syntax",
- "resource:target-syntax.txt",
- getClass(),
- ImmutableList.<Class<? extends OptionsBase>>of(),
- optionCategories,
- OptionsParser.HelpVerbosity.MEDIUM,
- productName));
+ private void emitTargetSyntaxHelp(
+ OutErr outErr,
+ ImmutableMap<String, String> optionCategories,
+ String productName,
+ boolean useNewCategoryEnum) {
+ outErr.printOut(
+ BlazeCommandUtils.expandHelpTopic(
+ "target-syntax",
+ "resource:target-syntax.txt",
+ getClass(),
+ ImmutableList.<Class<? extends OptionsBase>>of(),
+ optionCategories,
+ OptionsParser.HelpVerbosity.MEDIUM,
+ productName,
+ useNewCategoryEnum));
}
private void emitInfoKeysHelp(CommandEnvironment env, OutErr outErr) {
@@ -400,11 +440,19 @@ public final class HelpCommand implements BlazeCommand {
private static final class HtmlEmitter {
private final BlazeRuntime runtime;
- private final ImmutableMap<String, String> optionCategories;
+ private final ImmutableMap<String, String> deprecatedOptionCategoryDescriptions;
+ private final boolean useNewCategoriesEnum;
- private HtmlEmitter(BlazeRuntime runtime) {
+ private HtmlEmitter(BlazeRuntime runtime, boolean useNewCategoriesEnum) {
this.runtime = runtime;
- this.optionCategories = getOptionCategories(runtime);
+ this.useNewCategoriesEnum = useNewCategoriesEnum;
+ String productName = runtime.getProductName();
+ if (useNewCategoriesEnum) {
+ this.deprecatedOptionCategoryDescriptions = null;
+ } else {
+ this.deprecatedOptionCategoryDescriptions =
+ getDeprecatedOptionCategoriesDescriptions(productName);
+ }
}
private void emit(OutErr outErr) {
@@ -478,14 +526,67 @@ public final class HelpCommand implements BlazeCommand {
result.append("\n");
}
}
+
+ // Describe the tags once, any mentions above should link to these descriptions.
+ if (useNewCategoriesEnum) {
+ String productName = runtime.getProductName();
+ ImmutableMap<OptionEffectTag, String> effectTagDescriptions =
+ OptionFilterDescriptions.getOptionEffectTagDescription(productName);
+ result.append("<h3>Option Effect Tags</h3>\n");
+ result.append("<table>\n");
+ for (OptionEffectTag tag : OptionEffectTag.values()) {
+ String tagDescription = effectTagDescriptions.get(tag);
+
+ result.append("<tr>\n");
+ result.append(
+ String.format(
+ "<td id=\"effect_tag_%s\"><code>%s</code></td>\n",
+ tag, tag.name().toLowerCase()));
+ result.append(String.format("<td>%s</td>\n", HTML_ESCAPER.escape(tagDescription)));
+ result.append("</tr>\n");
+ }
+ result.append("</table>\n");
+
+ ImmutableMap<OptionMetadataTag, String> metadataTagDescriptions =
+ OptionFilterDescriptions.getOptionMetadataTagDescription(productName);
+ result.append("<h3>Option Metadata Tags</h3>\n");
+ result.append("<table>\n");
+ for (OptionMetadataTag tag : OptionMetadataTag.values()) {
+ // skip the tags that are reserved for undocumented flags.
+ if (!tag.equals(OptionMetadataTag.HIDDEN) && !tag.equals(OptionMetadataTag.INTERNAL)) {
+ String tagDescription = metadataTagDescriptions.get(tag);
+
+ result.append("<tr>\n");
+ result.append(
+ String.format(
+ "<td id=\"metadata_tag_%s\"><code>%s</code></td>\n",
+ tag, tag.name().toLowerCase()));
+ result.append(String.format("<td>%s</td>\n", HTML_ESCAPER.escape(tagDescription)));
+ result.append("</tr>\n");
+ }
+ }
+ result.append("</table>\n");
+ }
+
outErr.printOut(result.toString());
}
private void appendOptionsHtml(
StringBuilder result, Iterable<Class<? extends OptionsBase>> optionsClasses) {
OptionsParser parser = OptionsParser.newOptionsParser(optionsClasses);
- result.append(parser.describeOptionsHtml(optionCategories, HTML_ESCAPER)
- .replace("%{product}", runtime.getProductName()));
+ String productName = runtime.getProductName();
+ if (useNewCategoriesEnum) {
+ result.append(
+ parser
+ .describeOptionsHtml(HTML_ESCAPER, productName)
+ .replace("%{product}", productName));
+ } else {
+ result.append(
+ parser
+ .describeOptionsHtmlWithDeprecatedCategories(
+ deprecatedOptionCategoryDescriptions, HTML_ESCAPER)
+ .replace("%{product}", productName));
+ }
}
private static String capitalize(String s) {
@@ -510,3 +611,4 @@ public final class HelpCommand implements BlazeCommand {
void visit(String commandName, Command commandAnnotation, OptionsParser parser);
}
}
+
diff --git a/src/main/java/com/google/devtools/common/options/OptionFilterDescriptions.java b/src/main/java/com/google/devtools/common/options/OptionFilterDescriptions.java
new file mode 100644
index 0000000000..4c6efefe1d
--- /dev/null
+++ b/src/main/java/com/google/devtools/common/options/OptionFilterDescriptions.java
@@ -0,0 +1,175 @@
+// Copyright 2017 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.common.options;
+
+import com.google.common.collect.ImmutableMap;
+
+/**
+ * Provides descriptions of the options filters, for use in generated documentation and usage text.
+ */
+public class OptionFilterDescriptions {
+
+ /** The order that the categories should be listed in. */
+ static OptionDocumentationCategory[] documentationOrder = {
+ OptionDocumentationCategory.BAZEL_CLIENT_OPTIONS,
+ OptionDocumentationCategory.EXECUTION_STRATEGY,
+ OptionDocumentationCategory.TOOLCHAIN,
+ OptionDocumentationCategory.OUTPUT_SELECTION,
+ OptionDocumentationCategory.OUTPUT_PARAMETERS,
+ OptionDocumentationCategory.SIGNING,
+ OptionDocumentationCategory.TESTING,
+ OptionDocumentationCategory.QUERY,
+ OptionDocumentationCategory.BUILD_TIME_OPTIMIZATION,
+ OptionDocumentationCategory.LOGGING,
+ OptionDocumentationCategory.GENERIC_INPUTS,
+ OptionDocumentationCategory.UNCATEGORIZED
+ };
+
+ static ImmutableMap<OptionDocumentationCategory, String> getOptionCategoriesEnumDescription(
+ String productName) {
+ ImmutableMap.Builder<OptionDocumentationCategory, String> optionCategoriesBuilder =
+ ImmutableMap.builder();
+ optionCategoriesBuilder
+ .put(
+ OptionDocumentationCategory.UNCATEGORIZED,
+ "Miscellaneous options, not otherwise categorized.")
+ .put( // Here for completeness, the help output should not include this option.
+ OptionDocumentationCategory.UNDOCUMENTED,
+ "This feature should not be documented, as it is not meant for general use")
+ .put(
+ OptionDocumentationCategory.BAZEL_CLIENT_OPTIONS,
+ "Options that appear before the command and are parsed by the client")
+ .put(
+ OptionDocumentationCategory.LOGGING,
+ "Options that affect the verbosity, format or location of logging")
+ .put(OptionDocumentationCategory.EXECUTION_STRATEGY, "Options that control build execution")
+ .put(
+ OptionDocumentationCategory.BUILD_TIME_OPTIMIZATION,
+ "Options that trigger optimizations of the build time")
+ .put(
+ OptionDocumentationCategory.OUTPUT_SELECTION,
+ "Options that control the output of the command")
+ .put(
+ OptionDocumentationCategory.OUTPUT_PARAMETERS,
+ "Options that let the user configure the intended output, affecting its value, as "
+ + "opposed to its existence")
+ .put(
+ OptionDocumentationCategory.SIGNING,
+ "Options that affect the signing outputs of a build")
+ .put(
+ OptionDocumentationCategory.TESTING,
+ "Options that govern the behavior of the test environment or test runner")
+ .put(
+ OptionDocumentationCategory.TOOLCHAIN,
+ "Options that configure the toolchain used for action execution")
+ .put(OptionDocumentationCategory.QUERY, "Options relating to query output and semantics")
+ .put(
+ OptionDocumentationCategory.GENERIC_INPUTS,
+ "Options specifying or altering a generic input to a Bazel command that does not fall "
+ + "into other categories.");
+ return optionCategoriesBuilder.build();
+ }
+
+ public static ImmutableMap<OptionEffectTag, String> getOptionEffectTagDescription(
+ String productName) {
+ ImmutableMap.Builder<OptionEffectTag, String> effectTagDescriptionBuilder =
+ ImmutableMap.builder();
+ effectTagDescriptionBuilder
+ .put(OptionEffectTag.UNKNOWN, "This option has unknown, or undocumented, effect.")
+ .put(OptionEffectTag.NO_OP, "This option has literally no effect.")
+ .put(
+ OptionEffectTag.LOSES_INCREMENTAL_STATE,
+ "Changing the value of this option can cause significant loss of incremental "
+ + "state, which slows builds. State could be lost due to a server restart or to "
+ + "invalidation of a large part of the dependency graph.")
+ .put(
+ OptionEffectTag.CHANGES_INPUTS,
+ "This option actively changes the inputs that "
+ + productName
+ + " considers for the build, such as filesystem restrictions, repository versions, "
+ + "or other options.")
+ .put(
+ OptionEffectTag.AFFECTS_OUTPUTS,
+ "This option affects "
+ + productName
+ + "'s outputs. This tag is intentionally broad, can include transitive affects, "
+ + "and does not specify the type of output it affects.")
+ .put(
+ OptionEffectTag.BUILD_FILE_SEMANTICS,
+ "This option affects the semantics of BUILD or .bzl files.")
+ .put(
+ OptionEffectTag.BAZEL_INTERNAL_CONFIGURATION,
+ "This option affects settings of "
+ + productName
+ + "-internal machinery. This tag does not, on its own, mean that build artifacts "
+ + "are affected.")
+ .put(
+ OptionEffectTag.LOADING_AND_ANALYSIS,
+ "This option affects the loading and analysis of dependencies, and the building "
+ + "of the dependency graph.")
+ .put(
+ OptionEffectTag.EXECUTION,
+ "This option affects the execution phase, such as sandboxing or remote execution "
+ + "related options.")
+ .put(
+ OptionEffectTag.HOST_MACHINE_RESOURCE_OPTIMIZATIONS,
+ "This option triggers an optimization that may be machine specific and is not "
+ + "guaranteed to work on all machines. The optimization could include a tradeoff "
+ + "with other aspects of performance, such as memory or cpu cost.")
+ .put(
+ OptionEffectTag.EAGERNESS_TO_EXIT,
+ "This option changes how eagerly "
+ + productName
+ + " will exit from a failure, where a choice between continuing despite the "
+ + "failure and ending the invocation exists.")
+ .put(
+ OptionEffectTag.BAZEL_MONITORING,
+ "This option is used to monitor " + productName + "'s behavior and performance.")
+ .put(
+ OptionEffectTag.TERMINAL_OUTPUT,
+ "This option affects " + productName + "'s terminal output.")
+ .put(
+ OptionEffectTag.ACTION_OPTIONS,
+ "This option changes the command line arguments of one or more build actions.")
+ .put(
+ OptionEffectTag.TEST_RUNNER,
+ "This option changes the testrunner environment of the build.");
+ return effectTagDescriptionBuilder.build();
+ }
+
+ public static ImmutableMap<OptionMetadataTag, String> getOptionMetadataTagDescription(
+ String productName) {
+ ImmutableMap.Builder<OptionMetadataTag, String> effectTagDescriptionBuilder =
+ ImmutableMap.builder();
+ effectTagDescriptionBuilder
+ .put(
+ OptionMetadataTag.EXPERIMENTAL,
+ "This option triggers an experimental feature with no guarantees of functionality.")
+ .put(
+ OptionMetadataTag.INCOMPATIBLE_CHANGE,
+ "This option triggers a breaking change. Use this option to test your migration "
+ + "readiness or get early access to the new feature")
+ .put(
+ OptionMetadataTag.DEPRECATED,
+ "This option is deprecated. It might be that the feature it affects is deprecated, "
+ + "or that another method of supplying the information is preferred.")
+ .put(
+ OptionMetadataTag.HIDDEN, // Here for completeness, these options are UNDOCUMENTED.
+ "This option should not be used by a user, and should not be logged.")
+ .put(
+ OptionMetadataTag.INTERNAL, // Here for completeness, these options are UNDOCUMENTED.
+ "This option isn't even a option, and should not be logged.");
+ return effectTagDescriptionBuilder.build();
+ }
+}
diff --git a/src/main/java/com/google/devtools/common/options/OptionsParser.java b/src/main/java/com/google/devtools/common/options/OptionsParser.java
index 28c2206fbc..2b4bd2a3e4 100644
--- a/src/main/java/com/google/devtools/common/options/OptionsParser.java
+++ b/src/main/java/com/google/devtools/common/options/OptionsParser.java
@@ -17,8 +17,10 @@ package com.google.devtools.common.options;
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.base.Throwables;
+import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ListMultimap;
import com.google.common.escape.Escaper;
import com.google.devtools.common.options.OptionDefinition.NotAnOptionException;
import java.lang.reflect.Constructor;
@@ -32,6 +34,7 @@ import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -224,7 +227,9 @@ public class OptionsParser implements OptionsProvider {
public void parseAndExitUponError(OptionPriority priority, String source, String[] args) {
for (String arg : args) {
if (arg.equals("--help")) {
- System.out.println(describeOptions(ImmutableMap.of(), HelpVerbosity.LONG));
+ System.out.println(
+ describeOptionsWithDeprecatedCategories(ImmutableMap.of(), HelpVerbosity.LONG));
+
System.exit(0);
}
}
@@ -283,6 +288,78 @@ public class OptionsParser implements OptionsProvider {
* Returns a description of all the options this parser can digest. In addition to {@link Option}
* annotations, this method also interprets {@link OptionsUsage} annotations which give an
* intuitive short description for the options. Options of the same category (see {@link
+ * OptionDocumentationCategory}) will be grouped together.
+ *
+ * @param productName the name of this product (blaze, bazel)
+ * @param helpVerbosity if {@code long}, the options will be described verbosely, including their
+ * types, defaults and descriptions. If {@code medium}, the descriptions are omitted, and if
+ * {@code short}, the options are just enumerated.
+ */
+ public String describeOptions(String productName, HelpVerbosity helpVerbosity) {
+ StringBuilder desc = new StringBuilder();
+ LinkedHashMap<OptionDocumentationCategory, List<OptionDefinition>> optionsByCategory =
+ getOptionsSortedByCategory();
+ ImmutableMap<OptionDocumentationCategory, String> optionCategoryDescriptions =
+ OptionFilterDescriptions.getOptionCategoriesEnumDescription(productName);
+ for (Entry<OptionDocumentationCategory, List<OptionDefinition>> e :
+ optionsByCategory.entrySet()) {
+ String categoryDescription = optionCategoryDescriptions.get(e.getKey());
+ List<OptionDefinition> categorizedOptionList = e.getValue();
+
+ // Describe the category if we're going to end up using it at all.
+ if (!categorizedOptionList.isEmpty()) {
+ desc.append("\n").append(categoryDescription).append(":\n");
+ }
+ // Describe the options in this category.
+ for (OptionDefinition optionDef : categorizedOptionList) {
+ OptionsUsage.getUsage(optionDef, desc, helpVerbosity, impl.getOptionsData(), true);
+ }
+ }
+
+ return desc.toString().trim();
+ }
+
+ /**
+ * @return all documented options loaded in this parser, grouped by categories in display order.
+ */
+ private LinkedHashMap<OptionDocumentationCategory, List<OptionDefinition>>
+ getOptionsSortedByCategory() {
+ OptionsData data = impl.getOptionsData();
+ if (data.getOptionsClasses().isEmpty()) {
+ return new LinkedHashMap<>();
+ }
+
+ // Get the documented options grouped by category.
+ ListMultimap<OptionDocumentationCategory, OptionDefinition> optionsByCategories =
+ ArrayListMultimap.create();
+ for (Class<? extends OptionsBase> optionsClass : data.getOptionsClasses()) {
+ for (OptionDefinition optionDefinition :
+ OptionsData.getAllOptionDefinitionsForClass(optionsClass)) {
+ // Only track documented options.
+ if (optionDefinition.getDocumentationCategory()
+ != OptionDocumentationCategory.UNDOCUMENTED) {
+ optionsByCategories.put(optionDefinition.getDocumentationCategory(), optionDefinition);
+ }
+ }
+ }
+
+ // Put the categories into display order and sort the options in each category.
+ LinkedHashMap<OptionDocumentationCategory, List<OptionDefinition>> sortedCategoriesToOptions =
+ new LinkedHashMap<>(OptionFilterDescriptions.documentationOrder.length, 1);
+ for (OptionDocumentationCategory category : OptionFilterDescriptions.documentationOrder) {
+ List<OptionDefinition> optionList = optionsByCategories.get(category);
+ if (optionList != null) {
+ optionList.sort(OptionDefinition.BY_OPTION_NAME);
+ sortedCategoriesToOptions.put(category, optionList);
+ }
+ }
+ return sortedCategoriesToOptions;
+ }
+
+ /**
+ * Returns a description of all the options this parser can digest. In addition to {@link Option}
+ * annotations, this method also interprets {@link OptionsUsage} annotations which give an
+ * intuitive short description for the options. Options of the same category (see {@link
* Option#category}) will be grouped together.
*
* @param categoryDescriptions a mapping from category names to category descriptions.
@@ -291,7 +368,8 @@ public class OptionsParser implements OptionsProvider {
* types, defaults and descriptions. If {@code medium}, the descriptions are omitted, and if
* {@code short}, the options are just enumerated.
*/
- public String describeOptions(
+ @Deprecated
+ public String describeOptionsWithDeprecatedCategories(
Map<String, String> categoryDescriptions, HelpVerbosity helpVerbosity) {
OptionsData data = impl.getOptionsData();
StringBuilder desc = new StringBuilder();
@@ -318,7 +396,8 @@ public class OptionsParser implements OptionsProvider {
if (optionDefinition.getDocumentationCategory()
!= OptionDocumentationCategory.UNDOCUMENTED) {
- OptionsUsage.getUsage(optionDefinition, desc, helpVerbosity, impl.getOptionsData());
+ OptionsUsage.getUsage(
+ optionDefinition, desc, helpVerbosity, impl.getOptionsData(), false);
}
}
}
@@ -326,17 +405,17 @@ public class OptionsParser implements OptionsProvider {
}
/**
- * Returns a description of all the options this parser can digest.
- * In addition to {@link Option} annotations, this method also
- * interprets {@link OptionsUsage} annotations which give an intuitive short
- * description for the options.
+ * Returns a description of all the options this parser can digest. In addition to {@link Option}
+ * annotations, this method also interprets {@link OptionsUsage} annotations which give an
+ * intuitive short description for the options.
*
- * @param categoryDescriptions a mapping from category names to category
- * descriptions. Options of the same category (see {@link
- * Option#category}) will be grouped together, preceded by the description
- * of the category.
+ * @param categoryDescriptions a mapping from category names to category descriptions. Options of
+ * the same category (see {@link Option#category}) will be grouped together, preceded by the
+ * description of the category.
*/
- public String describeOptionsHtml(Map<String, String> categoryDescriptions, Escaper escaper) {
+ @Deprecated
+ public String describeOptionsHtmlWithDeprecatedCategories(
+ Map<String, String> categoryDescriptions, Escaper escaper) {
OptionsData data = impl.getOptionsData();
StringBuilder desc = new StringBuilder();
if (!data.getOptionsClasses().isEmpty()) {
@@ -366,7 +445,7 @@ public class OptionsParser implements OptionsProvider {
if (optionDefinition.getDocumentationCategory()
!= OptionDocumentationCategory.UNDOCUMENTED) {
- OptionsUsage.getUsageHtml(optionDefinition, desc, escaper, impl.getOptionsData());
+ OptionsUsage.getUsageHtml(optionDefinition, desc, escaper, impl.getOptionsData(), false);
}
}
desc.append("</dl>\n");
@@ -375,6 +454,37 @@ public class OptionsParser implements OptionsProvider {
}
/**
+ * Returns a description of all the options this parser can digest. In addition to {@link Option}
+ * annotations, this method also interprets {@link OptionsUsage} annotations which give an
+ * intuitive short description for the options.
+ */
+ public String describeOptionsHtml(Escaper escaper, String productName) {
+ StringBuilder desc = new StringBuilder();
+ LinkedHashMap<OptionDocumentationCategory, List<OptionDefinition>> optionsByCategory =
+ getOptionsSortedByCategory();
+ ImmutableMap<OptionDocumentationCategory, String> optionCategoryDescriptions =
+ OptionFilterDescriptions.getOptionCategoriesEnumDescription(productName);
+
+ for (Entry<OptionDocumentationCategory, List<OptionDefinition>> e :
+ optionsByCategory.entrySet()) {
+ desc.append("<dl>");
+ String categoryDescription = optionCategoryDescriptions.get(e.getKey());
+ List<OptionDefinition> categorizedOptionsList = e.getValue();
+
+ // Describe the category if we're going to end up using it at all.
+ if (!categorizedOptionsList.isEmpty()) {
+ desc.append(escaper.escape(categoryDescription)).append(":\n");
+ }
+ // Describe the options in this category.
+ for (OptionDefinition optionDef : categorizedOptionsList) {
+ OptionsUsage.getUsageHtml(optionDef, desc, escaper, impl.getOptionsData(), true);
+ }
+ desc.append("</dl>\n");
+ }
+ return desc.toString();
+ }
+
+ /**
* Returns a string listing the possible flag completion for this command along with the command
* completion if any. See {@link OptionsUsage#getCompletion(OptionDefinition, StringBuilder)} for
* more details on the format for the flag completion.
diff --git a/src/main/java/com/google/devtools/common/options/OptionsParserImpl.java b/src/main/java/com/google/devtools/common/options/OptionsParserImpl.java
index 221dcf030f..38248bf940 100644
--- a/src/main/java/com/google/devtools/common/options/OptionsParserImpl.java
+++ b/src/main/java/com/google/devtools/common/options/OptionsParserImpl.java
@@ -90,10 +90,9 @@ class OptionsParserImpl {
}
};
- /**
- * Create a new parser object
- */
+ /** Create a new parser object. Do not accept a null OptionsData object. */
OptionsParserImpl(OptionsData optionsData) {
+ Preconditions.checkNotNull(optionsData);
this.optionsData = optionsData;
}
diff --git a/src/main/java/com/google/devtools/common/options/OptionsUsage.java b/src/main/java/com/google/devtools/common/options/OptionsUsage.java
index 68a460e8e2..ecc7a6b61b 100644
--- a/src/main/java/com/google/devtools/common/options/OptionsUsage.java
+++ b/src/main/java/com/google/devtools/common/options/OptionsUsage.java
@@ -21,7 +21,11 @@ import com.google.common.collect.ImmutableList;
import com.google.common.escape.Escaper;
import java.text.BreakIterator;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
+import java.util.Locale;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
import javax.annotation.Nullable;
/** A renderer for usage messages for any combination of options classes. */
@@ -42,7 +46,7 @@ class OptionsUsage {
new ArrayList<>(OptionsData.getAllOptionDefinitionsForClass(optionsClass));
optionDefinitions.sort(OptionDefinition.BY_OPTION_NAME);
for (OptionDefinition optionDefinition : optionDefinitions) {
- getUsage(optionDefinition, usage, OptionsParser.HelpVerbosity.LONG, data);
+ getUsage(optionDefinition, usage, OptionsParser.HelpVerbosity.LONG, data, false);
}
}
@@ -95,19 +99,33 @@ class OptionsUsage {
}
+ // Placeholder tag "UNKNOWN" is ignored.
+ private static boolean shouldEffectTagBeListed(OptionEffectTag effectTag) {
+ return !effectTag.equals(OptionEffectTag.UNKNOWN);
+ }
+
+ // Tags that only apply to undocumented options are excluded.
+ private static boolean shouldMetadataTagBeListed(OptionMetadataTag metadataTag) {
+ return !metadataTag.equals(OptionMetadataTag.HIDDEN)
+ && !metadataTag.equals(OptionMetadataTag.INTERNAL);
+ }
+
/** Appends the usage message for a single option-field message to 'usage'. */
static void getUsage(
OptionDefinition optionDefinition,
StringBuilder usage,
OptionsParser.HelpVerbosity helpVerbosity,
- OptionsData optionsData) {
+ OptionsData optionsData,
+ boolean includeTags) {
String flagName = getFlagName(optionDefinition);
String typeDescription = getTypeDescription(optionDefinition);
usage.append(" --").append(flagName);
- if (helpVerbosity == OptionsParser.HelpVerbosity.SHORT) { // just the name
+ if (helpVerbosity == OptionsParser.HelpVerbosity.SHORT) {
usage.append('\n');
return;
}
+
+ // Add the option's type and default information. Stop there for "medium" verbosity.
if (optionDefinition.getAbbreviation() != '\0') {
usage.append(" [-").append(optionDefinition.getAbbreviation()).append(']');
}
@@ -127,9 +145,12 @@ class OptionsUsage {
usage.append(")");
}
usage.append("\n");
- if (helpVerbosity == OptionsParser.HelpVerbosity.MEDIUM) { // just the name and type.
+ if (helpVerbosity == OptionsParser.HelpVerbosity.MEDIUM) {
return;
}
+
+ // For verbosity "long," add the full description and expansion, along with the tag
+ // information if requested.
if (!optionDefinition.getHelpText().isEmpty()) {
usage.append(paragraphFill(optionDefinition.getHelpText(), /*indent=*/ 4, /*width=*/ 80));
usage.append('\n');
@@ -151,9 +172,28 @@ class OptionsUsage {
for (String req : optionDefinition.getImplicitRequirements()) {
requiredMsg.append(req).append(" ");
}
- usage.append(paragraphFill(requiredMsg.toString(), /*indent=*/ 6, /*width=*/ 80));
+ usage.append(paragraphFill(requiredMsg.toString(), 6, 80)); // (indent, width)
usage.append('\n');
}
+ if (!includeTags) {
+ return;
+ }
+
+ // If we are expected to include the tags, add them for high verbosity.
+ Stream<OptionEffectTag> effectTagStream =
+ Arrays.stream(optionDefinition.getOptionEffectTags())
+ .filter(OptionsUsage::shouldEffectTagBeListed);
+ Stream<OptionMetadataTag> metadataTagStream =
+ Arrays.stream(optionDefinition.getOptionMetadataTags())
+ .filter(OptionsUsage::shouldMetadataTagBeListed);
+ String tagList =
+ Stream.concat(effectTagStream, metadataTagStream)
+ .map(tag -> tag.toString().toLowerCase())
+ .collect(Collectors.joining(", "));
+ if (!tagList.isEmpty()) {
+ usage.append(paragraphFill("Tags: " + tagList, 6, 80)); // (indent, width)
+ usage.append("\n");
+ }
}
/** Append the usage message for a single option-field message to 'usage'. */
@@ -161,7 +201,8 @@ class OptionsUsage {
OptionDefinition optionDefinition,
StringBuilder usage,
Escaper escaper,
- OptionsData optionsData) {
+ OptionsData optionsData,
+ boolean includeTags) {
String plainFlagName = optionDefinition.getOptionName();
String flagName = getFlagName(optionDefinition);
String valueDescription = optionDefinition.getValueTypeHelpText();
@@ -215,7 +256,10 @@ class OptionsUsage {
Preconditions.checkArgument(!expansion.isEmpty());
expandsMsg = new StringBuilder("Expands to:<br/>\n");
for (String exp : expansion) {
- // TODO(ulfjack): Can we link to the expanded flags here?
+ // TODO(ulfjack): We should link to the expanded flags, but unfortunately we don't
+ // currently guarantee that all flags are only printed once. A flag in an OptionBase that
+ // is included by 2 different commands, but not inherited through a parent command, will
+ // be printed multiple times.
expandsMsg
.append("&nbsp;&nbsp;<code>")
.append(escaper.escape(exp))
@@ -225,6 +269,32 @@ class OptionsUsage {
usage.append(expandsMsg.toString());
}
+ // Add effect tags, if not UNKNOWN, and metadata tags, if not empty.
+ if (includeTags) {
+ Stream<OptionEffectTag> effectTagStream =
+ Arrays.stream(optionDefinition.getOptionEffectTags())
+ .filter(OptionsUsage::shouldEffectTagBeListed);
+ Stream<OptionMetadataTag> metadataTagStream =
+ Arrays.stream(optionDefinition.getOptionMetadataTags())
+ .filter(OptionsUsage::shouldMetadataTagBeListed);
+ String tagList =
+ Stream.concat(
+ effectTagStream.map(
+ tag ->
+ String.format(
+ "<a href=\"#effect_tag_%s\"><code>%s</code></a>",
+ tag, tag.name().toLowerCase())),
+ metadataTagStream.map(
+ tag ->
+ String.format(
+ "<a href=\"#metadata_tag_%s\"><code>%s</code></a>",
+ tag, tag.name().toLowerCase())))
+ .collect(Collectors.joining(", "));
+ if (!tagList.isEmpty()) {
+ usage.append("<br>Tags: \n").append(tagList);
+ }
+ }
+
usage.append("</dd>\n");
}
@@ -263,8 +333,10 @@ class OptionsUsage {
builder.append("={auto,yes,no}\n");
builder.append("--no").append(flagName).append("\n");
} else if (fieldType.isEnum()) {
- builder.append("={")
- .append(COMMA_JOINER.join(fieldType.getEnumConstants()).toLowerCase()).append("}\n");
+ builder
+ .append("={")
+ .append(COMMA_JOINER.join(fieldType.getEnumConstants()).toLowerCase(Locale.ENGLISH))
+ .append("}\n");
} else if (fieldType.getSimpleName().equals("Label")) {
// String comparison so we don't introduce a dependency to com.google.devtools.build.lib.
builder.append("=label\n");
diff --git a/src/test/java/com/google/devtools/common/options/OptionFilterDescriptionsTest.java b/src/test/java/com/google/devtools/common/options/OptionFilterDescriptionsTest.java
new file mode 100644
index 0000000000..a8d2814a5b
--- /dev/null
+++ b/src/test/java/com/google/devtools/common/options/OptionFilterDescriptionsTest.java
@@ -0,0 +1,73 @@
+// Copyright 2017 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.common.options;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.collect.ImmutableMap;
+import java.util.ArrayList;
+import java.util.Collections;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests that we have descriptions for every option tag. */
+@RunWith(JUnit4.class)
+public class OptionFilterDescriptionsTest {
+
+ @Test
+ public void documentationOrderIncludesAllDocumentedCategories() {
+ // Expect the documentation order to include everything but the undocumented category.
+ ArrayList<OptionDocumentationCategory> docOrderPlusUndocumented = new ArrayList<>();
+ Collections.addAll(docOrderPlusUndocumented, OptionFilterDescriptions.documentationOrder);
+ docOrderPlusUndocumented.add(OptionDocumentationCategory.UNDOCUMENTED);
+
+ assertThat(OptionDocumentationCategory.values())
+ .asList()
+ .containsExactlyElementsIn(docOrderPlusUndocumented);
+ }
+
+ @Test
+ public void optionDocumentationCategoryDescriptionsContainsAllCategories() {
+ // Check that we have a description for all valid option categories.
+ ImmutableMap<OptionDocumentationCategory, String> optionCategoryDescriptions =
+ OptionFilterDescriptions.getOptionCategoriesEnumDescription("blaze");
+
+ assertThat(OptionDocumentationCategory.values())
+ .asList()
+ .containsExactlyElementsIn(optionCategoryDescriptions.keySet());
+ }
+
+ @Test
+ public void optionEffectTagDescriptionsContainsAllTags() {
+ // Check that we have a description for all valid option tags.
+ ImmutableMap<OptionEffectTag, String> optionEffectTagDescription =
+ OptionFilterDescriptions.getOptionEffectTagDescription("blaze");
+
+ assertThat(OptionEffectTag.values())
+ .asList()
+ .containsExactlyElementsIn(optionEffectTagDescription.keySet());
+ }
+
+ @Test
+ public void optionMetadataTagDescriptionsContainsAllTags() {
+ // Check that we have a description for all valid option tags.
+ ImmutableMap<OptionMetadataTag, String> optionMetadataTagDescription =
+ OptionFilterDescriptions.getOptionMetadataTagDescription("blaze");
+
+ assertThat(OptionMetadataTag.values())
+ .asList()
+ .containsExactlyElementsIn(optionMetadataTagDescription.keySet());
+ }
+}
diff --git a/src/test/java/com/google/devtools/common/options/OptionsParserTest.java b/src/test/java/com/google/devtools/common/options/OptionsParserTest.java
index 41ad1f30a5..491facccfc 100644
--- a/src/test/java/com/google/devtools/common/options/OptionsParserTest.java
+++ b/src/test/java/com/google/devtools/common/options/OptionsParserTest.java
@@ -486,7 +486,7 @@ public class OptionsParserTest {
params,
ImmutableList.of(
"--baz\r\n'hello\nworld'\r\n--foo\r\nhello\\\r\nworld\r\n\r\n"
- + "--nodoc\r\n\"hello\r\nworld\""),
+ + "--nodoc\r\n\"hello\r\nworld\""),
StandardCharsets.UTF_8,
StandardOpenOption.CREATE);
@@ -1111,7 +1111,8 @@ public class OptionsParserTest {
// that an options parser be constructed.
OptionsParser parser = OptionsParser.newOptionsParser(ExpansionOptions.class);
String usage =
- parser.describeOptions(ImmutableMap.<String, String>of(), OptionsParser.HelpVerbosity.LONG);
+ parser.describeOptionsWithDeprecatedCategories(
+ ImmutableMap.<String, String>of(), OptionsParser.HelpVerbosity.LONG);
assertThat(usage).contains(" --expands\n Expands to: --underlying=from_expansion");
assertThat(usage).contains(" --expands_by_function\n Expands to: --expands");
}
@@ -1256,8 +1257,9 @@ public class OptionsParserTest {
OptionsParser parser = OptionsParser.newOptionsParser(ImplicitDependencyWarningOptions.class);
parser.parse("--second=second", "--first=first");
assertThat(parser.getWarnings())
- .containsExactly("Option 'second' is implicitly defined by "
- + "option 'first'; the implicitly set value overrides the previous one");
+ .containsExactly(
+ "Option 'second' is implicitly defined by "
+ + "option 'first'; the implicitly set value overrides the previous one");
}
@Test
@@ -1267,8 +1269,9 @@ public class OptionsParserTest {
assertThat(parser.getWarnings()).isEmpty();
parser.parse("--second=second");
assertThat(parser.getWarnings())
- .containsExactly("A new value for option 'second' overrides a"
- + " previous implicit setting of that option by option 'first'");
+ .containsExactly(
+ "A new value for option 'second' overrides a"
+ + " previous implicit setting of that option by option 'first'");
}
@Test
@@ -1276,8 +1279,9 @@ public class OptionsParserTest {
OptionsParser parser = OptionsParser.newOptionsParser(ImplicitDependencyWarningOptions.class);
parser.parse("--first=first", "--second=second");
assertThat(parser.getWarnings())
- .containsExactly("Option 'second' is implicitly defined by "
- + "option 'first'; the implicitly set value overrides the previous one");
+ .containsExactly(
+ "Option 'second' is implicitly defined by "
+ + "option 'first'; the implicitly set value overrides the previous one");
}
@Test
@@ -1287,8 +1291,8 @@ public class OptionsParserTest {
assertThat(parser.getWarnings()).isEmpty();
parser.parse("--third=third");
assertThat(parser.getWarnings())
- .containsExactly("Option 'second' is implicitly defined by both "
- + "option 'first' and option 'third'");
+ .containsExactly(
+ "Option 'second' is implicitly defined by both " + "option 'first' and option 'third'");
}
@Test
@@ -1476,9 +1480,10 @@ public class OptionsParserTest {
public void warningForExpansionOverridingExplicitOption() throws Exception {
OptionsParser parser = OptionsParser.newOptionsParser(ExpansionWarningOptions.class);
parser.parse("--underlying=underlying", "--first");
- assertThat(parser.getWarnings()).containsExactly(
- "The option 'first' was expanded and now overrides a "
- + "previous explicitly specified option 'underlying'");
+ assertThat(parser.getWarnings())
+ .containsExactly(
+ "The option 'first' was expanded and now overrides a "
+ + "previous explicitly specified option 'underlying'");
}
@Test
diff --git a/src/test/java/com/google/devtools/common/options/OptionsUsageTest.java b/src/test/java/com/google/devtools/common/options/OptionsUsageTest.java
index 6e665b47b2..c7554a96f2 100644
--- a/src/test/java/com/google/devtools/common/options/OptionsUsageTest.java
+++ b/src/test/java/com/google/devtools/common/options/OptionsUsageTest.java
@@ -35,130 +35,208 @@ public final class OptionsUsageTest {
data = OptionsParser.getOptionsDataInternal(TestOptions.class);
}
- private String getHtmlUsage(String fieldName) {
+ private String getHtmlUsageWithoutTags(String fieldName) {
StringBuilder builder = new StringBuilder();
OptionsUsage.getUsageHtml(
- data.getOptionDefinitionFromName(fieldName), builder, HTML_ESCAPER, data);
+ data.getOptionDefinitionFromName(fieldName), builder, HTML_ESCAPER, data, false);
return builder.toString();
}
- private String getTerminalUsage(String fieldName, HelpVerbosity verbosity) {
+ private String getHtmlUsageWithTags(String fieldName) {
StringBuilder builder = new StringBuilder();
- OptionsUsage.getUsage(data.getOptionDefinitionFromName(fieldName), builder, verbosity, data);
+ OptionsUsage.getUsageHtml(
+ data.getOptionDefinitionFromName(fieldName), builder, HTML_ESCAPER, data, true);
+ return builder.toString();
+ }
+
+ private String getTerminalUsageWithoutTags(String fieldName, HelpVerbosity verbosity) {
+ StringBuilder builder = new StringBuilder();
+ OptionsUsage.getUsage(
+ data.getOptionDefinitionFromName(fieldName), builder, verbosity, data, false);
+ return builder.toString();
+ }
+
+ /**
+ * Tests the future behavior of the options usage output. For short & medium verbosity, this
+ * should be the same as the current default
+ */
+ private String getTerminalUsageWithTags(String fieldName, HelpVerbosity verbosity) {
+ StringBuilder builder = new StringBuilder();
+ OptionsUsage.getUsage(
+ data.getOptionDefinitionFromName(fieldName), builder, verbosity, data, true);
return builder.toString();
}
@Test
public void stringValue_shortTerminalOutput() {
- assertThat(getTerminalUsage("test_string", HelpVerbosity.SHORT)).isEqualTo(" --test_string\n");
+ assertThat(getTerminalUsageWithoutTags("test_string", HelpVerbosity.SHORT))
+ .isEqualTo(" --test_string\n");
+ assertThat(getTerminalUsageWithoutTags("test_string", HelpVerbosity.SHORT))
+ .isEqualTo(getTerminalUsageWithTags("test_string", HelpVerbosity.SHORT));
}
@Test
public void stringValue_mediumTerminalOutput() {
- assertThat(getTerminalUsage("test_string", HelpVerbosity.MEDIUM))
+ assertThat(getTerminalUsageWithoutTags("test_string", HelpVerbosity.MEDIUM))
.isEqualTo(" --test_string (a string; default: \"test string default\")\n");
+ assertThat(getTerminalUsageWithoutTags("test_string", HelpVerbosity.MEDIUM))
+ .isEqualTo(getTerminalUsageWithTags("test_string", HelpVerbosity.MEDIUM));
}
@Test
public void stringValue_longTerminalOutput() {
- assertThat(getTerminalUsage("test_string", HelpVerbosity.LONG))
+ assertThat(getTerminalUsageWithoutTags("test_string", HelpVerbosity.LONG))
.isEqualTo(
" --test_string (a string; default: \"test string default\")\n"
+ " a string-valued option to test simple option operations\n");
+ assertThat(getTerminalUsageWithTags("test_string", HelpVerbosity.LONG))
+ .isEqualTo(
+ " --test_string (a string; default: \"test string default\")\n"
+ + " a string-valued option to test simple option operations\n"
+ + " Tags: no_op\n");
}
@Test
public void stringValue_htmlOutput() {
- assertThat(getHtmlUsage("test_string"))
+ assertThat(getHtmlUsageWithoutTags("test_string"))
+ .isEqualTo(
+ "<dt><code><a name=\"flag--test_string\"></a>"
+ + "--test_string=&lt;a string&gt</code> default: \"test string default\"</dt>\n"
+ + "<dd>\n"
+ + "a string-valued option to test simple option operations\n"
+ + "</dd>\n");
+ assertThat(getHtmlUsageWithTags("test_string"))
.isEqualTo(
"<dt><code><a name=\"flag--test_string\"></a>"
+ "--test_string=&lt;a string&gt</code> default: \"test string default\"</dt>\n"
+ "<dd>\n"
+ "a string-valued option to test simple option operations\n"
+ + "<br>Tags: \n"
+ + "<a href=\"#effect_tag_NO_OP\"><code>no_op</code></a>"
+ "</dd>\n");
}
@Test
public void intValue_shortTerminalOutput() {
- assertThat(getTerminalUsage("expanded_c", HelpVerbosity.SHORT)).isEqualTo(" --expanded_c\n");
+ assertThat(getTerminalUsageWithoutTags("expanded_c", HelpVerbosity.SHORT))
+ .isEqualTo(" --expanded_c\n");
+ assertThat(getTerminalUsageWithoutTags("expanded_c", HelpVerbosity.SHORT))
+ .isEqualTo(getTerminalUsageWithTags("expanded_c", HelpVerbosity.SHORT));
}
@Test
public void intValue_mediumTerminalOutput() {
- assertThat(getTerminalUsage("expanded_c", HelpVerbosity.MEDIUM))
+ assertThat(getTerminalUsageWithoutTags("expanded_c", HelpVerbosity.MEDIUM))
.isEqualTo(" --expanded_c (an integer; default: \"12\")\n");
+ assertThat(getTerminalUsageWithoutTags("expanded_c", HelpVerbosity.MEDIUM))
+ .isEqualTo(getTerminalUsageWithTags("expanded_c", HelpVerbosity.MEDIUM));
}
@Test
public void intValue_longTerminalOutput() {
- assertThat(getTerminalUsage("expanded_c", HelpVerbosity.LONG))
+ assertThat(getTerminalUsageWithoutTags("expanded_c", HelpVerbosity.LONG))
.isEqualTo(
" --expanded_c (an integer; default: \"12\")\n"
+ " an int-value'd flag used to test expansion logic\n");
+ assertThat(getTerminalUsageWithTags("expanded_c", HelpVerbosity.LONG))
+ .isEqualTo(
+ " --expanded_c (an integer; default: \"12\")\n"
+ + " an int-value'd flag used to test expansion logic\n"
+ + " Tags: no_op\n");
}
@Test
public void intValue_htmlOutput() {
- assertThat(getHtmlUsage("expanded_c"))
+ assertThat(getHtmlUsageWithoutTags("expanded_c"))
.isEqualTo(
"<dt><code><a name=\"flag--expanded_c\"></a>"
+ "--expanded_c=&lt;an integer&gt</code> default: \"12\"</dt>\n"
+ "<dd>\n"
+ "an int-value&#39;d flag used to test expansion logic\n"
+ "</dd>\n");
+ assertThat(getHtmlUsageWithTags("expanded_c"))
+ .isEqualTo(
+ "<dt><code><a name=\"flag--expanded_c\"></a>"
+ + "--expanded_c=&lt;an integer&gt</code> default: \"12\"</dt>\n"
+ + "<dd>\n"
+ + "an int-value&#39;d flag used to test expansion logic\n"
+ + "<br>Tags: \n"
+ + "<a href=\"#effect_tag_NO_OP\"><code>no_op</code></a>"
+ + "</dd>\n");
}
@Test
public void booleanValue_shortTerminalOutput() {
- assertThat(getTerminalUsage("expanded_a", HelpVerbosity.SHORT))
+ assertThat(getTerminalUsageWithoutTags("expanded_a", HelpVerbosity.SHORT))
.isEqualTo(" --[no]expanded_a\n");
+ assertThat(getTerminalUsageWithoutTags("expanded_a", HelpVerbosity.SHORT))
+ .isEqualTo(getTerminalUsageWithTags("expanded_a", HelpVerbosity.SHORT));
}
@Test
public void booleanValue_mediumTerminalOutput() {
- assertThat(getTerminalUsage("expanded_a", HelpVerbosity.MEDIUM))
+ assertThat(getTerminalUsageWithoutTags("expanded_a", HelpVerbosity.MEDIUM))
.isEqualTo(" --[no]expanded_a (a boolean; default: \"true\")\n");
+ assertThat(getTerminalUsageWithoutTags("expanded_a", HelpVerbosity.MEDIUM))
+ .isEqualTo(getTerminalUsageWithTags("expanded_a", HelpVerbosity.MEDIUM));
}
@Test
public void booleanValue_longTerminalOutput() {
- assertThat(getTerminalUsage("expanded_a", HelpVerbosity.LONG))
- .isEqualTo(" --[no]expanded_a (a boolean; default: \"true\")\n");
+ assertThat(getTerminalUsageWithoutTags("expanded_a", HelpVerbosity.LONG))
+ .isEqualTo(
+ " --[no]expanded_a (a boolean; default: \"true\")\n"
+ + " A boolean flag with unknown effect to test tagless usage text.\n");
+ // This flag has no useful tags, to verify that the tag line is omitted, so the usage line
+ // should be the same in both tag and tag-free world.
+ assertThat(getTerminalUsageWithoutTags("expanded_a", HelpVerbosity.LONG))
+ .isEqualTo(getTerminalUsageWithTags("expanded_a", HelpVerbosity.LONG));
}
@Test
public void booleanValue_htmlOutput() {
- assertThat(getHtmlUsage("expanded_a"))
+ assertThat(getHtmlUsageWithoutTags("expanded_a"))
.isEqualTo(
"<dt><code><a name=\"flag--expanded_a\"></a>"
+ "--[no]expanded_a</code> default: \"true\"</dt>\n"
+ "<dd>\n"
+ + "A boolean flag with unknown effect to test tagless usage text.\n"
+ "</dd>\n");
+ assertThat(getHtmlUsageWithoutTags("expanded_a")).isEqualTo(getHtmlUsageWithTags("expanded_a"));
}
@Test
public void multipleValue_shortTerminalOutput() {
- assertThat(getTerminalUsage("test_multiple_string", HelpVerbosity.SHORT))
+ assertThat(getTerminalUsageWithoutTags("test_multiple_string", HelpVerbosity.SHORT))
.isEqualTo(" --test_multiple_string\n");
+ assertThat(getTerminalUsageWithoutTags("test_multiple_string", HelpVerbosity.SHORT))
+ .isEqualTo(getTerminalUsageWithTags("test_multiple_string", HelpVerbosity.SHORT));
}
@Test
public void multipleValue_mediumTerminalOutput() {
- assertThat(getTerminalUsage("test_multiple_string", HelpVerbosity.MEDIUM))
+ assertThat(getTerminalUsageWithoutTags("test_multiple_string", HelpVerbosity.MEDIUM))
.isEqualTo(" --test_multiple_string (a string; may be used multiple times)\n");
+ assertThat(getTerminalUsageWithoutTags("test_multiple_string", HelpVerbosity.MEDIUM))
+ .isEqualTo(getTerminalUsageWithTags("test_multiple_string", HelpVerbosity.MEDIUM));
}
@Test
public void multipleValue_longTerminalOutput() {
- assertThat(getTerminalUsage("test_multiple_string", HelpVerbosity.LONG))
+ assertThat(getTerminalUsageWithoutTags("test_multiple_string", HelpVerbosity.LONG))
.isEqualTo(
" --test_multiple_string (a string; may be used multiple times)\n"
+ " a repeatable string-valued flag with its own unhelpful help text\n");
+ assertThat(getTerminalUsageWithTags("test_multiple_string", HelpVerbosity.LONG))
+ .isEqualTo(
+ " --test_multiple_string (a string; may be used multiple times)\n"
+ + " a repeatable string-valued flag with its own unhelpful help text\n"
+ + " Tags: no_op\n");
}
@Test
public void multipleValue_htmlOutput() {
- assertThat(getHtmlUsage("test_multiple_string"))
+ assertThat(getHtmlUsageWithoutTags("test_multiple_string"))
.isEqualTo(
"<dt><code><a name=\"flag--test_multiple_string\"></a>"
+ "--test_multiple_string=&lt;a string&gt</code> "
@@ -166,32 +244,52 @@ public final class OptionsUsageTest {
+ "<dd>\n"
+ "a repeatable string-valued flag with its own unhelpful help text\n"
+ "</dd>\n");
+ assertThat(getHtmlUsageWithTags("test_multiple_string"))
+ .isEqualTo(
+ "<dt><code><a name=\"flag--test_multiple_string\"></a>"
+ + "--test_multiple_string=&lt;a string&gt</code> "
+ + "multiple uses are accumulated</dt>\n"
+ + "<dd>\n"
+ + "a repeatable string-valued flag with its own unhelpful help text\n"
+ + "<br>Tags: \n"
+ + "<a href=\"#effect_tag_NO_OP\"><code>no_op</code></a>"
+ + "</dd>\n");
}
@Test
public void customConverterValue_shortTerminalOutput() {
- assertThat(getTerminalUsage("test_list_converters", HelpVerbosity.SHORT))
+ assertThat(getTerminalUsageWithoutTags("test_list_converters", HelpVerbosity.SHORT))
.isEqualTo(" --test_list_converters\n");
+ assertThat(getTerminalUsageWithoutTags("test_list_converters", HelpVerbosity.SHORT))
+ .isEqualTo(getTerminalUsageWithTags("test_list_converters", HelpVerbosity.SHORT));
}
@Test
public void customConverterValue_mediumTerminalOutput() {
- assertThat(getTerminalUsage("test_list_converters", HelpVerbosity.MEDIUM))
+ assertThat(getTerminalUsageWithoutTags("test_list_converters", HelpVerbosity.MEDIUM))
.isEqualTo(" --test_list_converters (a list of strings; may be used multiple times)\n");
+ assertThat(getTerminalUsageWithoutTags("test_list_converters", HelpVerbosity.MEDIUM))
+ .isEqualTo(getTerminalUsageWithTags("test_list_converters", HelpVerbosity.MEDIUM));
}
@Test
public void customConverterValue_longTerminalOutput() {
- assertThat(getTerminalUsage("test_list_converters", HelpVerbosity.LONG))
+ assertThat(getTerminalUsageWithoutTags("test_list_converters", HelpVerbosity.LONG))
.isEqualTo(
" --test_list_converters (a list of strings; may be used multiple times)\n"
+ " a repeatable flag that accepts lists, but doesn't want to have lists of \n"
+ " lists as a final type\n");
+ assertThat(getTerminalUsageWithTags("test_list_converters", HelpVerbosity.LONG))
+ .isEqualTo(
+ " --test_list_converters (a list of strings; may be used multiple times)\n"
+ + " a repeatable flag that accepts lists, but doesn't want to have lists of \n"
+ + " lists as a final type\n"
+ + " Tags: no_op\n");
}
@Test
public void customConverterValue_htmlOutput() {
- assertThat(getHtmlUsage("test_list_converters"))
+ assertThat(getHtmlUsageWithoutTags("test_list_converters"))
.isEqualTo(
"<dt><code><a name=\"flag--test_list_converters\"></a>"
+ "--test_list_converters=&lt;a list of strings&gt</code> "
@@ -200,33 +298,55 @@ public final class OptionsUsageTest {
+ "a repeatable flag that accepts lists, but doesn&#39;t want to have lists of \n"
+ "lists as a final type\n"
+ "</dd>\n");
+ assertThat(getHtmlUsageWithTags("test_list_converters"))
+ .isEqualTo(
+ "<dt><code><a name=\"flag--test_list_converters\"></a>"
+ + "--test_list_converters=&lt;a list of strings&gt</code> "
+ + "multiple uses are accumulated</dt>\n"
+ + "<dd>\n"
+ + "a repeatable flag that accepts lists, but doesn&#39;t want to have lists of \n"
+ + "lists as a final type\n"
+ + "<br>Tags: \n"
+ + "<a href=\"#effect_tag_NO_OP\"><code>no_op</code></a>"
+ + "</dd>\n");
}
@Test
public void staticExpansionOption_shortTerminalOutput() {
- assertThat(getTerminalUsage("test_expansion", HelpVerbosity.SHORT))
+ assertThat(getTerminalUsageWithoutTags("test_expansion", HelpVerbosity.SHORT))
.isEqualTo(" --test_expansion\n");
+ assertThat(getTerminalUsageWithoutTags("test_expansion", HelpVerbosity.SHORT))
+ .isEqualTo(getTerminalUsageWithTags("test_expansion", HelpVerbosity.SHORT));
}
@Test
public void staticExpansionOption_mediumTerminalOutput() {
- assertThat(getTerminalUsage("test_expansion", HelpVerbosity.MEDIUM))
+ assertThat(getTerminalUsageWithoutTags("test_expansion", HelpVerbosity.MEDIUM))
.isEqualTo(" --test_expansion\n");
+ assertThat(getTerminalUsageWithoutTags("test_expansion", HelpVerbosity.MEDIUM))
+ .isEqualTo(getTerminalUsageWithTags("test_expansion", HelpVerbosity.MEDIUM));
}
@Test
public void staticExpansionOption_longTerminalOutput() {
- assertThat(getTerminalUsage("test_expansion", HelpVerbosity.LONG))
+ assertThat(getTerminalUsageWithoutTags("test_expansion", HelpVerbosity.LONG))
.isEqualTo(
" --test_expansion\n"
+ " this expands to an alphabet soup.\n"
+ " Expands to: --noexpanded_a --expanded_b=false --expanded_c 42 --\n"
+ " expanded_d bar \n");
+ assertThat(getTerminalUsageWithTags("test_expansion", HelpVerbosity.LONG))
+ .isEqualTo(
+ " --test_expansion\n"
+ + " this expands to an alphabet soup.\n"
+ + " Expands to: --noexpanded_a --expanded_b=false --expanded_c 42 --\n"
+ + " expanded_d bar \n"
+ + " Tags: no_op\n");
}
@Test
public void staticExpansionOption_htmlOutput() {
- assertThat(getHtmlUsage("test_expansion"))
+ assertThat(getHtmlUsageWithoutTags("test_expansion"))
.isEqualTo(
"<dt><code><a name=\"flag--test_expansion\"></a>"
+ "--test_expansion</code></dt>\n"
@@ -241,33 +361,79 @@ public final class OptionsUsageTest {
+ "&nbsp;&nbsp;<code>--expanded_d</code><br/>\n"
+ "&nbsp;&nbsp;<code>bar</code><br/>\n"
+ "</dd>\n");
+ assertThat(getHtmlUsageWithTags("test_expansion"))
+ .isEqualTo(
+ "<dt><code><a name=\"flag--test_expansion\"></a>"
+ + "--test_expansion</code></dt>\n"
+ + "<dd>\n"
+ + "this expands to an alphabet soup.\n"
+ + "<br/>\n"
+ + "Expands to:<br/>\n"
+ + "&nbsp;&nbsp;<code>--noexpanded_a</code><br/>\n"
+ + "&nbsp;&nbsp;<code>--expanded_b=false</code><br/>\n"
+ + "&nbsp;&nbsp;<code>--expanded_c</code><br/>\n"
+ + "&nbsp;&nbsp;<code>42</code><br/>\n"
+ + "&nbsp;&nbsp;<code>--expanded_d</code><br/>\n"
+ + "&nbsp;&nbsp;<code>bar</code><br/>\n"
+ + "<br>Tags: \n"
+ + "<a href=\"#effect_tag_NO_OP\"><code>no_op</code></a>"
+ + "</dd>\n");
}
@Test
public void recursiveExpansionOption_shortTerminalOutput() {
- assertThat(getTerminalUsage("test_recursive_expansion_top_level", HelpVerbosity.SHORT))
+ assertThat(
+ getTerminalUsageWithoutTags("test_recursive_expansion_top_level", HelpVerbosity.SHORT))
.isEqualTo(" --test_recursive_expansion_top_level\n");
+ assertThat(
+ getTerminalUsageWithoutTags("test_recursive_expansion_top_level", HelpVerbosity.SHORT))
+ .isEqualTo(
+ getTerminalUsageWithTags("test_recursive_expansion_top_level", HelpVerbosity.SHORT));
}
@Test
public void recursiveExpansionOption_mediumTerminalOutput() {
- assertThat(getTerminalUsage("test_recursive_expansion_top_level", HelpVerbosity.MEDIUM))
+ assertThat(
+ getTerminalUsageWithoutTags("test_recursive_expansion_top_level", HelpVerbosity.MEDIUM))
.isEqualTo(" --test_recursive_expansion_top_level\n");
+ assertThat(
+ getTerminalUsageWithoutTags("test_recursive_expansion_top_level", HelpVerbosity.MEDIUM))
+ .isEqualTo(
+ getTerminalUsageWithTags("test_recursive_expansion_top_level", HelpVerbosity.MEDIUM));
}
@Test
public void recursiveExpansionOption_longTerminalOutput() {
- assertThat(getTerminalUsage("test_recursive_expansion_top_level", HelpVerbosity.LONG))
+ assertThat(
+ getTerminalUsageWithoutTags("test_recursive_expansion_top_level", HelpVerbosity.LONG))
.isEqualTo(
" --test_recursive_expansion_top_level\n"
+ " Lets the children do all the work.\n"
+ " Expands to: --test_recursive_expansion_middle1 --\n"
+ " test_recursive_expansion_middle2 \n");
+ assertThat(getTerminalUsageWithTags("test_recursive_expansion_top_level", HelpVerbosity.LONG))
+ .isEqualTo(
+ " --test_recursive_expansion_top_level\n"
+ + " Lets the children do all the work.\n"
+ + " Expands to: --test_recursive_expansion_middle1 --\n"
+ + " test_recursive_expansion_middle2 \n"
+ + " Tags: no_op\n");
}
@Test
public void recursiveExpansionOption_htmlOutput() {
- assertThat(getHtmlUsage("test_recursive_expansion_top_level"))
+ assertThat(getHtmlUsageWithoutTags("test_recursive_expansion_top_level"))
+ .isEqualTo(
+ "<dt><code><a name=\"flag--test_recursive_expansion_top_level\"></a>"
+ + "--test_recursive_expansion_top_level</code></dt>\n"
+ + "<dd>\n"
+ + "Lets the children do all the work.\n"
+ + "<br/>\n"
+ + "Expands to:<br/>\n"
+ + "&nbsp;&nbsp;<code>--test_recursive_expansion_middle1</code><br/>\n"
+ + "&nbsp;&nbsp;<code>--test_recursive_expansion_middle2</code><br/>\n"
+ + "</dd>\n");
+ assertThat(getHtmlUsageWithTags("test_recursive_expansion_top_level"))
.isEqualTo(
"<dt><code><a name=\"flag--test_recursive_expansion_top_level\"></a>"
+ "--test_recursive_expansion_top_level</code></dt>\n"
@@ -277,34 +443,58 @@ public final class OptionsUsageTest {
+ "Expands to:<br/>\n"
+ "&nbsp;&nbsp;<code>--test_recursive_expansion_middle1</code><br/>\n"
+ "&nbsp;&nbsp;<code>--test_recursive_expansion_middle2</code><br/>\n"
+ + "<br>Tags: \n"
+ + "<a href=\"#effect_tag_NO_OP\"><code>no_op</code></a>"
+ "</dd>\n");
}
@Test
public void expansionToMultipleValue_shortTerminalOutput() {
- assertThat(getTerminalUsage("test_expansion_to_repeatable", HelpVerbosity.SHORT))
+ assertThat(getTerminalUsageWithoutTags("test_expansion_to_repeatable", HelpVerbosity.SHORT))
.isEqualTo(" --test_expansion_to_repeatable\n");
+ assertThat(getTerminalUsageWithoutTags("test_expansion_to_repeatable", HelpVerbosity.SHORT))
+ .isEqualTo(getTerminalUsageWithTags("test_expansion_to_repeatable", HelpVerbosity.SHORT));
}
@Test
public void expansionToMultipleValue_mediumTerminalOutput() {
- assertThat(getTerminalUsage("test_expansion_to_repeatable", HelpVerbosity.MEDIUM))
+ assertThat(getTerminalUsageWithoutTags("test_expansion_to_repeatable", HelpVerbosity.MEDIUM))
.isEqualTo(" --test_expansion_to_repeatable\n");
+ assertThat(getTerminalUsageWithoutTags("test_expansion_to_repeatable", HelpVerbosity.MEDIUM))
+ .isEqualTo(getTerminalUsageWithTags("test_expansion_to_repeatable", HelpVerbosity.MEDIUM));
}
@Test
public void expansionToMultipleValue_longTerminalOutput() {
- assertThat(getTerminalUsage("test_expansion_to_repeatable", HelpVerbosity.LONG))
+ assertThat(getTerminalUsageWithoutTags("test_expansion_to_repeatable", HelpVerbosity.LONG))
.isEqualTo(
" --test_expansion_to_repeatable\n"
+ " Go forth and multiply, they said.\n"
+ " Expands to: --test_multiple_string=expandedFirstValue --\n"
+ " test_multiple_string=expandedSecondValue \n");
+ assertThat(getTerminalUsageWithTags("test_expansion_to_repeatable", HelpVerbosity.LONG))
+ .isEqualTo(
+ " --test_expansion_to_repeatable\n"
+ + " Go forth and multiply, they said.\n"
+ + " Expands to: --test_multiple_string=expandedFirstValue --\n"
+ + " test_multiple_string=expandedSecondValue \n"
+ + " Tags: no_op\n");
}
@Test
public void expansionToMultipleValue_htmlOutput() {
- assertThat(getHtmlUsage("test_expansion_to_repeatable"))
+ assertThat(getHtmlUsageWithoutTags("test_expansion_to_repeatable"))
+ .isEqualTo(
+ "<dt><code><a name=\"flag--test_expansion_to_repeatable\"></a>"
+ + "--test_expansion_to_repeatable</code></dt>\n"
+ + "<dd>\n"
+ + "Go forth and multiply, they said.\n"
+ + "<br/>\n"
+ + "Expands to:<br/>\n"
+ + "&nbsp;&nbsp;<code>--test_multiple_string=expandedFirstValue</code><br/>\n"
+ + "&nbsp;&nbsp;<code>--test_multiple_string=expandedSecondValue</code><br/>\n"
+ + "</dd>\n");
+ assertThat(getHtmlUsageWithTags("test_expansion_to_repeatable"))
.isEqualTo(
"<dt><code><a name=\"flag--test_expansion_to_repeatable\"></a>"
+ "--test_expansion_to_repeatable</code></dt>\n"
@@ -314,67 +504,109 @@ public final class OptionsUsageTest {
+ "Expands to:<br/>\n"
+ "&nbsp;&nbsp;<code>--test_multiple_string=expandedFirstValue</code><br/>\n"
+ "&nbsp;&nbsp;<code>--test_multiple_string=expandedSecondValue</code><br/>\n"
+ + "<br>Tags: \n"
+ + "<a href=\"#effect_tag_NO_OP\"><code>no_op</code></a>"
+ "</dd>\n");
}
@Test
public void implicitRequirementOption_shortTerminalOutput() {
- assertThat(getTerminalUsage("test_implicit_requirement", HelpVerbosity.SHORT))
+ assertThat(getTerminalUsageWithoutTags("test_implicit_requirement", HelpVerbosity.SHORT))
.isEqualTo(" --test_implicit_requirement\n");
+ assertThat(getTerminalUsageWithoutTags("test_implicit_requirement", HelpVerbosity.SHORT))
+ .isEqualTo(getTerminalUsageWithTags("test_implicit_requirement", HelpVerbosity.SHORT));
}
@Test
public void implicitRequirementOption_mediumTerminalOutput() {
- assertThat(getTerminalUsage("test_implicit_requirement", HelpVerbosity.MEDIUM))
+ assertThat(getTerminalUsageWithoutTags("test_implicit_requirement", HelpVerbosity.MEDIUM))
.isEqualTo(" --test_implicit_requirement (a string; default: \"direct implicit\")\n");
+ assertThat(getTerminalUsageWithoutTags("test_implicit_requirement", HelpVerbosity.MEDIUM))
+ .isEqualTo(getTerminalUsageWithTags("test_implicit_requirement", HelpVerbosity.MEDIUM));
}
@Test
public void implicitRequirementOption_longTerminalOutput() {
- assertThat(getTerminalUsage("test_implicit_requirement", HelpVerbosity.LONG))
+ assertThat(getTerminalUsageWithoutTags("test_implicit_requirement", HelpVerbosity.LONG))
.isEqualTo(
" --test_implicit_requirement (a string; default: \"direct implicit\")\n"
+ " this option really needs that other one, isolation of purpose has failed.\n"
+ " Using this option will also add: --implicit_requirement_a=implicit \n"
+ " requirement, required \n");
+ assertThat(getTerminalUsageWithTags("test_implicit_requirement", HelpVerbosity.LONG))
+ .isEqualTo(
+ " --test_implicit_requirement (a string; default: \"direct implicit\")\n"
+ + " this option really needs that other one, isolation of purpose has failed.\n"
+ + " Using this option will also add: --implicit_requirement_a=implicit \n"
+ + " requirement, required \n"
+ + " Tags: no_op\n");
}
@Test
public void implicitRequirementOption_htmlOutput() {
- assertThat(getHtmlUsage("test_implicit_requirement"))
+ assertThat(getHtmlUsageWithoutTags("test_implicit_requirement"))
+ .isEqualTo(
+ "<dt><code><a name=\"flag--test_implicit_requirement\"></a>"
+ + "--test_implicit_requirement=&lt;a string&gt</code> "
+ + "default: \"direct implicit\"</dt>\n"
+ + "<dd>\n"
+ + "this option really needs that other one, isolation of purpose has failed.\n"
+ + "</dd>\n");
+ assertThat(getHtmlUsageWithTags("test_implicit_requirement"))
.isEqualTo(
"<dt><code><a name=\"flag--test_implicit_requirement\"></a>"
+ "--test_implicit_requirement=&lt;a string&gt</code> "
+ "default: \"direct implicit\"</dt>\n"
+ "<dd>\n"
+ "this option really needs that other one, isolation of purpose has failed.\n"
+ + "<br>Tags: \n"
+ + "<a href=\"#effect_tag_NO_OP\"><code>no_op</code></a>"
+ "</dd>\n");
}
@Test
public void expansionFunctionOptionThatReadsUserValue_shortTerminalOutput() {
- assertThat(getTerminalUsage("test_expansion_function", HelpVerbosity.SHORT))
+ assertThat(getTerminalUsageWithoutTags("test_expansion_function", HelpVerbosity.SHORT))
.isEqualTo(" --test_expansion_function\n");
+ assertThat(getTerminalUsageWithoutTags("test_expansion_function", HelpVerbosity.SHORT))
+ .isEqualTo(getTerminalUsageWithTags("test_expansion_function", HelpVerbosity.SHORT));
}
@Test
public void expansionFunctionOptionThatReadsUserValue_mediumTerminalOutput() {
- assertThat(getTerminalUsage("test_expansion_function", HelpVerbosity.MEDIUM))
+ assertThat(getTerminalUsageWithoutTags("test_expansion_function", HelpVerbosity.MEDIUM))
.isEqualTo(" --test_expansion_function\n");
+ assertThat(getTerminalUsageWithoutTags("test_expansion_function", HelpVerbosity.MEDIUM))
+ .isEqualTo(getTerminalUsageWithTags("test_expansion_function", HelpVerbosity.MEDIUM));
}
@Test
public void expansionFunctionOptionThatReadsUserValue_longTerminalOutput() {
- assertThat(getTerminalUsage("test_expansion_function", HelpVerbosity.LONG))
+ assertThat(getTerminalUsageWithoutTags("test_expansion_function", HelpVerbosity.LONG))
.isEqualTo(
" --test_expansion_function\n"
+ " this is for testing expansion-by-function functionality.\n"
+ " Expands to unknown options.\n");
+ assertThat(getTerminalUsageWithTags("test_expansion_function", HelpVerbosity.LONG))
+ .isEqualTo(
+ " --test_expansion_function\n"
+ + " this is for testing expansion-by-function functionality.\n"
+ + " Expands to unknown options.\n"
+ + " Tags: no_op\n");
}
@Test
public void expansionFunctionOptionThatReadsUserValue_htmlOutput() {
- assertThat(getHtmlUsage("test_expansion_function"))
+ assertThat(getHtmlUsageWithoutTags("test_expansion_function"))
+ .isEqualTo(
+ "<dt><code><a name=\"flag--test_expansion_function\"></a>"
+ + "--test_expansion_function</code></dt>\n"
+ + "<dd>\n"
+ + "this is for testing expansion-by-function functionality.\n"
+ + "<br/>\n"
+ + "Expands to unknown options.<br/>\n"
+ + "</dd>\n");
+ assertThat(getHtmlUsageWithTags("test_expansion_function"))
.isEqualTo(
"<dt><code><a name=\"flag--test_expansion_function\"></a>"
+ "--test_expansion_function</code></dt>\n"
@@ -382,33 +614,56 @@ public final class OptionsUsageTest {
+ "this is for testing expansion-by-function functionality.\n"
+ "<br/>\n"
+ "Expands to unknown options.<br/>\n"
+ + "<br>Tags: \n"
+ + "<a href=\"#effect_tag_NO_OP\"><code>no_op</code></a>"
+ "</dd>\n");
}
@Test
public void expansionFunctionOptionThatExpandsBasedOnOtherLoadedOptions_shortTerminalOutput() {
- assertThat(getTerminalUsage("prefix_expansion", HelpVerbosity.SHORT))
+ assertThat(getTerminalUsageWithoutTags("prefix_expansion", HelpVerbosity.SHORT))
.isEqualTo(" --prefix_expansion\n");
+ assertThat(getTerminalUsageWithoutTags("prefix_expansion", HelpVerbosity.SHORT))
+ .isEqualTo(getTerminalUsageWithTags("prefix_expansion", HelpVerbosity.SHORT));
}
@Test
public void expansionFunctionOptionThatExpandsBasedOnOtherLoadedOptions_mediumTerminalOutput() {
- assertThat(getTerminalUsage("prefix_expansion", HelpVerbosity.MEDIUM))
+ assertThat(getTerminalUsageWithoutTags("prefix_expansion", HelpVerbosity.MEDIUM))
.isEqualTo(" --prefix_expansion\n");
+ assertThat(getTerminalUsageWithoutTags("prefix_expansion", HelpVerbosity.MEDIUM))
+ .isEqualTo(getTerminalUsageWithTags("prefix_expansion", HelpVerbosity.MEDIUM));
}
@Test
public void expansionFunctionOptionThatExpandsBasedOnOtherLoadedOptions_longTerminalOutput() {
- assertThat(getTerminalUsage("prefix_expansion", HelpVerbosity.LONG))
+ assertThat(getTerminalUsageWithoutTags("prefix_expansion", HelpVerbosity.LONG))
.isEqualTo(
" --prefix_expansion\n"
+ " Expands to all options with a specific prefix.\n"
+ " Expands to: --specialexp_bar --specialexp_foo \n");
+ assertThat(getTerminalUsageWithTags("prefix_expansion", HelpVerbosity.LONG))
+ .isEqualTo(
+ " --prefix_expansion\n"
+ + " Expands to all options with a specific prefix.\n"
+ + " Expands to: --specialexp_bar --specialexp_foo \n"
+ + " Tags: no_op\n");
}
@Test
public void expansionFunctionOptionThatExpandsBasedOnOtherLoadedOptions_htmlOutput() {
- assertThat(getHtmlUsage("prefix_expansion"))
+ assertThat(getHtmlUsageWithoutTags("prefix_expansion"))
+ .isEqualTo(
+ "<dt><code><a name=\"flag--prefix_expansion\"></a>"
+ + "--prefix_expansion</code></dt>\n"
+ + "<dd>\n"
+ + "Expands to all options with a specific prefix.\n"
+ + "<br/>\n"
+ + "Expands to:<br/>\n"
+ + "&nbsp;&nbsp;<code>--specialexp_bar</code><br/>\n"
+ + "&nbsp;&nbsp;<code>--specialexp_foo</code><br/>\n"
+ + "</dd>\n");
+ assertThat(getHtmlUsageWithTags("prefix_expansion"))
.isEqualTo(
"<dt><code><a name=\"flag--prefix_expansion\"></a>"
+ "--prefix_expansion</code></dt>\n"
@@ -418,6 +673,70 @@ public final class OptionsUsageTest {
+ "Expands to:<br/>\n"
+ "&nbsp;&nbsp;<code>--specialexp_bar</code><br/>\n"
+ "&nbsp;&nbsp;<code>--specialexp_foo</code><br/>\n"
+ + "<br>Tags: \n"
+ + "<a href=\"#effect_tag_NO_OP\"><code>no_op</code></a>"
+ + "</dd>\n");
+ }
+
+ @Test
+ public void tagHeavyExpansionOption_shortTerminalOutput() {
+ assertThat(getTerminalUsageWithoutTags("test_void_expansion_function", HelpVerbosity.SHORT))
+ .isEqualTo(" --test_void_expansion_function\n");
+ assertThat(getTerminalUsageWithoutTags("test_void_expansion_function", HelpVerbosity.SHORT))
+ .isEqualTo(getTerminalUsageWithTags("test_void_expansion_function", HelpVerbosity.SHORT));
+ }
+
+ @Test
+ public void tagHeavyExpansionOption_mediumTerminalOutput() {
+ assertThat(getTerminalUsageWithoutTags("test_void_expansion_function", HelpVerbosity.MEDIUM))
+ .isEqualTo(" --test_void_expansion_function\n");
+ assertThat(getTerminalUsageWithoutTags("test_void_expansion_function", HelpVerbosity.MEDIUM))
+ .isEqualTo(getTerminalUsageWithTags("test_void_expansion_function", HelpVerbosity.MEDIUM));
+ }
+
+ @Test
+ public void tagHeavyExpansionOption_longTerminalOutput() {
+ assertThat(getTerminalUsageWithoutTags("test_void_expansion_function", HelpVerbosity.LONG))
+ .isEqualTo(
+ " --test_void_expansion_function\n"
+ + " Listing a ton of random tags to test the usage output.\n"
+ + " Expands to: --expanded_d void expanded \n");
+ assertThat(getTerminalUsageWithTags("test_void_expansion_function", HelpVerbosity.LONG))
+ .isEqualTo(
+ " --test_void_expansion_function\n"
+ + " Listing a ton of random tags to test the usage output.\n"
+ + " Expands to: --expanded_d void expanded \n"
+ + " Tags: action_options, test_runner, terminal_output, experimental\n");
+ }
+
+ @Test
+ public void tagHeavyExpansionOption_htmlOutput() {
+ assertThat(getHtmlUsageWithoutTags("test_void_expansion_function"))
+ .isEqualTo(
+ "<dt><code><a name=\"flag--test_void_expansion_function\"></a>"
+ + "--test_void_expansion_function</code></dt>\n"
+ + "<dd>\n"
+ + "Listing a ton of random tags to test the usage output.\n"
+ + "<br/>\n"
+ + "Expands to:<br/>\n"
+ + "&nbsp;&nbsp;<code>--expanded_d</code><br/>\n"
+ + "&nbsp;&nbsp;<code>void expanded</code><br/>\n"
+ + "</dd>\n");
+ assertThat(getHtmlUsageWithTags("test_void_expansion_function"))
+ .isEqualTo(
+ "<dt><code><a name=\"flag--test_void_expansion_function\"></a>"
+ + "--test_void_expansion_function</code></dt>\n"
+ + "<dd>\n"
+ + "Listing a ton of random tags to test the usage output.\n"
+ + "<br/>\n"
+ + "Expands to:<br/>\n"
+ + "&nbsp;&nbsp;<code>--expanded_d</code><br/>\n"
+ + "&nbsp;&nbsp;<code>void expanded</code><br/>\n"
+ + "<br>Tags: \n"
+ + "<a href=\"#effect_tag_ACTION_OPTIONS\"><code>action_options</code></a>, "
+ + "<a href=\"#effect_tag_TEST_RUNNER\"><code>test_runner</code></a>, "
+ + "<a href=\"#effect_tag_TERMINAL_OUTPUT\"><code>terminal_output</code></a>, "
+ + "<a href=\"#metadata_tag_EXPERIMENTAL\"><code>experimental</code></a>"
+ "</dd>\n");
}
}
diff --git a/src/test/java/com/google/devtools/common/options/TestOptions.java b/src/test/java/com/google/devtools/common/options/TestOptions.java
index 04aeee9bc0..4eb1a2d8e6 100644
--- a/src/test/java/com/google/devtools/common/options/TestOptions.java
+++ b/src/test/java/com/google/devtools/common/options/TestOptions.java
@@ -141,8 +141,9 @@ public class TestOptions extends OptionsBase {
@Option(
name = "expanded_a",
documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
- effectTags = {OptionEffectTag.NO_OP},
- defaultValue = "true"
+ effectTags = {OptionEffectTag.UNKNOWN},
+ defaultValue = "true",
+ help = "A boolean flag with unknown effect to test tagless usage text."
)
public boolean expandedA;
@@ -283,8 +284,14 @@ public class TestOptions extends OptionsBase {
name = "test_void_expansion_function",
defaultValue = "null",
documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
- effectTags = {OptionEffectTag.NO_OP},
- expansionFunction = TestVoidExpansionFunction.class
+ effectTags = {
+ OptionEffectTag.ACTION_OPTIONS,
+ OptionEffectTag.TEST_RUNNER,
+ OptionEffectTag.TERMINAL_OUTPUT
+ },
+ metadataTags = {OptionMetadataTag.EXPERIMENTAL},
+ expansionFunction = TestVoidExpansionFunction.class,
+ help = "Listing a ton of random tags to test the usage output."
)
public Void testVoidExpansionFunction;