diff options
author | 2018-05-01 10:32:30 -0700 | |
---|---|---|
committer | 2018-05-01 10:33:46 -0700 | |
commit | 2415cb4ab69ec05d2e7ba15c75a20e2d2da2fc90 (patch) | |
tree | 33a8c5810973f58ff8c084b14078be87cc2bf5ad /src/main/java/com/google/devtools/build/docgen | |
parent | 2e4f703d361823fa12df9ddb57f21189743b2c74 (diff) |
Use a new pattern for builtin Provider objects with @SkylarkCallable.
This deprecates the old NativeProvider pattern.
The new pattern is demonstrated using AppleStaticLibraryInfo.
RELNOTES: None.
PiperOrigin-RevId: 194956883
Diffstat (limited to 'src/main/java/com/google/devtools/build/docgen')
3 files changed, 78 insertions, 18 deletions
diff --git a/src/main/java/com/google/devtools/build/docgen/SkylarkDocumentationCollector.java b/src/main/java/com/google/devtools/build/docgen/SkylarkDocumentationCollector.java index 6b614b381b..67c46f0e95 100644 --- a/src/main/java/com/google/devtools/build/docgen/SkylarkDocumentationCollector.java +++ b/src/main/java/com/google/devtools/build/docgen/SkylarkDocumentationCollector.java @@ -37,6 +37,7 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.TreeMap; +import javax.annotation.Nullable; /** * A helper class that collects Skylark module documentation. @@ -125,6 +126,10 @@ final class SkylarkDocumentationCollector { while (!toProcess.isEmpty()) { Class<?> c = toProcess.removeFirst(); + if (done.contains(c)) { + continue; + } + SkylarkModuleDoc module = getSkylarkModuleDoc(c, modules); done.add(c); @@ -132,21 +137,57 @@ final class SkylarkDocumentationCollector { ImmutableMap<Method, SkylarkCallable> methods = FuncallExpression.collectSkylarkMethodsWithAnnotation(c); for (Map.Entry<Method, SkylarkCallable> entry : methods.entrySet()) { - module.addMethod( - new SkylarkJavaMethodDoc(module.getName(), entry.getKey(), entry.getValue())); - } + if (entry.getKey().isAnnotationPresent(SkylarkConstructor.class)) { + collectConstructor(modules, module.getName(), entry.getKey(), entry.getValue()); + } else { + module.addMethod( + new SkylarkJavaMethodDoc(module.getName(), entry.getKey(), entry.getValue())); + } - for (Map.Entry<Method, SkylarkCallable> method : methods.entrySet()) { - Class<?> returnClass = method.getKey().getReturnType(); - if (returnClass.isAnnotationPresent(SkylarkModule.class) - && !done.contains(returnClass)) { + Class<?> returnClass = entry.getKey().getReturnType(); + if (returnClass.isAnnotationPresent(SkylarkModule.class)) { toProcess.addLast(returnClass); + } else { + Map.Entry<Method, SkylarkCallable> selfCallConstructor = + getSelfCallConstructorMethod(returnClass); + if (selfCallConstructor != null) { + // If the class to be processed is not annotated with @SkylarkModule, then its + // @SkylarkCallable methods are not processed, as it does not have its own + // documentation page. However, if it is a callable object (has a selfCall method) + // that is also a constructor for another type, we still want to ensure that method + // is documented. + // This is used for builtin providers, which typically are not marked @SkylarkModule, + // but which have selfCall constructors for their corresponding Info class. + + // For example, the "mymodule" module may return a callable object at mymodule.foo + // which constructs instances of the Bar class. The type returned by mymodule.foo + // may have no documentation, but mymodule.foo should be documented as a + // constructor of Bar objects. + collectConstructor(modules, module.getName(), + selfCallConstructor.getKey(), selfCallConstructor.getValue()); + } } } } } } + @Nullable + private static Map.Entry<Method, SkylarkCallable> getSelfCallConstructorMethod( + Class<?> objectClass) { + ImmutableMap<Method, SkylarkCallable> methods = + FuncallExpression.collectSkylarkMethodsWithAnnotation(objectClass); + for (Map.Entry<Method, SkylarkCallable> entry : methods.entrySet()) { + if (entry.getValue().selfCall() + && entry.getKey().isAnnotationPresent(SkylarkConstructor.class)) { + // It's illegal, and checked by the interpreter, for there to be more than one method + // annotated with selfCall. Thus, it's valid to return on the first find. + return entry; + } + } + return null; + } + private static void collectBuiltinDoc(Map<String, SkylarkModuleDoc> modules, Field[] fields) { for (Field field : fields) { if (field.isAnnotationPresent(SkylarkSignature.class)) { @@ -168,15 +209,19 @@ final class SkylarkDocumentationCollector { FuncallExpression.collectSkylarkMethodsWithAnnotation(moduleClass); for (Map.Entry<Method, SkylarkCallable> entry : methods.entrySet()) { if (entry.getKey().isAnnotationPresent(SkylarkConstructor.class)) { - SkylarkConstructor constructorAnnotation = - entry.getKey().getAnnotation(SkylarkConstructor.class); - Class<?> objectClass = constructorAnnotation.objectType(); - SkylarkModuleDoc module = getSkylarkModuleDoc(objectClass, modules); - module.addMethod( - new SkylarkJavaMethodDoc("", entry.getKey(), entry.getValue())); + collectConstructor(modules, "", entry.getKey(), entry.getValue()); } else { topLevelModuleDoc.addMethod(new SkylarkJavaMethodDoc("", entry.getKey(), entry.getValue())); } } } + + private static void collectConstructor(Map<String, SkylarkModuleDoc> modules, + String originatingModuleName, Method method, SkylarkCallable callable) { + SkylarkConstructor constructorAnnotation = + Preconditions.checkNotNull(method.getAnnotation(SkylarkConstructor.class)); + Class<?> objectClass = constructorAnnotation.objectType(); + SkylarkModuleDoc module = getSkylarkModuleDoc(objectClass, modules); + module.setConstructor(new SkylarkJavaMethodDoc(originatingModuleName, method, callable)); + } } 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 index 60c19874fd..95f740e002 100644 --- a/src/main/java/com/google/devtools/build/docgen/skylark/SkylarkDoc.java +++ b/src/main/java/com/google/devtools/build/docgen/skylark/SkylarkDoc.java @@ -17,6 +17,7 @@ import com.google.common.collect.ImmutableList; import com.google.devtools.build.lib.collect.nestedset.NestedSet; import com.google.devtools.build.lib.skylarkinterface.Param; import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable; +import com.google.devtools.build.lib.skylarkinterface.SkylarkInterfaceUtils; import com.google.devtools.build.lib.skylarkinterface.SkylarkModule; import com.google.devtools.build.lib.skylarkinterface.SkylarkSignature; import com.google.devtools.build.lib.syntax.EvalUtils; @@ -69,8 +70,8 @@ abstract class SkylarkDoc { return "<a class=\"anchor\" href=\"" + TOP_LEVEL_ID + ".html#None\">None</a>"; } else if (type.equals(NestedSet.class)) { return "<a class=\"anchor\" href=\"depset.html\">depset</a>"; - } else if (type.isAnnotationPresent(SkylarkModule.class)) { - SkylarkModule module = type.getAnnotation(SkylarkModule.class); + } else if (SkylarkInterfaceUtils.getSkylarkModule(type) != null) { + SkylarkModule module = SkylarkInterfaceUtils.getSkylarkModule(type); if (module.documented()) { return String.format("<a class=\"anchor\" href=\"%1$s.html\">%1$s</a>", module.name()); 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 index 898c841085..24da2f3e96 100644 --- a/src/main/java/com/google/devtools/build/docgen/skylark/SkylarkModuleDoc.java +++ b/src/main/java/com/google/devtools/build/docgen/skylark/SkylarkModuleDoc.java @@ -15,6 +15,7 @@ package com.google.devtools.build.docgen.skylark; import com.google.common.base.Preconditions; import com.google.common.collect.HashMultimap; +import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.collect.Multimap; import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable; @@ -25,10 +26,12 @@ import java.util.Collection; import java.util.Locale; import java.util.Map; import java.util.TreeMap; +import javax.annotation.Nullable; /** - * A class representing a Skylark built-in object with its {@link SkylarkSignature} annotation - * and the {@link SkylarkCallable} methods it might have. + * A class representing documentation for a Skylark built-in object with its {@link SkylarkModule} + * annotation and with the {@link SkylarkCallable} methods and {@link SkylarkSignature} fields it + * documents. */ public final class SkylarkModuleDoc extends SkylarkDoc { private final SkylarkModule module; @@ -37,6 +40,7 @@ public final class SkylarkModuleDoc extends SkylarkDoc { private final Multimap<String, SkylarkJavaMethodDoc> javaMethods; private TreeMap<String, SkylarkMethodDoc> methodMap; private final String title; + @Nullable private SkylarkJavaMethodDoc javaConstructor; public SkylarkModuleDoc(SkylarkModule module, Class<?> classObject) { this.module = Preconditions.checkNotNull( @@ -74,6 +78,12 @@ public final class SkylarkModuleDoc extends SkylarkDoc { return classObject; } + public void setConstructor(SkylarkJavaMethodDoc method) { + Preconditions.checkState(javaConstructor == null); + javaConstructor = method; + methodMap.put(method.getName(), method); + } + public void addMethod(SkylarkBuiltinMethodDoc method) { methodMap.put(method.getName(), method); builtinMethodMap.put(method.getName(), method); @@ -114,7 +124,11 @@ public final class SkylarkModuleDoc extends SkylarkDoc { } public Collection<SkylarkJavaMethodDoc> getJavaMethods() { - return javaMethods.values(); + ImmutableList.Builder<SkylarkJavaMethodDoc> returnedMethods = ImmutableList.builder(); + if (javaConstructor != null) { + returnedMethods.add(javaConstructor); + } + return returnedMethods.addAll(javaMethods.values()).build(); } public Collection<SkylarkMethodDoc> getMethods() { |