| // Copyright 2014 The Bazel Authors. 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.ImmutableList; |
| import com.google.devtools.build.docgen.skylark.SkylarkBuiltinMethodDoc; |
| import com.google.devtools.build.docgen.skylark.SkylarkJavaMethodDoc; |
| import com.google.devtools.build.docgen.skylark.SkylarkMethodDoc; |
| import com.google.devtools.build.docgen.skylark.SkylarkModuleDoc; |
| import com.google.devtools.build.lib.skylarkinterface.SkylarkModuleCategory; |
| import com.google.devtools.build.lib.util.Classpath.ClassPathException; |
| import java.io.File; |
| import java.io.IOException; |
| import java.text.Collator; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Locale; |
| import java.util.Map; |
| |
| /** A class to assemble documentation for Skylark. */ |
| public final class SkylarkDocumentationProcessor { |
| |
| private static final ImmutableList<SkylarkModuleCategory> GLOBAL_CATEGORIES = |
| ImmutableList.<SkylarkModuleCategory>of( |
| SkylarkModuleCategory.NONE, SkylarkModuleCategory.TOP_LEVEL_TYPE); |
| |
| private SkylarkDocumentationProcessor() {} |
| |
| /** Generates the Skylark documentation to the given output directory. */ |
| public static void generateDocumentation(String outputDir, String... clazz) |
| throws IOException, ClassPathException { |
| Map<String, SkylarkModuleDoc> modules = SkylarkDocumentationCollector.collectModules(clazz); |
| |
| // Generate the top level module first in the doc |
| SkylarkModuleDoc topLevelModule = modules.remove( |
| SkylarkDocumentationCollector.getTopLevelModule().name()); |
| writePage(outputDir, topLevelModule); |
| |
| Map<SkylarkModuleCategory, List<SkylarkModuleDoc>> modulesByCategory = new HashMap<>(); |
| for (SkylarkModuleCategory c : SkylarkModuleCategory.values()) { |
| modulesByCategory.put(c, new ArrayList<SkylarkModuleDoc>()); |
| } |
| |
| modulesByCategory.get(topLevelModule.getAnnotation().category()).add(topLevelModule); |
| |
| for (SkylarkModuleDoc module : modules.values()) { |
| if (module.getAnnotation().documented()) { |
| writePage(outputDir, module); |
| modulesByCategory.get(module.getAnnotation().category()).add(module); |
| } |
| } |
| Collator us = Collator.getInstance(Locale.US); |
| for (List<SkylarkModuleDoc> module : modulesByCategory.values()) { |
| Collections.sort(module, (doc1, doc2) -> us.compare(doc1.getTitle(), doc2.getTitle())); |
| } |
| writeCategoryPage(SkylarkModuleCategory.CONFIGURATION_FRAGMENT, outputDir, modulesByCategory); |
| writeCategoryPage(SkylarkModuleCategory.BUILTIN, outputDir, modulesByCategory); |
| writeCategoryPage(SkylarkModuleCategory.PROVIDER, outputDir, modulesByCategory); |
| writeNavPage(outputDir, modulesByCategory.get(SkylarkModuleCategory.TOP_LEVEL_TYPE)); |
| |
| // In the code, there are two SkylarkModuleCategory instances that have no heading: |
| // TOP_LEVEL_TYPE and NONE. |
| |
| // TOP_LEVEL_TYPE also contains the "global" module. |
| // We remove both categories and the "global" module from the map and display them manually: |
| // - Methods in the "global" module are displayed under "Global Methods and Constants". |
| // - Modules in both categories are displayed under "Global Modules" (except for the global |
| // module itself). |
| List<String> globalFunctions = new ArrayList<>(); |
| SkylarkModuleDoc globalModule = findGlobalModule(modulesByCategory); |
| for (SkylarkMethodDoc method : globalModule.getMethods()) { |
| if (method.documented()) { |
| globalFunctions.add(method.getName()); |
| } |
| } |
| |
| List<String> globalModules = new ArrayList<>(); |
| for (SkylarkModuleCategory globalCategory : GLOBAL_CATEGORIES) { |
| List<SkylarkModuleDoc> allGlobalModules = modulesByCategory.remove(globalCategory); |
| for (SkylarkModuleDoc module : allGlobalModules) { |
| if (!module.getName().equals(globalModule.getName())) { |
| globalModules.add(module.getName()); |
| } |
| } |
| } |
| |
| Collections.sort(globalModules, us); |
| writeOverviewPage( |
| outputDir, globalModule.getName(), globalFunctions, globalModules, modulesByCategory); |
| } |
| |
| private static SkylarkModuleDoc findGlobalModule( |
| Map<SkylarkModuleCategory, List<SkylarkModuleDoc>> modulesByCategory) { |
| List<SkylarkModuleDoc> topLevelModules = |
| modulesByCategory.get(SkylarkModuleCategory.TOP_LEVEL_TYPE); |
| String globalModuleName = SkylarkDocumentationCollector.getTopLevelModule().name(); |
| for (SkylarkModuleDoc module : topLevelModules) { |
| if (module.getName().equals(globalModuleName)) { |
| return module; |
| } |
| } |
| |
| throw new IllegalStateException("No globals module in the top level category."); |
| } |
| |
| private static void writePage(String outputDir, SkylarkModuleDoc module) throws IOException { |
| File skylarkDocPath = new File(outputDir + "/" + module.getName() + ".html"); |
| Page page = TemplateEngine.newPage(DocgenConsts.SKYLARK_LIBRARY_TEMPLATE); |
| page.add("module", module); |
| page.write(skylarkDocPath); |
| } |
| |
| private static void writeCategoryPage( |
| SkylarkModuleCategory category, |
| String outputDir, |
| Map<SkylarkModuleCategory, List<SkylarkModuleDoc>> modules) throws IOException { |
| File skylarkDocPath = new File(String.format("%s/skylark-%s.html", |
| outputDir, category.getTemplateIdentifier())); |
| Page page = TemplateEngine.newPage(DocgenConsts.SKYLARK_MODULE_CATEGORY_TEMPLATE); |
| page.add("category", category); |
| page.add("modules", modules.get(category)); |
| page.write(skylarkDocPath); |
| } |
| |
| private static void writeNavPage(String outputDir, List<SkylarkModuleDoc> navModules) |
| throws IOException { |
| File navFile = new File(outputDir + "/skylark-nav.html"); |
| Page page = TemplateEngine.newPage(DocgenConsts.SKYLARK_NAV_TEMPLATE); |
| page.add("modules", navModules); |
| page.write(navFile); |
| } |
| |
| private static void writeOverviewPage( |
| String outputDir, |
| String globalModuleName, |
| List<String> globalFunctions, |
| List<String> globalModules, |
| Map<SkylarkModuleCategory, List<SkylarkModuleDoc>> modulesPerCategory) |
| throws IOException { |
| File skylarkDocPath = new File(outputDir + "/skylark-overview.html"); |
| Page page = TemplateEngine.newPage(DocgenConsts.SKYLARK_OVERVIEW_TEMPLATE); |
| page.add("global_name", globalModuleName); |
| page.add("global_functions", globalFunctions); |
| page.add("global_modules", globalModules); |
| page.add("modules", modulesPerCategory); |
| page.write(skylarkDocPath); |
| } |
| |
| /** |
| * Returns the API doc for the specified Skylark object in a command line printable format, |
| * params[0] identifies either a module or a top-level object, the optional params[1] identifies a |
| * method in the module.<br> |
| * Returns null if no Skylark object is found. |
| */ |
| public static String getCommandLineAPIDoc(String[] params) throws ClassPathException { |
| Map<String, SkylarkModuleDoc> modules = SkylarkDocumentationCollector.collectModules(); |
| SkylarkModuleDoc toplevelModuleDoc = modules.get( |
| SkylarkDocumentationCollector.getTopLevelModule().name()); |
| if (modules.containsKey(params[0])) { |
| // Top level module |
| SkylarkModuleDoc module = modules.get(params[0]); |
| if (params.length == 1) { |
| String moduleName = module.getAnnotation().name(); |
| StringBuilder sb = new StringBuilder(); |
| sb.append(moduleName).append("\n\t").append(module.getAnnotation().doc()).append("\n"); |
| // Print the signature of all built-in methods |
| for (SkylarkBuiltinMethodDoc method : module.getBuiltinMethods().values()) { |
| printBuiltinFunctionDoc(moduleName, method, sb); |
| } |
| // Print all Java methods |
| for (SkylarkJavaMethodDoc method : module.getJavaMethods()) { |
| printJavaFunctionDoc(method, sb); |
| } |
| return DocgenConsts.toCommandLineFormat(sb.toString()); |
| } else { |
| return getFunctionDoc(module.getAnnotation().name(), params[1], module); |
| } |
| } else if (toplevelModuleDoc.getBuiltinMethods().containsKey(params[0])){ |
| // Top level object / function |
| return getFunctionDoc(null, params[0], toplevelModuleDoc); |
| } |
| return null; |
| } |
| |
| private static String getFunctionDoc(String moduleName, String methodName, |
| SkylarkModuleDoc module) { |
| if (module.getBuiltinMethods().containsKey(methodName)) { |
| // Create the doc for the built-in function |
| SkylarkBuiltinMethodDoc method = module.getBuiltinMethods().get(methodName); |
| StringBuilder sb = new StringBuilder(); |
| printBuiltinFunctionDoc(moduleName, method, sb); |
| sb.append(method.getParams()); |
| return DocgenConsts.removeDuplicatedNewLines(DocgenConsts.toCommandLineFormat(sb.toString())); |
| } else { |
| // Search if there are matching Java functions |
| StringBuilder sb = new StringBuilder(); |
| boolean foundMatchingMethod = false; |
| for (SkylarkJavaMethodDoc method : module.getJavaMethods()) { |
| if (method.getName().equals(methodName)) { |
| printJavaFunctionDoc(method, sb); |
| foundMatchingMethod = true; |
| } |
| } |
| if (foundMatchingMethod) { |
| return DocgenConsts.toCommandLineFormat(sb.toString()); |
| } |
| } |
| return null; |
| } |
| |
| private static void printBuiltinFunctionDoc(String moduleName, SkylarkBuiltinMethodDoc method, |
| StringBuilder sb) { |
| if (moduleName != null) { |
| sb.append(moduleName).append("."); |
| } |
| sb.append(method.getName()).append("\n\t").append(method.getDocumentation()).append("\n"); |
| } |
| |
| private static void printJavaFunctionDoc(SkylarkJavaMethodDoc method, StringBuilder sb) { |
| sb.append(method.getSignature()) |
| .append("\t").append(method.getDocumentation()).append("\n"); |
| } |
| } |