From 24f2d99880317c928b36bc972eefe43e3134e27a Mon Sep 17 00:00:00 2001 From: David Chen Date: Mon, 17 Aug 2015 17:25:46 +0000 Subject: Split Skylark Library into multiple pages, one per module. Add collapsible submenus to sidebar, and expand and highlight the link for the current page. Collapse sidebar on mobile and expose button for toggling sidebar. -- MOS_MIGRATED_REVID=100836792 --- src/main/java/BUILD | 8 +- .../google/devtools/build/docgen/DocgenConsts.java | 6 +- .../docgen/SkylarkDocumentationCollector.java | 196 +++++++++ .../docgen/SkylarkDocumentationGenerator.java | 3 +- .../docgen/SkylarkDocumentationProcessor.java | 456 +++------------------ .../build/docgen/SkylarkJavaInterfaceExplorer.java | 160 -------- .../docgen/skylark/SkylarkBuiltinMethodDoc.java | 97 +++++ .../devtools/build/docgen/skylark/SkylarkDoc.java | 85 ++++ .../build/docgen/skylark/SkylarkJavaMethodDoc.java | 71 ++++ .../build/docgen/skylark/SkylarkMethodDoc.java | 116 ++++++ .../build/docgen/skylark/SkylarkModuleDoc.java | 98 +++++ .../build/docgen/skylark/SkylarkParamDoc.java | 61 +++ .../devtools/build/docgen/templates/be-header.vm | 8 +- .../build/docgen/templates/skylark-body.html | 58 --- .../build/docgen/templates/skylark-library.vm | 58 +++ .../devtools/build/docgen/templates/skylark-nav.vm | 4 + .../devtools/build/lib/actions/Artifact.java | 2 +- .../lib/analysis/TransitiveInfoCollection.java | 6 +- .../devtools/build/lib/packages/Attribute.java | 4 +- .../devtools/build/lib/rules/SkylarkAttr.java | 9 +- .../devtools/build/lib/rules/SkylarkFileType.java | 6 +- .../build/lib/rules/SkylarkRuleClassFunctions.java | 4 +- .../build/lib/rules/SkylarkRuleContext.java | 4 +- .../devtools/build/lib/syntax/ClassObject.java | 2 +- .../devtools/build/lib/syntax/MethodLibrary.java | 6 +- .../build/lib/syntax/SkylarkNestedSet.java | 2 +- 26 files changed, 874 insertions(+), 656 deletions(-) create mode 100644 src/main/java/com/google/devtools/build/docgen/SkylarkDocumentationCollector.java delete mode 100644 src/main/java/com/google/devtools/build/docgen/SkylarkJavaInterfaceExplorer.java create mode 100644 src/main/java/com/google/devtools/build/docgen/skylark/SkylarkBuiltinMethodDoc.java create mode 100644 src/main/java/com/google/devtools/build/docgen/skylark/SkylarkDoc.java create mode 100644 src/main/java/com/google/devtools/build/docgen/skylark/SkylarkJavaMethodDoc.java create mode 100644 src/main/java/com/google/devtools/build/docgen/skylark/SkylarkMethodDoc.java create mode 100644 src/main/java/com/google/devtools/build/docgen/skylark/SkylarkModuleDoc.java create mode 100644 src/main/java/com/google/devtools/build/docgen/skylark/SkylarkParamDoc.java delete mode 100644 src/main/java/com/google/devtools/build/docgen/templates/skylark-body.html create mode 100644 src/main/java/com/google/devtools/build/docgen/templates/skylark-library.vm create mode 100644 src/main/java/com/google/devtools/build/docgen/templates/skylark-nav.vm (limited to 'src') diff --git a/src/main/java/BUILD b/src/main/java/BUILD index f1cc25377f..b51694e22d 100644 --- a/src/main/java/BUILD +++ b/src/main/java/BUILD @@ -525,7 +525,7 @@ filegroup( java_library( name = "docgen_javalib", srcs = glob( - ["com/google/devtools/build/docgen/*.java"], + ["com/google/devtools/build/docgen/**/*.java"], exclude = [ "com/google/devtools/build/docgen/BuildEncyclopediaGenerator.java", "com/google/devtools/build/docgen/SkylarkDocumentationGenerator.java", @@ -586,8 +586,10 @@ genrule( genrule( name = "gen_skylarklibrary", - outs = ["skylark-library.html"], - cmd = "$(location :skydoc_bin) $@", + outs = ["skylark-library.zip"], + cmd = "mkdir -p $(@D)/skylark-lib &&" + + "$(location :skydoc_bin) $(@D)/skylark-lib &&" + + "zip -qj $@ $(@D)/skylark-lib/*", tools = [":skydoc_bin"], ) diff --git a/src/main/java/com/google/devtools/build/docgen/DocgenConsts.java b/src/main/java/com/google/devtools/build/docgen/DocgenConsts.java index 31f3359247..e50058854d 100644 --- a/src/main/java/com/google/devtools/build/docgen/DocgenConsts.java +++ b/src/main/java/com/google/devtools/build/docgen/DocgenConsts.java @@ -32,7 +32,11 @@ public class DocgenConsts { public static final String HEADER_TEMPLATE = "templates/be-header.html"; public static final String FOOTER_TEMPLATE = "templates/be-footer.html"; public static final String BODY_TEMPLATE = "templates/be-body.html"; - public static final String SKYLARK_BODY_TEMPLATE = "templates/skylark-body.html"; + + public static final String SKYLARK_LIBRARY_TEMPLATE = + "com/google/devtools/build/docgen/templates/skylark-library.vm"; + public static final String SKYLARK_NAV_TEMPLATE = + "com/google/devtools/build/docgen/templates/skylark-nav.vm"; public static final String VAR_LEFT_PANEL = "LEFT_PANEL"; diff --git a/src/main/java/com/google/devtools/build/docgen/SkylarkDocumentationCollector.java b/src/main/java/com/google/devtools/build/docgen/SkylarkDocumentationCollector.java new file mode 100644 index 0000000000..f7ae83b0e4 --- /dev/null +++ b/src/main/java/com/google/devtools/build/docgen/SkylarkDocumentationCollector.java @@ -0,0 +1,196 @@ +// 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.annotations.VisibleForTesting; +import com.google.common.collect.ImmutableMap; +import com.google.devtools.build.docgen.skylark.SkylarkBuiltinMethodDoc; +import com.google.devtools.build.docgen.skylark.SkylarkJavaMethodDoc; +import com.google.devtools.build.docgen.skylark.SkylarkModuleDoc; +import com.google.devtools.build.lib.analysis.TransitiveInfoCollection; +import com.google.devtools.build.lib.rules.SkylarkModules; +import com.google.devtools.build.lib.rules.SkylarkRuleContext; +import com.google.devtools.build.lib.syntax.Environment; +import com.google.devtools.build.lib.syntax.FuncallExpression; +import com.google.devtools.build.lib.syntax.MethodLibrary; +import com.google.devtools.build.lib.syntax.SkylarkCallable; +import com.google.devtools.build.lib.syntax.SkylarkModule; +import com.google.devtools.build.lib.syntax.SkylarkSignature; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Deque; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.TreeMap; + +/** + * A helper class that collects Skylark module documentation. + */ +final class SkylarkDocumentationCollector { + @SkylarkModule(name = "globals", + doc = "Objects, functions and modules registered in the global environment.") + private static final class TopLevelModule {} + + private SkylarkDocumentationCollector() {} + + /** + * Returns the SkylarkModule annotation for the top-level Skylark module. + */ + public static SkylarkModule getTopLevelModule() { + return TopLevelModule.class.getAnnotation(SkylarkModule.class); + } + + /** + * Collects the documentation for all Skylark modules and returns a map that maps Skylark + * module name to the module documentation. + */ + public static Map collectModules() { + Map modules = new TreeMap<>(); + Map builtinModules = collectBuiltinModules(); + Map> builtinJavaObjects = collectBuiltinJavaObjects(); + + modules.putAll(builtinModules); + for (SkylarkModuleDoc builtinObject : builtinModules.values()) { + // Check the return type for built-in functions, it can be a module previously not added. + for (SkylarkBuiltinMethodDoc builtinMethod : builtinObject.getBuiltinMethods().values()) { + Class type = builtinMethod.getAnnotation().returnType(); + if (type.isAnnotationPresent(SkylarkModule.class)) { + collectJavaObjects(type.getAnnotation(SkylarkModule.class), type, modules); + } + } + collectJavaObjects(builtinObject.getAnnotation(), builtinObject.getClassObject(), modules); + } + for (Entry> builtinModule : builtinJavaObjects.entrySet()) { + collectJavaObjects(builtinModule.getKey(), builtinModule.getValue(), modules); + } + return modules; + } + + /** + * Collects and returns all the Java objects reachable in Skylark from (and including) + * firstClass with the corresponding SkylarkModule annotation. + * + *

Note that the {@link SkylarkModule} annotation for firstClass - firstModule - + * is also an input parameter, because some top level Skylark built-in objects and methods + * are not annotated on the class, but on a field referencing them. + */ + @VisibleForTesting + static void collectJavaObjects(SkylarkModule firstModule, Class firstClass, + Map modules) { + Set> done = new HashSet<>(); + Deque> toProcess = new LinkedList<>(); + Map, SkylarkModule> annotations = new HashMap<>(); + + toProcess.addLast(firstClass); + annotations.put(firstClass, firstModule); + + while (!toProcess.isEmpty()) { + Class c = toProcess.removeFirst(); + SkylarkModule annotation = annotations.get(c); + done.add(c); + if (!modules.containsKey(annotation.name())) { + modules.put(annotation.name(), new SkylarkModuleDoc(annotation, c)); + } + SkylarkModuleDoc module = modules.get(annotation.name()); + + if (module.javaMethodsNotCollected()) { + ImmutableMap methods = + FuncallExpression.collectSkylarkMethodsWithAnnotation(c); + for (Map.Entry entry : methods.entrySet()) { + module.addMethod(new SkylarkJavaMethodDoc(module, entry.getKey(), entry.getValue())); + } + + for (Map.Entry method : methods.entrySet()) { + Class returnClass = method.getKey().getReturnType(); + if (returnClass.isAnnotationPresent(SkylarkModule.class) + && !done.contains(returnClass)) { + toProcess.addLast(returnClass); + annotations.put(returnClass, returnClass.getAnnotation(SkylarkModule.class)); + } + } + } + } + } + + private static Map collectBuiltinModules() { + Map modules = new HashMap<>(); + collectBuiltinDoc(modules, Environment.class.getDeclaredFields()); + collectBuiltinDoc(modules, MethodLibrary.class.getDeclaredFields()); + for (Class moduleClass : SkylarkModules.MODULES) { + collectBuiltinDoc(modules, moduleClass.getDeclaredFields()); + } + return modules; + } + + private static void collectBuiltinDoc(Map modules, Field[] fields) { + for (Field field : fields) { + if (field.isAnnotationPresent(SkylarkSignature.class)) { + SkylarkSignature skylarkSignature = field.getAnnotation(SkylarkSignature.class); + Class moduleClass = skylarkSignature.objectType(); + SkylarkModule skylarkModule = moduleClass.equals(Object.class) + ? getTopLevelModule() + : moduleClass.getAnnotation(SkylarkModule.class); + if (!modules.containsKey(skylarkModule.name())) { + modules.put(skylarkModule.name(), new SkylarkModuleDoc(skylarkModule, moduleClass)); + } + + SkylarkModuleDoc module = modules.get(skylarkModule.name()); + module.addMethod(new SkylarkBuiltinMethodDoc(module, skylarkSignature, field.getType())); + } + } + } + + private static Map> collectBuiltinJavaObjects() { + Map> modules = new HashMap<>(); + collectBuiltinModule(modules, SkylarkRuleContext.class); + collectBuiltinModule(modules, TransitiveInfoCollection.class); + return modules; + } + + private static void collectBuiltinModule( + Map> modules, Class moduleClass) { + if (moduleClass.isAnnotationPresent(SkylarkModule.class)) { + SkylarkModule skylarkModule = moduleClass.getAnnotation(SkylarkModule.class); + modules.put(skylarkModule, moduleClass); + } + } + + /** + * Returns the top level modules and functions with their documentation in a command-line + * printable format. + */ + public static Map collectTopLevelModules() { + Map modules = new TreeMap<>(); + for (SkylarkModuleDoc doc : collectBuiltinModules().values()) { + if (doc.getAnnotation() == getTopLevelModule()) { + for (Map.Entry entry : + doc.getBuiltinMethods().entrySet()) { + if (entry.getValue().documented()) { + modules.put(entry.getKey(), + DocgenConsts.toCommandLineFormat(entry.getValue().getDocumentation())); + } + } + } else { + modules.put(doc.getAnnotation().name(), + DocgenConsts.toCommandLineFormat(doc.getAnnotation().doc())); + } + } + return modules; + } +} diff --git a/src/main/java/com/google/devtools/build/docgen/SkylarkDocumentationGenerator.java b/src/main/java/com/google/devtools/build/docgen/SkylarkDocumentationGenerator.java index c4f8cf3de7..6e0d572a48 100644 --- a/src/main/java/com/google/devtools/build/docgen/SkylarkDocumentationGenerator.java +++ b/src/main/java/com/google/devtools/build/docgen/SkylarkDocumentationGenerator.java @@ -39,9 +39,8 @@ public class SkylarkDocumentationGenerator { public static void main(String[] args) { if (checkArgs(args)) { System.out.println("Generating Skylark documentation..."); - SkylarkDocumentationProcessor processor = new SkylarkDocumentationProcessor(); try { - processor.generateDocumentation(args[0]); + SkylarkDocumentationProcessor.generateDocumentation(args[0]); } catch (Throwable e) { fail(e, true); } diff --git a/src/main/java/com/google/devtools/build/docgen/SkylarkDocumentationProcessor.java b/src/main/java/com/google/devtools/build/docgen/SkylarkDocumentationProcessor.java index fa5933415c..816a46524b 100644 --- a/src/main/java/com/google/devtools/build/docgen/SkylarkDocumentationProcessor.java +++ b/src/main/java/com/google/devtools/build/docgen/SkylarkDocumentationProcessor.java @@ -13,389 +13,59 @@ // limitations under the License. package com.google.devtools.build.docgen; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Function; -import com.google.common.base.Joiner; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Iterables; -import com.google.common.io.Files; -import com.google.devtools.build.docgen.SkylarkJavaInterfaceExplorer.SkylarkBuiltinMethod; -import com.google.devtools.build.docgen.SkylarkJavaInterfaceExplorer.SkylarkJavaMethod; -import com.google.devtools.build.docgen.SkylarkJavaInterfaceExplorer.SkylarkModuleDoc; -import com.google.devtools.build.lib.analysis.TransitiveInfoCollection; -import com.google.devtools.build.lib.rules.SkylarkModules; -import com.google.devtools.build.lib.rules.SkylarkRuleContext; -import com.google.devtools.build.lib.syntax.BaseFunction; -import com.google.devtools.build.lib.syntax.Environment; -import com.google.devtools.build.lib.syntax.Environment.NoneType; -import com.google.devtools.build.lib.syntax.EvalUtils; -import com.google.devtools.build.lib.syntax.FuncallExpression; -import com.google.devtools.build.lib.syntax.MethodLibrary; -import com.google.devtools.build.lib.syntax.SkylarkCallable; -import com.google.devtools.build.lib.syntax.SkylarkList; -import com.google.devtools.build.lib.syntax.SkylarkModule; -import com.google.devtools.build.lib.syntax.SkylarkSignature; -import com.google.devtools.build.lib.syntax.SkylarkSignature.Param; -import com.google.devtools.build.lib.syntax.SkylarkSignatureProcessor.HackHackEitherList; +import com.google.devtools.build.docgen.skylark.SkylarkBuiltinMethodDoc; +import com.google.devtools.build.docgen.skylark.SkylarkJavaMethodDoc; +import com.google.devtools.build.docgen.skylark.SkylarkModuleDoc; -import java.io.BufferedWriter; import java.io.File; import java.io.IOException; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Map.Entry; -import java.util.TreeMap; /** * A class to assemble documentation for Skylark. */ -public class SkylarkDocumentationProcessor { - - private static final String TOP_LEVEL_ID = "_top_level"; - - private static final boolean USE_TEMPLATE = false; - - @SkylarkModule(name = "Global objects, functions and modules", - doc = "Objects, functions and modules registered in the global environment.") - private static final class TopLevelModule {} - - static SkylarkModule getTopLevelModule() { - return TopLevelModule.class.getAnnotation(SkylarkModule.class); - } +public final class SkylarkDocumentationProcessor { + private SkylarkDocumentationProcessor() {} /** * Generates the Skylark documentation to the given output directory. */ - public void generateDocumentation(String outputPath) throws IOException, + public static void generateDocumentation(String outputDir) throws IOException, BuildEncyclopediaDocException { - File skylarkDocPath = new File(outputPath); - try (BufferedWriter bw = new BufferedWriter( - Files.newWriter(skylarkDocPath, StandardCharsets.UTF_8))) { - if (USE_TEMPLATE) { - bw.write(SourceFileReader.readTemplateContents(DocgenConsts.SKYLARK_BODY_TEMPLATE, - ImmutableMap.of( - DocgenConsts.VAR_SECTION_SKYLARK_BUILTIN, generateAllBuiltinDoc()))); - } else { - bw.write(generateAllBuiltinDoc()); - } - System.out.println("Skylark documentation generated: " + skylarkDocPath.getAbsolutePath()); - } - } - - @VisibleForTesting - Map collectModules() { - Map modules = new TreeMap<>(); - Map builtinModules = collectBuiltinModules(); - Map> builtinJavaObjects = collectBuiltinJavaObjects(); - - modules.putAll(builtinModules); - SkylarkJavaInterfaceExplorer explorer = new SkylarkJavaInterfaceExplorer(); - for (SkylarkModuleDoc builtinObject : builtinModules.values()) { - // Check the return type for built-in functions, it can be a module previously not added. - for (SkylarkBuiltinMethod builtinMethod : builtinObject.getBuiltinMethods().values()) { - Class type = builtinMethod.annotation.returnType(); - if (type.isAnnotationPresent(SkylarkModule.class)) { - explorer.collect(type.getAnnotation(SkylarkModule.class), type, modules); - } - } - explorer.collect(builtinObject.getAnnotation(), builtinObject.getClassObject(), modules); - } - for (Entry> builtinModule : builtinJavaObjects.entrySet()) { - explorer.collect(builtinModule.getKey(), builtinModule.getValue(), modules); - } - return modules; - } - - private String generateAllBuiltinDoc() { - Map modules = collectModules(); + Map modules = SkylarkDocumentationCollector.collectModules(); + List navModules = new ArrayList<>(); - StringBuilder sb = new StringBuilder(); // Generate the top level module first in the doc - SkylarkModuleDoc topLevelModule = modules.remove(getTopLevelModule().name()); - generateModuleDoc(topLevelModule, sb); + SkylarkModuleDoc topLevelModule = modules.remove( + SkylarkDocumentationCollector.getTopLevelModule().name()); + topLevelModule.setTitle("Globals"); + writePage(outputDir, topLevelModule); + navModules.add(topLevelModule); + for (SkylarkModuleDoc module : modules.values()) { if (module.getAnnotation().documented()) { - sb.append("


"); - generateModuleDoc(module, sb); + writePage(outputDir, module); + navModules.add(module); } } - return sb.toString(); + writeNavPage(outputDir, navModules); } - private void generateModuleDoc(SkylarkModuleDoc module, StringBuilder sb) { - SkylarkModule annotation = module.getAnnotation(); - sb.append(String.format("

%s

\n", - getModuleId(annotation), - annotation.name())) - .append(annotation.doc()) - .append("\n"); - sb.append("
    "); - // Sort Java and Skylark builtin methods together. The map key is only used for sorting. - TreeMap methodMap = new TreeMap<>(); - for (SkylarkJavaMethod method : module.getJavaMethods()) { - methodMap.put(method.name + method.method.getParameterTypes().length, method); - } - for (SkylarkBuiltinMethod builtin : module.getBuiltinMethods().values()) { - methodMap.put(builtin.annotation.name(), builtin); - } - for (Object object : methodMap.values()) { - if (object instanceof SkylarkJavaMethod) { - SkylarkJavaMethod method = (SkylarkJavaMethod) object; - generateDirectJavaMethodDoc(annotation.name(), method.name, method.method, - method.callable, sb); - } - if (object instanceof SkylarkBuiltinMethod) { - generateBuiltinItemDoc(getModuleId(annotation), (SkylarkBuiltinMethod) object, sb); - } - } - sb.append("
"); + 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 String getModuleId(SkylarkModule annotation) { - if (annotation == getTopLevelModule()) { - return TOP_LEVEL_ID; - } else { - return annotation.name(); - } - } - - private void generateBuiltinItemDoc( - String moduleId, SkylarkBuiltinMethod method, StringBuilder sb) { - SkylarkSignature annotation = method.annotation; - if (!annotation.documented()) { - return; - } - sb.append(String.format("
  • %s

    \n", - moduleId, - annotation.name(), - annotation.name())); - - if (BaseFunction.class.isAssignableFrom(method.fieldClass)) { - sb.append(getSignature(moduleId, annotation)); - } else { - if (!annotation.returnType().equals(Object.class)) { - sb.append("" + getTypeAnchor(annotation.returnType()) + "
    "); - } - } - - sb.append(annotation.doc() + "\n"); - printParams(moduleId, annotation, sb); - } - - // Elide self parameter from mandatoryPositionals in class methods. - private static Param[] adjustedMandatoryPositionals(SkylarkSignature annotation) { - Param[] mandatoryPos = annotation.mandatoryPositionals(); - if (mandatoryPos.length > 0 - && annotation.objectType() != Object.class - && !FuncallExpression.isNamespace(annotation.objectType())) { - // Skip the self parameter, which is the first mandatory positional parameter. - return Arrays.copyOfRange(mandatoryPos, 1, mandatoryPos.length); - } else { - return mandatoryPos; - } - } - - private void printParams(String moduleId, SkylarkSignature annotation, StringBuilder sb) { - Param[] mandatoryPos = adjustedMandatoryPositionals(annotation); - Param[] optionalPos = annotation.optionalPositionals(); - Param[] optionalKey = annotation.optionalNamedOnly(); - Param[] mandatoryKey = annotation.mandatoryNamedOnly(); - Param[] star = annotation.extraPositionals(); - Param[] starStar = annotation.extraKeywords(); - - if (mandatoryPos.length + optionalPos.length + optionalKey.length + mandatoryKey.length - + star.length + starStar.length > 0) { - sb.append("

    Parameters

    \n"); - printParams(moduleId, annotation.name(), mandatoryPos, sb); - printParams(moduleId, annotation.name(), optionalPos, sb); - printParams(moduleId, annotation.name(), star, sb); - printParams(moduleId, annotation.name(), mandatoryKey, sb); - printParams(moduleId, annotation.name(), optionalKey, sb); - printParams(moduleId, annotation.name(), starStar, sb); - } else { - sb.append("
    \n"); - } - } - - private void generateDirectJavaMethodDoc(String objectName, String methodName, - Method method, SkylarkCallable annotation, StringBuilder sb) { - if (!annotation.documented()) { - return; - } - if (annotation.doc().isEmpty()) { - throw new RuntimeException(String.format( - "empty SkylarkCallable.doc() for object %s, method %s", objectName, methodName)); - } - - sb.append(String.format("
  • %s

    \n%s\n", - objectName, - methodName, - methodName, - getSignature(objectName, methodName, method))) - .append(annotation.doc()) - .append(getReturnTypeExtraMessage(annotation)) - .append("\n"); - } - - private String getReturnTypeExtraMessage(SkylarkCallable annotation) { - if (annotation.allowReturnNones()) { - return " May return None.\n"; - } - return ""; - } - - private String getSignature(String objectName, String methodName, Method method) { - String args = method.getAnnotation(SkylarkCallable.class).structField() - ? "" : "(" + getParameterString(method) + ")"; - - return String.format("%s %s.%s%s
    ", - getTypeAnchor(method.getReturnType()), objectName, methodName, args); - } - - private String getSignature(String objectName, SkylarkSignature method) { - List argList = new ArrayList<>(); - for (Param param : adjustedMandatoryPositionals(method)) { - argList.add(param.name()); - } - for (Param param : method.optionalPositionals()) { - argList.add(formatOptionalParameter(param)); - } - for (Param param : method.extraPositionals()) { - argList.add("*" + param.name()); - } - if (argList.size() > 0 && method.extraPositionals().length == 0 - && (method.optionalNamedOnly().length > 0 || method.mandatoryNamedOnly().length > 0)) { - argList.add("*"); - } - for (Param param : method.mandatoryNamedOnly()) { - argList.add(param.name()); - } - for (Param param : method.optionalNamedOnly()) { - argList.add(formatOptionalParameter(param)); - } - for (Param param : method.extraKeywords()) { - argList.add("**" + param.name()); - } - String args = "(" + Joiner.on(", ").join(argList) + ")"; - if (!objectName.equals(TOP_LEVEL_ID)) { - return String.format("%s %s.%s%s
    \n", - getTypeAnchor(method.returnType()), objectName, method.name(), args); - } else { - return String.format("%s %s%s
    \n", - getTypeAnchor(method.returnType()), method.name(), args); - } - } - - private String formatOptionalParameter(Param param) { - String defaultValue = param.defaultValue(); - - return String.format("%s=%s", param.name(), - (defaultValue == null || defaultValue.isEmpty()) ? "…" : defaultValue); - } - - private String getTypeAnchor(Class returnType, Class generic1) { - return getTypeAnchor(returnType) + " of " + getTypeAnchor(generic1) + "s"; - } - - private String getTypeAnchor(Class type) { - if (type.equals(Boolean.class) || type.equals(boolean.class)) { - return "bool"; - } else if (type.equals(String.class)) { - return "string"; - } else if (Map.class.isAssignableFrom(type)) { - return "dict"; - } else if (List.class.isAssignableFrom(type) || SkylarkList.class.isAssignableFrom(type) - || type == HackHackEitherList.class) { - // Annotated Java methods can return simple java.util.Lists (which get auto-converted). - return "list"; - } else if (type.equals(Void.TYPE) || type.equals(NoneType.class)) { - return "None"; - } else if (type.isAnnotationPresent(SkylarkModule.class)) { - // TODO(bazel-team): this can produce dead links for types don't show up in the doc. - // The correct fix is to generate those types (e.g. SkylarkFileType) too. - String module = type.getAnnotation(SkylarkModule.class).name(); - return "" + module + ""; - } else { - return EvalUtils.getDataTypeNameFromClass(type); - } - } - - private String getParameterString(Method method) { - return Joiner.on(", ").join(Iterables.transform( - ImmutableList.copyOf(method.getParameterTypes()), new Function, String>() { - @Override - public String apply(Class input) { - return getTypeAnchor(input); - } - })); - } - - private void printParams(String moduleId, String methodName, - Param[] params, StringBuilder sb) { - if (params.length > 0) { - sb.append("
      \n"); - for (Param param : params) { - String paramType = param.type().equals(Object.class) ? "" - : (param.generic1().equals(Object.class) - ? " (" + getTypeAnchor(param.type()) + ")" - : " (" + getTypeAnchor(param.type(), param.generic1()) + ")"); - sb.append(String.format("\t
    • %s%s: ", - moduleId, - methodName, - param.name(), - param.name(), - paramType)) - .append(param.doc()) - .append("\n\t
    • \n"); - } - sb.append("
    \n"); - } - } - - private Map collectBuiltinModules() { - Map modules = new HashMap<>(); - collectBuiltinDoc(modules, Environment.class.getDeclaredFields()); - collectBuiltinDoc(modules, MethodLibrary.class.getDeclaredFields()); - for (Class moduleClass : SkylarkModules.MODULES) { - collectBuiltinDoc(modules, moduleClass.getDeclaredFields()); - } - return modules; - } - - private Map> collectBuiltinJavaObjects() { - Map> modules = new HashMap<>(); - collectBuiltinModule(modules, SkylarkRuleContext.class); - collectBuiltinModule(modules, TransitiveInfoCollection.class); - return modules; - } - - /** - * Returns the top level modules and functions with their documentation in a command-line - * printable format. - */ - public Map collectTopLevelModules() { - Map modules = new TreeMap<>(); - for (SkylarkModuleDoc doc : collectBuiltinModules().values()) { - if (doc.getAnnotation() == getTopLevelModule()) { - for (Map.Entry entry : doc.getBuiltinMethods().entrySet()) { - if (entry.getValue().annotation.documented()) { - modules.put(entry.getKey(), - DocgenConsts.toCommandLineFormat(entry.getValue().annotation.doc())); - } - } - } else { - modules.put(doc.getAnnotation().name(), - DocgenConsts.toCommandLineFormat(doc.getAnnotation().doc())); - } - } - return modules; + private static void writeNavPage(String outputDir, List 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); } /** @@ -404,9 +74,10 @@ public class SkylarkDocumentationProcessor { * method in the module.
    * Returns null if no Skylark object is found. */ - public String getCommandLineAPIDoc(String[] params) { - Map modules = collectModules(); - SkylarkModuleDoc toplevelModuleDoc = modules.get(getTopLevelModule().name()); + public static String getCommandLineAPIDoc(String[] params) { + Map modules = SkylarkDocumentationCollector.collectModules(); + SkylarkModuleDoc toplevelModuleDoc = modules.get( + SkylarkDocumentationCollector.getTopLevelModule().name()); if (modules.containsKey(params[0])) { // Top level module SkylarkModuleDoc module = modules.get(params[0]); @@ -415,12 +86,12 @@ public class SkylarkDocumentationProcessor { 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 (SkylarkBuiltinMethod method : module.getBuiltinMethods().values()) { - printBuiltinFunctionDoc(moduleName, method.annotation, sb); + for (SkylarkBuiltinMethodDoc method : module.getBuiltinMethods().values()) { + printBuiltinFunctionDoc(moduleName, method, sb); } // Print all Java methods - for (SkylarkJavaMethod method : module.getJavaMethods()) { - printJavaFunctionDoc(moduleName, method, sb); + for (SkylarkJavaMethodDoc method : module.getJavaMethods()) { + printJavaFunctionDoc(method, sb); } return DocgenConsts.toCommandLineFormat(sb.toString()); } else { @@ -433,67 +104,42 @@ public class SkylarkDocumentationProcessor { return null; } - private String getFunctionDoc(String moduleName, String methodName, SkylarkModuleDoc module) { + private static String getFunctionDoc(String moduleName, String methodName, + SkylarkModuleDoc module) { if (module.getBuiltinMethods().containsKey(methodName)) { // Create the doc for the built-in function - SkylarkBuiltinMethod method = module.getBuiltinMethods().get(methodName); + SkylarkBuiltinMethodDoc method = module.getBuiltinMethods().get(methodName); StringBuilder sb = new StringBuilder(); - printBuiltinFunctionDoc(moduleName, method.annotation, sb); - printParams(moduleName, method.annotation, sb); + 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 (SkylarkJavaMethod method : module.getJavaMethods()) { - if (method.name.equals(methodName)) { - printJavaFunctionDoc(moduleName, method, sb); + for (SkylarkJavaMethodDoc method : module.getJavaMethods()) { + if (method.getName().equals(methodName)) { + printJavaFunctionDoc(method, sb); foundMatchingMethod = true; } } if (foundMatchingMethod) { - return DocgenConsts.toCommandLineFormat(sb.toString()); + return DocgenConsts.toCommandLineFormat(sb.toString()); } } return null; } - private void printBuiltinFunctionDoc( - String moduleName, SkylarkSignature annotation, StringBuilder sb) { + private static void printBuiltinFunctionDoc(String moduleName, SkylarkBuiltinMethodDoc method, + StringBuilder sb) { if (moduleName != null) { sb.append(moduleName).append("."); } - sb.append(annotation.name()).append("\n\t").append(annotation.doc()).append("\n"); - } - - private void printJavaFunctionDoc(String moduleName, SkylarkJavaMethod method, StringBuilder sb) { - sb.append(getSignature(moduleName, method.name, method.method)) - .append("\t").append(method.callable.doc()).append("\n"); - } - - private void collectBuiltinModule( - Map> modules, Class moduleClass) { - if (moduleClass.isAnnotationPresent(SkylarkModule.class)) { - SkylarkModule skylarkModule = moduleClass.getAnnotation(SkylarkModule.class); - modules.put(skylarkModule, moduleClass); - } + sb.append(method.getName()).append("\n\t").append(method.getDocumentation()).append("\n"); } - private void collectBuiltinDoc(Map modules, Field[] fields) { - for (Field field : fields) { - if (field.isAnnotationPresent(SkylarkSignature.class)) { - SkylarkSignature skylarkSignature = field.getAnnotation(SkylarkSignature.class); - Class moduleClass = skylarkSignature.objectType(); - SkylarkModule skylarkModule = moduleClass.equals(Object.class) - ? getTopLevelModule() - : moduleClass.getAnnotation(SkylarkModule.class); - if (!modules.containsKey(skylarkModule.name())) { - modules.put(skylarkModule.name(), new SkylarkModuleDoc(skylarkModule, moduleClass)); - } - modules.get(skylarkModule.name()).getBuiltinMethods() - .put(skylarkSignature.name(), - new SkylarkBuiltinMethod(skylarkSignature, field.getType())); - } - } + private static void printJavaFunctionDoc(SkylarkJavaMethodDoc method, StringBuilder sb) { + sb.append(method.getSignature()) + .append("\t").append(method.getDocumentation()).append("\n"); } } diff --git a/src/main/java/com/google/devtools/build/docgen/SkylarkJavaInterfaceExplorer.java b/src/main/java/com/google/devtools/build/docgen/SkylarkJavaInterfaceExplorer.java deleted file mode 100644 index 1118991d66..0000000000 --- a/src/main/java/com/google/devtools/build/docgen/SkylarkJavaInterfaceExplorer.java +++ /dev/null @@ -1,160 +0,0 @@ -// 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.base.Preconditions; -import com.google.common.collect.ImmutableMap; -import com.google.devtools.build.lib.syntax.FuncallExpression; -import com.google.devtools.build.lib.syntax.SkylarkCallable; -import com.google.devtools.build.lib.syntax.SkylarkModule; -import com.google.devtools.build.lib.syntax.SkylarkSignature; -import com.google.devtools.build.lib.util.StringUtilities; - -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.Map; -import java.util.Set; -import java.util.TreeMap; - -/** - * A helper class to collect all the Java objects / methods reachable from Skylark. - */ -public class SkylarkJavaInterfaceExplorer { - /** - * A class representing a Java method callable from Skylark with annotation. - */ - static final class SkylarkJavaMethod { - public final String name; - public final Method method; - public final SkylarkCallable callable; - - private String getName(Method method, SkylarkCallable callable) { - return callable.name().isEmpty() - ? StringUtilities.toPythonStyleFunctionName(method.getName()) - : callable.name(); - } - - SkylarkJavaMethod(Method method, SkylarkCallable callable) { - this.name = getName(method, callable); - this.method = method; - this.callable = callable; - } - } - - /** - * A class representing a Skylark built-in object or method. - */ - static final class SkylarkBuiltinMethod { - public final SkylarkSignature annotation; - public final Class fieldClass; - - public SkylarkBuiltinMethod(SkylarkSignature annotation, Class fieldClass) { - this.annotation = annotation; - this.fieldClass = fieldClass; - } - } - - /** - * A class representing a Skylark built-in object with its {@link SkylarkSignature} annotation - * and the {@link SkylarkCallable} methods it might have. - */ - static final class SkylarkModuleDoc { - - private final SkylarkModule module; - private final Class classObject; - private final Map builtin; - private ArrayList methods = null; - - SkylarkModuleDoc(SkylarkModule module, Class classObject) { - this.module = Preconditions.checkNotNull( - module, "Class has to be annotated with SkylarkModule: %s", classObject); - this.classObject = classObject; - this.builtin = new TreeMap<>(); - } - - SkylarkModule getAnnotation() { - return module; - } - - Class getClassObject() { - return classObject; - } - - private boolean javaMethodsNotCollected() { - return methods == null; - } - - private void setJavaMethods(ArrayList methods) { - this.methods = methods; - } - - Map getBuiltinMethods() { - return builtin; - } - - ArrayList getJavaMethods() { - return methods; - } - } - - /** - * Collects and returns all the Java objects reachable in Skylark from (and including) - * firstClassObject with the corresponding SkylarkSignature annotations. - * - *

    Note that the {@link SkylarkSignature} annotation for firstClassObject - firstAnnotation - - * is also an input parameter, because some top level Skylark built-in objects and methods - * are not annotated on the class, but on a field referencing them. - */ - void collect(SkylarkModule firstModule, Class firstClass, - Map modules) { - Set> processedClasses = new HashSet<>(); - LinkedList> classesToProcess = new LinkedList<>(); - Map, SkylarkModule> annotations = new HashMap<>(); - - classesToProcess.addLast(firstClass); - annotations.put(firstClass, firstModule); - - while (!classesToProcess.isEmpty()) { - Class classObject = classesToProcess.removeFirst(); - SkylarkModule annotation = annotations.get(classObject); - processedClasses.add(classObject); - if (!modules.containsKey(annotation.name())) { - modules.put(annotation.name(), new SkylarkModuleDoc(annotation, classObject)); - } - SkylarkModuleDoc module = modules.get(annotation.name()); - - if (module.javaMethodsNotCollected()) { - ImmutableMap methods = - FuncallExpression.collectSkylarkMethodsWithAnnotation(classObject); - ArrayList methodList = new ArrayList<>(); - for (Map.Entry entry : methods.entrySet()) { - methodList.add(new SkylarkJavaMethod(entry.getKey(), entry.getValue())); - } - module.setJavaMethods(methodList); - - for (Map.Entry method : methods.entrySet()) { - Class returnClass = method.getKey().getReturnType(); - if (returnClass.isAnnotationPresent(SkylarkModule.class) - && !processedClasses.contains(returnClass)) { - classesToProcess.addLast(returnClass); - annotations.put(returnClass, returnClass.getAnnotation(SkylarkModule.class)); - } - } - } - } - } -} diff --git a/src/main/java/com/google/devtools/build/docgen/skylark/SkylarkBuiltinMethodDoc.java b/src/main/java/com/google/devtools/build/docgen/skylark/SkylarkBuiltinMethodDoc.java new file mode 100644 index 0000000000..553cd1141f --- /dev/null +++ b/src/main/java/com/google/devtools/build/docgen/skylark/SkylarkBuiltinMethodDoc.java @@ -0,0 +1,97 @@ +// 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.skylark; + +import com.google.devtools.build.lib.syntax.BaseFunction; +import com.google.devtools.build.lib.syntax.SkylarkSignature; +import com.google.devtools.build.lib.syntax.SkylarkSignature.Param; + +import java.util.ArrayList; +import java.util.List; + +/** + * A class representing a Skylark built-in object or method. + */ +public final class SkylarkBuiltinMethodDoc extends SkylarkMethodDoc { + private final SkylarkModuleDoc module; + private final SkylarkSignature annotation; + private final Class fieldClass; + private List params; + + public SkylarkBuiltinMethodDoc(SkylarkModuleDoc module, SkylarkSignature annotation, + Class fieldClass) { + this.module = module; + this.annotation = annotation; + this.fieldClass = fieldClass; + this.params = new ArrayList<>(); + processParams(); + } + + public SkylarkSignature getAnnotation() { + return annotation; + } + + @Override + public boolean documented() { + return annotation.documented(); + } + + @Override + public String getName() { + return annotation.name(); + } + + @Override + public String getDocumentation() { + return annotation.doc(); + } + + /** + * Returns a string representing the method signature with links to the types if + * available. + * + *

    If the built-in method is a function, the construct the method signature. Otherwise, + * return a string containing the return type of the method. + */ + @Override + public String getSignature() { + if (BaseFunction.class.isAssignableFrom(fieldClass)) { + return getSignature(module.getName(), annotation); + } + if (!annotation.returnType().equals(Object.class)) { + return getTypeAnchor(annotation.returnType()); + } + return ""; + } + + @Override + public List getParams() { + return params; + } + + private void processParams() { + processParams(adjustedMandatoryPositionals(annotation)); + processParams(annotation.optionalPositionals()); + processParams(annotation.optionalNamedOnly()); + processParams(annotation.mandatoryNamedOnly()); + processParams(annotation.extraPositionals()); + processParams(annotation.extraKeywords()); + } + + private void processParams(Param[] params) { + for (Param param : params) { + this.params.add(new SkylarkParamDoc(this, param)); + } + } +} diff --git a/src/main/java/com/google/devtools/build/docgen/skylark/SkylarkDoc.java b/src/main/java/com/google/devtools/build/docgen/skylark/SkylarkDoc.java new file mode 100644 index 0000000000..065ab68c0c --- /dev/null +++ b/src/main/java/com/google/devtools/build/docgen/skylark/SkylarkDoc.java @@ -0,0 +1,85 @@ +// 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.skylark; + +import com.google.devtools.build.lib.syntax.Environment.NoneType; +import com.google.devtools.build.lib.syntax.EvalUtils; +import com.google.devtools.build.lib.syntax.FuncallExpression; +import com.google.devtools.build.lib.syntax.SkylarkList; +import com.google.devtools.build.lib.syntax.SkylarkModule; +import com.google.devtools.build.lib.syntax.SkylarkSignature; +import com.google.devtools.build.lib.syntax.SkylarkSignature.Param; +import com.google.devtools.build.lib.syntax.SkylarkSignatureProcessor.HackHackEitherList; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + * Abstract class for containing documentation for a Skylark syntactic entity. + */ +abstract class SkylarkDoc { + protected static final String TOP_LEVEL_ID = "globals"; + + /** + * Returns a string containing the name of the entity being documented. + */ + public abstract String getName(); + + /** + * Returns a string containing the HTML documentation of the entity being + * documented. + */ + public abstract String getDocumentation(); + + protected String getTypeAnchor(Class returnType, Class generic1) { + return getTypeAnchor(returnType) + " of " + getTypeAnchor(generic1) + "s"; + } + + protected String getTypeAnchor(Class type) { + if (type.equals(Boolean.class) || type.equals(boolean.class)) { + return "bool"; + } else if (type.equals(String.class)) { + return "string"; + } else if (Map.class.isAssignableFrom(type)) { + return "dict"; + } else if (List.class.isAssignableFrom(type) || SkylarkList.class.isAssignableFrom(type) + || type == HackHackEitherList.class) { + // Annotated Java methods can return simple java.util.Lists (which get auto-converted). + return "list"; + } else if (type.equals(Void.TYPE) || type.equals(NoneType.class)) { + return "None"; + } else if (type.isAnnotationPresent(SkylarkModule.class)) { + // TODO(bazel-team): this can produce dead links for types don't show up in the doc. + // The correct fix is to generate those types (e.g. SkylarkFileType) too. + String module = type.getAnnotation(SkylarkModule.class).name(); + return "" + module + ""; + } else { + return EvalUtils.getDataTypeNameFromClass(type); + } + } + + // Elide self parameter from mandatoryPositionals in class methods. + protected static Param[] adjustedMandatoryPositionals(SkylarkSignature annotation) { + Param[] mandatoryPos = annotation.mandatoryPositionals(); + if (mandatoryPos.length > 0 + && annotation.objectType() != Object.class + && !FuncallExpression.isNamespace(annotation.objectType())) { + // Skip the self parameter, which is the first mandatory positional parameter. + return Arrays.copyOfRange(mandatoryPos, 1, mandatoryPos.length); + } else { + return mandatoryPos; + } + } +} diff --git a/src/main/java/com/google/devtools/build/docgen/skylark/SkylarkJavaMethodDoc.java b/src/main/java/com/google/devtools/build/docgen/skylark/SkylarkJavaMethodDoc.java new file mode 100644 index 0000000000..d4212f7dc7 --- /dev/null +++ b/src/main/java/com/google/devtools/build/docgen/skylark/SkylarkJavaMethodDoc.java @@ -0,0 +1,71 @@ +// 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.skylark; + +import com.google.devtools.build.lib.syntax.SkylarkCallable; +import com.google.devtools.build.lib.util.StringUtilities; + +import java.lang.reflect.Method; + +/** + * A class representing a Java method callable from Skylark with annotation. + */ +public final class SkylarkJavaMethodDoc extends SkylarkMethodDoc { + private final SkylarkModuleDoc module; + private final String name; + private final Method method; + private final SkylarkCallable callable; + + public SkylarkJavaMethodDoc(SkylarkModuleDoc module, Method method, + SkylarkCallable callable) { + this.module = module; + this.name = callable.name().isEmpty() + ? StringUtilities.toPythonStyleFunctionName(method.getName()) + : callable.name(); + this.method = method; + this.callable = callable; + } + + public Method getMethod() { + return method; + } + + @Override + public boolean documented() { + return callable.documented(); + } + + @Override + public String getName() { + return name; + } + + @Override + public String getDocumentation() { + return callable.doc(); + } + + @Override + public String getSignature() { + return getSignature(module.getName(), name, method); + } + + @Override + public String getReturnTypeExtraMessage() { + if (callable.allowReturnNones()) { + return " May return None.\n"; + } + return ""; + } +} diff --git a/src/main/java/com/google/devtools/build/docgen/skylark/SkylarkMethodDoc.java b/src/main/java/com/google/devtools/build/docgen/skylark/SkylarkMethodDoc.java new file mode 100644 index 0000000000..294634f90d --- /dev/null +++ b/src/main/java/com/google/devtools/build/docgen/skylark/SkylarkMethodDoc.java @@ -0,0 +1,116 @@ +// 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.skylark; + +import com.google.common.base.Function; +import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import com.google.devtools.build.lib.syntax.SkylarkCallable; +import com.google.devtools.build.lib.syntax.SkylarkSignature; +import com.google.devtools.build.lib.syntax.SkylarkSignature.Param; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + +/** + * An abstract class containing documentation for a Skylark method. + */ +abstract class SkylarkMethodDoc extends SkylarkDoc { + /** + * Returns whether the Skylark method is documented. + */ + public abstract boolean documented(); + + /** + * Returns a string representing the method signature of the Skylark method, which contains + * HTML links to the documentation of parameter types if available. + */ + public abstract String getSignature(); + + /** + * Returns a string containing additional documentation about the method's return value. + * + *

    Returns an empty string by default. + */ + public String getReturnTypeExtraMessage() { + return ""; + } + + /** + * Returns a list containing the documentation for each of the method's parameters. + */ + public List getParams() { + return ImmutableList.of(); + } + + private String getParameterString(Method method) { + return Joiner.on(", ").join(Iterables.transform( + ImmutableList.copyOf(method.getParameterTypes()), new Function, String>() { + @Override + public String apply(Class input) { + return getTypeAnchor(input); + } + })); + } + + protected String getSignature(String objectName, String methodName, Method method) { + String args = method.getAnnotation(SkylarkCallable.class).structField() + ? "" : "(" + getParameterString(method) + ")"; + + return String.format("%s %s.%s%s", + getTypeAnchor(method.getReturnType()), objectName, methodName, args); + } + + protected String getSignature(String objectName, SkylarkSignature method) { + List argList = new ArrayList<>(); + for (Param param : adjustedMandatoryPositionals(method)) { + argList.add(param.name()); + } + for (Param param : method.optionalPositionals()) { + argList.add(formatOptionalParameter(param)); + } + for (Param param : method.extraPositionals()) { + argList.add("*" + param.name()); + } + if (argList.size() > 0 && method.extraPositionals().length == 0 + && (method.optionalNamedOnly().length > 0 || method.mandatoryNamedOnly().length > 0)) { + argList.add("*"); + } + for (Param param : method.mandatoryNamedOnly()) { + argList.add(param.name()); + } + for (Param param : method.optionalNamedOnly()) { + argList.add(formatOptionalParameter(param)); + } + for (Param param : method.extraKeywords()) { + argList.add("**" + param.name()); + } + String args = "(" + Joiner.on(", ").join(argList) + ")"; + if (!objectName.equals(TOP_LEVEL_ID)) { + return String.format("%s %s.%s%s\n", + getTypeAnchor(method.returnType()), objectName, method.name(), args); + } else { + return String.format("%s %s%s\n", + getTypeAnchor(method.returnType()), method.name(), args); + } + } + + private String formatOptionalParameter(Param param) { + String defaultValue = param.defaultValue(); + return String.format("%s=%s", param.name(), + (defaultValue == null || defaultValue.isEmpty()) ? "…" : defaultValue); + } +} diff --git a/src/main/java/com/google/devtools/build/docgen/skylark/SkylarkModuleDoc.java b/src/main/java/com/google/devtools/build/docgen/skylark/SkylarkModuleDoc.java new file mode 100644 index 0000000000..881f932e14 --- /dev/null +++ b/src/main/java/com/google/devtools/build/docgen/skylark/SkylarkModuleDoc.java @@ -0,0 +1,98 @@ +// 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.skylark; + +import com.google.common.base.Preconditions; +import com.google.devtools.build.lib.syntax.SkylarkModule; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +/** + * A class representing a Skylark built-in object with its {@link SkylarkSignature} annotation + * and the {@link SkylarkCallable} methods it might have. + */ +public final class SkylarkModuleDoc extends SkylarkDoc { + private final SkylarkModule module; + private final Class classObject; + private final Map builtinMethodMap; + private ArrayList javaMethods; + private TreeMap methodMap; + private String title; + + public SkylarkModuleDoc(SkylarkModule module, Class classObject) { + this.module = Preconditions.checkNotNull( + module, "Class has to be annotated with SkylarkModule: %s", classObject); + this.classObject = classObject; + this.builtinMethodMap = new TreeMap<>(); + this.methodMap = new TreeMap<>(); + this.javaMethods = new ArrayList<>(); + this.title = module.name(); + } + + @Override + public String getName() { + return module.name(); + } + + @Override + public String getDocumentation() { + return module.doc(); + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public SkylarkModule getAnnotation() { + return module; + } + + public Class getClassObject() { + return classObject; + } + + public void addMethod(SkylarkBuiltinMethodDoc method) { + methodMap.put(method.getName(), method); + builtinMethodMap.put(method.getName(), method); + } + + public void addMethod(SkylarkJavaMethodDoc method) { + methodMap.put(method.getName() + "$" + method.getMethod().getParameterTypes().length, method); + javaMethods.add(method); + } + + public boolean javaMethodsNotCollected() { + return javaMethods.isEmpty(); + } + + public Map getBuiltinMethods() { + return builtinMethodMap; + } + + public List getJavaMethods() { + return javaMethods; + } + + public Collection getMethods() { + return methodMap.values(); + } +} diff --git a/src/main/java/com/google/devtools/build/docgen/skylark/SkylarkParamDoc.java b/src/main/java/com/google/devtools/build/docgen/skylark/SkylarkParamDoc.java new file mode 100644 index 0000000000..d733e7de65 --- /dev/null +++ b/src/main/java/com/google/devtools/build/docgen/skylark/SkylarkParamDoc.java @@ -0,0 +1,61 @@ +// 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.skylark; + +import com.google.devtools.build.lib.syntax.SkylarkSignature.Param; + +/** + * A class containing the documentation for a Skylark method parameter. + */ +public final class SkylarkParamDoc extends SkylarkDoc { + private SkylarkMethodDoc method; + private Param param; + + public SkylarkParamDoc(SkylarkMethodDoc method, Param param) { + this.method = method; + this.param = param; + } + + /** + * Returns the string representing the type of this parameter with the link to the + * documentation for the type if available. + * + *

    If the parameter type is Object, then returns the empty string. If the parameter + * type is not a generic, then this method returns a string representing the type name + * with a link to the documentation for the type if available. If the parameter type + * is a generic, then this method returns a string "CONTAINER of TYPE". + */ + public String getType() { + if (param.type().equals(Object.class)) { + return ""; + } + if (param.generic1().equals(Object.class)) { + return getTypeAnchor(param.type()); + } else { + return getTypeAnchor(param.type(), param.generic1()); + } + } + + public SkylarkMethodDoc getMethod() { + return method; + } + + public String getName() { + return param.name(); + } + + public String getDocumentation() { + return param.doc(); + } +} diff --git a/src/main/java/com/google/devtools/build/docgen/templates/be-header.vm b/src/main/java/com/google/devtools/build/docgen/templates/be-header.vm index 8e841a626d..3eba02b93e 100644 --- a/src/main/java/com/google/devtools/build/docgen/templates/be-header.vm +++ b/src/main/java/com/google/devtools/build/docgen/templates/be-header.vm @@ -20,7 +20,7 @@ ${LEFT_PANEL}

    Concepts and terminology

    - - -
      +
      • load
      • package
      • diff --git a/src/main/java/com/google/devtools/build/docgen/templates/skylark-body.html b/src/main/java/com/google/devtools/build/docgen/templates/skylark-body.html deleted file mode 100644 index 5becb1a7cd..0000000000 --- a/src/main/java/com/google/devtools/build/docgen/templates/skylark-body.html +++ /dev/null @@ -1,58 +0,0 @@ - - - Skylark documentation - - - - - - - -

        Skylark documentation

        - -

        Built-in objects and functions in the Skylark Environment

        - - -${SECTION_BUILTIN} - - - - diff --git a/src/main/java/com/google/devtools/build/docgen/templates/skylark-library.vm b/src/main/java/com/google/devtools/build/docgen/templates/skylark-library.vm new file mode 100644 index 0000000000..0cd39e4969 --- /dev/null +++ b/src/main/java/com/google/devtools/build/docgen/templates/skylark-library.vm @@ -0,0 +1,58 @@ +

        ${module.title}

        + +${module.documentation} + +#if (!$module.methods.isEmpty()) +
        +

        Methods

        +
          + #foreach ($method in $module.methods) + #if ($method.documented()) +
        • ${method.name}
        • + #end + #end +
        +
        +#end +#foreach ($method in $module.methods) + #if ($method.documented()) +

        ${method.name}

        + #if (!$method.signature.isEmpty()) +

        ${method.signature}

        + #end + + ${method.documentation} + + #if (!$method.params.isEmpty()) +

        Parameters

        + + + + + + + + + + + + + #foreach ($param in $method.params) + + + + + #end + +
        ParameterDescription
        + ${param.name} + + #if (!$param.type.isEmpty()) +

        ${param.type}

        + #end +

        ${param.documentation}

        +
        + #end + ${method.returnTypeExtraMessage} + #end +#end diff --git a/src/main/java/com/google/devtools/build/docgen/templates/skylark-nav.vm b/src/main/java/com/google/devtools/build/docgen/templates/skylark-nav.vm new file mode 100644 index 0000000000..54194c0c24 --- /dev/null +++ b/src/main/java/com/google/devtools/build/docgen/templates/skylark-nav.vm @@ -0,0 +1,4 @@ +#foreach ($module in $modules) + +
      • ${module.title}
      • +#end diff --git a/src/main/java/com/google/devtools/build/lib/actions/Artifact.java b/src/main/java/com/google/devtools/build/lib/actions/Artifact.java index 1166cffd8e..e620ef63c6 100644 --- a/src/main/java/com/google/devtools/build/lib/actions/Artifact.java +++ b/src/main/java/com/google/devtools/build/lib/actions/Artifact.java @@ -367,7 +367,7 @@ public class Artifact implements FileType.HasFilename, ActionInput { @SkylarkCallable(name = "path", structField = true, doc = "The execution path of this file, relative to the execution directory. It consists of " + "two parts, an optional first part called the root (see also the root module), and the second part which is the " + + "href=\"root.html\">root module), and the second part which is the " + "short_path. The root may be empty, which it usually is for non-generated " + "files. For generated files it usually contains a configuration-specific path fragment that" + " encodes things like the target CPU architecture that was used while building said file.") diff --git a/src/main/java/com/google/devtools/build/lib/analysis/TransitiveInfoCollection.java b/src/main/java/com/google/devtools/build/lib/analysis/TransitiveInfoCollection.java index 2ba49d79d9..fcc9c641a2 100644 --- a/src/main/java/com/google/devtools/build/lib/analysis/TransitiveInfoCollection.java +++ b/src/main/java/com/google/devtools/build/lib/analysis/TransitiveInfoCollection.java @@ -86,10 +86,10 @@ import javax.annotation.Nullable; "A BUILD target. It is essentially a struct with the following fields:" + "
          " + "
        • label

          Label Target.label
          The identifier of the target.
        • " + + "href=\"Label.html\">Label Target.label
          The identifier of the target." + "
        • files

          set Target.files
          The (transitive) set of Files produced by this target.
        • " + + "href=\"set.html\">set Target.files
          The (transitive) set of Files produced by this target." + "
        • Extra providers

          For rule targets all " + "additional providers provided by this target are accessible as struct fields. " + "These extra providers are defined in the struct returned by the rule " diff --git a/src/main/java/com/google/devtools/build/lib/packages/Attribute.java b/src/main/java/com/google/devtools/build/lib/packages/Attribute.java index 38bab82545..4d31424e46 100644 --- a/src/main/java/com/google/devtools/build/lib/packages/Attribute.java +++ b/src/main/java/com/google/devtools/build/lib/packages/Attribute.java @@ -90,8 +90,8 @@ public final class Attribute implements Comparable { */ @SkylarkModule(name = "ConfigurationTransition", doc = "Declares how the configuration should change when following a dependency. " - + "It can be either DATA_CFG or " - + "HOST_CFG.") + + "It can be either DATA_CFG or " + + "HOST_CFG.") public enum ConfigurationTransition implements Transition { /** No transition, i.e., the same configuration as the current. */ NONE, diff --git a/src/main/java/com/google/devtools/build/lib/rules/SkylarkAttr.java b/src/main/java/com/google/devtools/build/lib/rules/SkylarkAttr.java index fc85bb1874..9c3fedf7e1 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/SkylarkAttr.java +++ b/src/main/java/com/google/devtools/build/lib/rules/SkylarkAttr.java @@ -44,10 +44,9 @@ import java.util.Map; /** * A helper class to provide Attr module in Skylark. * - * It exposes functions (e.g. 'attr.string', 'attr.label_list', etc.) to Skylark + *

          It exposes functions (e.g. 'attr.string', 'attr.label_list', etc.) to Skylark * users. The functions are executed through reflection. As everywhere in Skylark, * arguments are type-checked with the signature and cannot be null. - * */ @SkylarkModule( name = "attr", @@ -55,7 +54,7 @@ import java.util.Map; onlyLoadingPhase = true, doc = "Module for creating new attributes. " - + "They are only for use with the rule function." + + "They are only for use with the rule function." ) public final class SkylarkAttr { @@ -463,7 +462,7 @@ public final class SkylarkAttr { name = "label_list", doc = "Creates an attribute of type list of labels. " - + "See label for more information.", + + "See label for more information.", objectType = SkylarkAttr.class, returnType = Attribute.Builder.class, optionalNamedOnly = { @@ -623,7 +622,7 @@ public final class SkylarkAttr { name = "output_list", doc = "Creates an attribute of type list of outputs. Its default value is []. " - + "See output above for more information.", + + "See output above for more information.", objectType = SkylarkAttr.class, returnType = Attribute.Builder.class, optionalNamedOnly = { diff --git a/src/main/java/com/google/devtools/build/lib/rules/SkylarkFileType.java b/src/main/java/com/google/devtools/build/lib/rules/SkylarkFileType.java index 0571cb410d..e47b47e76c 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/SkylarkFileType.java +++ b/src/main/java/com/google/devtools/build/lib/rules/SkylarkFileType.java @@ -47,9 +47,9 @@ public class SkylarkFileType { @SkylarkCallable(doc = "Returns a list created from the elements of the parameter containing all the " - + "Files that match the FileType. The parameter " - + "must be a set or a " - + "list.") + + "Files that match the FileType. The parameter " + + "must be a set or a " + + "list.") public List filter(Iterable files) { return ImmutableList.copyOf(FileType.filter(files, fileType)); } diff --git a/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleClassFunctions.java b/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleClassFunctions.java index e128d7eec7..2f129501cb 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleClassFunctions.java +++ b/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleClassFunctions.java @@ -191,7 +191,7 @@ public class SkylarkRuleClassFunctions { mandatoryPositionals = { @Param(name = "implementation", type = BaseFunction.class, doc = "the function implementing this rule, must have exactly one parameter: " - + "ctx. The function is called during the analysis phase " + + "ctx. The function is called during the analysis phase " + "for each instance of the rule. It can access the attributes provided by the user. " + "It must create actions to generate all the declared outputs.") }, @@ -202,7 +202,7 @@ public class SkylarkRuleClassFunctions { + "and there must be an action that generates ctx.outputs.executable."), @Param(name = "attrs", type = Map.class, noneable = true, defaultValue = "None", doc = "dictionary to declare all the attributes of the rule. It maps from an attribute name " - + "to an attribute object (see attr module). " + + "to an attribute object (see attr module). " + "Attributes starting with _ are private, and can be used to add " + "an implicit dependency on a label. The attribute name is implicitly " + "added and must not be specified. Attributes visibility, " diff --git a/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleContext.java b/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleContext.java index 385b04d8ca..8221066a3a 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleContext.java +++ b/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleContext.java @@ -329,14 +329,14 @@ public final class SkylarkRuleContext { } @SkylarkCallable(name = "configuration", structField = true, - doc = "Returns the default configuration. See the " + doc = "Returns the default configuration. See the " + "configuration type for more details.") public BuildConfiguration getConfiguration() { return ruleContext.getConfiguration(); } @SkylarkCallable(name = "host_configuration", structField = true, - doc = "Returns the host configuration. See the " + doc = "Returns the host configuration. See the " + "configuration type for more details.") public BuildConfiguration getHostConfiguration() { return ruleContext.getHostConfiguration(); diff --git a/src/main/java/com/google/devtools/build/lib/syntax/ClassObject.java b/src/main/java/com/google/devtools/build/lib/syntax/ClassObject.java index 9ed20cc68a..c38ea774fd 100644 --- a/src/main/java/com/google/devtools/build/lib/syntax/ClassObject.java +++ b/src/main/java/com/google/devtools/build/lib/syntax/ClassObject.java @@ -57,7 +57,7 @@ public interface ClassObject { @Immutable @SkylarkModule(name = "struct", doc = "A special language element to support structs (i.e. simple value objects). " - + "See the global struct function " + + "See the global struct function " + "for more details.") public class SkylarkClassObject implements ClassObject { diff --git a/src/main/java/com/google/devtools/build/lib/syntax/MethodLibrary.java b/src/main/java/com/google/devtools/build/lib/syntax/MethodLibrary.java index d8b625e328..2e0d38c4f5 100644 --- a/src/main/java/com/google/devtools/build/lib/syntax/MethodLibrary.java +++ b/src/main/java/com/google/devtools/build/lib/syntax/MethodLibrary.java @@ -952,7 +952,7 @@ public class MethodLibrary { }; @SkylarkSignature(name = "set", returnType = SkylarkNestedSet.class, - doc = "Creates a set from the items." + doc = "Creates a set from the items." + " The set supports nesting other sets of the same element" + " type in it. A desired iteration order can also be specified.
          " + " Examples:

          set([\"a\", \"b\"])\n"
          @@ -965,7 +965,7 @@ public class MethodLibrary {
                       doc = "The ordering strategy for the set if it's nested, "
                       + "possible values are: stable (default), compile, "
                       + "link or naive_link. An explanation of the "
          -            + "values can be found here.")},
          +            + "values can be found here.")},
                 useLocation = true)
             private static final BuiltinFunction set = new BuiltinFunction("set") {
               public SkylarkNestedSet invoke(Object items, String order,
          @@ -1022,7 +1022,7 @@ public class MethodLibrary {
           
             @SkylarkSignature(name = "union", objectType = SkylarkNestedSet.class,
                 returnType = SkylarkNestedSet.class,
          -      doc = "Creates a new set that contains both "
          +      doc = "Creates a new set that contains both "
                     + "the input set as well as all additional elements.",
                 mandatoryPositionals = {
                   @Param(name = "input", type = SkylarkNestedSet.class, doc = "The input set"),
          diff --git a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkNestedSet.java b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkNestedSet.java
          index e4eeba3333..004d4c4143 100644
          --- a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkNestedSet.java
          +++ b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkNestedSet.java
          @@ -35,7 +35,7 @@ import javax.annotation.Nullable;
            */
           @SkylarkModule(name = "set",
               doc = "A language built-in type that supports (nested) sets. "
          -        + "Sets can be created using the set function, and "
          +        + "Sets can be created using the set function, and "
                   + "they support the + operator to extend the set with more elements or "
                   + "to nest other sets inside of it. Examples:
          " + "
          s = set([1, 2])\n"
          -- 
          cgit v1.2.3