blob: 41469c44c14b41bfcb6f2455797ed212f099bfd6 [file] [log] [blame]
Damien Martin-Guillerezf88f4d82015-09-25 13:56:55 +00001// Copyright 2014 The Bazel Authors. All rights reserved.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01002//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14package com.google.devtools.build.lib.runtime;
15
16import com.google.common.collect.ImmutableList;
Ulf Adams755dd842016-06-22 13:43:38 +000017import com.google.common.collect.ImmutableSet;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010018import com.google.common.collect.Iterables;
19import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider;
laurentlb92c43cd2019-02-18 08:27:55 -080020import com.google.devtools.build.lib.packages.StarlarkSemanticsOptions;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010021import com.google.devtools.build.lib.util.ResourceFileLoader;
22import com.google.devtools.common.options.OptionsBase;
23import com.google.devtools.common.options.OptionsParser;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010024import java.io.IOException;
25import java.util.Collection;
26import java.util.Collections;
27import java.util.HashSet;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010028import java.util.Set;
29
30/**
31 * Utility class for functionality related to Blaze commands.
32 */
33public class BlazeCommandUtils {
34 /**
35 * Options classes used as startup options in Blaze core.
36 */
Ulf Adams352211d2016-06-22 09:24:28 +000037 private static final ImmutableList<Class<? extends OptionsBase>> DEFAULT_STARTUP_OPTIONS =
brandjon41655a92017-08-17 22:28:22 +020038 ImmutableList.of(
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010039 BlazeServerStartupOptions.class,
40 HostJvmStartupOptions.class);
41
ccalvarinb5158a92017-10-26 23:41:08 +020042 /** The set of option-classes that are common to all Blaze commands. */
Ulf Adams352211d2016-06-22 09:24:28 +000043 private static final ImmutableList<Class<? extends OptionsBase>> COMMON_COMMAND_OPTIONS =
brandjon41655a92017-08-17 22:28:22 +020044 ImmutableList.of(
45 BlazeCommandEventHandler.Options.class,
46 CommonCommandOptions.class,
ccalvarinb5158a92017-10-26 23:41:08 +020047 ClientOptions.class,
brandjon41655a92017-08-17 22:28:22 +020048 // Skylark options aren't applicable to all commands, but making them a common option
49 // allows users to put them in the common section of the bazelrc. See issue #3538.
laurentlb92c43cd2019-02-18 08:27:55 -080050 StarlarkSemanticsOptions.class);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010051
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010052 private BlazeCommandUtils() {}
53
54 public static ImmutableList<Class<? extends OptionsBase>> getStartupOptions(
55 Iterable<BlazeModule> modules) {
56 Set<Class<? extends OptionsBase>> options = new HashSet<>();
57 options.addAll(DEFAULT_STARTUP_OPTIONS);
58 for (BlazeModule blazeModule : modules) {
59 Iterables.addAll(options, blazeModule.getStartupOptions());
60 }
61
62 return ImmutableList.copyOf(options);
63 }
64
Ulf Adams755dd842016-06-22 13:43:38 +000065 public static ImmutableSet<Class<? extends OptionsBase>> getCommonOptions(
66 Iterable<BlazeModule> modules) {
67 ImmutableSet.Builder<Class<? extends OptionsBase>> builder = ImmutableSet.builder();
68 builder.addAll(COMMON_COMMAND_OPTIONS);
69 for (BlazeModule blazeModule : modules) {
70 builder.addAll(blazeModule.getCommonCommandOptions());
71 }
72 return builder.build();
Ulf Adams352211d2016-06-22 09:24:28 +000073 }
74
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010075 /**
76 * Returns the set of all options (including those inherited directly and
77 * transitively) for this AbstractCommand's @Command annotation.
78 *
79 * <p>Why does metaprogramming always seem like such a bright idea in the
80 * beginning?
81 */
82 public static ImmutableList<Class<? extends OptionsBase>> getOptions(
83 Class<? extends BlazeCommand> clazz,
84 Iterable<BlazeModule> modules,
85 ConfiguredRuleClassProvider ruleClassProvider) {
86 Command commandAnnotation = clazz.getAnnotation(Command.class);
87 if (commandAnnotation == null) {
88 throw new IllegalStateException("@Command missing for " + clazz.getName());
89 }
90
91 Set<Class<? extends OptionsBase>> options = new HashSet<>();
Ulf Adams755dd842016-06-22 13:43:38 +000092 options.addAll(getCommonOptions(modules));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010093 Collections.addAll(options, commandAnnotation.options());
94
95 if (commandAnnotation.usesConfigurationOptions()) {
96 options.addAll(ruleClassProvider.getConfigurationOptions());
97 }
98
99 for (BlazeModule blazeModule : modules) {
100 Iterables.addAll(options, blazeModule.getCommandOptions(commandAnnotation));
101 }
102
103 for (Class<? extends BlazeCommand> base : commandAnnotation.inherits()) {
104 options.addAll(getOptions(base, modules, ruleClassProvider));
105 }
106 return ImmutableList.copyOf(options);
107 }
108
109 /**
110 * Returns the expansion of the specified help topic.
111 *
112 * @param topic the name of the help topic; used in %{command} expansion.
ccalvarin77e3a5c2017-09-26 18:11:53 -0400113 * @param help the text template of the help message. Certain %{x} variables will be expanded. A
114 * prefix of "resource:" means use the .jar resource of that name.
ccalvarin77e3a5c2017-09-26 18:11:53 -0400115 * @param helpVerbosity a tri-state verbosity option selecting between just names, names and
116 * syntax, and full description.
Luis Fernando Pino Duquebe102182016-05-23 14:03:55 +0000117 * @param productName the product name
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100118 */
ccalvarin77e3a5c2017-09-26 18:11:53 -0400119 public static final String expandHelpTopic(
120 String topic,
121 String help,
brandjon41655a92017-08-17 22:28:22 +0200122 Class<? extends BlazeCommand> commandClass,
123 Collection<Class<? extends OptionsBase>> options,
brandjon41655a92017-08-17 22:28:22 +0200124 OptionsParser.HelpVerbosity helpVerbosity,
125 String productName) {
jcater706a22e2019-06-17 09:56:03 -0700126 OptionsParser parser = OptionsParser.builder().optionsClasses(options).build();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100127
128 String template;
129 if (help.startsWith("resource:")) {
130 String resourceName = help.substring("resource:".length());
131 try {
132 template = ResourceFileLoader.loadResource(commandClass, resourceName);
133 } catch (IOException e) {
ccalvarin77e3a5c2017-09-26 18:11:53 -0400134 throw new IllegalStateException(
135 "failed to load help resource '"
136 + resourceName
137 + "' due to I/O error: "
138 + e.getMessage(),
139 e);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100140 }
141 } else {
142 template = help;
143 }
144
145 if (!template.contains("%{options}")) {
146 throw new IllegalStateException("Help template for '" + topic + "' omits %{options}!");
147 }
148
ccalvarin77e3a5c2017-09-26 18:11:53 -0400149 String optionStr;
ccalvarin77e3a5c2017-09-26 18:11:53 -0400150 optionStr =
151 parser.describeOptions(productName, helpVerbosity).replace("%{product}", productName);
ccalvarin8bbb6c22018-03-27 13:38:15 -0700152
Han-Wen Nienhuysd03f3212016-01-19 16:21:57 +0000153 return template
Luis Fernando Pino Duquebe102182016-05-23 14:03:55 +0000154 .replace("%{product}", productName)
Han-Wen Nienhuysd03f3212016-01-19 16:21:57 +0000155 .replace("%{command}", topic)
156 .replace("%{options}", optionStr)
157 .trim()
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100158 + "\n\n"
159 + (helpVerbosity == OptionsParser.HelpVerbosity.MEDIUM
Han-Wen Nienhuysd03f3212016-01-19 16:21:57 +0000160 ? "(Use 'help --long' for full details or --short to just enumerate options.)\n"
161 : "");
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100162 }
163
164 /**
165 * The help page for this command.
166 *
ccalvarin77e3a5c2017-09-26 18:11:53 -0400167 * @param verbosity a tri-state verbosity option selecting between just names, names and syntax,
168 * and full description.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100169 */
170 public static String getUsage(
171 Class<? extends BlazeCommand> commandClass,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100172 OptionsParser.HelpVerbosity verbosity,
173 Iterable<BlazeModule> blazeModules,
Luis Fernando Pino Duquebe102182016-05-23 14:03:55 +0000174 ConfiguredRuleClassProvider ruleClassProvider,
ccalvarin8bbb6c22018-03-27 13:38:15 -0700175 String productName) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100176 Command commandAnnotation = commandClass.getAnnotation(Command.class);
177 return BlazeCommandUtils.expandHelpTopic(
178 commandAnnotation.name(),
179 commandAnnotation.help(),
180 commandClass,
181 BlazeCommandUtils.getOptions(commandClass, blazeModules, ruleClassProvider),
Luis Fernando Pino Duquebe102182016-05-23 14:03:55 +0000182 verbosity,
ccalvarin8bbb6c22018-03-27 13:38:15 -0700183 productName);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100184 }
185}