blob: d88a46887be0a49042a1b3d6944d65205b6566e2 [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.docgen;
15
16import com.google.common.base.Preconditions;
17import com.google.common.collect.ImmutableMap;
18import com.google.common.collect.ImmutableSet;
Luis Fernando Pino Duque2b0b5cc2016-04-26 09:31:27 +000019import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010020import com.google.devtools.build.lib.analysis.RuleDefinition;
Lukacs Berki6e91eb92015-09-21 09:12:37 +000021import com.google.devtools.build.lib.cmdline.Label;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010022import com.google.devtools.build.lib.packages.Attribute;
Lukacs Berkiffa73ad2015-09-18 11:40:12 +000023import com.google.devtools.build.lib.packages.BuildType;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010024import com.google.devtools.build.lib.packages.TriState;
Lukacs Berkiffa73ad2015-09-18 11:40:12 +000025import com.google.devtools.build.lib.syntax.Type;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010026import java.util.HashMap;
27import java.util.LinkedList;
28import java.util.Map;
29import java.util.Set;
30
31/**
twerth756380c2018-11-14 00:23:06 -080032 * A class storing a rule attribute documentation along with some meta information. The class
33 * provides functionality to compute the ancestry level of this attribute's generator rule
34 * definition class compared to other rule definition classes.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010035 *
36 * <p>Warning, two RuleDocumentationAttribute objects are equal based on only the attributeName.
37 */
twerth756380c2018-11-14 00:23:06 -080038public class RuleDocumentationAttribute
39 implements Comparable<RuleDocumentationAttribute>, Cloneable {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010040
Googlercffe7352017-03-02 16:01:13 +000041 private static final ImmutableMap<Type<?>, String> TYPE_DESC =
42 ImmutableMap.<Type<?>, String>builder()
43 .put(Type.BOOLEAN, "Boolean")
44 .put(Type.INTEGER, "Integer")
45 .put(Type.INTEGER_LIST, "List of integers")
46 .put(Type.STRING, "String")
gregced5cfbfe2017-07-14 22:21:36 +020047 .put(Type.STRING_DICT, "Dictionary: String -> String")
Googlercffe7352017-03-02 16:01:13 +000048 .put(Type.STRING_LIST, "List of strings")
49 .put(BuildType.TRISTATE, "Integer")
50 .put(BuildType.LABEL, "<a href=\"../build-ref.html#labels\">Label</a>")
51 .put(BuildType.LABEL_LIST, "List of <a href=\"../build-ref.html#labels\">labels</a>")
52 .put(
53 BuildType.LABEL_DICT_UNARY,
54 "Dictionary mapping strings to <a href=\"../build-ref.html#labels\">labels</a>")
gregced5cfbfe2017-07-14 22:21:36 +020055 .put(BuildType.LICENSE, "Licence type")
Googlercffe7352017-03-02 16:01:13 +000056 .put(BuildType.NODEP_LABEL, "<a href=\"../build-ref.html#name\">Name</a>")
57 .put(BuildType.NODEP_LABEL_LIST, "List of <a href=\"../build-ref.html#name\">names</a>")
58 .put(BuildType.OUTPUT, "<a href=\"../build-ref.html#filename\">Filename</a>")
59 .put(
60 BuildType.OUTPUT_LIST, "List of <a href=\"../build-ref.html#filename\">filenames</a>")
61 .build();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010062
63 private final Class<? extends RuleDefinition> definitionClass;
64 private final String attributeName;
65 private final String htmlDocumentation;
66 private final String commonType;
David Chenac13e222016-02-24 22:17:42 +000067 // Used to expand rule link references in the attribute documentation.
68 private RuleLinkExpander linkExpander;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010069 private int startLineCnt;
David Chenac13e222016-02-24 22:17:42 +000070 private String fileName;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010071 private Set<String> flags;
David Chen31fce142015-08-18 08:39:51 +000072 private Attribute attribute;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010073
twerth756380c2018-11-14 00:23:06 -080074
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010075 /**
76 * Creates common RuleDocumentationAttribute such as deps or data.
77 * These attribute docs have no definitionClass or htmlDocumentation (it's in the BE header).
78 */
79 static RuleDocumentationAttribute create(
80 String attributeName, String commonType, String htmlDocumentation) {
81 RuleDocumentationAttribute docAttribute = new RuleDocumentationAttribute(
David Chenac13e222016-02-24 22:17:42 +000082 null, attributeName, htmlDocumentation, 0, "", ImmutableSet.<String>of(), commonType);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010083 return docAttribute;
84 }
85
86 /**
87 * Creates a RuleDocumentationAttribute with all the necessary fields for explicitly
88 * defined rule attributes.
89 */
90 static RuleDocumentationAttribute create(Class<? extends RuleDefinition> definitionClass,
David Chenac13e222016-02-24 22:17:42 +000091 String attributeName, String htmlDocumentation, int startLineCnt, String fileName,
92 Set<String> flags) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010093 return new RuleDocumentationAttribute(definitionClass, attributeName, htmlDocumentation,
David Chenac13e222016-02-24 22:17:42 +000094 startLineCnt, fileName, flags, null);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010095 }
96
97 private RuleDocumentationAttribute(Class<? extends RuleDefinition> definitionClass,
David Chenac13e222016-02-24 22:17:42 +000098 String attributeName, String htmlDocumentation, int startLineCnt, String fileName,
99 Set<String> flags, String commonType) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100100 Preconditions.checkNotNull(attributeName, "AttributeName must not be null.");
101 this.definitionClass = definitionClass;
102 this.attributeName = attributeName;
103 this.htmlDocumentation = htmlDocumentation;
104 this.startLineCnt = startLineCnt;
105 this.flags = flags;
106 this.commonType = commonType;
twerth756380c2018-11-14 00:23:06 -0800107 this.fileName = fileName;
108 }
109
110 @Override
111 protected Object clone() throws CloneNotSupportedException {
112 return super.clone();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100113 }
114
115 /**
David Chen31fce142015-08-18 08:39:51 +0000116 * Sets the Attribute object that this documents.
117 */
118 void setAttribute(Attribute attribute) {
119 this.attribute = attribute;
120 }
121
122 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100123 * Returns the name of the rule attribute.
124 */
David Chen31fce142015-08-18 08:39:51 +0000125 public String getAttributeName() {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100126 return attributeName;
127 }
128
twerth756380c2018-11-14 00:23:06 -0800129 /** Returns the file name where the rule attribute is defined. */
130 public String getFileName() {
131 return fileName;
132 }
133
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100134 /**
David Chen31fce142015-08-18 08:39:51 +0000135 * Returns whether this attribute is marked as deprecated.
136 */
137 public boolean isDeprecated() {
138 return hasFlag(DocgenConsts.FLAG_DEPRECATED);
139 }
140
141 /**
David Chenac13e222016-02-24 22:17:42 +0000142 * Sets the {@link RuleLinkExpander} to be used to expand links in the HTML documentation.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100143 */
David Chenac13e222016-02-24 22:17:42 +0000144 public void setRuleLinkExpander(RuleLinkExpander linkExpander) {
145 this.linkExpander = linkExpander;
146 }
147
148 /**
149 * Returns the html documentation of the rule attribute.
150 */
151 public String getHtmlDocumentation() throws BuildEncyclopediaDocException {
152 String expandedHtmlDoc = htmlDocumentation;
153 if (linkExpander != null) {
154 try {
155 expandedHtmlDoc = linkExpander.expand(expandedHtmlDoc);
156 } catch (IllegalArgumentException e) {
157 throw new BuildEncyclopediaDocException(fileName, startLineCnt, e.getMessage());
158 }
159 }
160 return expandedHtmlDoc;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100161 }
162
Googlerc68c27d2018-09-13 05:12:58 -0700163 /** Returns whether the param is required or optional. */
164 public boolean isMandatory() {
165 if (attribute == null) {
166 return false;
167 }
168 return attribute.isMandatory();
169 }
170
David Chen31fce142015-08-18 08:39:51 +0000171 private String getDefaultValue() {
172 if (attribute == null) {
173 return "";
174 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100175 String prefix = "; default is ";
juliexxia84d1a662018-12-26 14:07:04 -0800176 Object value = attribute.getDefaultValueUnchecked();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100177 if (value instanceof Boolean) {
hlopkof23d6cc2018-12-13 09:01:41 -0800178 return prefix + ((Boolean) value ? "True" : "False");
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100179 } else if (value instanceof Integer) {
180 return prefix + String.valueOf(value);
Googler2411ab92019-01-08 03:13:12 -0800181 } else if (value instanceof String && !((String) value).isEmpty()) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100182 return prefix + "\"" + value + "\"";
183 } else if (value instanceof TriState) {
184 switch((TriState) value) {
185 case AUTO:
186 return prefix + "-1";
187 case NO:
188 return prefix + "0";
189 case YES:
190 return prefix + "1";
191 }
192 } else if (value instanceof Label) {
193 return prefix + "<code>" + value + "</code>";
194 }
195 return "";
196 }
197
198 /**
David Chen31fce142015-08-18 08:39:51 +0000199 * Returns a string containing the synopsis for this attribute.
200 */
201 public String getSynopsis() {
202 if (attribute == null) {
203 return "";
204 }
205 StringBuilder sb = new StringBuilder()
206 .append(TYPE_DESC.get(attribute.getType()))
207 .append("; " + (attribute.isMandatory() ? "required" : "optional"))
gregce27a91eb2017-04-04 22:30:25 +0000208 .append(!attribute.isConfigurable()
209 ? String.format("; <a href=\"%s#configurable-attributes\">nonconfigurable</a>",
210 RuleDocumentation.COMMON_DEFINITIONS_PAGE)
211 : "")
David Chen31fce142015-08-18 08:39:51 +0000212 .append(getDefaultValue());
213 return sb.toString();
214 }
215
216 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100217 * Returns the number of first line of the attribute documentation in its declaration file.
218 */
219 int getStartLineCnt() {
220 return startLineCnt;
221 }
222
223 /**
224 * Returns true if the attribute doc is of a common attribute type.
225 */
David Chen31fce142015-08-18 08:39:51 +0000226 public boolean isCommonType() {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100227 return commonType != null;
228 }
229
230 /**
231 * Returns the common attribute type if this attribute doc is of a common type
232 * otherwise actualRule.
233 */
234 String getGeneratedInRule(String actualRule) {
235 return isCommonType() ? commonType : actualRule;
236 }
237
238 /**
239 * Returns true if this attribute documentation has the parameter flag.
240 */
241 boolean hasFlag(String flag) {
242 return flags.contains(flag);
243 }
244
245 /**
246 * Returns the length of a shortest path from usingClass to the definitionClass of this
247 * RuleDocumentationAttribute in the rule definition ancestry graph. Returns -1
248 * if definitionClass is not the ancestor (transitively) of usingClass.
249 */
Luis Fernando Pino Duque2b0b5cc2016-04-26 09:31:27 +0000250 int getDefinitionClassAncestryLevel(Class<? extends RuleDefinition> usingClass,
251 ConfiguredRuleClassProvider ruleClassProvider) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100252 if (usingClass.equals(definitionClass)) {
253 return 0;
254 }
255 // Storing nodes (rule class definitions) with the length of the shortest path from usingClass
256 Map<Class<? extends RuleDefinition>, Integer> visited = new HashMap<>();
257 LinkedList<Class<? extends RuleDefinition>> toVisit = new LinkedList<>();
258 visited.put(usingClass, 0);
259 toVisit.add(usingClass);
260 // Searching the shortest path from usingClass to this.definitionClass using BFS
261 do {
262 Class<? extends RuleDefinition> ancestor = toVisit.removeFirst();
Luis Fernando Pino Duque2b0b5cc2016-04-26 09:31:27 +0000263 visitAncestor(ancestor, visited, toVisit, ruleClassProvider);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100264 if (ancestor.equals(definitionClass)) {
265 return visited.get(ancestor);
266 }
267 } while (!toVisit.isEmpty());
268 return -1;
269 }
270
271 private void visitAncestor(
272 Class<? extends RuleDefinition> usingClass,
273 Map<Class<? extends RuleDefinition>, Integer> visited,
Luis Fernando Pino Duque2b0b5cc2016-04-26 09:31:27 +0000274 LinkedList<Class<? extends RuleDefinition>> toVisit,
275 ConfiguredRuleClassProvider ruleClassProvider) {
276 RuleDefinition instance = getRuleDefinition(usingClass, ruleClassProvider);
Googler58505032015-03-19 16:12:34 +0000277 for (Class<? extends RuleDefinition> ancestor : instance.getMetadata().ancestors()) {
278 if (!visited.containsKey(ancestor)) {
279 toVisit.addLast(ancestor);
280 visited.put(ancestor, visited.get(usingClass) + 1);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100281 }
282 }
283 }
284
Luis Fernando Pino Duque2b0b5cc2016-04-26 09:31:27 +0000285 private RuleDefinition getRuleDefinition(Class<? extends RuleDefinition> usingClass,
286 ConfiguredRuleClassProvider ruleClassProvider) {
287 if (ruleClassProvider == null) {
288 try {
Ulf Adams653a16a2016-08-11 12:39:46 +0000289 return usingClass.getConstructor().newInstance();
290 } catch (ReflectiveOperationException | IllegalArgumentException e) {
Luis Fernando Pino Duque2b0b5cc2016-04-26 09:31:27 +0000291 throw new IllegalStateException(e);
292 }
293 }
294 return ruleClassProvider.getRuleClassDefinition(usingClass.getName());
295 }
296
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100297 private int getAttributeOrderingPriority(RuleDocumentationAttribute attribute) {
298 if (DocgenConsts.ATTRIBUTE_ORDERING.containsKey(attribute.attributeName)) {
299 return DocgenConsts.ATTRIBUTE_ORDERING.get(attribute.attributeName);
300 } else {
301 return 0;
302 }
303 }
304
305 @Override
306 public int compareTo(RuleDocumentationAttribute o) {
307 int thisPriority = getAttributeOrderingPriority(this);
308 int otherPriority = getAttributeOrderingPriority(o);
309 if (thisPriority > otherPriority) {
310 return 1;
311 } else if (thisPriority < otherPriority) {
312 return -1;
313 } else {
314 return this.attributeName.compareTo(o.attributeName);
315 }
316 }
317
318 @Override
319 public boolean equals(Object obj) {
320 if (this == obj) {
321 return true;
322 }
323 if (!(obj instanceof RuleDocumentationAttribute)) {
324 return false;
325 }
326 return attributeName.equals(((RuleDocumentationAttribute) obj).attributeName);
327 }
328
329 @Override
330 public int hashCode() {
331 return attributeName.hashCode();
332 }
333}