aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexandre Rostovtsev <arostovtsev@google.com>2023-07-24 21:43:33 -0400
committerGitHub <noreply@github.com>2023-07-24 21:43:33 -0400
commitf8fab820143a75810e6b7d9f764fc7552b82cbdb (patch)
tree422a8361e87994b417a520c55b17932b57da6e3e
parent6b6acb39c69ca713420454a2e78d90e327cad08f (diff)
downloadstardoc-f8fab820143a75810e6b7d9f764fc7552b82cbdb.tar.gz
Use native starlark_doc_extract rule for doc extraction if it is available
* When available (i.e. in Bazel 7, or in current development Bazel at HEAD), try use the `starlark_doc_extract` native rule for doc extraction instead of the legacy pre-built extraction jar. This behavior can be disabled by passing `use_starlark_doc_extract = False` to the `stardoc` macro. * Add templates and markdown rendering functionality for repository rule and module extension info protos (which are output by `starlark_doc_extract`). * Temporary wart: for module extensions, by default we would want the summary blurb to look something like ``` my_ext = use_extension("@my_local_repo//some:file.bzl", "my_ext") my_ext.tag(foo, bar) ``` but alas, we don't have a good way to get the name of the local repo from Starlark when bzlmod is enabled. * ... and of course, update tests. Which means in some cases, we have to fork the golden files into current (i.e. `starlark_doc_extract`-enabled) and legacy flavors. Fixes #69 Fixes #76 Fixes #81 Fixes #123
-rw-r--r--.bazelci/presubmit.yml9
-rw-r--r--MODULE.bazel2
-rw-r--r--docs/stardoc_rule.md8
-rw-r--r--setup.bzl6
-rw-r--r--src/main/java/com/google/devtools/build/skydoc/renderer/RendererMain.java98
-rw-r--r--src/main/java/com/google/devtools/build/skydoc/renderer/RendererOptions.java12
-rw-r--r--src/main/java/com/google/devtools/build/skydoc/rendering/MarkdownRenderer.java81
-rw-r--r--src/main/java/com/google/devtools/build/skydoc/rendering/MarkdownUtil.java49
-rw-r--r--src/test/java/com/google/devtools/build/skydoc/rendering/MarkdownUtilTest.java2
-rw-r--r--stardoc/BUILD1
-rw-r--r--stardoc/html_tables_stardoc.bzl2
-rw-r--r--stardoc/private/stardoc.bzl162
-rw-r--r--stardoc/stardoc.bzl120
-rw-r--r--stardoc/templates/html_tables/module_extension.vm56
-rw-r--r--stardoc/templates/html_tables/repository_rule.vm50
-rw-r--r--stardoc/templates/markdown_tables/module_extension.vm34
-rw-r--r--stardoc/templates/markdown_tables/repository_rule.vm31
-rw-r--r--test/BUILD18
-rw-r--r--test/stardoc_test.bzl84
-rw-r--r--[-rwxr-xr-x]test/testdata/android_basic_test/golden.md0
-rw-r--r--[-rwxr-xr-x]test/testdata/angle_bracket_test/golden.md2
-rw-r--r--test/testdata/angle_bracket_test/legacy_golden.md126
-rw-r--r--[-rwxr-xr-x]test/testdata/apple_basic_test/golden.md0
-rw-r--r--[-rwxr-xr-x]test/testdata/aspect_test/golden.md4
-rw-r--r--test/testdata/aspect_test/legacy_golden.md78
-rw-r--r--[-rwxr-xr-x]test/testdata/attribute_defaults_test/golden.md2
-rw-r--r--test/testdata/attribute_defaults_test/legacy_golden.md74
-rw-r--r--[-rwxr-xr-x]test/testdata/attribute_types_test/golden.md4
-rw-r--r--test/testdata/attribute_types_test/legacy_golden.md34
-rw-r--r--[-rwxr-xr-x]test/testdata/cc_api_test/golden.md0
-rw-r--r--[-rwxr-xr-x]test/testdata/config_apis_test/golden.md0
-rw-r--r--[-rwxr-xr-x]test/testdata/cpp_basic_test/golden.md0
-rw-r--r--[-rwxr-xr-x]test/testdata/filter_rules_test/golden.md0
-rw-r--r--[-rwxr-xr-x]test/testdata/function_basic_test/golden.md0
-rw-r--r--test/testdata/function_wrap_multiple_lines_test/golden.md2
-rw-r--r--test/testdata/function_wrap_multiple_lines_test/legacy_golden.md56
-rw-r--r--[-rwxr-xr-x]test/testdata/html_tables_template_test/golden.md0
-rw-r--r--[-rwxr-xr-x]test/testdata/input_template_test/golden.md0
-rw-r--r--[-rwxr-xr-x]test/testdata/java_basic_test/golden.md0
-rw-r--r--[-rwxr-xr-x]test/testdata/macro_kwargs_test/golden.md0
-rw-r--r--[-rwxr-xr-x]test/testdata/misc_apis_test/golden.md2
-rw-r--r--test/testdata/misc_apis_test/legacy_golden.md76
-rw-r--r--test/testdata/module_extension_test/golden.md45
-rw-r--r--test/testdata/module_extension_test/input.bzl38
-rw-r--r--[-rwxr-xr-x]test/testdata/multi_level_namespace_test/golden.md0
-rw-r--r--[-rwxr-xr-x]test/testdata/multiple_files_test/golden.md0
-rw-r--r--[-rwxr-xr-x]test/testdata/multiple_rules_test/golden.md0
-rw-r--r--[-rwxr-xr-x]test/testdata/namespace_test/golden.md0
-rw-r--r--test/testdata/proto_format_test/golden.binaryproto21
-rw-r--r--test/testdata/proto_format_test/legacy_golden.binaryproto (renamed from test/testdata/proto_format_test/golden.raw)bin531 -> 531 bytes
-rw-r--r--[-rwxr-xr-x]test/testdata/provider_basic_test/golden.md4
-rw-r--r--test/testdata/provider_basic_test/legacy_golden.md59
-rw-r--r--[-rwxr-xr-x]test/testdata/providers_for_attributes_test/golden.md0
-rw-r--r--test/testdata/pure_markdown_template_test/golden.md4
-rw-r--r--test/testdata/pure_markdown_template_test/input.bzl4
-rw-r--r--[-rwxr-xr-x]test/testdata/py_rule_test/golden.md0
-rw-r--r--[-rwxr-xr-x]test/testdata/repo_rules_test/golden.md8
-rw-r--r--test/testdata/repo_rules_test/input.bzl1
-rw-r--r--test/testdata/repo_rules_test/legacy_golden.md24
-rw-r--r--[-rwxr-xr-x]test/testdata/same_level_file_test/golden.md0
-rw-r--r--[-rwxr-xr-x]test/testdata/simple_test/golden.md0
-rw-r--r--[-rwxr-xr-x]test/testdata/struct_default_value_test/golden.md0
-rw-r--r--[-rwxr-xr-x]test/testdata/unknown_name_test/golden.md0
-rwxr-xr-xupdate-stardoc-docs.sh5
-rwxr-xr-xupdate-stardoc-tests.sh69
65 files changed, 1404 insertions, 163 deletions
diff --git a/.bazelci/presubmit.yml b/.bazelci/presubmit.yml
index 90b25c8..ee3e3ef 100644
--- a/.bazelci/presubmit.yml
+++ b/.bazelci/presubmit.yml
@@ -15,6 +15,15 @@ tasks:
test_targets:
- "//..."
+ build_and_test_last_green:
+ name: Build and test - last green
+ platform: ${{ platform }}
+ bazel: last_green
+ build_targets:
+ - "//..."
+ test_targets:
+ - "//..."
+
bzlmod:
name: Bzlmod example
platform: ${{ platform }}
diff --git a/MODULE.bazel b/MODULE.bazel
index 98866ac..38dc741 100644
--- a/MODULE.bazel
+++ b/MODULE.bazel
@@ -4,7 +4,7 @@ module(
compatibility_level = 1,
)
-bazel_dep(name = "bazel_skylib", version = "1.2.1")
+bazel_dep(name = "bazel_skylib", version = "1.4.2")
bazel_dep(name = "rules_java", version = "6.1.1")
bazel_dep(name = "rules_jvm_external", version = "4.5")
bazel_dep(name = "rules_license", version = "0.0.7")
diff --git a/docs/stardoc_rule.md b/docs/stardoc_rule.md
index 8e45059..3c5dc79 100644
--- a/docs/stardoc_rule.md
+++ b/docs/stardoc_rule.md
@@ -8,7 +8,8 @@ Starlark rule for stardoc: a documentation generator tool written in Java.
<pre>
stardoc(<a href="#stardoc-name">name</a>, <a href="#stardoc-input">input</a>, <a href="#stardoc-out">out</a>, <a href="#stardoc-deps">deps</a>, <a href="#stardoc-format">format</a>, <a href="#stardoc-symbol_names">symbol_names</a>, <a href="#stardoc-semantic_flags">semantic_flags</a>, <a href="#stardoc-stardoc">stardoc</a>, <a href="#stardoc-renderer">renderer</a>,
- <a href="#stardoc-aspect_template">aspect_template</a>, <a href="#stardoc-func_template">func_template</a>, <a href="#stardoc-header_template">header_template</a>, <a href="#stardoc-provider_template">provider_template</a>, <a href="#stardoc-rule_template">rule_template</a>, <a href="#stardoc-kwargs">kwargs</a>)
+ <a href="#stardoc-aspect_template">aspect_template</a>, <a href="#stardoc-func_template">func_template</a>, <a href="#stardoc-header_template">header_template</a>, <a href="#stardoc-provider_template">provider_template</a>, <a href="#stardoc-rule_template">rule_template</a>,
+ <a href="#stardoc-repository_rule_template">repository_rule_template</a>, <a href="#stardoc-module_extension_template">module_extension_template</a>, <a href="#stardoc-use_starlark_doc_extract">use_starlark_doc_extract</a>, <a href="#stardoc-kwargs">kwargs</a>)
</pre>
Generates documentation for exported starlark rule definitions in a target starlark file.
@@ -25,13 +26,16 @@ Generates documentation for exported starlark rule definitions in a target starl
| <a id="stardoc-format"></a>format | The format of the output file. Valid values: 'markdown' or 'proto'. | `"markdown"` |
| <a id="stardoc-symbol_names"></a>symbol_names | A list of symbol names to generate documentation for. These should correspond to the names of rule definitions in the input file. If this list is empty, then documentation for all exported rule definitions will be generated. | `[]` |
| <a id="stardoc-semantic_flags"></a>semantic_flags | A list of canonical flags to affect Starlark semantics for the Starlark interpreter during documentation generation. This should only be used to maintain compatibility with non-default semantic flags required to use the given Starlark symbols.<br><br>For example, if <code>//foo:bar.bzl</code> does not build except when a user would specify <code>--incompatible_foo_semantic=false</code>, then this attribute should contain "--incompatible_foo_semantic=false". | `[]` |
-| <a id="stardoc-stardoc"></a>stardoc | The location of the stardoc tool. | `Label("//stardoc:prebuilt_stardoc_binary")` |
+| <a id="stardoc-stardoc"></a>stardoc | The location of the legacy Stardoc extractor. Ignored when using the native <code>starlark_doc_extract</code> rule. | `Label("//stardoc:prebuilt_stardoc_binary")` |
| <a id="stardoc-renderer"></a>renderer | The location of the renderer tool. | `Label("//stardoc:renderer")` |
| <a id="stardoc-aspect_template"></a>aspect_template | The input file template for generating documentation of aspects | `Label("//stardoc:templates/markdown_tables/aspect.vm")` |
| <a id="stardoc-func_template"></a>func_template | The input file template for generating documentation of functions. | `Label("//stardoc:templates/markdown_tables/func.vm")` |
| <a id="stardoc-header_template"></a>header_template | The input file template for the header of the output documentation. | `Label("//stardoc:templates/markdown_tables/header.vm")` |
| <a id="stardoc-provider_template"></a>provider_template | The input file template for generating documentation of providers. | `Label("//stardoc:templates/markdown_tables/provider.vm")` |
| <a id="stardoc-rule_template"></a>rule_template | The input file template for generating documentation of rules. | `Label("//stardoc:templates/markdown_tables/rule.vm")` |
+| <a id="stardoc-repository_rule_template"></a>repository_rule_template | The input file template for generating documentation of repository rules. This template is used only when using the native <code>starlark_doc_extract</code> rule. | `Label("//stardoc:templates/markdown_tables/repository_rule.vm")` |
+| <a id="stardoc-module_extension_template"></a>module_extension_template | The input file template for generating documentation of module extensions. This template is used only when using the native <code>starlark_doc_extract</code> rule. | `Label("//stardoc:templates/markdown_tables/module_extension.vm")` |
+| <a id="stardoc-use_starlark_doc_extract"></a>use_starlark_doc_extract | Use the native <code>starlark_doc_extract</code> rule if available. | `True` |
| <a id="stardoc-kwargs"></a>kwargs | Further arguments to pass to stardoc. | none |
diff --git a/setup.bzl b/setup.bzl
index 9797de6..3009dae 100644
--- a/setup.bzl
+++ b/setup.bzl
@@ -26,10 +26,10 @@ def stardoc_repositories():
http_archive,
name = "bazel_skylib",
urls = [
- "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.2.1/bazel-skylib-1.2.1.tar.gz",
- "https://github.com/bazelbuild/bazel-skylib/releases/download/1.2.1/bazel-skylib-1.2.1.tar.gz",
+ "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.4.2/bazel-skylib-1.4.2.tar.gz",
+ "https://github.com/bazelbuild/bazel-skylib/releases/download/1.4.2/bazel-skylib-1.4.2.tar.gz",
],
- sha256 = "f7be3474d42aae265405a592bb7da8e171919d74c16f082a5457840f06054728",
+ sha256 = "66ffd9315665bfaafc96b52278f57c7e2dd09f5ede279ea6d39b2be471e7e3aa",
)
_include_if_not_defined(
http_archive,
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 29ceb92..3d7d642 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
@@ -24,8 +24,11 @@ import com.google.common.collect.ImmutableMap;
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.ModuleExtensionInfo;
+import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.ModuleExtensionTagClassInfo;
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.RepositoryRuleInfo;
import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.RuleInfo;
import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.StarlarkFunctionInfo;
import com.google.protobuf.ExtensionRegistry;
@@ -56,19 +59,7 @@ public final class RendererMain {
String inputPath = rendererOptions.inputPath;
String outputPath = rendererOptions.outputFilePath;
- String headerTemplatePath = rendererOptions.headerTemplateFilePath;
- String ruleTemplatePath = rendererOptions.ruleTemplateFilePath;
- String providerTemplatePath = rendererOptions.providerTemplateFilePath;
- String funcTemplatePath = rendererOptions.funcTemplateFilePath;
- String aspectTemplatePath = rendererOptions.aspectTemplateFilePath;
-
- MarkdownRenderer renderer =
- new MarkdownRenderer(
- headerTemplatePath,
- ruleTemplatePath,
- providerTemplatePath,
- funcTemplatePath,
- aspectTemplatePath);
+
try (PrintWriter printWriter =
new PrintWriter(outputPath, UTF_8) {
// Use consistent line endings on all platforms.
@@ -80,11 +71,25 @@ public final class RendererMain {
ModuleInfo moduleInfo =
ModuleInfo.parseFrom(
new FileInputStream(inputPath), ExtensionRegistry.getEmptyRegistry());
+
+ MarkdownRenderer renderer =
+ new MarkdownRenderer(
+ rendererOptions.headerTemplateFilePath,
+ rendererOptions.ruleTemplateFilePath,
+ rendererOptions.providerTemplateFilePath,
+ rendererOptions.funcTemplateFilePath,
+ rendererOptions.aspectTemplateFilePath,
+ rendererOptions.repositoryRuleTemplateFilePath,
+ rendererOptions.moduleExtensionTemplateFilePath,
+ moduleInfo.getFile() != null ? moduleInfo.getFile() : "...");
+
printWriter.println(renderer.renderMarkdownHeader(moduleInfo));
printRuleInfos(printWriter, renderer, moduleInfo.getRuleInfoList());
printProviderInfos(printWriter, renderer, moduleInfo.getProviderInfoList());
printStarlarkFunctions(printWriter, renderer, moduleInfo.getFuncInfoList());
printAspectInfos(printWriter, renderer, moduleInfo.getAspectInfoList());
+ printRepositoryRuleInfos(printWriter, renderer, moduleInfo.getRepositoryRuleInfoList());
+ printModuleExtensionInfos(printWriter, renderer, moduleInfo.getModuleExtensionInfoList());
} catch (InvalidProtocolBufferException e) {
throw new IllegalArgumentException("Input file is not a valid ModuleInfo proto.", e);
}
@@ -129,6 +134,39 @@ public final class RendererMain {
.build();
}
+ private static RepositoryRuleInfo withSortedRuleAttributes(
+ RepositoryRuleInfo repositoryRuleInfo) {
+ return repositoryRuleInfo.toBuilder()
+ .clearAttribute()
+ .addAllAttribute(
+ ImmutableList.sortedCopyOf(
+ comparing(AttributeInfo::getName, ATTRIBUTE_NAME_COMPARATOR),
+ repositoryRuleInfo.getAttributeList()))
+ .build();
+ }
+
+ private static ModuleExtensionTagClassInfo withSortedTagAttributes(
+ ModuleExtensionTagClassInfo moduleExtensionTagClassInfo) {
+ return moduleExtensionTagClassInfo.toBuilder()
+ .clearAttribute()
+ .addAllAttribute(
+ ImmutableList.sortedCopyOf(
+ comparing(AttributeInfo::getName, ATTRIBUTE_NAME_COMPARATOR),
+ moduleExtensionTagClassInfo.getAttributeList()))
+ .build();
+ }
+
+ private static ModuleExtensionInfo withSortedTagAttributes(
+ ModuleExtensionInfo moduleExtensionInfo) {
+ return moduleExtensionInfo.toBuilder()
+ .clearTagClass()
+ .addAllTagClass(
+ moduleExtensionInfo.getTagClassList().stream()
+ .map(RendererMain::withSortedTagAttributes)
+ .collect(toImmutableList()))
+ .build();
+ }
+
private static void printRuleInfos(
PrintWriter printWriter, MarkdownRenderer renderer, List<RuleInfo> ruleInfos)
throws IOException {
@@ -184,5 +222,39 @@ public final class RendererMain {
}
}
+ private static void printRepositoryRuleInfos(
+ PrintWriter printWriter, MarkdownRenderer renderer, List<RepositoryRuleInfo> ruleInfos)
+ throws IOException {
+ // Repository rules are printed sorted by their qualified name, and their attributes are sorted
+ // by name, with ATTRIBUTE_ORDERING specifying a fixed sort order for some standard attributes.
+ ImmutableList<RepositoryRuleInfo> sortedRepositoryRuleInfos =
+ ruleInfos.stream()
+ .map(RendererMain::withSortedRuleAttributes)
+ .sorted(comparing(RepositoryRuleInfo::getRuleName))
+ .collect(toImmutableList());
+ for (RepositoryRuleInfo repositoryRuleProto : sortedRepositoryRuleInfos) {
+ printWriter.println(renderer.render(repositoryRuleProto.getRuleName(), repositoryRuleProto));
+ printWriter.println();
+ }
+ }
+
+ private static void printModuleExtensionInfos(
+ PrintWriter printWriter, MarkdownRenderer renderer, List<ModuleExtensionInfo> ruleInfos)
+ throws IOException {
+ // Module extension are printed sorted by their qualified name, and their tag classes'
+ // attributes are sorted by name, with ATTRIBUTE_ORDERING specifying a fixed sort order for some
+ // standard attributes.
+ ImmutableList<ModuleExtensionInfo> sortedModuleExtensionInfos =
+ ruleInfos.stream()
+ .map(RendererMain::withSortedTagAttributes)
+ .sorted(comparing(ModuleExtensionInfo::getExtensionName))
+ .collect(toImmutableList());
+ for (ModuleExtensionInfo moduleExtensionProto : sortedModuleExtensionInfos) {
+ printWriter.println(
+ renderer.render(moduleExtensionProto.getExtensionName(), moduleExtensionProto));
+ printWriter.println();
+ }
+ }
+
private RendererMain() {}
}
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 5abef40..52ed165 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
@@ -64,6 +64,18 @@ class RendererOptions {
String aspectTemplateFilePath;
@Parameter(
+ names = "--repository_rule_template",
+ required = true,
+ description = "The template for the documentation of a repository rule")
+ String repositoryRuleTemplateFilePath;
+
+ @Parameter(
+ names = "--module_extension_template",
+ required = true,
+ description = "The template for the documentation of a module extension")
+ String moduleExtensionTemplateFilePath;
+
+ @Parameter(
names = {"--help", "-h"},
description = "Print help and exit",
help = true)
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 7f6abeb..2ff4a4e 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
@@ -18,8 +18,10 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.collect.ImmutableMap;
import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.AspectInfo;
+import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.ModuleExtensionInfo;
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.RepositoryRuleInfo;
import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.RuleInfo;
import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.StarlarkFunctionInfo;
import com.google.escapevelocity.EvaluationException;
@@ -42,18 +44,27 @@ public class MarkdownRenderer {
private final String providerTemplateFilename;
private final String functionTemplateFilename;
private final String aspectTemplateFilename;
+ private final String repositoryRuleTemplateFilename;
+ private final String moduleExtensionTemplateFilename;
+ private final String extensionBzlFile;
public MarkdownRenderer(
String headerTemplate,
String ruleTemplate,
String providerTemplate,
String functionTemplate,
- String aspectTemplate) {
+ String aspectTemplate,
+ String repositoryRuleTemplate,
+ String moduleExtensionTemplate,
+ String extensionBzlFile) {
this.headerTemplateFilename = headerTemplate;
this.ruleTemplateFilename = ruleTemplate;
this.providerTemplateFilename = providerTemplate;
this.functionTemplateFilename = functionTemplate;
this.aspectTemplateFilename = aspectTemplate;
+ this.repositoryRuleTemplateFilename = repositoryRuleTemplate;
+ this.moduleExtensionTemplateFilename = moduleExtensionTemplate;
+ this.extensionBzlFile = extensionBzlFile;
}
/**
@@ -63,7 +74,10 @@ public class MarkdownRenderer {
public String renderMarkdownHeader(ModuleInfo moduleInfo) throws IOException {
ImmutableMap<String, Object> vars =
ImmutableMap.of(
- "util", new MarkdownUtil(), "moduleDocstring", moduleInfo.getModuleDocstring());
+ "util",
+ new MarkdownUtil(extensionBzlFile),
+ "moduleDocstring",
+ moduleInfo.getModuleDocstring());
Reader reader = readerFromPath(headerTemplateFilename);
try {
return Template.parseFrom(reader).evaluate(vars);
@@ -78,7 +92,8 @@ public class MarkdownRenderer {
*/
public String render(String ruleName, RuleInfo ruleInfo) throws IOException {
ImmutableMap<String, Object> vars =
- ImmutableMap.of("util", new MarkdownUtil(), "ruleName", ruleName, "ruleInfo", ruleInfo);
+ ImmutableMap.of(
+ "util", new MarkdownUtil(extensionBzlFile), "ruleName", ruleName, "ruleInfo", ruleInfo);
Reader reader = readerFromPath(ruleTemplateFilename);
try {
return Template.parseFrom(reader).evaluate(vars);
@@ -94,7 +109,12 @@ public class MarkdownRenderer {
public String render(String providerName, ProviderInfo providerInfo) throws IOException {
ImmutableMap<String, Object> vars =
ImmutableMap.of(
- "util", new MarkdownUtil(), "providerName", providerName, "providerInfo", providerInfo);
+ "util",
+ new MarkdownUtil(extensionBzlFile),
+ "providerName",
+ providerName,
+ "providerInfo",
+ providerInfo);
Reader reader = readerFromPath(providerTemplateFilename);
try {
return Template.parseFrom(reader).evaluate(vars);
@@ -109,7 +129,7 @@ public class MarkdownRenderer {
*/
public String render(StarlarkFunctionInfo functionInfo) throws IOException {
ImmutableMap<String, Object> vars =
- ImmutableMap.of("util", new MarkdownUtil(), "funcInfo", functionInfo);
+ ImmutableMap.of("util", new MarkdownUtil(extensionBzlFile), "funcInfo", functionInfo);
Reader reader = readerFromPath(functionTemplateFilename);
try {
return Template.parseFrom(reader).evaluate(vars);
@@ -125,7 +145,12 @@ public class MarkdownRenderer {
public String render(String aspectName, AspectInfo aspectInfo) throws IOException {
ImmutableMap<String, Object> vars =
ImmutableMap.of(
- "util", new MarkdownUtil(), "aspectName", aspectName, "aspectInfo", aspectInfo);
+ "util",
+ new MarkdownUtil(extensionBzlFile),
+ "aspectName",
+ aspectName,
+ "aspectInfo",
+ aspectInfo);
Reader reader = readerFromPath(aspectTemplateFilename);
try {
return Template.parseFrom(reader).evaluate(vars);
@@ -135,6 +160,50 @@ public class MarkdownRenderer {
}
/**
+ * Returns a markdown rendering of repository rule documentation for the given repository rule
+ * information object with the given name.
+ */
+ public String render(String repositoryRuleName, RepositoryRuleInfo repositoryRuleInfo)
+ throws IOException {
+ ImmutableMap<String, Object> vars =
+ ImmutableMap.of(
+ "util",
+ new MarkdownUtil(extensionBzlFile),
+ "ruleName",
+ repositoryRuleName,
+ "ruleInfo",
+ repositoryRuleInfo);
+ Reader reader = readerFromPath(repositoryRuleTemplateFilename);
+ try {
+ return Template.parseFrom(reader).evaluate(vars);
+ } catch (ParseException | EvaluationException e) {
+ throw new IOException(e);
+ }
+ }
+
+ /**
+ * Returns a markdown rendering of module extension documentation for the given module extension
+ * information object with the given name.
+ */
+ public String render(String moduleExtensionName, ModuleExtensionInfo moduleExtensionInfo)
+ throws IOException {
+ ImmutableMap<String, Object> vars =
+ ImmutableMap.of(
+ "util",
+ new MarkdownUtil(extensionBzlFile),
+ "extensionName",
+ moduleExtensionName,
+ "extensionInfo",
+ moduleExtensionInfo);
+ Reader reader = readerFromPath(moduleExtensionTemplateFilename);
+ try {
+ return Template.parseFrom(reader).evaluate(vars);
+ } catch (ParseException | EvaluationException e) {
+ throw new IOException(e);
+ }
+ }
+
+ /**
* 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 a7dbee2..00580c2 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
@@ -27,6 +27,9 @@ import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.Func
import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.ProviderInfo;
import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.ProviderNameGroup;
import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.RuleInfo;
+import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.RepositoryRuleInfo;
+import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.ModuleExtensionInfo;
+import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.ModuleExtensionTagClassInfo;
import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.StarlarkFunctionInfo;
import java.util.ArrayList;
import java.util.List;
@@ -34,8 +37,14 @@ import java.util.regex.Pattern;
/** Contains a number of utility methods for markdown rendering. */
public final class MarkdownUtil {
+ private final String extensionBzlFile;
+
private static final int MAX_LINE_LENGTH = 100;
+ public MarkdownUtil(String extensionBzlFile) {
+ this.extensionBzlFile = extensionBzlFile;
+ }
+
/**
* Return a string that formats the input string so it is displayable in a markdown table cell.
* This performs the following operations:
@@ -155,6 +164,46 @@ public final class MarkdownUtil {
}
/**
+ * Return a string representing the repository rule summary for the given repository rule with the given name.
+ *
+ * <p>For example: 'my_repo_rule(foo, bar)'. The summary will contain hyperlinks for each attribute.
+ */
+ @SuppressWarnings("unused") // Used by markdown template.
+ public String repositoryRuleSummary(String ruleName, RepositoryRuleInfo ruleInfo) {
+ ImmutableList<String> attributeNames =
+ ruleInfo.getAttributeList().stream()
+ .map(AttributeInfo::getName)
+ .collect(toImmutableList());
+ return summary(ruleName, attributeNames);
+ }
+
+ /**
+ * Return a string representing the module extension summary for the given module extension with the given name.
+ *
+ * <p>For example:
+ * <pre>
+ * my_ext = use_extension("//some:file.bzl", "my_ext")
+ * my_ext.tag1(foo, bar)
+ * my_ext.tag2(baz)
+ * </pre>
+ *
+ * <p>The summary will contain hyperlinks for each attribute.
+ */
+ @SuppressWarnings("unused") // Used by markdown template.
+ public String moduleExtensionSummary(String extensionName, ModuleExtensionInfo extensionInfo) {
+ StringBuilder summaryBuilder = new StringBuilder();
+ summaryBuilder.append(String.format("%s = use_extension(\"%s\", \"%s\")", extensionName, extensionBzlFile, extensionName));
+ for (ModuleExtensionTagClassInfo tagClass : extensionInfo.getTagClassList()) {
+ ImmutableList<String> attributeNames =
+ tagClass.getAttributeList().stream()
+ .map(AttributeInfo::getName)
+ .collect(toImmutableList());
+ summaryBuilder.append("\n").append(summary(String.format("%s.%s", extensionName, tagClass.getTagName()), attributeNames));
+ }
+ return summaryBuilder.toString();
+ }
+
+ /**
* Return a string representing the summary for the given user-defined function.
*
* <p>For example: 'my_func(foo, bar)'. The summary will contain hyperlinks for each parameter.
diff --git a/src/test/java/com/google/devtools/build/skydoc/rendering/MarkdownUtilTest.java b/src/test/java/com/google/devtools/build/skydoc/rendering/MarkdownUtilTest.java
index cf84964..14d3c2a 100644
--- a/src/test/java/com/google/devtools/build/skydoc/rendering/MarkdownUtilTest.java
+++ b/src/test/java/com/google/devtools/build/skydoc/rendering/MarkdownUtilTest.java
@@ -24,7 +24,7 @@ import org.junit.runners.JUnit4;
@RunWith(JUnit4.class)
public class MarkdownUtilTest {
- MarkdownUtil util = new MarkdownUtil();
+ MarkdownUtil util = new MarkdownUtil("//:test.bzl");
@Test
public void markdownCodeSpan() {
diff --git a/stardoc/BUILD b/stardoc/BUILD
index da02ce0..159ca0e 100644
--- a/stardoc/BUILD
+++ b/stardoc/BUILD
@@ -26,6 +26,7 @@ bzl_library(
visibility = ["//visibility:public"],
deps = [
"//stardoc/private:stardoc_lib",
+ "@bazel_skylib//rules:copy_file",
"@rules_java//java:rules",
],
)
diff --git a/stardoc/html_tables_stardoc.bzl b/stardoc/html_tables_stardoc.bzl
index 0fef7f8..e2487a5 100644
--- a/stardoc/html_tables_stardoc.bzl
+++ b/stardoc/html_tables_stardoc.bzl
@@ -33,5 +33,7 @@ def html_tables_stardoc(name, **kwargs):
header_template = Label("//stardoc:templates/html_tables/header.vm"),
provider_template = Label("//stardoc:templates/html_tables/provider.vm"),
rule_template = Label("//stardoc:templates/html_tables/rule.vm"),
+ repository_rule_template = Label("//stardoc:templates/html_tables/repository_rule.vm"),
+ module_extension_template = Label("//stardoc:templates/html_tables/module_extension.vm"),
**kwargs
)
diff --git a/stardoc/private/stardoc.bzl b/stardoc/private/stardoc.bzl
index 204af38..9eeb270 100644
--- a/stardoc/private/stardoc.bzl
+++ b/stardoc/private/stardoc.bzl
@@ -14,6 +14,38 @@
"""Starlark rule for stardoc: a documentation generator tool written in Java."""
+def _renderer_action_run(ctx, out_file, proto_file):
+ """Helper for declaring the markdown renderer action"""
+ renderer_args = ctx.actions.args()
+ renderer_args.add("--input=" + str(proto_file.path))
+ renderer_args.add("--output=" + str(ctx.outputs.out.path))
+ renderer_args.add("--aspect_template=" + str(ctx.file.aspect_template.path))
+ renderer_args.add("--header_template=" + str(ctx.file.header_template.path))
+ renderer_args.add("--func_template=" + str(ctx.file.func_template.path))
+ renderer_args.add("--provider_template=" + str(ctx.file.provider_template.path))
+ renderer_args.add("--rule_template=" + str(ctx.file.rule_template.path))
+ renderer_args.add("--repository_rule_template=" + str(ctx.file.repository_rule_template.path))
+ renderer_args.add("--module_extension_template=" + str(ctx.file.module_extension_template.path))
+ renderer = ctx.executable.renderer
+ ctx.actions.run(
+ outputs = [out_file],
+ inputs = [
+ proto_file,
+ ctx.file.aspect_template,
+ ctx.file.header_template,
+ ctx.file.func_template,
+ ctx.file.provider_template,
+ ctx.file.rule_template,
+ ctx.file.repository_rule_template,
+ ctx.file.module_extension_template,
+ ],
+ executable = renderer,
+ arguments = [renderer_args],
+ mnemonic = "Renderer",
+ progress_message = ("Converting proto format of %s to markdown format" %
+ (ctx.label.name)),
+ )
+
def _stardoc_impl(ctx):
"""Implementation of the stardoc rule."""
for semantic_flag in ctx.attr.semantic_flags:
@@ -38,7 +70,7 @@ def _stardoc_impl(ctx):
executable = stardoc,
arguments = [stardoc_args],
mnemonic = "Stardoc",
- progress_message = ("Generating Starlark doc for %s" %
+ progress_message = ("Generating proto for Starlark doc for %s" %
(ctx.label.name)),
)
elif ctx.attr.format == "markdown":
@@ -52,24 +84,7 @@ def _stardoc_impl(ctx):
progress_message = ("Generating proto for Starlark doc for %s" %
(ctx.label.name)),
)
- renderer_args = ctx.actions.args()
- renderer_args.add("--input=" + str(proto_file.path))
- renderer_args.add("--output=" + str(ctx.outputs.out.path))
- renderer_args.add("--aspect_template=" + str(ctx.file.aspect_template.path))
- renderer_args.add("--header_template=" + str(ctx.file.header_template.path))
- renderer_args.add("--func_template=" + str(ctx.file.func_template.path))
- renderer_args.add("--provider_template=" + str(ctx.file.provider_template.path))
- renderer_args.add("--rule_template=" + str(ctx.file.rule_template.path))
- renderer = ctx.executable.renderer
- ctx.actions.run(
- outputs = [out_file],
- inputs = [proto_file, ctx.file.aspect_template, ctx.file.header_template, ctx.file.func_template, ctx.file.provider_template, ctx.file.rule_template],
- executable = renderer,
- arguments = [renderer_args],
- mnemonic = "Renderer",
- progress_message = ("Converting proto format of %s to markdown format" %
- (ctx.label.name)),
- )
+ _renderer_action_run(ctx, out_file = out_file, proto_file = proto_file)
# Work around default outputs not getting captured by sh_binary:
# https://github.com/bazelbuild/bazel/issues/15043.
@@ -77,6 +92,57 @@ def _stardoc_impl(ctx):
outputs = [out_file]
return [DefaultInfo(files = depset(outputs), runfiles = ctx.runfiles(files = outputs))]
+_common_renderer_attrs = {
+ "out": attr.output(
+ doc = "The (markdown) file to which documentation will be output.",
+ mandatory = True,
+ ),
+ "renderer": attr.label(
+ doc = "The location of the renderer tool.",
+ allow_files = True,
+ cfg = "exec",
+ executable = True,
+ mandatory = True,
+ ),
+ "aspect_template": attr.label(
+ doc = "The input file template for generating documentation of aspects.",
+ allow_single_file = [".vm"],
+ mandatory = True,
+ ),
+ "header_template": attr.label(
+ doc = "The input file template for the header of the output documentation.",
+ allow_single_file = [".vm"],
+ mandatory = True,
+ ),
+ "func_template": attr.label(
+ doc = "The input file template for generating documentation of functions.",
+ allow_single_file = [".vm"],
+ mandatory = True,
+ ),
+ "provider_template": attr.label(
+ doc = "The input file template for generating documentation of providers.",
+ allow_single_file = [".vm"],
+ mandatory = True,
+ ),
+ "rule_template": attr.label(
+ doc = "The input file template for generating documentation of rules.",
+ allow_single_file = [".vm"],
+ mandatory = True,
+ ),
+ "repository_rule_template": attr.label(
+ doc = "The input file template for generating documentation of repository rules." +
+ " This template is not used when rendering the output of the legacy extractor.",
+ allow_single_file = [".vm"],
+ mandatory = True,
+ ),
+ "module_extension_template": attr.label(
+ doc = "The input file template for generating documentation of module extensions." +
+ " This template is not used when rendering the output of the legacy extractor.",
+ allow_single_file = [".vm"],
+ mandatory = True,
+ ),
+}
+
stardoc = rule(
_stardoc_impl,
doc = """
@@ -88,10 +154,6 @@ Generates documentation for starlark skylark rule definitions in a target starla
allow_single_file = [".bzl"],
mandatory = True,
),
- "out": attr.output(
- doc = "The (markdown) file to which documentation will be output.",
- mandatory = True,
- ),
"format": attr.string(
doc = "The format of the output file. Valid values: 'markdown' or 'proto'.",
values = ["markdown", "proto"],
@@ -124,37 +186,29 @@ For example, if `//foo:bar.bzl` does not build except when a user would specify
executable = True,
mandatory = True,
),
- "renderer": attr.label(
- doc = "The location of the renderer tool.",
- allow_files = True,
- cfg = "exec",
- executable = True,
- mandatory = True,
- ),
- "aspect_template": attr.label(
- doc = "The input file template for generating documentation of aspects.",
- allow_single_file = [".vm"],
- mandatory = True,
- ),
- "header_template": attr.label(
- doc = "The input file template for the header of the output documentation.",
- allow_single_file = [".vm"],
- mandatory = True,
- ),
- "func_template": attr.label(
- doc = "The input file template for generating documentation of functions.",
- allow_single_file = [".vm"],
- mandatory = True,
- ),
- "provider_template": attr.label(
- doc = "The input file template for generating documentation of providers.",
- allow_single_file = [".vm"],
- mandatory = True,
- ),
- "rule_template": attr.label(
- doc = "The input file template for generating documentation of rules.",
- allow_single_file = [".vm"],
+ } | _common_renderer_attrs,
+)
+
+def _stardoc_markdown_renderer_impl(ctx):
+ out_file = ctx.outputs.out
+ _renderer_action_run(ctx, out_file = out_file, proto_file = ctx.file.src)
+
+ # Work around default outputs not getting captured by sh_binary:
+ # https://github.com/bazelbuild/bazel/issues/15043.
+ # See discussion in https://github.com/bazelbuild/stardoc/pull/139.
+ outputs = [out_file]
+ return [DefaultInfo(files = depset(outputs), runfiles = ctx.runfiles(files = outputs))]
+
+stardoc_markdown_renderer = rule(
+ _stardoc_markdown_renderer_impl,
+ doc = """
+Generates markdown documentation for starlark rule definitions from the corresponding binary proto.
+""",
+ attrs = {
+ "src": attr.label(
+ doc = "The .binaryproto file from which to generate documentation.",
+ allow_single_file = [".binaryproto"],
mandatory = True,
),
- },
+ } | _common_renderer_attrs,
)
diff --git a/stardoc/stardoc.bzl b/stardoc/stardoc.bzl
index 1728dfc..09e51b1 100644
--- a/stardoc/stardoc.bzl
+++ b/stardoc/stardoc.bzl
@@ -15,7 +15,8 @@
"""Starlark rule for stardoc: a documentation generator tool written in Java."""
load("@rules_java//java:defs.bzl", "java_binary")
-load("//stardoc/private:stardoc.bzl", _stardoc = "stardoc")
+load("@bazel_skylib//rules:copy_file.bzl", "copy_file")
+load("//stardoc/private:stardoc.bzl", "stardoc_markdown_renderer", _stardoc = "stardoc")
def stardoc(
*,
@@ -33,6 +34,9 @@ def stardoc(
header_template = Label("//stardoc:templates/markdown_tables/header.vm"),
provider_template = Label("//stardoc:templates/markdown_tables/provider.vm"),
rule_template = Label("//stardoc:templates/markdown_tables/rule.vm"),
+ repository_rule_template = Label("//stardoc:templates/markdown_tables/repository_rule.vm"),
+ module_extension_template = Label("//stardoc:templates/markdown_tables/module_extension.vm"),
+ use_starlark_doc_extract = True,
**kwargs):
"""Generates documentation for exported starlark rule definitions in a target starlark file.
@@ -52,42 +56,100 @@ def stardoc(
For example, if `//foo:bar.bzl` does not build except when a user would specify
`--incompatible_foo_semantic=false`, then this attribute should contain
"--incompatible_foo_semantic=false".
- stardoc: The location of the stardoc tool.
+ stardoc: The location of the legacy Stardoc extractor. Ignored when using the native `starlark_doc_extract` rule.
renderer: The location of the renderer tool.
aspect_template: The input file template for generating documentation of aspects
header_template: The input file template for the header of the output documentation.
func_template: The input file template for generating documentation of functions.
provider_template: The input file template for generating documentation of providers.
rule_template: The input file template for generating documentation of rules.
+ repository_rule_template: The input file template for generating documentation of repository rules.
+ This template is used only when using the native `starlark_doc_extract` rule.
+ module_extension_template: The input file template for generating documentation of module extensions.
+ This template is used only when using the native `starlark_doc_extract` rule.
+ use_starlark_doc_extract: Use the native `starlark_doc_extract` rule if available.
**kwargs: Further arguments to pass to stardoc.
"""
- stardoc_with_runfiles_name = name + "_stardoc"
+ if format not in ["markdown", "proto"]:
+ fail("`format` must be \"markdown\" or \"proto\"")
- testonly = {"testonly": kwargs["testonly"]} if "testonly" in kwargs else {}
- java_binary(
- name = stardoc_with_runfiles_name,
- main_class = "com.google.devtools.build.skydoc.SkydocMain",
- runtime_deps = [stardoc],
- data = [input] + deps,
- tags = ["manual"],
- visibility = ["//visibility:private"],
- **testonly
- )
+ auxiliary_target_kwargs = {
+ "tags": ["manual"],
+ "visibility": ["//visibility:private"],
+ }
+ if "testonly" in kwargs:
+ auxiliary_target_kwargs["testonly"] = kwargs["testonly"]
- _stardoc(
- name = name,
- input = input,
- out = out,
- format = format,
- symbol_names = symbol_names,
- semantic_flags = semantic_flags,
- stardoc = stardoc_with_runfiles_name,
- renderer = renderer,
- aspect_template = aspect_template,
- func_template = func_template,
- header_template = header_template,
- provider_template = provider_template,
- rule_template = rule_template,
- **kwargs
- )
+ if use_starlark_doc_extract and hasattr(native, "starlark_doc_extract"):
+ # Use native.starlark_doc_extract as extractor
+ if format == "proto" and Label(name + ".binaryproto") == Label(out):
+ extractor_is_main_target = True
+ extractor_name = name
+ else:
+ extractor_is_main_target = False
+ extractor_name = name + ".extract"
+
+ proto_name = extractor_name + ".binaryproto"
+
+ native.starlark_doc_extract(
+ name = extractor_name,
+ src = input,
+ deps = deps,
+ symbol_names = symbol_names,
+ **(kwargs if extractor_is_main_target else auxiliary_target_kwargs)
+ )
+
+ if format == "markdown":
+ stardoc_markdown_renderer(
+ name = name,
+ src = proto_name,
+ out = out,
+ renderer = renderer,
+ aspect_template = aspect_template,
+ func_template = func_template,
+ header_template = header_template,
+ provider_template = provider_template,
+ rule_template = rule_template,
+ repository_rule_template = repository_rule_template,
+ module_extension_template = module_extension_template,
+ **kwargs
+ )
+ elif format == "proto" and not extractor_is_main_target:
+ copy_file(
+ name = name,
+ src = proto_name,
+ out = out,
+ **kwargs
+ )
+
+ else:
+ # Use legacy extractor
+ stardoc_with_runfiles_name = name + "_stardoc"
+
+ java_binary(
+ name = stardoc_with_runfiles_name,
+ main_class = "com.google.devtools.build.skydoc.SkydocMain",
+ runtime_deps = [stardoc],
+ data = [input] + deps,
+ **auxiliary_target_kwargs
+ )
+
+ _stardoc(
+ name = name,
+ input = input,
+ out = out,
+ format = format,
+ symbol_names = symbol_names,
+ semantic_flags = semantic_flags,
+ stardoc = stardoc_with_runfiles_name,
+ renderer = renderer,
+ aspect_template = aspect_template,
+ func_template = func_template,
+ header_template = header_template,
+ provider_template = provider_template,
+ rule_template = rule_template,
+ repository_rule_template = repository_rule_template,
+ module_extension_template = module_extension_template,
+ **kwargs
+ )
diff --git a/stardoc/templates/html_tables/module_extension.vm b/stardoc/templates/html_tables/module_extension.vm
new file mode 100644
index 0000000..1cbc32e
--- /dev/null
+++ b/stardoc/templates/html_tables/module_extension.vm
@@ -0,0 +1,56 @@
+<a id="${extensionName}"></a>
+
+#[[##]]# ${extensionName}
+
+<pre>
+${util.moduleExtensionSummary($extensionName, $extensionInfo)}
+</pre>
+#if (!$extensionInfo.docString.isEmpty())
+
+${extensionInfo.docString}
+#end
+
+#if (!$extensionInfo.getTagClassList().isEmpty())
+
+**TAG CLASSES**
+#foreach ($tagClass in $extensionInfo.getTagClassList())
+
+<a id="${extensionName}.${tagClass.tagName}"></a>
+
+#[[###]]# ${tagClass.tagName}
+#if (!$tagClass.docString.isEmpty())
+
+${tagClass.docString}
+#end
+
+**Attributes**
+
+#if (!$tagClass.getAttributeList().isEmpty())
+<table class="params-table">
+<colgroup>
+<col class="col-param" />
+<col class="col-description" />
+</colgroup>
+<tbody>
+#foreach ($attribute in $tagClass.getAttributeList())
+<tr id="${extensionName}.${tagClass.tagName}-${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
+#end
+#end
diff --git a/stardoc/templates/html_tables/repository_rule.vm b/stardoc/templates/html_tables/repository_rule.vm
new file mode 100644
index 0000000..6594130
--- /dev/null
+++ b/stardoc/templates/html_tables/repository_rule.vm
@@ -0,0 +1,50 @@
+<a id="${ruleName}"></a>
+
+#[[##]]# ${ruleName}
+
+<pre>
+${util.repositoryRuleSummary($ruleName, $ruleInfo)}
+</pre>
+#if (!$ruleInfo.docString.isEmpty())
+
+${ruleInfo.docString}
+#end
+
+**ATTRIBUTES**
+
+#if (!$ruleInfo.getAttributeList().isEmpty())
+<table class="params-table">
+<colgroup>
+<col class="col-param" />
+<col class="col-description" />
+</colgroup>
+<tbody>
+#foreach ($attribute in $ruleInfo.getAttributeList())
+<tr id="${ruleName}-${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
+#if (!$ruleInfo.getEnvironList().isEmpty())
+
+**ENVIRONMENT VARIABLES**
+
+This repository rule depends on the following environment variables:
+#foreach ($var in $ruleInfo.getEnvironList())
+* ${util.markdownCodeSpan($var)}
+#end
+#end
diff --git a/stardoc/templates/markdown_tables/module_extension.vm b/stardoc/templates/markdown_tables/module_extension.vm
new file mode 100644
index 0000000..0ef7367
--- /dev/null
+++ b/stardoc/templates/markdown_tables/module_extension.vm
@@ -0,0 +1,34 @@
+<a id="${extensionName}"></a>
+
+#[[##]]# ${extensionName}
+
+<pre>
+${util.moduleExtensionSummary($extensionName, $extensionInfo)}
+</pre>
+#if (!$extensionInfo.docString.isEmpty())
+
+${extensionInfo.docString}
+#end
+
+#if (!$extensionInfo.getTagClassList().isEmpty())
+
+**TAG CLASSES**
+#foreach ($tagClass in $extensionInfo.getTagClassList())
+
+<a id="${extensionName}.${tagClass.tagName}"></a>
+
+#[[###]]# ${tagClass.tagName}
+#if (!$tagClass.docString.isEmpty())
+
+${tagClass.docString}
+#end
+
+**Attributes**
+
+| Name | Description | Type | Mandatory | Default |
+| :------------- | :------------- | :------------- | :------------- | :------------- |
+#foreach ($attribute in $tagClass.getAttributeList())
+| <a id="${extensionName}.${tagClass.tagName}-${attribute.name}"></a>$attribute.name | #if(!$attribute.docString.isEmpty()) ${util.markdownCellFormat($attribute.docString)} #else - #end | ${util.attributeTypeString($attribute)} | ${util.mandatoryString($attribute)} | #if(!$attribute.defaultValue.isEmpty()) ${util.markdownCodeSpan($attribute.defaultValue)} #end |
+#end
+#end
+#end
diff --git a/stardoc/templates/markdown_tables/repository_rule.vm b/stardoc/templates/markdown_tables/repository_rule.vm
new file mode 100644
index 0000000..ed4a2a9
--- /dev/null
+++ b/stardoc/templates/markdown_tables/repository_rule.vm
@@ -0,0 +1,31 @@
+<a id="${ruleName}"></a>
+
+#[[##]]# ${ruleName}
+
+<pre>
+${util.repositoryRuleSummary($ruleName, $ruleInfo)}
+</pre>
+#if (!$ruleInfo.docString.isEmpty())
+
+${ruleInfo.docString}
+#end
+
+**ATTRIBUTES**
+
+#if (!$ruleInfo.getAttributeList().isEmpty())
+
+| Name | Description | Type | Mandatory | Default |
+| :------------- | :------------- | :------------- | :------------- | :------------- |
+#foreach ($attribute in $ruleInfo.getAttributeList())
+| <a id="${ruleName}-${attribute.name}"></a>$attribute.name | #if(!$attribute.docString.isEmpty()) ${util.markdownCellFormat($attribute.docString)} #else - #end | ${util.attributeTypeString($attribute)} | ${util.mandatoryString($attribute)} | #if(!$attribute.defaultValue.isEmpty()) ${util.markdownCodeSpan($attribute.defaultValue)} #end |
+#end
+#end
+#if (!$ruleInfo.getEnvironList().isEmpty())
+
+**ENVIRONMENT VARIABLES**
+
+This repository rule depends on the following environment variables:
+#foreach ($var in $ruleInfo.getEnvironList())
+* ${util.markdownCodeSpan($var)}
+#end
+#end
diff --git a/test/BUILD b/test/BUILD
index 6dfe44e..141868f 100644
--- a/test/BUILD
+++ b/test/BUILD
@@ -34,13 +34,15 @@ stardoc_test(
name = "angle_bracket_test",
golden_file = "testdata/angle_bracket_test/golden.md",
input_file = "testdata/angle_bracket_test/input.bzl",
+ legacy_golden_file = "testdata/angle_bracket_test/legacy_golden.md",
)
stardoc_test(
name = "proto_format_test",
format = "proto",
- golden_file = "testdata/proto_format_test/golden.raw",
+ golden_file = "testdata/proto_format_test/golden.binaryproto",
input_file = "testdata/proto_format_test/input.bzl",
+ legacy_golden_file = "testdata/proto_format_test/legacy_golden.binaryproto",
)
stardoc_test(
@@ -60,6 +62,14 @@ stardoc_test(
name = "repo_rules_test",
golden_file = "testdata/repo_rules_test/golden.md",
input_file = "testdata/repo_rules_test/input.bzl",
+ legacy_golden_file = "testdata/repo_rules_test/legacy_golden.md",
+)
+
+stardoc_test(
+ name = "module_extension_test",
+ golden_file = "testdata/module_extension_test/golden.md",
+ input_file = "testdata/module_extension_test/input.bzl",
+ test_legacy_extractor = False,
)
stardoc_test(
@@ -129,12 +139,14 @@ stardoc_test(
name = "misc_apis_test",
golden_file = "testdata/misc_apis_test/golden.md",
input_file = "testdata/misc_apis_test/input.bzl",
+ legacy_golden_file = "testdata/misc_apis_test/legacy_golden.md",
)
stardoc_test(
name = "attribute_types_test",
golden_file = "testdata/attribute_types_test/golden.md",
input_file = "testdata/attribute_types_test/input.bzl",
+ legacy_golden_file = "testdata/attribute_types_test/legacy_golden.md",
symbol_names = ["my_rule"],
)
@@ -155,6 +167,7 @@ stardoc_test(
name = "provider_basic_test",
golden_file = "testdata/provider_basic_test/golden.md",
input_file = "testdata/provider_basic_test/input.bzl",
+ legacy_golden_file = "testdata/provider_basic_test/legacy_golden.md",
)
stardoc_test(
@@ -167,6 +180,7 @@ stardoc_test(
name = "function_wrap_multiple_lines_test",
golden_file = "testdata/function_wrap_multiple_lines_test/golden.md",
input_file = "testdata/function_wrap_multiple_lines_test/input.bzl",
+ legacy_golden_file = "testdata/function_wrap_multiple_lines_test/legacy_golden.md",
)
stardoc_test(
@@ -229,6 +243,7 @@ stardoc_test(
name = "aspect_test",
golden_file = "testdata/aspect_test/golden.md",
input_file = "testdata/aspect_test/input.bzl",
+ legacy_golden_file = "testdata/aspect_test/legacy_golden.md",
)
stardoc_test(
@@ -251,6 +266,7 @@ stardoc_test(
name = "attribute_defaults_test",
golden_file = "testdata/attribute_defaults_test/golden.md",
input_file = "testdata/attribute_defaults_test/input.bzl",
+ legacy_golden_file = "testdata/attribute_defaults_test/legacy_golden.md",
)
stardoc_test(
diff --git a/test/stardoc_test.bzl b/test/stardoc_test.bzl
index fc44ba8..f4fc9a9 100644
--- a/test/stardoc_test.bzl
+++ b/test/stardoc_test.bzl
@@ -21,21 +21,27 @@ def stardoc_test(
name,
input_file,
golden_file,
+ legacy_golden_file = None,
+ test_legacy_extractor = True,
deps = [],
test = "default",
**kwargs):
"""Convenience macro for stardoc e2e test suites.
- Each invocation creates four targets:
+ Each invocation creates multiple targets:
1. A sh_test target which verifies that stardoc-built-from-source, when run on an input file,
- creates output matching the contents of a golden file, named "{name}_e2e_test".
- 2. A `stardoc` target which will generate a new golden file given an input file
- with stardoc-built-from-source. This target should be used to regenerate
- the golden file when updating stardoc, named "regenerate_{name}_golden".
+ creates output matching the contents of a golden file, named "{name}_e2e_legacy_test".
+ 2. A `stardoc` target which will generate a new golden file given an input file with the
+ legacy extractor built from Bazel source. This target should be used to regenerate the
+ legacy golden file when updating stardoc, named "regenerate_{name}_legacy_golden".
3 & 4. Targets identical to (1) and (2) except they use the prebuilt-stardoc jar, and
- are named "{name}_e2e_jar_test" and "regenerate_with_jar_{name}_golden".
- 5. A bzl_library target for convenient wrapping of input bzl files, named "{name}_lib".
+ are named "{name}_e2e_jar_legacy_test" and "regenerate_with_jar_{name}_legacy_golden".
+ 5 & 6. Only if the host Bazel supports the `native.starlark_doc_extract` rule: Targets
+ identical to (1) and (2) except they use starlark_doc_extract, and are named "{name}_e2e_test"
+ and "regenerate_{name}_golden". The latter target should be used to regenerate the golden
+ file when updating Stardoc.
+ 7. A bzl_library target for convenient wrapping of input bzl files, named "{name}_lib".
Args:
name: A unique name to qualify the created targets.
@@ -43,36 +49,60 @@ def stardoc_test(
in this test.
golden_file: The label string of the golden file containing the documentation when stardoc
is run on the input file.
+ legacy_golden_file: The label string of the golden file when using the legacy documentation
+ extractor. If `legacy_golden_file` is not set, `golden_file` will be used for both extractors.
+ test_legacy_extractor: Whether to create legacy extractor test targets.
deps: A list of label strings of starlark file dependencies of the input_file.
test: The type of test (default or html_tables).
**kwargs: A dictionary of input template names mapped to template file path for which documentation is generated.
"""
+ if legacy_golden_file == None:
+ legacy_golden_file = golden_file
+
bzl_library(
name = "%s_lib" % name,
srcs = [input_file],
deps = deps,
)
- _create_test_targets(
- test_name = "%s_e2e_test" % name,
- genrule_name = "regenerate_%s_golden" % name,
- lib_name = "%s_lib" % name,
- input_file = input_file,
- golden_file = golden_file,
- stardoc_bin = "@io_bazel//src/main/java/com/google/devtools/build/skydoc:skydoc_deploy.jar",
- test = test,
- **kwargs
- )
- _create_test_targets(
- test_name = "%s_e2e_jar_test" % name,
- genrule_name = "regenerate_with_jar_%s_golden" % name,
- lib_name = "%s_lib" % name,
- input_file = input_file,
- golden_file = golden_file,
- stardoc_bin = "@io_bazel//src/main/java/com/google/devtools/build/skydoc:skydoc_deploy.jar",
- test = test,
- **kwargs
- )
+
+ if test_legacy_extractor:
+ _create_test_targets(
+ test_name = "%s_e2e_legacy_test" % name,
+ genrule_name = "regenerate_%s_legacy_golden" % name,
+ lib_name = "%s_lib" % name,
+ input_file = input_file,
+ golden_file = legacy_golden_file,
+ stardoc_bin = "@io_bazel//src/main/java/com/google/devtools/build/skydoc:skydoc_deploy.jar",
+ test = test,
+ use_starlark_doc_extract = False,
+ **kwargs
+ )
+
+ _create_test_targets(
+ test_name = "%s_e2e_jar_legacy_test" % name,
+ genrule_name = "regenerate_with_jar_%s_legacy_golden" % name,
+ lib_name = "%s_lib" % name,
+ input_file = input_file,
+ golden_file = legacy_golden_file,
+ stardoc_bin = "@io_bazel//src/main/java/com/google/devtools/build/skydoc:skydoc_deploy.jar",
+ test = test,
+ use_starlark_doc_extract = False,
+ **kwargs
+ )
+
+ if hasattr(native, "starlark_doc_extract"):
+ _create_test_targets(
+ test_name = "%s_e2e_test" % name,
+ genrule_name = "regenerate_%s_golden" % name,
+ lib_name = "%s_lib" % name,
+ input_file = input_file,
+ golden_file = golden_file,
+ stardoc_bin = None,
+ test = test,
+ use_starlark_doc_extract = True,
+ **kwargs
+ )
def _create_test_targets(
test_name,
diff --git a/test/testdata/android_basic_test/golden.md b/test/testdata/android_basic_test/golden.md
index b22186d..b22186d 100755..100644
--- a/test/testdata/android_basic_test/golden.md
+++ b/test/testdata/android_basic_test/golden.md
diff --git a/test/testdata/angle_bracket_test/golden.md b/test/testdata/angle_bracket_test/golden.md
index a9ccc14..d487cfd 100755..100644
--- a/test/testdata/angle_bracket_test/golden.md
+++ b/test/testdata/angle_bracket_test/golden.md
@@ -30,7 +30,7 @@ Rule with \<brackets>
| :------------- | :------------- | :------------- | :------------- | :------------- |
| <a id="my_anglebrac-name"></a>name | A unique name for this target. | <a href="https://bazel.build/concepts/labels#target-names">Name</a> | required | |
| <a id="my_anglebrac-also_useless"></a>also_useless | Args with some formatted tags: <code>&lt;tag&gt;</code> | String | optional | `"1<<5"` |
-| <a id="my_anglebrac-useless"></a>useless | Args with some tags: &lt;tag1&gt;, &lt;tag2&gt; | String | optional | `"Find \<brackets>"` |
+| <a id="my_anglebrac-useless"></a>useless | Args with some tags: &lt;tag1&gt;, &lt;tag2&gt; | String | optional | `"Find \\<brackets>"` |
<a id="bracketuse"></a>
diff --git a/test/testdata/angle_bracket_test/legacy_golden.md b/test/testdata/angle_bracket_test/legacy_golden.md
new file mode 100644
index 0000000..a9ccc14
--- /dev/null
+++ b/test/testdata/angle_bracket_test/legacy_golden.md
@@ -0,0 +1,126 @@
+<!-- Generated with Stardoc: http://skydoc.bazel.build -->
+
+Input file to test angle bracket bug (https://github.com/bazelbuild/skydoc/issues/186)
+
+See https://github.com/bazelbuild/skydoc/issues/186,
+https://github.com/bazelbuild/stardoc/issues/132,
+and https://github.com/bazelbuild/stardoc/issues/137.
+
+<b>HTML formatting</b> can be used in docstrings, just as in regular Markdown.
+Literal angle brackets can be obtained by escaping them with a backslash, where
+the backslash itself must be escaped for use in a Starlark docstring
+(`\\<` becomes \<), or by using HTML entities (`&lt;` becomes &lt;).
+Angle brackets are also preserved in inline code blocks (`#include <vector>`).
+
+
+<a id="my_anglebrac"></a>
+
+## my_anglebrac
+
+<pre>
+my_anglebrac(<a href="#my_anglebrac-name">name</a>, <a href="#my_anglebrac-also_useless">also_useless</a>, <a href="#my_anglebrac-useless">useless</a>)
+</pre>
+
+Rule with \<brackets>
+
+**ATTRIBUTES**
+
+
+| Name | Description | Type | Mandatory | Default |
+| :------------- | :------------- | :------------- | :------------- | :------------- |
+| <a id="my_anglebrac-name"></a>name | A unique name for this target. | <a href="https://bazel.build/concepts/labels#target-names">Name</a> | required | |
+| <a id="my_anglebrac-also_useless"></a>also_useless | Args with some formatted tags: <code>&lt;tag&gt;</code> | String | optional | `"1<<5"` |
+| <a id="my_anglebrac-useless"></a>useless | Args with some tags: &lt;tag1&gt;, &lt;tag2&gt; | String | optional | `"Find \<brackets>"` |
+
+
+<a id="bracketuse"></a>
+
+## bracketuse
+
+<pre>
+bracketuse(<a href="#bracketuse-foo">foo</a>, <a href="#bracketuse-bar">bar</a>, <a href="#bracketuse-baz">baz</a>)
+</pre>
+
+Information with \<brackets>
+
+**FIELDS**
+
+
+| Name | Description |
+| :------------- | :------------- |
+| <a id="bracketuse-foo"></a>foo | A string representing &lt;foo&gt; |
+| <a id="bracketuse-bar"></a>bar | A string representing bar |
+| <a id="bracketuse-baz"></a>baz | A string representing baz |
+
+
+<a id="bracket_function"></a>
+
+## bracket_function
+
+<pre>
+bracket_function(<a href="#bracket_function-param">param</a>, <a href="#bracket_function-md_string">md_string</a>)
+</pre>
+
+Dummy docstring with \<brackets>.
+
+This rule runs checks on `<angle brackets>`.
+
+Sometimes, we have such things on their own, but they may
+also appear in code blocks, like
+
+```starlark
+foo = "<thing>"
+```
+
+
+**PARAMETERS**
+
+
+| Name | Description | Default Value |
+| :------------- | :------------- | :------------- |
+| <a id="bracket_function-param"></a>param | an arg with **formatted** docstring, <code>&lt;default&gt;</code> by default. | `"<default>"` |
+| <a id="bracket_function-md_string"></a>md_string | A markdown string. | ``"foo `1<<10` bar"`` |
+
+**RETURNS**
+
+some \<angled> brackets
+
+**DEPRECATED**
+
+deprecated for \<reasons> as well as `<reasons>`.
+
+
+<a id="bracket_aspect"></a>
+
+## bracket_aspect
+
+<pre>
+bracket_aspect(<a href="#bracket_aspect-name">name</a>, <a href="#bracket_aspect-brackets">brackets</a>)
+</pre>
+
+Aspect.
+
+Sometimes, we want a code block like
+```starlark
+foo = "<brackets>"
+```
+which includes angle brackets.
+
+
+**ASPECT ATTRIBUTES**
+
+
+| Name | Type |
+| :------------- | :------------- |
+| deps| String |
+
+
+**ATTRIBUTES**
+
+
+| Name | Description | Type | Mandatory | Default |
+| :------------- | :------------- | :------------- | :------------- | :------------- |
+| <a id="bracket_aspect-name"></a>name | A unique name for this target. | <a href="https://bazel.build/concepts/labels#target-names">Name</a> | required | |
+| <a id="bracket_aspect-brackets"></a>brackets | Attribute with \&lt;brackets&gt; | String | optional | `"<default>"` |
+
+
diff --git a/test/testdata/apple_basic_test/golden.md b/test/testdata/apple_basic_test/golden.md
index b9e7a5b..b9e7a5b 100755..100644
--- a/test/testdata/apple_basic_test/golden.md
+++ b/test/testdata/apple_basic_test/golden.md
diff --git a/test/testdata/aspect_test/golden.md b/test/testdata/aspect_test/golden.md
index b61e57a..8be6341 100755..100644
--- a/test/testdata/aspect_test/golden.md
+++ b/test/testdata/aspect_test/golden.md
@@ -62,10 +62,6 @@ This is another aspect.
**ASPECT ATTRIBUTES**
-| Name | Type |
-| :------------- | :------------- |
-| *| String |
-
**ATTRIBUTES**
diff --git a/test/testdata/aspect_test/legacy_golden.md b/test/testdata/aspect_test/legacy_golden.md
new file mode 100644
index 0000000..b61e57a
--- /dev/null
+++ b/test/testdata/aspect_test/legacy_golden.md
@@ -0,0 +1,78 @@
+<!-- Generated with Stardoc: http://skydoc.bazel.build -->
+
+The input file for the aspect test
+
+<a id="my_aspect_impl"></a>
+
+## my_aspect_impl
+
+<pre>
+my_aspect_impl(<a href="#my_aspect_impl-ctx">ctx</a>)
+</pre>
+
+
+
+**PARAMETERS**
+
+
+| Name | Description | Default Value |
+| :------------- | :------------- | :------------- |
+| <a id="my_aspect_impl-ctx"></a>ctx | <p align="center"> - </p> | none |
+
+
+<a id="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**
+
+
+| Name | Type |
+| :------------- | :------------- |
+| deps| String |
+| attr_aspect| String |
+
+
+**ATTRIBUTES**
+
+
+| Name | Description | Type | Mandatory | Default |
+| :------------- | :------------- | :------------- | :------------- | :------------- |
+| <a id="my_aspect-name"></a>name | A unique name for this target. | <a href="https://bazel.build/concepts/labels#target-names">Name</a> | required | |
+| <a id="my_aspect-first"></a>first | - | Boolean | required | |
+| <a id="my_aspect-second"></a>second | - | String | required | |
+
+
+<a id="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**
+
+
+| Name | Type |
+| :------------- | :------------- |
+| *| String |
+
+
+**ATTRIBUTES**
+
+
+| Name | Description | Type | Mandatory | Default |
+| :------------- | :------------- | :------------- | :------------- | :------------- |
+| <a id="other_aspect-name"></a>name | A unique name for this target. | <a href="https://bazel.build/concepts/labels#target-names">Name</a> | required | |
+| <a id="other_aspect-third"></a>third | - | Integer | required | |
+
+
diff --git a/test/testdata/attribute_defaults_test/golden.md b/test/testdata/attribute_defaults_test/golden.md
index 4eaf4b9..221d32d 100755..100644
--- a/test/testdata/attribute_defaults_test/golden.md
+++ b/test/testdata/attribute_defaults_test/golden.md
@@ -21,7 +21,7 @@ This is my rule. It does stuff.
| <a id="my_rule-a"></a>a | Some bool | Boolean | optional | `False` |
| <a id="my_rule-b"></a>b | Some int | Integer | optional | `2` |
| <a id="my_rule-c"></a>c | Some int_list | List of integers | optional | `[0, 1]` |
-| <a id="my_rule-d"></a>d | Some label | <a href="https://bazel.build/concepts/labels">Label</a> | optional | `//foo:bar` |
+| <a id="my_rule-d"></a>d | Some label | <a href="https://bazel.build/concepts/labels">Label</a> | optional | `"//foo:bar"` |
| <a id="my_rule-e"></a>e | Some label_keyed_string_dict | <a href="https://bazel.build/rules/lib/dict">Dictionary: Label -> String</a> | optional | `{"//foo:bar": "hello", "//bar:baz": "goodbye"}` |
| <a id="my_rule-f"></a>f | Some label_list | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | `["//foo:bar", "//bar:baz"]` |
| <a id="my_rule-g"></a>g | Some string | String | optional | `""` |
diff --git a/test/testdata/attribute_defaults_test/legacy_golden.md b/test/testdata/attribute_defaults_test/legacy_golden.md
new file mode 100644
index 0000000..4eaf4b9
--- /dev/null
+++ b/test/testdata/attribute_defaults_test/legacy_golden.md
@@ -0,0 +1,74 @@
+<!-- Generated with Stardoc: http://skydoc.bazel.build -->
+
+A golden test to verify attribute default values.
+
+<a id="my_rule"></a>
+
+## my_rule
+
+<pre>
+my_rule(<a href="#my_rule-name">name</a>, <a href="#my_rule-a">a</a>, <a href="#my_rule-b">b</a>, <a href="#my_rule-c">c</a>, <a href="#my_rule-d">d</a>, <a href="#my_rule-e">e</a>, <a href="#my_rule-f">f</a>, <a href="#my_rule-g">g</a>, <a href="#my_rule-h">h</a>, <a href="#my_rule-i">i</a>, <a href="#my_rule-j">j</a>, <a href="#my_rule-k">k</a>, <a href="#my_rule-l">l</a>, <a href="#my_rule-m">m</a>, <a href="#my_rule-n">n</a>, <a href="#my_rule-o">o</a>, <a href="#my_rule-p">p</a>, <a href="#my_rule-q">q</a>, <a href="#my_rule-r">r</a>, <a href="#my_rule-s">s</a>, <a href="#my_rule-t">t</a>, <a href="#my_rule-u">u</a>, <a href="#my_rule-v">v</a>, <a href="#my_rule-w">w</a>)
+</pre>
+
+This is my rule. It does stuff.
+
+**ATTRIBUTES**
+
+
+| Name | Description | Type | Mandatory | Default |
+| :------------- | :------------- | :------------- | :------------- | :------------- |
+| <a id="my_rule-name"></a>name | A unique name for this target. | <a href="https://bazel.build/concepts/labels#target-names">Name</a> | required | |
+| <a id="my_rule-a"></a>a | Some bool | Boolean | optional | `False` |
+| <a id="my_rule-b"></a>b | Some int | Integer | optional | `2` |
+| <a id="my_rule-c"></a>c | Some int_list | List of integers | optional | `[0, 1]` |
+| <a id="my_rule-d"></a>d | Some label | <a href="https://bazel.build/concepts/labels">Label</a> | optional | `//foo:bar` |
+| <a id="my_rule-e"></a>e | Some label_keyed_string_dict | <a href="https://bazel.build/rules/lib/dict">Dictionary: Label -> String</a> | optional | `{"//foo:bar": "hello", "//bar:baz": "goodbye"}` |
+| <a id="my_rule-f"></a>f | Some label_list | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | `["//foo:bar", "//bar:baz"]` |
+| <a id="my_rule-g"></a>g | Some string | String | optional | `""` |
+| <a id="my_rule-h"></a>h | Some string_dict | <a href="https://bazel.build/rules/lib/dict">Dictionary: String -> String</a> | optional | `{"animal": "bunny", "color": "orange"}` |
+| <a id="my_rule-i"></a>i | Some string_list | List of strings | optional | `["cat", "dog"]` |
+| <a id="my_rule-j"></a>j | Some string_list_dict | <a href="https://bazel.build/rules/lib/dict">Dictionary: String -> List of strings</a> | optional | `{"animal": ["cat", "bunny"], "color": ["blue", "orange"]}` |
+| <a id="my_rule-k"></a>k | Some bool | Boolean | required | |
+| <a id="my_rule-l"></a>l | Some int | Integer | required | |
+| <a id="my_rule-m"></a>m | Some int_list | List of integers | required | |
+| <a id="my_rule-n"></a>n | Some label | <a href="https://bazel.build/concepts/labels">Label</a> | required | |
+| <a id="my_rule-o"></a>o | Some label_keyed_string_dict | <a href="https://bazel.build/rules/lib/dict">Dictionary: Label -> String</a> | required | |
+| <a id="my_rule-p"></a>p | Some label_list | <a href="https://bazel.build/concepts/labels">List of labels</a> | required | |
+| <a id="my_rule-q"></a>q | Some string | String | required | |
+| <a id="my_rule-r"></a>r | Some string_dict | <a href="https://bazel.build/rules/lib/dict">Dictionary: String -> String</a> | required | |
+| <a id="my_rule-s"></a>s | Some string_list | List of strings | required | |
+| <a id="my_rule-t"></a>t | Some string_list_dict | <a href="https://bazel.build/rules/lib/dict">Dictionary: String -> List of strings</a> | required | |
+| <a id="my_rule-u"></a>u | - | String | optional | `""` |
+| <a id="my_rule-v"></a>v | - | <a href="https://bazel.build/concepts/labels">Label</a> | optional | `None` |
+| <a id="my_rule-w"></a>w | - | Integer | optional | `0` |
+
+
+<a id="my_aspect"></a>
+
+## my_aspect
+
+<pre>
+my_aspect(<a href="#my_aspect-name">name</a>, <a href="#my_aspect-y">y</a>, <a href="#my_aspect-z">z</a>)
+</pre>
+
+This is my aspect. It does stuff.
+
+**ASPECT ATTRIBUTES**
+
+
+| Name | Type |
+| :------------- | :------------- |
+| deps| String |
+| attr_aspect| String |
+
+
+**ATTRIBUTES**
+
+
+| Name | Description | Type | Mandatory | Default |
+| :------------- | :------------- | :------------- | :------------- | :------------- |
+| <a id="my_aspect-name"></a>name | A unique name for this target. | <a href="https://bazel.build/concepts/labels#target-names">Name</a> | required | |
+| <a id="my_aspect-y"></a>y | some string | String | optional | `"why"` |
+| <a id="my_aspect-z"></a>z | - | String | required | |
+
+
diff --git a/test/testdata/attribute_types_test/golden.md b/test/testdata/attribute_types_test/golden.md
index 4df4916..994f154 100755..100644
--- a/test/testdata/attribute_types_test/golden.md
+++ b/test/testdata/attribute_types_test/golden.md
@@ -24,8 +24,8 @@ This is my rule. It does stuff.
| <a id="my_rule-d"></a>d | Some label | <a href="https://bazel.build/concepts/labels">Label</a> | required | |
| <a id="my_rule-e"></a>e | Some label_keyed_string_dict | <a href="https://bazel.build/rules/lib/dict">Dictionary: Label -> String</a> | required | |
| <a id="my_rule-f"></a>f | Some label_list | <a href="https://bazel.build/concepts/labels">List of labels</a> | required | |
-| <a id="my_rule-g"></a>g | Some output | <a href="https://bazel.build/concepts/labels">Label</a> | optional | |
-| <a id="my_rule-h"></a>h | Some output_list | List of labels | optional | |
+| <a id="my_rule-g"></a>g | Some output | <a href="https://bazel.build/concepts/labels">Label</a> | optional | `None` |
+| <a id="my_rule-h"></a>h | Some output_list | List of labels | optional | `[]` |
| <a id="my_rule-i"></a>i | Some string | String | required | |
| <a id="my_rule-j"></a>j | Some string_dict | <a href="https://bazel.build/rules/lib/dict">Dictionary: String -> String</a> | required | |
| <a id="my_rule-k"></a>k | Some string_list | List of strings | required | |
diff --git a/test/testdata/attribute_types_test/legacy_golden.md b/test/testdata/attribute_types_test/legacy_golden.md
new file mode 100644
index 0000000..4df4916
--- /dev/null
+++ b/test/testdata/attribute_types_test/legacy_golden.md
@@ -0,0 +1,34 @@
+<!-- Generated with Stardoc: http://skydoc.bazel.build -->
+
+
+
+<a id="my_rule"></a>
+
+## my_rule
+
+<pre>
+my_rule(<a href="#my_rule-name">name</a>, <a href="#my_rule-a">a</a>, <a href="#my_rule-b">b</a>, <a href="#my_rule-c">c</a>, <a href="#my_rule-d">d</a>, <a href="#my_rule-e">e</a>, <a href="#my_rule-f">f</a>, <a href="#my_rule-g">g</a>, <a href="#my_rule-h">h</a>, <a href="#my_rule-i">i</a>, <a href="#my_rule-j">j</a>, <a href="#my_rule-k">k</a>, <a href="#my_rule-l">l</a>)
+</pre>
+
+This is my rule. It does stuff.
+
+**ATTRIBUTES**
+
+
+| Name | Description | Type | Mandatory | Default |
+| :------------- | :------------- | :------------- | :------------- | :------------- |
+| <a id="my_rule-name"></a>name | A unique name for this target. | <a href="https://bazel.build/concepts/labels#target-names">Name</a> | required | |
+| <a id="my_rule-a"></a>a | Some bool | Boolean | required | |
+| <a id="my_rule-b"></a>b | Some int | Integer | required | |
+| <a id="my_rule-c"></a>c | Some int_list | List of integers | required | |
+| <a id="my_rule-d"></a>d | Some label | <a href="https://bazel.build/concepts/labels">Label</a> | required | |
+| <a id="my_rule-e"></a>e | Some label_keyed_string_dict | <a href="https://bazel.build/rules/lib/dict">Dictionary: Label -> String</a> | required | |
+| <a id="my_rule-f"></a>f | Some label_list | <a href="https://bazel.build/concepts/labels">List of labels</a> | required | |
+| <a id="my_rule-g"></a>g | Some output | <a href="https://bazel.build/concepts/labels">Label</a> | optional | |
+| <a id="my_rule-h"></a>h | Some output_list | List of labels | optional | |
+| <a id="my_rule-i"></a>i | Some string | String | required | |
+| <a id="my_rule-j"></a>j | Some string_dict | <a href="https://bazel.build/rules/lib/dict">Dictionary: String -> String</a> | required | |
+| <a id="my_rule-k"></a>k | Some string_list | List of strings | required | |
+| <a id="my_rule-l"></a>l | Some string_list_dict | <a href="https://bazel.build/rules/lib/dict">Dictionary: String -> List of strings</a> | optional | `{}` |
+
+
diff --git a/test/testdata/cc_api_test/golden.md b/test/testdata/cc_api_test/golden.md
index 183402e..183402e 100755..100644
--- a/test/testdata/cc_api_test/golden.md
+++ b/test/testdata/cc_api_test/golden.md
diff --git a/test/testdata/config_apis_test/golden.md b/test/testdata/config_apis_test/golden.md
index e1d5fdf..e1d5fdf 100755..100644
--- a/test/testdata/config_apis_test/golden.md
+++ b/test/testdata/config_apis_test/golden.md
diff --git a/test/testdata/cpp_basic_test/golden.md b/test/testdata/cpp_basic_test/golden.md
index abce00a..abce00a 100755..100644
--- a/test/testdata/cpp_basic_test/golden.md
+++ b/test/testdata/cpp_basic_test/golden.md
diff --git a/test/testdata/filter_rules_test/golden.md b/test/testdata/filter_rules_test/golden.md
index 2801a16..2801a16 100755..100644
--- a/test/testdata/filter_rules_test/golden.md
+++ b/test/testdata/filter_rules_test/golden.md
diff --git a/test/testdata/function_basic_test/golden.md b/test/testdata/function_basic_test/golden.md
index d3db17d..d3db17d 100755..100644
--- a/test/testdata/function_basic_test/golden.md
+++ b/test/testdata/function_basic_test/golden.md
diff --git a/test/testdata/function_wrap_multiple_lines_test/golden.md b/test/testdata/function_wrap_multiple_lines_test/golden.md
index 817f02b..81f7f9e 100644
--- a/test/testdata/function_wrap_multiple_lines_test/golden.md
+++ b/test/testdata/function_wrap_multiple_lines_test/golden.md
@@ -21,7 +21,7 @@ Runs [ANTLR 3](https://www.antlr3.org//) on a set of grammars.
| Name | Description | Type | Mandatory | Default |
| :------------- | :------------- | :------------- | :------------- | :------------- |
| <a id="antlr-name"></a>name | A unique name for this target. | <a href="https://bazel.build/concepts/labels#target-names">Name</a> | required | |
-| <a id="antlr-deps"></a>deps | The dependencies to use. Defaults to the most recent ANTLR 3 release, but if you need to use a different version, you can specify the dependencies here. | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | `[Label("@antlr3_runtimes//:tool")]` |
+| <a id="antlr-deps"></a>deps | The dependencies to use. Defaults to the most recent ANTLR 3 release, but if you need to use a different version, you can specify the dependencies here. | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | `["@antlr3_runtimes//:tool"]` |
| <a id="antlr-srcs"></a>srcs | The grammar files to process. | <a href="https://bazel.build/concepts/labels">List of labels</a> | required | |
| <a id="antlr-Xconversiontimeout"></a>Xconversiontimeout | Set NFA conversion timeout for each decision. | Integer | optional | `0` |
| <a id="antlr-Xdbgconversion"></a>Xdbgconversion | Dump lots of info during NFA conversion. | Boolean | optional | `False` |
diff --git a/test/testdata/function_wrap_multiple_lines_test/legacy_golden.md b/test/testdata/function_wrap_multiple_lines_test/legacy_golden.md
new file mode 100644
index 0000000..817f02b
--- /dev/null
+++ b/test/testdata/function_wrap_multiple_lines_test/legacy_golden.md
@@ -0,0 +1,56 @@
+<!-- Generated with Stardoc: http://skydoc.bazel.build -->
+
+Rules for ANTLR 3.
+
+<a id="antlr"></a>
+
+## antlr
+
+<pre>
+antlr(<a href="#antlr-name">name</a>, <a href="#antlr-deps">deps</a>, <a href="#antlr-srcs">srcs</a>, <a href="#antlr-Xconversiontimeout">Xconversiontimeout</a>, <a href="#antlr-Xdbgconversion">Xdbgconversion</a>, <a href="#antlr-Xdbgst">Xdbgst</a>, <a href="#antlr-Xdfa">Xdfa</a>, <a href="#antlr-Xdfaverbose">Xdfaverbose</a>, <a href="#antlr-Xgrtree">Xgrtree</a>, <a href="#antlr-Xm">Xm</a>,
+ <a href="#antlr-Xmaxdfaedges">Xmaxdfaedges</a>, <a href="#antlr-Xmaxinlinedfastates">Xmaxinlinedfastates</a>, <a href="#antlr-Xminswitchalts">Xminswitchalts</a>, <a href="#antlr-Xmultithreaded">Xmultithreaded</a>, <a href="#antlr-Xnfastates">Xnfastates</a>, <a href="#antlr-Xnocollapse">Xnocollapse</a>,
+ <a href="#antlr-Xnomergestopstates">Xnomergestopstates</a>, <a href="#antlr-Xnoprune">Xnoprune</a>, <a href="#antlr-XsaveLexer">XsaveLexer</a>, <a href="#antlr-Xwatchconversion">Xwatchconversion</a>, <a href="#antlr-debug">debug</a>, <a href="#antlr-depend">depend</a>, <a href="#antlr-dfa">dfa</a>, <a href="#antlr-dump">dump</a>, <a href="#antlr-imports">imports</a>,
+ <a href="#antlr-language">language</a>, <a href="#antlr-message_format">message_format</a>, <a href="#antlr-nfa">nfa</a>, <a href="#antlr-package">package</a>, <a href="#antlr-profile">profile</a>, <a href="#antlr-report">report</a>, <a href="#antlr-trace">trace</a>)
+</pre>
+
+Runs [ANTLR 3](https://www.antlr3.org//) on a set of grammars.
+
+**ATTRIBUTES**
+
+
+| Name | Description | Type | Mandatory | Default |
+| :------------- | :------------- | :------------- | :------------- | :------------- |
+| <a id="antlr-name"></a>name | A unique name for this target. | <a href="https://bazel.build/concepts/labels#target-names">Name</a> | required | |
+| <a id="antlr-deps"></a>deps | The dependencies to use. Defaults to the most recent ANTLR 3 release, but if you need to use a different version, you can specify the dependencies here. | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | `[Label("@antlr3_runtimes//:tool")]` |
+| <a id="antlr-srcs"></a>srcs | The grammar files to process. | <a href="https://bazel.build/concepts/labels">List of labels</a> | required | |
+| <a id="antlr-Xconversiontimeout"></a>Xconversiontimeout | Set NFA conversion timeout for each decision. | Integer | optional | `0` |
+| <a id="antlr-Xdbgconversion"></a>Xdbgconversion | Dump lots of info during NFA conversion. | Boolean | optional | `False` |
+| <a id="antlr-Xdbgst"></a>Xdbgst | Put tags at start/stop of all templates in output. | Boolean | optional | `False` |
+| <a id="antlr-Xdfa"></a>Xdfa | Print DFA as text. | Boolean | optional | `False` |
+| <a id="antlr-Xdfaverbose"></a>Xdfaverbose | Generate DFA states in DOT with NFA configs. | Boolean | optional | `False` |
+| <a id="antlr-Xgrtree"></a>Xgrtree | Print the grammar AST. | Boolean | optional | `False` |
+| <a id="antlr-Xm"></a>Xm | Max number of rule invocations during conversion. | Integer | optional | `0` |
+| <a id="antlr-Xmaxdfaedges"></a>Xmaxdfaedges | Max &quot;comfortable&quot; number of edges for single DFA state. | Integer | optional | `0` |
+| <a id="antlr-Xmaxinlinedfastates"></a>Xmaxinlinedfastates | Max DFA states before table used rather than inlining. | Integer | optional | `0` |
+| <a id="antlr-Xminswitchalts"></a>Xminswitchalts | Don't generate switch() statements for dfas smaller than given number. | Integer | optional | `0` |
+| <a id="antlr-Xmultithreaded"></a>Xmultithreaded | Run the analysis in 2 threads. | Boolean | optional | `False` |
+| <a id="antlr-Xnfastates"></a>Xnfastates | For nondeterminisms, list NFA states for each path. | Boolean | optional | `False` |
+| <a id="antlr-Xnocollapse"></a>Xnocollapse | Collapse incident edges into DFA states. | Boolean | optional | `False` |
+| <a id="antlr-Xnomergestopstates"></a>Xnomergestopstates | Max DFA states before table used rather than inlining. | Boolean | optional | `False` |
+| <a id="antlr-Xnoprune"></a>Xnoprune | Do not test EBNF block exit branches. | Boolean | optional | `False` |
+| <a id="antlr-XsaveLexer"></a>XsaveLexer | For nondeterminisms, list NFA states for each path. | Boolean | optional | `False` |
+| <a id="antlr-Xwatchconversion"></a>Xwatchconversion | Don't delete temporary lexers generated from combined grammars. | Boolean | optional | `False` |
+| <a id="antlr-debug"></a>debug | Generate a parser that emits debugging events. | Boolean | optional | `False` |
+| <a id="antlr-depend"></a>depend | Generate file dependencies; don't actually run antlr. | Boolean | optional | `False` |
+| <a id="antlr-dfa"></a>dfa | Generate a DFA for each decision point. | Boolean | optional | `False` |
+| <a id="antlr-dump"></a>dump | Print out the grammar without actions. | Boolean | optional | `False` |
+| <a id="antlr-imports"></a>imports | The grammar and .tokens files to import. Must be all in the same directory. | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | `[]` |
+| <a id="antlr-language"></a>language | The code generation target language. Either C, Cpp, CSharp2, CSharp3, JavaScript, Java, ObjC, Python, Python3 or Ruby (case-sensitive). | String | optional | `""` |
+| <a id="antlr-message_format"></a>message_format | Specify output style for messages. | String | optional | `""` |
+| <a id="antlr-nfa"></a>nfa | Generate an NFA for each rule. | Boolean | optional | `False` |
+| <a id="antlr-package"></a>package | The package/namespace for the generated code. | String | optional | `""` |
+| <a id="antlr-profile"></a>profile | Generate a parser that computes profiling information. | Boolean | optional | `False` |
+| <a id="antlr-report"></a>report | Print out a report about the grammar(s) processed. | Boolean | optional | `False` |
+| <a id="antlr-trace"></a>trace | Generate a parser with trace output. If the default output is not enough, you can override the traceIn and traceOut methods. | Boolean | optional | `False` |
+
+
diff --git a/test/testdata/html_tables_template_test/golden.md b/test/testdata/html_tables_template_test/golden.md
index 5ac16a8..5ac16a8 100755..100644
--- a/test/testdata/html_tables_template_test/golden.md
+++ b/test/testdata/html_tables_template_test/golden.md
diff --git a/test/testdata/input_template_test/golden.md b/test/testdata/input_template_test/golden.md
index a7a78bc..a7a78bc 100755..100644
--- a/test/testdata/input_template_test/golden.md
+++ b/test/testdata/input_template_test/golden.md
diff --git a/test/testdata/java_basic_test/golden.md b/test/testdata/java_basic_test/golden.md
index e98bbd6..e98bbd6 100755..100644
--- a/test/testdata/java_basic_test/golden.md
+++ b/test/testdata/java_basic_test/golden.md
diff --git a/test/testdata/macro_kwargs_test/golden.md b/test/testdata/macro_kwargs_test/golden.md
index 1a9df5b..1a9df5b 100755..100644
--- a/test/testdata/macro_kwargs_test/golden.md
+++ b/test/testdata/macro_kwargs_test/golden.md
diff --git a/test/testdata/misc_apis_test/golden.md b/test/testdata/misc_apis_test/golden.md
index 1a6b867..1512fd0 100755..100644
--- a/test/testdata/misc_apis_test/golden.md
+++ b/test/testdata/misc_apis_test/golden.md
@@ -22,7 +22,7 @@ This rule exercises some of the build API.
| <a id="my_rule-src"></a>src | The source file. | <a href="https://bazel.build/concepts/labels">Label</a> | optional | `None` |
| <a id="my_rule-out"></a>out | The output file. | <a href="https://bazel.build/concepts/labels">Label</a> | required | |
| <a id="my_rule-extra_arguments"></a>extra_arguments | - | List of strings | optional | `[]` |
-| <a id="my_rule-tool"></a>tool | The location of the tool to use. | <a href="https://bazel.build/concepts/labels">Label</a> | optional | `//foo/bar/baz:target` |
+| <a id="my_rule-tool"></a>tool | The location of the tool to use. | <a href="https://bazel.build/concepts/labels">Label</a> | optional | `"//foo/bar/baz:target"` |
<a id="MyInfo"></a>
diff --git a/test/testdata/misc_apis_test/legacy_golden.md b/test/testdata/misc_apis_test/legacy_golden.md
new file mode 100644
index 0000000..1a6b867
--- /dev/null
+++ b/test/testdata/misc_apis_test/legacy_golden.md
@@ -0,0 +1,76 @@
+<!-- Generated with Stardoc: http://skydoc.bazel.build -->
+
+
+
+<a id="my_rule"></a>
+
+## my_rule
+
+<pre>
+my_rule(<a href="#my_rule-name">name</a>, <a href="#my_rule-deps">deps</a>, <a href="#my_rule-src">src</a>, <a href="#my_rule-out">out</a>, <a href="#my_rule-extra_arguments">extra_arguments</a>, <a href="#my_rule-tool">tool</a>)
+</pre>
+
+This rule exercises some of the build API.
+
+**ATTRIBUTES**
+
+
+| Name | Description | Type | Mandatory | Default |
+| :------------- | :------------- | :------------- | :------------- | :------------- |
+| <a id="my_rule-name"></a>name | A unique name for this target. | <a href="https://bazel.build/concepts/labels#target-names">Name</a> | required | |
+| <a id="my_rule-deps"></a>deps | A list of dependencies. | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | `[]` |
+| <a id="my_rule-src"></a>src | The source file. | <a href="https://bazel.build/concepts/labels">Label</a> | optional | `None` |
+| <a id="my_rule-out"></a>out | The output file. | <a href="https://bazel.build/concepts/labels">Label</a> | required | |
+| <a id="my_rule-extra_arguments"></a>extra_arguments | - | List of strings | optional | `[]` |
+| <a id="my_rule-tool"></a>tool | The location of the tool to use. | <a href="https://bazel.build/concepts/labels">Label</a> | optional | `//foo/bar/baz:target` |
+
+
+<a id="MyInfo"></a>
+
+## MyInfo
+
+<pre>
+MyInfo(<a href="#MyInfo-foo">foo</a>, <a href="#MyInfo-bar">bar</a>)
+</pre>
+
+
+
+**FIELDS**
+
+
+| Name | Description |
+| :------------- | :------------- |
+| <a id="MyInfo-foo"></a>foo | Something foo-related. |
+| <a id="MyInfo-bar"></a>bar | Something bar-related. |
+
+
+<a id="exercise_the_api"></a>
+
+## exercise_the_api
+
+<pre>
+exercise_the_api()
+</pre>
+
+
+
+
+
+<a id="my_rule_impl"></a>
+
+## my_rule_impl
+
+<pre>
+my_rule_impl(<a href="#my_rule_impl-ctx">ctx</a>)
+</pre>
+
+
+
+**PARAMETERS**
+
+
+| Name | Description | Default Value |
+| :------------- | :------------- | :------------- |
+| <a id="my_rule_impl-ctx"></a>ctx | <p align="center"> - </p> | none |
+
+
diff --git a/test/testdata/module_extension_test/golden.md b/test/testdata/module_extension_test/golden.md
new file mode 100644
index 0000000..3b8c51e
--- /dev/null
+++ b/test/testdata/module_extension_test/golden.md
@@ -0,0 +1,45 @@
+<!-- Generated with Stardoc: http://skydoc.bazel.build -->
+
+Minimal example of a .bzl file defining a module extension.
+
+<a id="my_ext"></a>
+
+## my_ext
+
+<pre>
+my_ext = use_extension("//test:testdata/module_extension_test/input.bzl", "my_ext")
+my_ext.install(<a href="#my_ext.install-artifacts">artifacts</a>)
+my_ext.artifact(<a href="#my_ext.artifact-artifact">artifact</a>, <a href="#my_ext.artifact-group">group</a>)
+</pre>
+
+Minimal example of a module extension.
+
+
+**TAG CLASSES**
+
+<a id="my_ext.install"></a>
+
+### install
+
+Install tag
+
+**Attributes**
+
+| Name | Description | Type | Mandatory | Default |
+| :------------- | :------------- | :------------- | :------------- | :------------- |
+| <a id="my_ext.install-artifacts"></a>artifacts | Install artifacts | List of strings | optional | `[]` |
+
+<a id="my_ext.artifact"></a>
+
+### artifact
+
+Artifact tag
+
+**Attributes**
+
+| Name | Description | Type | Mandatory | Default |
+| :------------- | :------------- | :------------- | :------------- | :------------- |
+| <a id="my_ext.artifact-artifact"></a>artifact | Artifact | String | required | |
+| <a id="my_ext.artifact-group"></a>group | Group name | String | optional | `"my_group"` |
+
+
diff --git a/test/testdata/module_extension_test/input.bzl b/test/testdata/module_extension_test/input.bzl
new file mode 100644
index 0000000..ceffee4
--- /dev/null
+++ b/test/testdata/module_extension_test/input.bzl
@@ -0,0 +1,38 @@
+"""Minimal example of a .bzl file defining a module extension."""
+
+# buildifier: disable=unused-variable
+def _impl(module_ctx):
+ """No-op"""
+ pass
+
+_artifact = tag_class(
+ doc = "Artifact tag",
+ attrs = {
+ "group": attr.string(
+ doc = "Group name",
+ default = "my_group",
+ ),
+ "artifact": attr.string(
+ doc = "Artifact",
+ mandatory = True,
+ ),
+ },
+)
+
+_install = tag_class(
+ doc = "Install tag",
+ attrs = {
+ "artifacts": attr.string_list(
+ doc = "Install artifacts",
+ ),
+ },
+)
+
+my_ext = module_extension(
+ implementation = _impl,
+ doc = "Minimal example of a module extension.",
+ tag_classes = {
+ "install": _install,
+ "artifact": _artifact,
+ },
+)
diff --git a/test/testdata/multi_level_namespace_test/golden.md b/test/testdata/multi_level_namespace_test/golden.md
index 3e753c0..3e753c0 100755..100644
--- a/test/testdata/multi_level_namespace_test/golden.md
+++ b/test/testdata/multi_level_namespace_test/golden.md
diff --git a/test/testdata/multiple_files_test/golden.md b/test/testdata/multiple_files_test/golden.md
index 0fab337..0fab337 100755..100644
--- a/test/testdata/multiple_files_test/golden.md
+++ b/test/testdata/multiple_files_test/golden.md
diff --git a/test/testdata/multiple_rules_test/golden.md b/test/testdata/multiple_rules_test/golden.md
index 085fec8..085fec8 100755..100644
--- a/test/testdata/multiple_rules_test/golden.md
+++ b/test/testdata/multiple_rules_test/golden.md
diff --git a/test/testdata/namespace_test/golden.md b/test/testdata/namespace_test/golden.md
index 6e4c453..6e4c453 100755..100644
--- a/test/testdata/namespace_test/golden.md
+++ b/test/testdata/namespace_test/golden.md
diff --git a/test/testdata/proto_format_test/golden.binaryproto b/test/testdata/proto_format_test/golden.binaryproto
new file mode 100644
index 0000000..5685b48
--- /dev/null
+++ b/test/testdata/proto_format_test/golden.binaryproto
@@ -0,0 +1,21 @@
+
+Ä
+
+my_exampleSmall example of rule.*
+nameA unique name for this target. 7
+uselessThis argument will be ignored.2
+"ignoreme""9
+
+my_example+//test:testdata/proto_format_test/input.bzlÍ
+example$Stores information about an example.
+fooA string representing foo
+barA string representing bar
+bazA string representing baz"6
+example+//test:testdata/proto_format_test/input.bzl…
+check_function%
+fooA unique name for this rule. ŒRuns some checks on the given function parameter.
+
+This rule runs checks on a given function parameter.
+Use `bazel build` to run the check.
+2=
+check_function+//test:testdata/proto_format_test/input.bzl* Input file for proto format test2+//test:testdata/proto_format_test/input.bzl \ No newline at end of file
diff --git a/test/testdata/proto_format_test/golden.raw b/test/testdata/proto_format_test/legacy_golden.binaryproto
index a93d367..a93d367 100644
--- a/test/testdata/proto_format_test/golden.raw
+++ b/test/testdata/proto_format_test/legacy_golden.binaryproto
Binary files differ
diff --git a/test/testdata/provider_basic_test/golden.md b/test/testdata/provider_basic_test/golden.md
index a9d76bf..4397bed 100755..100644
--- a/test/testdata/provider_basic_test/golden.md
+++ b/test/testdata/provider_basic_test/golden.md
@@ -17,8 +17,8 @@ Stores information about a foo.
| Name | Description |
| :------------- | :------------- |
-| <a id="MyFooInfo-bar"></a>bar | (Undocumented) |
-| <a id="MyFooInfo-baz"></a>baz | (Undocumented) |
+| <a id="MyFooInfo-bar"></a>bar | - |
+| <a id="MyFooInfo-baz"></a>baz | - |
<a id="MyPoorlyDocumentedInfo"></a>
diff --git a/test/testdata/provider_basic_test/legacy_golden.md b/test/testdata/provider_basic_test/legacy_golden.md
new file mode 100644
index 0000000..a9d76bf
--- /dev/null
+++ b/test/testdata/provider_basic_test/legacy_golden.md
@@ -0,0 +1,59 @@
+<!-- Generated with Stardoc: http://skydoc.bazel.build -->
+
+
+
+<a id="MyFooInfo"></a>
+
+## MyFooInfo
+
+<pre>
+MyFooInfo(<a href="#MyFooInfo-bar">bar</a>, <a href="#MyFooInfo-baz">baz</a>)
+</pre>
+
+Stores information about a foo.
+
+**FIELDS**
+
+
+| Name | Description |
+| :------------- | :------------- |
+| <a id="MyFooInfo-bar"></a>bar | (Undocumented) |
+| <a id="MyFooInfo-baz"></a>baz | (Undocumented) |
+
+
+<a id="MyPoorlyDocumentedInfo"></a>
+
+## MyPoorlyDocumentedInfo
+
+<pre>
+MyPoorlyDocumentedInfo()
+</pre>
+
+
+
+**FIELDS**
+
+
+
+<a id="MyVeryDocumentedInfo"></a>
+
+## MyVeryDocumentedInfo
+
+<pre>
+MyVeryDocumentedInfo(<a href="#MyVeryDocumentedInfo-favorite_food">favorite_food</a>, <a href="#MyVeryDocumentedInfo-favorite_color">favorite_color</a>)
+</pre>
+
+
+A provider with some really neat documentation.
+Look on my works, ye mighty, and despair!
+
+
+**FIELDS**
+
+
+| Name | Description |
+| :------------- | :------------- |
+| <a id="MyVeryDocumentedInfo-favorite_food"></a>favorite_food | A string representing my favorite food |
+| <a id="MyVeryDocumentedInfo-favorite_color"></a>favorite_color | A string representing my favorite color |
+
+
diff --git a/test/testdata/providers_for_attributes_test/golden.md b/test/testdata/providers_for_attributes_test/golden.md
index a427cf2..a427cf2 100755..100644
--- a/test/testdata/providers_for_attributes_test/golden.md
+++ b/test/testdata/providers_for_attributes_test/golden.md
diff --git a/test/testdata/pure_markdown_template_test/golden.md b/test/testdata/pure_markdown_template_test/golden.md
index cedeaf1..9b5ecff 100644
--- a/test/testdata/pure_markdown_template_test/golden.md
+++ b/test/testdata/pure_markdown_template_test/golden.md
@@ -19,7 +19,7 @@ Small example of rule using a markdown template.
| :------------- | :------------- | :------------- | :------------- | :------------- |
| <a id="example_rule-name"></a>name | A unique name for this target. | <a href="https://bazel.build/concepts/labels#target-names">Name</a> | required | |
| <a id="example_rule-first"></a>first | This is the first attribute | String | optional | `""` |
-| <a id="example_rule-second"></a>second | - | String | optional | `"2"` |
+| <a id="example_rule-second"></a>second | - | Integer | optional | `2` |
<a id="ExampleProviderInfo"></a>
@@ -86,7 +86,7 @@ Small example of aspect using a markdown template.
| Name | Description | Type | Mandatory | Default |
| :------------- | :------------- | :------------- | :------------- | :------------- |
| <a id="example_aspect-name"></a>name | A unique name for this target. | <a href="https://bazel.build/concepts/labels#target-names">Name</a> | required | |
-| <a id="example_aspect-first"></a>first | - | <a href="https://bazel.build/concepts/labels">Label</a> | required | |
+| <a id="example_aspect-first"></a>first | - | String | required | |
| <a id="example_aspect-second"></a>second | This is the second attribute. | String | optional | `""` |
diff --git a/test/testdata/pure_markdown_template_test/input.bzl b/test/testdata/pure_markdown_template_test/input.bzl
index 814acdd..61d185d 100644
--- a/test/testdata/pure_markdown_template_test/input.bzl
+++ b/test/testdata/pure_markdown_template_test/input.bzl
@@ -38,7 +38,7 @@ example_rule = rule(
doc = "Small example of rule using a markdown template.",
attrs = {
"first": attr.string(doc = "This is the first attribute"),
- "second": attr.string(default = "2"),
+ "second": attr.int(default = 2),
},
)
@@ -51,7 +51,7 @@ example_aspect = aspect(
doc = "Small example of aspect using a markdown template.",
attr_aspects = ["deps", "attr_aspect"],
attrs = {
- "first": attr.label(mandatory = True, allow_single_file = True),
+ "first": attr.string(mandatory = True),
"second": attr.string(doc = "This is the second attribute."),
},
)
diff --git a/test/testdata/py_rule_test/golden.md b/test/testdata/py_rule_test/golden.md
index d2a8394..d2a8394 100755..100644
--- a/test/testdata/py_rule_test/golden.md
+++ b/test/testdata/py_rule_test/golden.md
diff --git a/test/testdata/repo_rules_test/golden.md b/test/testdata/repo_rules_test/golden.md
index 639b5c7..75befef 100755..100644
--- a/test/testdata/repo_rules_test/golden.md
+++ b/test/testdata/repo_rules_test/golden.md
@@ -18,7 +18,13 @@ Minimal example of a repository rule.
| Name | Description | Type | Mandatory | Default |
| :------------- | :------------- | :------------- | :------------- | :------------- |
| <a id="my_repo-name"></a>name | A unique name for this repository. | <a href="https://bazel.build/concepts/labels#target-names">Name</a> | required | |
-| <a id="my_repo-repo_mapping"></a>repo_mapping | A dictionary from local repository name to global repository name. This allows controls over workspace dependency resolution for dependencies of this repository.&lt;p&gt;For example, an entry <code>"@foo": "@bar"</code> declares that, for any time this repository depends on <code>@foo</code> (such as a dependency on <code>@foo//some:target</code>, it should actually resolve that dependency within globally-declared <code>@bar</code> (<code>@bar//some:target</code>). | <a href="https://bazel.build/rules/lib/dict">Dictionary: String -> String</a> | required | |
+| <a id="my_repo-repo_mapping"></a>repo_mapping | In <code>WORKSPACE</code> context only: a dictionary from local repository name to global repository name. This allows controls over workspace dependency resolution for dependencies of this repository.<br><br>For example, an entry <code>"@foo": "@bar"</code> declares that, for any time this repository depends on <code>@foo</code> (such as a dependency on <code>@foo//some:target</code>, it should actually resolve that dependency within globally-declared <code>@bar</code> (<code>@bar//some:target</code>).<br><br>This attribute is _not_ supported in <code>MODULE.bazel</code> context (when invoking a repository rule inside a module extension's implementation function). | <a href="https://bazel.build/rules/lib/dict">Dictionary: String -> String</a> | optional | |
| <a id="my_repo-useless"></a>useless | This argument will be ignored. You don't have to specify it, but you may. | String | optional | `"ignoreme"` |
+**ENVIRONMENT VARIABLES**
+
+This repository rule depends on the following environment variables:
+* `FOO_CC`
+* `BAR_PATH`
+
diff --git a/test/testdata/repo_rules_test/input.bzl b/test/testdata/repo_rules_test/input.bzl
index aca64f8..404d3a7 100644
--- a/test/testdata/repo_rules_test/input.bzl
+++ b/test/testdata/repo_rules_test/input.bzl
@@ -11,4 +11,5 @@ my_repo = repository_rule(
default = "ignoreme",
),
},
+ environ = ["FOO_CC", "BAR_PATH"],
)
diff --git a/test/testdata/repo_rules_test/legacy_golden.md b/test/testdata/repo_rules_test/legacy_golden.md
new file mode 100644
index 0000000..639b5c7
--- /dev/null
+++ b/test/testdata/repo_rules_test/legacy_golden.md
@@ -0,0 +1,24 @@
+<!-- Generated with Stardoc: http://skydoc.bazel.build -->
+
+
+
+<a id="my_repo"></a>
+
+## my_repo
+
+<pre>
+my_repo(<a href="#my_repo-name">name</a>, <a href="#my_repo-repo_mapping">repo_mapping</a>, <a href="#my_repo-useless">useless</a>)
+</pre>
+
+Minimal example of a repository rule.
+
+**ATTRIBUTES**
+
+
+| Name | Description | Type | Mandatory | Default |
+| :------------- | :------------- | :------------- | :------------- | :------------- |
+| <a id="my_repo-name"></a>name | A unique name for this repository. | <a href="https://bazel.build/concepts/labels#target-names">Name</a> | required | |
+| <a id="my_repo-repo_mapping"></a>repo_mapping | A dictionary from local repository name to global repository name. This allows controls over workspace dependency resolution for dependencies of this repository.&lt;p&gt;For example, an entry <code>"@foo": "@bar"</code> declares that, for any time this repository depends on <code>@foo</code> (such as a dependency on <code>@foo//some:target</code>, it should actually resolve that dependency within globally-declared <code>@bar</code> (<code>@bar//some:target</code>). | <a href="https://bazel.build/rules/lib/dict">Dictionary: String -> String</a> | required | |
+| <a id="my_repo-useless"></a>useless | This argument will be ignored. You don't have to specify it, but you may. | String | optional | `"ignoreme"` |
+
+
diff --git a/test/testdata/same_level_file_test/golden.md b/test/testdata/same_level_file_test/golden.md
index 66b9a97..66b9a97 100755..100644
--- a/test/testdata/same_level_file_test/golden.md
+++ b/test/testdata/same_level_file_test/golden.md
diff --git a/test/testdata/simple_test/golden.md b/test/testdata/simple_test/golden.md
index a5a2e39..a5a2e39 100755..100644
--- a/test/testdata/simple_test/golden.md
+++ b/test/testdata/simple_test/golden.md
diff --git a/test/testdata/struct_default_value_test/golden.md b/test/testdata/struct_default_value_test/golden.md
index 38f9908..38f9908 100755..100644
--- a/test/testdata/struct_default_value_test/golden.md
+++ b/test/testdata/struct_default_value_test/golden.md
diff --git a/test/testdata/unknown_name_test/golden.md b/test/testdata/unknown_name_test/golden.md
index 8bbe43c..8bbe43c 100755..100644
--- a/test/testdata/unknown_name_test/golden.md
+++ b/test/testdata/unknown_name_test/golden.md
diff --git a/update-stardoc-docs.sh b/update-stardoc-docs.sh
index 986f5c2..d4d7ce3 100755
--- a/update-stardoc-docs.sh
+++ b/update-stardoc-docs.sh
@@ -19,8 +19,11 @@
set -eu
+# Allow users to override the bazel command with e.g. bazelisk.
+: "${BAZEL:=bazel}"
+
echo "** Generating Stardoc documentation..."
-bazel build //stardoc:stardoc_doc.md
+${BAZEL} build //stardoc:stardoc_doc.md
echo "** Copying result to docs/stardoc_rule.md ..."
cp bazel-bin/stardoc/stardoc_doc.md docs/stardoc_rule.md
diff --git a/update-stardoc-tests.sh b/update-stardoc-tests.sh
index f47819a..d0264d0 100755
--- a/update-stardoc-tests.sh
+++ b/update-stardoc-tests.sh
@@ -21,27 +21,80 @@
set -eu
+function run_buildozer () {
+ # buildozer uses return code 3 to signal a no-op, so we can't use "set -e"
+ set +e
+ buildozer "$@"
+ ret=$?
+ set -e
+ if [[ $ret != 0 && $ret != 3 ]]; then
+ return $ret
+ fi
+}
+
+# Allow users to override the bazel command with e.g. bazelisk.
+: "${BAZEL:=bazel}"
+
+echo "** Verifying Bazel version..."
+if ! ${BAZEL} build --experimental_enable_starlark_doc_extract &> /dev/null; then
+ echo >&2 "Please use a development version of Bazel, or Bazel 7 once it is released."
+ echo >&2 "For example: USE_BAZEL_VERSION=last_green BAZEL=bazelisk ./update-stardoc-tests.sh"
+ exit 1
+fi
+
# Some tests cannot be automatically regenerated using this script, as they don't fall under the normal
# golden test pattern
-EXCLUDED_TESTS="namespace_test_with_whitelist|proto_format_test|multi_level_namespace_test_with_whitelist|local_repository_test"
+EXCLUDED_TESTS="namespace_test_with_allowlist|multi_level_namespace_test_with_allowlist|local_repository_test"
echo "** Querying for tests..."
-regen_targets=$(bazel query //test:all | grep regenerate_with_jar | grep -vE "_golden_stardoc\$|$EXCLUDED_TESTS")
+regen_legacy_targets=$(${BAZEL} query //test:all | grep regenerate_with_jar | grep -vE "_golden_stardoc|$EXCLUDED_TESTS")
+regen_starlark_doc_extract_targets=$(${BAZEL} query //test:all | grep regenerate_ | grep -vE "_legacy|_golden\.extract|$EXCLUDED_TESTS")
echo "** Building goldens..."
-bazel build $regen_targets
+${BAZEL} build $regen_legacy_targets $regen_starlark_doc_extract_targets
+
+echo "** Copying starlark_doc_extract goldens..."
+for regen_target in $regen_starlark_doc_extract_targets; do
+ base_target_name=$(echo $regen_target | sed 's/\/\/test://g')
+ testdata_pkg_name=$(echo $base_target_name | sed 's/regenerate_//g' | sed 's/_golden//g')
+ out_file="bazel-bin/test/${base_target_name}.out"
+ if [[ $regen_target == *"proto_format"* ]]; then
+ ext="binaryproto"
+ else
+ ext="md"
+ fi
+ golden="test/testdata/${testdata_pkg_name}/golden.${ext}"
+ cp "${out_file}" "${golden}"
+ chmod 644 "${golden}"
+done
-echo "** Copying goldens..."
-for regen_target in $regen_targets; do
+echo "** Copying legacy goldens..."
+for regen_target in $regen_legacy_targets; do
base_target_name=$(echo $regen_target | sed 's/\/\/test://g')
- testdata_pkg_name=$(echo $base_target_name | sed 's/regenerate_with_jar_//g' | sed 's/_golden//g')
+ testdata_pkg_name=$(echo $base_target_name | sed 's/regenerate_with_jar_//g' | sed 's/_legacy_golden//g')
out_file="bazel-bin/test/${base_target_name}.out"
- cp $out_file "test/testdata/${testdata_pkg_name}/golden.md"
+ if [[ $regen_target == *"proto_format"* ]]; then
+ ext="binaryproto"
+ else
+ ext="md"
+ fi
+ golden="test/testdata/${testdata_pkg_name}/golden.${ext}"
+ legacy_golden="test/testdata/${testdata_pkg_name}/legacy_golden.${ext}"
+ legacy_golden_target="${legacy_golden#test/}"
+ if diff "${out_file}" "${golden}" > /dev/null; then
+ # legacy_golden is no longer needed; we only need golden
+ [[ -e "${legacy_golden}" ]] && rm "${legacy_golden}"
+ run_buildozer "remove legacy_golden_file" "//test:${testdata_pkg_name}"
+ else
+ cp "${out_file}" "${legacy_golden}"
+ chmod 644 "${legacy_golden}"
+ run_buildozer "set legacy_golden_file \"${legacy_golden_target}\"" "//test:${testdata_pkg_name}"
+ fi
done
echo "** Files copied."
echo "Please note that not all golden files are correctly copied by this script."
echo "You may want to manually run:"
echo ""
-echo "bazel test //test:all"
+echo "${BAZEL} test //test:all"
echo ""
echo "...and manually update tests which are still broken."