diff options
author | 2015-06-25 12:46:55 +0000 | |
---|---|---|
committer | 2015-06-26 15:29:40 +0000 | |
commit | 43ce49753d4b2b5cb28eb415d4c6b64fcd99eaa6 (patch) | |
tree | 71166369738ed0b4a410e36b6a474586565992bd /src/main/java/com/google/devtools/build/docgen/BuildDocCollector.java | |
parent | 7d1dae33f572aeb9cb0c9804618e830dfc352e4e (diff) |
Use Apache Velocity for templating for generating Build Encyclopedia. Refactor code used for extracting documentation from rule classes into separate class.
Todo after this CL:
* Use RuleDocumentation and RuleDocumentationAttribute directly in .vm templates to further simplify BuildEncyclopediaProcessor code
* Use VTL #include for including common attribute documentation
* Split BE into multiple pages
* Use templating for Skylark Library
--
MOS_MIGRATED_REVID=96865477
Diffstat (limited to 'src/main/java/com/google/devtools/build/docgen/BuildDocCollector.java')
-rw-r--r-- | src/main/java/com/google/devtools/build/docgen/BuildDocCollector.java | 184 |
1 files changed, 184 insertions, 0 deletions
diff --git a/src/main/java/com/google/devtools/build/docgen/BuildDocCollector.java b/src/main/java/com/google/devtools/build/docgen/BuildDocCollector.java new file mode 100644 index 0000000000..0bf4f20030 --- /dev/null +++ b/src/main/java/com/google/devtools/build/docgen/BuildDocCollector.java @@ -0,0 +1,184 @@ +// Copyright 2014 Google Inc. 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.build.docgen; + +import com.google.common.collect.LinkedListMultimap; +import com.google.common.collect.ListMultimap; +import com.google.devtools.build.docgen.DocgenConsts.RuleType; +import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider; +import com.google.devtools.build.lib.analysis.RuleDefinition; +import com.google.devtools.build.lib.packages.Attribute; +import com.google.devtools.build.lib.packages.RuleClass; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; + +/** + * Class that parses the documentation fragments of rule-classes and + * generates the html format documentation. + */ +class BuildDocCollector { + private ConfiguredRuleClassProvider ruleClassProvider; + private boolean printMessages; + + public BuildDocCollector(ConfiguredRuleClassProvider ruleClassProvider, + boolean printMessages) { + this.ruleClassProvider = ruleClassProvider; + this.printMessages = printMessages; + } + + /** + * Collects all the rule and attribute documentation present in inputDirs, integrates the + * attribute documentation in the rule documentation and returns the rule documentation. + */ + public Map<String, RuleDocumentation> collect(String[] inputDirs) + throws BuildEncyclopediaDocException, IOException { + // RuleDocumentations are generated in order (based on rule type then alphabetically). + // The ordering is also used to determine in which rule doc the common attribute docs are + // generated (they are generated at the first appearance). + Map<String, RuleDocumentation> ruleDocEntries = new TreeMap<>(); + // RuleDocumentationAttribute objects equal based on attributeName so they have to be + // collected in a List instead of a Set. + ListMultimap<String, RuleDocumentationAttribute> attributeDocEntries = + LinkedListMultimap.create(); + + // Map of rule class name to file that defined it. + Map<String, File> ruleClassFiles = new HashMap<>(); + + // Set of files already processed. The same file may be encountered multiple times because + // directories are processed recursively, and an input directory may be a subdirectory of + // another one. + Set<File> processedFiles = new HashSet<>(); + + for (String inputDir : inputDirs) { + if (printMessages) { + System.out.println(" Processing input directory: " + inputDir); + } + int ruleNum = ruleDocEntries.size(); + collectDocs(processedFiles, ruleClassFiles, ruleDocEntries, attributeDocEntries, + new File(inputDir)); + if (printMessages) { + System.out.println(" " + (ruleDocEntries.size() - ruleNum) + + " rule documentations found."); + } + } + + processAttributeDocs(ruleDocEntries.values(), attributeDocEntries); + return ruleDocEntries; + } + + /** + * Go through all attributes of all documented rules and search the best attribute documentation + * if exists. The best documentation is the closest documentation in the ancestor graph. E.g. if + * java_library.deps documented in $rule and $java_rule then the one in $java_rule is going to + * apply since it's a closer ancestor of java_library. + */ + private void processAttributeDocs(Iterable<RuleDocumentation> ruleDocEntries, + ListMultimap<String, RuleDocumentationAttribute> attributeDocEntries) + throws BuildEncyclopediaDocException { + for (RuleDocumentation ruleDoc : ruleDocEntries) { + RuleClass ruleClass = ruleClassProvider.getRuleClassMap().get(ruleDoc.getRuleName()); + if (ruleClass != null) { + if (ruleClass.isDocumented()) { + Class<? extends RuleDefinition> ruleDefinition = + ruleClassProvider.getRuleClassDefinition(ruleDoc.getRuleName()); + for (Attribute attribute : ruleClass.getAttributes()) { + String attrName = attribute.getName(); + List<RuleDocumentationAttribute> attributeDocList = + attributeDocEntries.get(attrName); + if (attributeDocList != null) { + // There are attribute docs for this attribute. + // Search the closest one in the ancestor graph. + // Note that there can be only one 'closest' attribute since we forbid multiple + // inheritance of the same attribute in RuleClass. + int minLevel = Integer.MAX_VALUE; + RuleDocumentationAttribute bestAttributeDoc = null; + for (RuleDocumentationAttribute attributeDoc : attributeDocList) { + int level = attributeDoc.getDefinitionClassAncestryLevel(ruleDefinition); + if (level >= 0 && level < minLevel) { + bestAttributeDoc = attributeDoc; + minLevel = level; + } + } + if (bestAttributeDoc != null) { + ruleDoc.addAttribute(bestAttributeDoc); + // If there is no matching attribute doc try to add the common. + } else if (ruleDoc.getRuleType().equals(RuleType.BINARY) + && PredefinedAttributes.BINARY_ATTRIBUTES.containsKey(attrName)) { + ruleDoc.addAttribute(PredefinedAttributes.BINARY_ATTRIBUTES.get(attrName)); + } else if (ruleDoc.getRuleType().equals(RuleType.TEST) + && PredefinedAttributes.TEST_ATTRIBUTES.containsKey(attrName)) { + ruleDoc.addAttribute(PredefinedAttributes.TEST_ATTRIBUTES.get(attrName)); + } else if (PredefinedAttributes.COMMON_ATTRIBUTES.containsKey(attrName)) { + ruleDoc.addAttribute(PredefinedAttributes.COMMON_ATTRIBUTES.get(attrName)); + } + } + } + } + } else { + throw ruleDoc.createException("Can't find RuleClass for " + ruleDoc.getRuleName()); + } + } + } + + /** + * Goes through all the html files and subdirs under inputPath and collects the rule + * and attribute documentations using the ruleDocEntries and attributeDocEntries variable. + */ + public void collectDocs( + Set<File> processedFiles, + Map<String, File> ruleClassFiles, + Map<String, RuleDocumentation> ruleDocEntries, + ListMultimap<String, RuleDocumentationAttribute> attributeDocEntries, + File inputPath) throws BuildEncyclopediaDocException, IOException { + if (processedFiles.contains(inputPath)) { + return; + } + + if (inputPath.isFile()) { + if (DocgenConsts.JAVA_SOURCE_FILE_SUFFIX.apply(inputPath.getName())) { + SourceFileReader sfr = new SourceFileReader( + ruleClassProvider, inputPath.getAbsolutePath()); + sfr.readDocsFromComments(); + for (RuleDocumentation d : sfr.getRuleDocEntries()) { + String ruleName = d.getRuleName(); + if (ruleDocEntries.containsKey(ruleName) + && !ruleClassFiles.get(ruleName).equals(inputPath)) { + System.err.printf("WARNING: '%s' from '%s' overrides value already in map from '%s'\n", + d.getRuleName(), inputPath, ruleClassFiles.get(ruleName)); + } + ruleClassFiles.put(ruleName, inputPath); + ruleDocEntries.put(ruleName, d); + } + if (attributeDocEntries != null) { + // Collect all attribute documentations from this file. + attributeDocEntries.putAll(sfr.getAttributeDocEntries()); + } + } + } else if (inputPath.isDirectory()) { + for (File childPath : inputPath.listFiles()) { + collectDocs(processedFiles, ruleClassFiles, ruleDocEntries, attributeDocEntries, childPath); + } + } + + processedFiles.add(inputPath); + } +} |