Allows Renderer Binary to output aspect information as a markdown output
Depends on CL 259570926.
Work toward https://github.com/bazelbuild/skydoc/issues/196
PiperOrigin-RevId: 260145437
diff --git a/src/main/java/com/google/devtools/build/skydoc/SkydocMain.java b/src/main/java/com/google/devtools/build/skydoc/SkydocMain.java
index c379921..9045fd4 100644
--- a/src/main/java/com/google/devtools/build/skydoc/SkydocMain.java
+++ b/src/main/java/com/google/devtools/build/skydoc/SkydocMain.java
@@ -173,6 +173,8 @@
"com/google/devtools/build/skydoc/rendering/templates/provider.vm";
private static final String FUNCTION_TEMPLATE_PATH =
"com/google/devtools/build/skydoc/rendering/templates/func.vm";
+ private static final String ASPECT_TEMPLATE_PATH =
+ "com/google/devtools/build/skydoc/rendering/templates/aspect.vm";
public SkydocMain(SkylarkFileAccessor fileAccessor, String workspaceName, List<String> depRoots) {
this.fileAccessor = fileAccessor;
@@ -268,7 +270,8 @@
HEADER_TEMPLATE_PATH,
RULE_TEMPLATE_PATH,
PROVIDER_TEMPLATE_PATH,
- FUNCTION_TEMPLATE_PATH);
+ FUNCTION_TEMPLATE_PATH,
+ ASPECT_TEMPLATE_PATH);
try (PrintWriter printWriter = new PrintWriter(outputPath, "UTF-8")) {
printWriter.println(renderer.renderMarkdownHeader());
printRuleInfos(printWriter, renderer, filteredRuleInfos);
diff --git a/src/main/java/com/google/devtools/build/skydoc/renderer/RendererMain.java b/src/main/java/com/google/devtools/build/skydoc/renderer/RendererMain.java
index ce17b66..1a58afc 100644
--- a/src/main/java/com/google/devtools/build/skydoc/renderer/RendererMain.java
+++ b/src/main/java/com/google/devtools/build/skydoc/renderer/RendererMain.java
@@ -15,6 +15,7 @@
package com.google.devtools.build.skydoc.renderer;
import com.google.devtools.build.skydoc.rendering.MarkdownRenderer;
+import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.AspectInfo;
import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.ModuleInfo;
import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.ProviderInfo;
import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.RuleInfo;
@@ -50,16 +51,22 @@
String ruleTemplatePath = rendererOptions.ruleTemplateFilePath;
String providerTemplatePath = rendererOptions.providerTemplateFilePath;
String funcTemplatePath = rendererOptions.funcTemplateFilePath;
+ String aspectTemplatePath = rendererOptions.aspectTemplateFilePath;
MarkdownRenderer renderer =
new MarkdownRenderer(
- headerTemplatePath, ruleTemplatePath, providerTemplatePath, funcTemplatePath);
+ headerTemplatePath,
+ ruleTemplatePath,
+ providerTemplatePath,
+ funcTemplatePath,
+ aspectTemplatePath);
try (PrintWriter printWriter = new PrintWriter(outputPath, "UTF-8")) {
ModuleInfo moduleInfo = ModuleInfo.parseFrom(new FileInputStream(inputPath));
printWriter.println(renderer.renderMarkdownHeader());
printRuleInfos(printWriter, renderer, moduleInfo.getRuleInfoList());
printProviderInfos(printWriter, renderer, moduleInfo.getProviderInfoList());
printUserDefinedFunctions(printWriter, renderer, moduleInfo.getFuncInfoList());
+ printAspectInfos(printWriter, renderer, moduleInfo.getAspectInfoList());
} catch (InvalidProtocolBufferException e) {
throw new IllegalArgumentException("Input file is not a valid ModuleInfo proto.", e);
}
@@ -93,4 +100,13 @@
printWriter.println();
}
}
+
+ private static void printAspectInfos(
+ PrintWriter printWriter, MarkdownRenderer renderer, List<AspectInfo> aspectInfos)
+ throws IOException {
+ for (AspectInfo aspectProto : aspectInfos) {
+ printWriter.println(renderer.render(aspectProto.getAspectName(), aspectProto));
+ printWriter.println();
+ }
+ }
}
diff --git a/src/main/java/com/google/devtools/build/skydoc/renderer/RendererOptions.java b/src/main/java/com/google/devtools/build/skydoc/renderer/RendererOptions.java
index b4f3a6b..730cb06 100644
--- a/src/main/java/com/google/devtools/build/skydoc/renderer/RendererOptions.java
+++ b/src/main/java/com/google/devtools/build/skydoc/renderer/RendererOptions.java
@@ -77,4 +77,14 @@
"The template for the documentation of a function. If the option is"
+ " unspecified, a default markdown output template will be used.")
public String funcTemplateFilePath;
+
+ @Option(
+ name = "aspect_template",
+ defaultValue = "com/google/devtools/build/skydoc/rendering/templates/aspect.vm",
+ documentationCategory = OptionDocumentationCategory.UNDOCUMENTED,
+ effectTags = OptionEffectTag.UNKNOWN,
+ help =
+ "The template for the documentation of a aspect. If the option is unspecified, a"
+ + " default markdown output template will be used.")
+ public String aspectTemplateFilePath;
}
diff --git a/src/main/java/com/google/devtools/build/skydoc/rendering/MarkdownRenderer.java b/src/main/java/com/google/devtools/build/skydoc/rendering/MarkdownRenderer.java
index 8334f69..4f2dd8c 100644
--- a/src/main/java/com/google/devtools/build/skydoc/rendering/MarkdownRenderer.java
+++ b/src/main/java/com/google/devtools/build/skydoc/rendering/MarkdownRenderer.java
@@ -16,6 +16,7 @@
import static java.nio.charset.StandardCharsets.UTF_8;
+import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.AspectInfo;
import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.ProviderInfo;
import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.RuleInfo;
import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.UserDefinedFunctionInfo;
@@ -45,6 +46,7 @@
private final String ruleTemplateFilename;
private final String providerTemplateFilename;
private final String functionTemplateFilename;
+ private final String aspectTemplateFilename;
private final VelocityEngine velocityEngine;
@@ -52,12 +54,14 @@
String headerTemplate,
String ruleTemplate,
String providerTemplate,
- String functionTemplate) {
+ String functionTemplate,
+ String aspectTemplate) {
this.headerTemplateFilename = headerTemplate;
this.ruleTemplateFilename = ruleTemplate;
this.providerTemplateFilename = providerTemplate;
this.functionTemplateFilename = functionTemplate;
-
+ this.aspectTemplateFilename = aspectTemplate;
+
this.velocityEngine = new VelocityEngine();
velocityEngine.setProperty("resource.loader", "classpath, jar");
velocityEngine.setProperty("classpath.resource.loader.class",
@@ -143,6 +147,25 @@
}
/**
+ * Returns a markdown rendering of aspect documentation for the given aspect information object
+ * with the given aspect name.
+ */
+ public String render(String aspectName, AspectInfo aspectInfo) throws IOException {
+ VelocityContext context = new VelocityContext();
+ context.put("util", new MarkdownUtil());
+ context.put("aspectName", aspectName);
+ context.put("aspectInfo", aspectInfo);
+
+ StringWriter stringWriter = new StringWriter();
+ Reader reader = readerFromPath(aspectTemplateFilename);
+ try {
+ velocityEngine.evaluate(context, stringWriter, aspectTemplateFilename, reader);
+ } catch (ResourceNotFoundException | ParseErrorException | MethodInvocationException e) {
+ throw new IOException(e);
+ }
+ return stringWriter.toString();
+ }
+ /**
* Returns a reader from the given path.
*
* @param filePath The given path, either a filesystem path or a java Resource
diff --git a/src/main/java/com/google/devtools/build/skydoc/rendering/MarkdownUtil.java b/src/main/java/com/google/devtools/build/skydoc/rendering/MarkdownUtil.java
index 06c969b..44e6e45 100644
--- a/src/main/java/com/google/devtools/build/skydoc/rendering/MarkdownUtil.java
+++ b/src/main/java/com/google/devtools/build/skydoc/rendering/MarkdownUtil.java
@@ -15,6 +15,7 @@
package com.google.devtools.build.skydoc.rendering;
import com.google.common.base.Joiner;
+import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.AspectInfo;
import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.AttributeInfo;
import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.AttributeType;
import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.FunctionParamInfo;
@@ -68,6 +69,20 @@
}
/**
+ * Return a string representing the aspect summary for the given aspect with the given name.
+ *
+ * <p>For example: 'my_aspect(foo, bar)'. The summary will contain hyperlinks for each attribute.
+ */
+ @SuppressWarnings("unused") // Used by markdown template.
+ public String aspectSummary(String aspectName, AspectInfo aspectInfo) {
+ List<String> attributeNames =
+ aspectInfo.getAttributeList().stream()
+ .map(attr -> attr.getName())
+ .collect(Collectors.toList());
+ return summary(aspectName, attributeNames);
+ }
+
+ /**
* Return a string representing the summary for the given user-defined function.
*
* For example: 'my_func(foo, bar)'.
diff --git a/src/main/java/com/google/devtools/build/skydoc/rendering/templates/aspect.vm b/src/main/java/com/google/devtools/build/skydoc/rendering/templates/aspect.vm
new file mode 100644
index 0000000..35e5441
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/skydoc/rendering/templates/aspect.vm
@@ -0,0 +1,56 @@
+<a name="#${aspectName}"></a>
+
+#[[##]]# ${aspectName}
+
+<pre>
+${util.aspectSummary($aspectName, $aspectInfo)}
+</pre>
+
+$aspectInfo.getDocString()
+
+#[[###]]# Aspect Attributes
+
+#if (!$aspectInfo.getAspectAttributeList().isEmpty())
+<table class="params-table">
+ <colgroup>
+ <col class="col-param" />
+ <col class="col-description" />
+ </colgroup>
+ <tbody>
+#foreach ($aspectAttribute in $aspectInfo.getAspectAttributeList())
+ <tr id="${aspectName}-${aspectAttribute}">
+ <td><code>${aspectAttribute}</code></td>
+ <td>
+ String; required.
+#end
+ </td>
+ </tr>
+#end
+ </tbody>
+</table>
+
+#[[###]]# Attributes
+
+#if (!$aspectInfo.getAttributeList().isEmpty())
+<table class="params-table">
+ <colgroup>
+ <col class="col-param" />
+ <col class="col-description" />
+ </colgroup>
+ <tbody>
+#foreach ($attribute in $aspectInfo.getAttributeList())
+ <tr id="${aspectName}-${attribute.name}">
+ <td><code>${attribute.name}</code></td>
+ <td>
+ ${util.attributeTypeString($attribute)}; ${util.mandatoryString($attribute)}
+#if (!$attribute.docString.isEmpty())
+ <p>
+ ${attribute.docString.trim()}
+ </p>
+#end
+ </td>
+ </tr>
+#end
+ </tbody>
+</table>
+#end
diff --git a/src/test/java/com/google/devtools/build/skydoc/BUILD b/src/test/java/com/google/devtools/build/skydoc/BUILD
index d5b6ecc..7c0c9e2 100644
--- a/src/test/java/com/google/devtools/build/skydoc/BUILD
+++ b/src/test/java/com/google/devtools/build/skydoc/BUILD
@@ -17,6 +17,7 @@
filegroup(
name = "test_template_files",
srcs = [
+ "//src/test/java/com/google/devtools/build/skydoc:test_templates/aspect.vm",
"//src/test/java/com/google/devtools/build/skydoc:test_templates/func.vm",
"//src/test/java/com/google/devtools/build/skydoc:test_templates/header.vm",
"//src/test/java/com/google/devtools/build/skydoc:test_templates/provider.vm",
@@ -247,6 +248,12 @@
input_file = "testdata/struct_default_value_test/input.bzl",
)
+skydoc_test(
+ name = "aspect_test",
+ golden_file = "testdata/aspect_test/golden.txt",
+ input_file = "testdata/aspect_test/input.bzl",
+)
+
genrule(
name = "generate_bzl_test_dep",
srcs = ["testdata/generated_bzl_test/dep.bzl.tpl"],
diff --git a/src/test/java/com/google/devtools/build/skydoc/MarkdownRendererTest.java b/src/test/java/com/google/devtools/build/skydoc/MarkdownRendererTest.java
index 2bf849a..440d9c1 100644
--- a/src/test/java/com/google/devtools/build/skydoc/MarkdownRendererTest.java
+++ b/src/test/java/com/google/devtools/build/skydoc/MarkdownRendererTest.java
@@ -17,6 +17,7 @@
import static com.google.common.truth.Truth.assertThat;
import com.google.devtools.build.skydoc.rendering.MarkdownRenderer;
+import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.AspectInfo;
import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.AttributeInfo;
import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.AttributeType;
import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.FunctionParamInfo;
@@ -39,9 +40,15 @@
private final String providerTemplatePath =
"com/google/devtools/build/skydoc/test_templates/provider.vm";
private final String funcTemplatePath = "com/google/devtools/build/skydoc/test_templates/func.vm";
+ private final String aspectTemplatePath =
+ "com/google/devtools/build/skydoc/test_templates/aspect.vm";
private final MarkdownRenderer renderer =
new MarkdownRenderer(
- headerTemplatePath, ruleTemplatePath, providerTemplatePath, funcTemplatePath);
+ headerTemplatePath,
+ ruleTemplatePath,
+ providerTemplatePath,
+ funcTemplatePath,
+ aspectTemplatePath);
@Test
public void testHeaderStrings() throws IOException {
@@ -150,4 +157,46 @@
+ " the first parameter\n"
+ " </p>\n");
}
+
+ @Test
+ public void testAspectStrings() throws IOException {
+ AttributeInfo attrInfo =
+ AttributeInfo.newBuilder()
+ .setName("first")
+ .setDocString("the first attribute")
+ .setTypeValue(AttributeType.STRING.getNumber())
+ .build();
+ AspectInfo aspectInfo =
+ AspectInfo.newBuilder()
+ .setAspectName("my_aspect")
+ .setDocString("This aspect does things.")
+ .addAttribute(attrInfo)
+ .addAspectAttribute("deps")
+ .build();
+
+ assertThat(renderer.render(aspectInfo.getAspectName(), aspectInfo))
+ .isEqualTo(
+ "<a name=\"#my_aspect\"></a>\n"
+ + "\n"
+ + "## my_aspect\n"
+ + "\n"
+ + "<pre>\n"
+ + "null(<a href=\"#null-first\">first</a>)\n"
+ + "</pre>\n"
+ + "\n"
+ + "This aspect does things.\n"
+ + "\n"
+ + "### Aspect Attributes\n"
+ + "\n"
+ + " <code>deps</code><\n"
+ + " String; required.\n"
+ + "\n"
+ + "### Attributes\n"
+ + "\n"
+ + " <code>first</code>\n"
+ + " String; optional\n"
+ + " <p>\n"
+ + " the first attribute\n"
+ + " </p>\n");
+ }
}
diff --git a/src/test/java/com/google/devtools/build/skydoc/test_templates/aspect.vm b/src/test/java/com/google/devtools/build/skydoc/test_templates/aspect.vm
new file mode 100644
index 0000000..747f073
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/skydoc/test_templates/aspect.vm
@@ -0,0 +1,32 @@
+<a name="#${aspectName}"></a>
+
+#[[##]]# ${aspectName}
+
+<pre>
+${util.aspectSummary(aspectName, $aspectInfo)}
+</pre>
+
+$aspectInfo.getDocString()
+
+#[[###]]# Aspect Attributes
+
+#if (!$aspectInfo.getAspectAttributeList().isEmpty())
+#foreach ($aspectAttribute in $aspectInfo.getAspectAttributeList())
+ <code>${aspectAttribute}</code><
+ String; required.
+#end
+#end
+
+#[[###]]# Attributes
+
+#if (!$aspectInfo.getAttributeList().isEmpty())
+#foreach ($attribute in $aspectInfo.getAttributeList())
+ <code>${attribute.name}</code>
+ ${util.attributeTypeString($attribute)}; ${util.mandatoryString($attribute)}
+#if (!$attribute.docString.isEmpty())
+ <p>
+ ${attribute.docString.trim()}
+ </p>
+#end
+#end
+#end
diff --git a/src/test/java/com/google/devtools/build/skydoc/testdata/aspect_test/golden.txt b/src/test/java/com/google/devtools/build/skydoc/testdata/aspect_test/golden.txt
new file mode 100644
index 0000000..a4d2093
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/skydoc/testdata/aspect_test/golden.txt
@@ -0,0 +1,148 @@
+<!-- Generated with Stardoc: http://skydoc.bazel.build -->
+
+<a name="#my_aspect_impl"></a>
+
+## my_aspect_impl
+
+<pre>
+my_aspect_impl(<a href="#my_aspect_impl-ctx">ctx</a>)
+</pre>
+
+
+
+### Parameters
+
+<table class="params-table">
+ <colgroup>
+ <col class="col-param" />
+ <col class="col-description" />
+ </colgroup>
+ <tbody>
+ <tr id="my_aspect_impl-ctx">
+ <td><code>ctx</code></td>
+ <td>
+ required.
+ </td>
+ </tr>
+ </tbody>
+</table>
+
+
+<a name="#my_aspect"></a>
+
+## my_aspect
+
+<pre>
+my_aspect(<a href="#my_aspect-name">name</a>, <a href="#my_aspect-first">first</a>, <a href="#my_aspect-second">second</a>)
+</pre>
+
+This is my aspect. It does stuff.
+
+### Aspect Attributes
+
+<table class="params-table">
+ <colgroup>
+ <col class="col-param" />
+ <col class="col-description" />
+ </colgroup>
+ <tbody>
+ <tr id="my_aspect-deps">
+ <td><code>deps</code></td>
+ <td>
+ String; required.
+ <tr id="my_aspect-attr_aspect">
+ <td><code>attr_aspect</code></td>
+ <td>
+ String; required.
+ </td>
+ </tr>
+ </tbody>
+</table>
+
+### Attributes
+
+<table class="params-table">
+ <colgroup>
+ <col class="col-param" />
+ <col class="col-description" />
+ </colgroup>
+ <tbody>
+ <tr id="my_aspect-name">
+ <td><code>name</code></td>
+ <td>
+ <a href="https://bazel.build/docs/build-ref.html#name">Name</a>; required
+ <p>
+ A unique name for this target.
+ </p>
+ </td>
+ </tr>
+ <tr id="my_aspect-first">
+ <td><code>first</code></td>
+ <td>
+ <a href="https://bazel.build/docs/build-ref.html#labels">Label</a>; required
+ </td>
+ </tr>
+ <tr id="my_aspect-second">
+ <td><code>second</code></td>
+ <td>
+ <a href="https://bazel.build/docs/skylark/lib/dict.html">Dictionary: String -> String</a>; required
+ </td>
+ </tr>
+ </tbody>
+</table>
+
+
+<a name="#other_aspect"></a>
+
+## other_aspect
+
+<pre>
+other_aspect(<a href="#other_aspect-name">name</a>, <a href="#other_aspect-third">third</a>)
+</pre>
+
+This is another aspect.
+
+### Aspect Attributes
+
+<table class="params-table">
+ <colgroup>
+ <col class="col-param" />
+ <col class="col-description" />
+ </colgroup>
+ <tbody>
+ <tr id="other_aspect-*">
+ <td><code>*</code></td>
+ <td>
+ String; required.
+ </td>
+ </tr>
+ </tbody>
+</table>
+
+### Attributes
+
+<table class="params-table">
+ <colgroup>
+ <col class="col-param" />
+ <col class="col-description" />
+ </colgroup>
+ <tbody>
+ <tr id="other_aspect-name">
+ <td><code>name</code></td>
+ <td>
+ <a href="https://bazel.build/docs/build-ref.html#name">Name</a>; required
+ <p>
+ A unique name for this target.
+ </p>
+ </td>
+ </tr>
+ <tr id="other_aspect-third">
+ <td><code>third</code></td>
+ <td>
+ Integer; required
+ </td>
+ </tr>
+ </tbody>
+</table>
+
+
diff --git a/src/test/java/com/google/devtools/build/skydoc/testdata/aspect_test/input.bzl b/src/test/java/com/google/devtools/build/skydoc/testdata/aspect_test/input.bzl
new file mode 100644
index 0000000..1ffedb4
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/skydoc/testdata/aspect_test/input.bzl
@@ -0,0 +1,24 @@
+"""The input file for the aspect test"""
+
+def my_aspect_impl(ctx):
+ return []
+
+my_aspect = aspect(
+ implementation = my_aspect_impl,
+ doc = "This is my aspect. It does stuff.",
+ attr_aspects = ["deps", "attr_aspect"],
+ attrs = {
+ "first": attr.label(mandatory = True, allow_single_file = True),
+ "second": attr.string_dict(mandatory = True),
+ },
+)
+
+other_aspect = aspect(
+ implementation = my_aspect_impl,
+ doc = "This is another aspect.",
+ attr_aspects = ["*"],
+ attrs = {
+ "_hidden": attr.string(),
+ "third": attr.int(mandatory = True),
+ },
+)