Damien Martin-Guillerez | f88f4d8 | 2015-09-25 13:56:55 +0000 | [diff] [blame] | 1 | // Copyright 2014 The Bazel Authors. All rights reserved. |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 2 | // |
| 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. |
| 14 | package com.google.devtools.build.lib.runtime; |
| 15 | |
| 16 | import com.google.common.collect.ImmutableList; |
Ulf Adams | 755dd84 | 2016-06-22 13:43:38 +0000 | [diff] [blame] | 17 | import com.google.common.collect.ImmutableSet; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 18 | import com.google.common.collect.Iterables; |
| 19 | import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider; |
laurentlb | 92c43cd | 2019-02-18 08:27:55 -0800 | [diff] [blame] | 20 | import com.google.devtools.build.lib.packages.StarlarkSemanticsOptions; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 21 | import com.google.devtools.build.lib.util.ResourceFileLoader; |
| 22 | import com.google.devtools.common.options.OptionsBase; |
| 23 | import com.google.devtools.common.options.OptionsParser; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 24 | import java.io.IOException; |
| 25 | import java.util.Collection; |
| 26 | import java.util.Collections; |
| 27 | import java.util.HashSet; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 28 | import java.util.Set; |
| 29 | |
| 30 | /** |
| 31 | * Utility class for functionality related to Blaze commands. |
| 32 | */ |
| 33 | public class BlazeCommandUtils { |
| 34 | /** |
| 35 | * Options classes used as startup options in Blaze core. |
| 36 | */ |
Ulf Adams | 352211d | 2016-06-22 09:24:28 +0000 | [diff] [blame] | 37 | private static final ImmutableList<Class<? extends OptionsBase>> DEFAULT_STARTUP_OPTIONS = |
brandjon | 41655a9 | 2017-08-17 22:28:22 +0200 | [diff] [blame] | 38 | ImmutableList.of( |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 39 | BlazeServerStartupOptions.class, |
| 40 | HostJvmStartupOptions.class); |
| 41 | |
ccalvarin | b5158a9 | 2017-10-26 23:41:08 +0200 | [diff] [blame] | 42 | /** The set of option-classes that are common to all Blaze commands. */ |
Ulf Adams | 352211d | 2016-06-22 09:24:28 +0000 | [diff] [blame] | 43 | private static final ImmutableList<Class<? extends OptionsBase>> COMMON_COMMAND_OPTIONS = |
brandjon | 41655a9 | 2017-08-17 22:28:22 +0200 | [diff] [blame] | 44 | ImmutableList.of( |
| 45 | BlazeCommandEventHandler.Options.class, |
| 46 | CommonCommandOptions.class, |
ccalvarin | b5158a9 | 2017-10-26 23:41:08 +0200 | [diff] [blame] | 47 | ClientOptions.class, |
brandjon | 41655a9 | 2017-08-17 22:28:22 +0200 | [diff] [blame] | 48 | // 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. |
laurentlb | 92c43cd | 2019-02-18 08:27:55 -0800 | [diff] [blame] | 50 | StarlarkSemanticsOptions.class); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 51 | |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 52 | 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 Adams | 755dd84 | 2016-06-22 13:43:38 +0000 | [diff] [blame] | 65 | 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 Adams | 352211d | 2016-06-22 09:24:28 +0000 | [diff] [blame] | 73 | } |
| 74 | |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 75 | /** |
| 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 Adams | 755dd84 | 2016-06-22 13:43:38 +0000 | [diff] [blame] | 92 | options.addAll(getCommonOptions(modules)); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 93 | 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. |
ccalvarin | 77e3a5c | 2017-09-26 18:11:53 -0400 | [diff] [blame] | 113 | * @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. |
ccalvarin | 77e3a5c | 2017-09-26 18:11:53 -0400 | [diff] [blame] | 115 | * @param helpVerbosity a tri-state verbosity option selecting between just names, names and |
| 116 | * syntax, and full description. |
Luis Fernando Pino Duque | be10218 | 2016-05-23 14:03:55 +0000 | [diff] [blame] | 117 | * @param productName the product name |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 118 | */ |
ccalvarin | 77e3a5c | 2017-09-26 18:11:53 -0400 | [diff] [blame] | 119 | public static final String expandHelpTopic( |
| 120 | String topic, |
| 121 | String help, |
brandjon | 41655a9 | 2017-08-17 22:28:22 +0200 | [diff] [blame] | 122 | Class<? extends BlazeCommand> commandClass, |
| 123 | Collection<Class<? extends OptionsBase>> options, |
brandjon | 41655a9 | 2017-08-17 22:28:22 +0200 | [diff] [blame] | 124 | OptionsParser.HelpVerbosity helpVerbosity, |
| 125 | String productName) { |
jcater | 706a22e | 2019-06-17 09:56:03 -0700 | [diff] [blame^] | 126 | OptionsParser parser = OptionsParser.builder().optionsClasses(options).build(); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 127 | |
| 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) { |
ccalvarin | 77e3a5c | 2017-09-26 18:11:53 -0400 | [diff] [blame] | 134 | throw new IllegalStateException( |
| 135 | "failed to load help resource '" |
| 136 | + resourceName |
| 137 | + "' due to I/O error: " |
| 138 | + e.getMessage(), |
| 139 | e); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 140 | } |
| 141 | } else { |
| 142 | template = help; |
| 143 | } |
| 144 | |
| 145 | if (!template.contains("%{options}")) { |
| 146 | throw new IllegalStateException("Help template for '" + topic + "' omits %{options}!"); |
| 147 | } |
| 148 | |
ccalvarin | 77e3a5c | 2017-09-26 18:11:53 -0400 | [diff] [blame] | 149 | String optionStr; |
ccalvarin | 77e3a5c | 2017-09-26 18:11:53 -0400 | [diff] [blame] | 150 | optionStr = |
| 151 | parser.describeOptions(productName, helpVerbosity).replace("%{product}", productName); |
ccalvarin | 8bbb6c2 | 2018-03-27 13:38:15 -0700 | [diff] [blame] | 152 | |
Han-Wen Nienhuys | d03f321 | 2016-01-19 16:21:57 +0000 | [diff] [blame] | 153 | return template |
Luis Fernando Pino Duque | be10218 | 2016-05-23 14:03:55 +0000 | [diff] [blame] | 154 | .replace("%{product}", productName) |
Han-Wen Nienhuys | d03f321 | 2016-01-19 16:21:57 +0000 | [diff] [blame] | 155 | .replace("%{command}", topic) |
| 156 | .replace("%{options}", optionStr) |
| 157 | .trim() |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 158 | + "\n\n" |
| 159 | + (helpVerbosity == OptionsParser.HelpVerbosity.MEDIUM |
Han-Wen Nienhuys | d03f321 | 2016-01-19 16:21:57 +0000 | [diff] [blame] | 160 | ? "(Use 'help --long' for full details or --short to just enumerate options.)\n" |
| 161 | : ""); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 162 | } |
| 163 | |
| 164 | /** |
| 165 | * The help page for this command. |
| 166 | * |
ccalvarin | 77e3a5c | 2017-09-26 18:11:53 -0400 | [diff] [blame] | 167 | * @param verbosity a tri-state verbosity option selecting between just names, names and syntax, |
| 168 | * and full description. |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 169 | */ |
| 170 | public static String getUsage( |
| 171 | Class<? extends BlazeCommand> commandClass, |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 172 | OptionsParser.HelpVerbosity verbosity, |
| 173 | Iterable<BlazeModule> blazeModules, |
Luis Fernando Pino Duque | be10218 | 2016-05-23 14:03:55 +0000 | [diff] [blame] | 174 | ConfiguredRuleClassProvider ruleClassProvider, |
ccalvarin | 8bbb6c2 | 2018-03-27 13:38:15 -0700 | [diff] [blame] | 175 | String productName) { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 176 | 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 Duque | be10218 | 2016-05-23 14:03:55 +0000 | [diff] [blame] | 182 | verbosity, |
ccalvarin | 8bbb6c2 | 2018-03-27 13:38:15 -0700 | [diff] [blame] | 183 | productName); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 184 | } |
| 185 | } |