diff options
author | 2015-04-09 20:48:04 +0000 | |
---|---|---|
committer | 2015-04-10 08:02:30 +0000 | |
commit | 29728d4c37721aa88621a8578a7c2d7bfe8b5c68 (patch) | |
tree | b01b6032585956f1ce4312b54aab745fca2de59c | |
parent | 6379d2e4dc41bf32dfada75a001c39684083acd6 (diff) |
Added an help command to dump all options for completion
`bazel help completion` dump all options completion pattern
for each command, giving hints on the format of the completion
residue (e.g., `label`, `path`, `{a,enum}`, ...). This
dump can be used to generate completion scripts.
--
MOS_MIGRATED_REVID=90743024
10 files changed, 135 insertions, 0 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/Command.java b/src/main/java/com/google/devtools/build/lib/runtime/Command.java index 1797cd3b46..318e3e419b 100644 --- a/src/main/java/com/google/devtools/build/lib/runtime/Command.java +++ b/src/main/java/com/google/devtools/build/lib/runtime/Command.java @@ -105,4 +105,13 @@ public @interface Command { */ boolean canRunInOutputDirectory() default false; + /** + * Returns the type completion help for this command, that is the type arguments that this command + * expects. It can be a whitespace separated list if the command take several arguments. The type + * of each arguments can be <code>label</code>, <code>path</code>, <code>string</code>, ... + * It can also be a comma separated list of values, e.g. <code>{value1,value2}<code>. If a command + * accept several argument types, they can be combined with |, e.g <code>label|path</code>. + */ + String completion() default ""; + } diff --git a/src/main/java/com/google/devtools/build/lib/runtime/commands/BuildCommand.java b/src/main/java/com/google/devtools/build/lib/runtime/commands/BuildCommand.java index d6f61eb494..94a0ada3d3 100644 --- a/src/main/java/com/google/devtools/build/lib/runtime/commands/BuildCommand.java +++ b/src/main/java/com/google/devtools/build/lib/runtime/commands/BuildCommand.java @@ -46,6 +46,7 @@ import java.util.List; usesConfigurationOptions = true, shortDescription = "Builds the specified targets.", allowResidue = true, + completion = "label", help = "resource:build.txt") public final class BuildCommand implements BlazeCommand { 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 0539da5e44..227227f7af 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 @@ -13,6 +13,7 @@ // limitations under the License. package com.google.devtools.build.lib.runtime.commands; +import com.google.common.base.Joiner; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.devtools.build.docgen.BlazeRuleHelpPrinter; @@ -38,6 +39,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Set; /** * The 'blaze help' command, which prints all available commands as well as @@ -48,8 +50,11 @@ import java.util.Map; allowResidue = true, mustRunInWorkspace = false, shortDescription = "Prints help for commands, or the index.", + completion = "command|{startup_options,target-syntax,info-keys}", help = "resource:help.txt") public final class HelpCommand implements BlazeCommand { + private static final Joiner SPACE_JOINER = Joiner.on(" "); + public static class Options extends OptionsBase { @Option(name = "help_verbosity", @@ -151,6 +156,9 @@ public final class HelpCommand implements BlazeCommand { } else if (helpSubject.equals("info-keys")) { emitInfoKeysHelp(runtime, outErr); return ExitCode.SUCCESS; + } else if (helpSubject.equals("completion")) { + emitCompletionHelp(runtime, outErr); + return ExitCode.SUCCESS; } BlazeCommand command = runtime.getCommandMap().get(helpSubject); @@ -195,6 +203,42 @@ public final class HelpCommand implements BlazeCommand { helpVerbosity)); } + private void emitCompletionHelp(BlazeRuntime runtime, OutErr outErr) { + // First startup_options + Iterable<BlazeModule> blazeModules = runtime.getBlazeModules(); + ConfiguredRuleClassProvider ruleClassProvider = runtime.getRuleClassProvider(); + Map<String, BlazeCommand> commandsByName = runtime.getCommandMap(); + Set<String> commands = commandsByName.keySet(); + + outErr.printOutLn("BAZEL_COMMAND_LIST=\"" + SPACE_JOINER.join(commands) + "\""); + + outErr.printOutLn("BAZEL_INFO_KEYS=\""); + for (InfoKey key : InfoKey.values()) { + outErr.printOutLn(key.getName()); + } + outErr.printOutLn("\""); + + outErr.printOutLn("BAZEL_STARTUP_OPTIONS=\""); + Iterable<Class<? extends OptionsBase>> options = + BlazeCommandUtils.getStartupOptions(blazeModules); + outErr.printOut(OptionsParser.newOptionsParser(options).getOptionsCompletion()); + outErr.printOutLn("\""); + + for (String name : commands) { + BlazeCommand command = commandsByName.get(name); + String varName = name.toUpperCase().replace("-", "_"); + Command annotation = command.getClass().getAnnotation(Command.class); + if (!annotation.completion().isEmpty()) { + outErr.printOutLn("BAZEL_COMMAND_" + varName + "_ARGUMENT=\"" + + annotation.completion() + "\""); + } + options = BlazeCommandUtils.getOptions(command.getClass(), blazeModules, ruleClassProvider); + outErr.printOutLn("BAZEL_COMMAND_" + varName + "_FLAGS=\""); + outErr.printOut(OptionsParser.newOptionsParser(options).getOptionsCompletion()); + outErr.printOutLn("\""); + } + } + private void emitTargetSyntaxHelp(OutErr outErr, ImmutableMap<String, String> optionCategories) { outErr.printOut(BlazeCommandUtils.expandHelpTopic("target-syntax", "resource:target-syntax.txt", diff --git a/src/main/java/com/google/devtools/build/lib/runtime/commands/InfoCommand.java b/src/main/java/com/google/devtools/build/lib/runtime/commands/InfoCommand.java index 2d0e720b17..2448d90aca 100644 --- a/src/main/java/com/google/devtools/build/lib/runtime/commands/InfoCommand.java +++ b/src/main/java/com/google/devtools/build/lib/runtime/commands/InfoCommand.java @@ -73,6 +73,7 @@ import java.util.TreeMap; help = "resource:info.txt", shortDescription = "Displays runtime info about the %{product} server.", options = { InfoCommand.Options.class }, + completion = "info-key", // We have InfoCommand inherit from {@link BuildCommand} because we want all // configuration defaults specified in ~/.blazerc for {@code build} to apply to // {@code info} too, even though it doesn't actually do a build. diff --git a/src/main/java/com/google/devtools/build/lib/runtime/commands/ProfileCommand.java b/src/main/java/com/google/devtools/build/lib/runtime/commands/ProfileCommand.java index 08cb072a9b..298768c197 100644 --- a/src/main/java/com/google/devtools/build/lib/runtime/commands/ProfileCommand.java +++ b/src/main/java/com/google/devtools/build/lib/runtime/commands/ProfileCommand.java @@ -69,6 +69,7 @@ import java.util.Map; shortDescription = "Analyzes build profile data.", help = "resource:analyze-profile.txt", allowResidue = true, + completion = "path", mustRunInWorkspace = false) public final class ProfileCommand implements BlazeCommand { diff --git a/src/main/java/com/google/devtools/build/lib/runtime/commands/QueryCommand.java b/src/main/java/com/google/devtools/build/lib/runtime/commands/QueryCommand.java index 7b54eb1bd0..b8f4981f78 100644 --- a/src/main/java/com/google/devtools/build/lib/runtime/commands/QueryCommand.java +++ b/src/main/java/com/google/devtools/build/lib/runtime/commands/QueryCommand.java @@ -54,6 +54,7 @@ import java.util.Set; shortDescription = "Executes a dependency graph query.", allowResidue = true, binaryStdOut = true, + completion = "label", canRunInOutputDirectory = true) public final class QueryCommand implements BlazeCommand { diff --git a/src/main/java/com/google/devtools/build/lib/runtime/commands/RunCommand.java b/src/main/java/com/google/devtools/build/lib/runtime/commands/RunCommand.java index 81f0cb26e1..715ef0e385 100644 --- a/src/main/java/com/google/devtools/build/lib/runtime/commands/RunCommand.java +++ b/src/main/java/com/google/devtools/build/lib/runtime/commands/RunCommand.java @@ -78,6 +78,7 @@ import java.util.List; help = "resource:run.txt", allowResidue = true, binaryStdOut = true, + completion = "label-bin", binaryStdErr = true) public class RunCommand implements BlazeCommand { diff --git a/src/main/java/com/google/devtools/build/lib/runtime/commands/TestCommand.java b/src/main/java/com/google/devtools/build/lib/runtime/commands/TestCommand.java index 0d2ee2e1a7..6e9ddc7772 100644 --- a/src/main/java/com/google/devtools/build/lib/runtime/commands/TestCommand.java +++ b/src/main/java/com/google/devtools/build/lib/runtime/commands/TestCommand.java @@ -53,6 +53,7 @@ import java.util.List; options = { TestSummaryOptions.class }, shortDescription = "Builds and runs the specified test targets.", help = "resource:test.txt", + completion = "label-test", allowResidue = true) public class TestCommand implements BlazeCommand { private AnsiTerminalPrinter printer; 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 9564daa5a1..4eb5ce28a5 100644 --- a/src/main/java/com/google/devtools/common/options/OptionsParser.java +++ b/src/main/java/com/google/devtools/common/options/OptionsParser.java @@ -417,6 +417,29 @@ public class OptionsParser implements OptionsProvider { } /** + * Returns a string listing the possible flag completion for this command along with the command + * completion if any. See {@link OptionsUsage#getCompletion(Field, StringBuilder)} for more + * details on the format for the flag completion. + */ + public String getOptionsCompletion() { + StringBuilder desc = new StringBuilder(); + + // List all options + List<Field> allFields = Lists.newArrayList(); + for (Class<? extends OptionsBase> optionsClass : impl.getOptionsClasses()) { + allFields.addAll(impl.getAnnotatedFieldsFor(optionsClass)); + } + for (Field optionField : allFields) { + String category = optionField.getAnnotation(Option.class).category(); + if (documentationLevel(category) == DocumentationLevel.DOCUMENTED) { + OptionsUsage.getCompletion(optionField, desc); + } + } + + return desc.toString(); + } + + /** * Returns a description of the option value set by the last previous call to * {@link #parse(OptionPriority, String, List)} that successfully set the given * option. If the option is of type {@link List}, the description will 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 c48a53295c..fdd997c6cb 100644 --- a/src/main/java/com/google/devtools/common/options/OptionsUsage.java +++ b/src/main/java/com/google/devtools/common/options/OptionsUsage.java @@ -15,6 +15,7 @@ package com.google.devtools.common.options; import static com.google.devtools.common.options.OptionsParserImpl.findConverter; +import com.google.common.base.Joiner; import com.google.common.base.Splitter; import com.google.common.base.Strings; import com.google.common.collect.Lists; @@ -31,6 +32,7 @@ import java.util.List; class OptionsUsage { private static final Splitter NEWLINE_SPLITTER = Splitter.on('\n'); + private static final Joiner COMMA_JOINER = Joiner.on(","); /** * Given an options class, render the usage string into the usage, @@ -124,6 +126,57 @@ class OptionsUsage { } } + /** + * Returns the available completion for the given option field. The completions are the exact + * command line option (with the prepending '--') that one should pass. It is suitable for + * completion script to use. If the option expect an argument, the kind of argument is given + * after the equals. If the kind is a enum, the various enum values are given inside an accolade + * in a comma separated list. For other special kind, the type is given as a name (e.g., + * <code>label</code>, <code>float</ode>, <code>path</code>...). Example outputs of this + * function are for, respectively, a tristate flag <code>tristate_flag</code>, a enum + * flag <code>enum_flag</code> which can take <code>value1</code>, <code>value2</code> and + * <code>value3</code>, a path fragment flag <code>path_flag</code>, a string flag + * <code>string_flag</code> and a void flag <code>void_flag</code>: + * <pre> + * --tristate_flag={auto,yes,no} + * --notristate_flag + * --enum_flag={value1,value2,value3} + * --path_flag=path + * --string_flag= + * --void_flag + * </pre> + * + * @param field The field to return completion for + * @param builder the string builder to store the completion values + */ + static void getCompletion(Field field, StringBuilder builder) { + // Return the list of possible completions for this option + String flagName = field.getAnnotation(Option.class).name(); + Class<?> fieldType = field.getType(); + builder.append("--").append(flagName); + if (fieldType.equals(boolean.class)) { + builder.append("\n"); + builder.append("--no").append(flagName).append("\n"); + } else if (fieldType.equals(TriState.class)) { + 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"); + } 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"); + } else if (fieldType.getSimpleName().equals("PathFragment")) { + builder.append("=path\n"); + } else if (Void.class.isAssignableFrom(fieldType)) { + builder.append("\n"); + } else { + // TODO(bazel-team): add more types. Maybe even move the completion type + // to the @Option annotation? + builder.append("=\n"); + } + } + private static final Comparator<Field> BY_NAME = new Comparator<Field>() { @Override public int compare(Field left, Field right) { |