Support Repository Rules in Stardoc
Make the fake implementation of repository_rule register the rule
instead of blindly returning the implementation function. In this
way, the documentation generated for repository rules contains the
correct arguments.
Fixes https://github.com/bazelbuild/skydoc/issues/168
Change-Id: I4b4101a9a604282051eeaadafccdc9a987b14264
PiperOrigin-RevId: 239029265
diff --git a/src/main/java/com/google/devtools/build/docgen/SymbolFamilies.java b/src/main/java/com/google/devtools/build/docgen/SymbolFamilies.java
index e3ef9ea..f90f566 100644
--- a/src/main/java/com/google/devtools/build/docgen/SymbolFamilies.java
+++ b/src/main/java/com/google/devtools/build/docgen/SymbolFamilies.java
@@ -197,7 +197,8 @@
PlatformBootstrap platformBootstrap = new PlatformBootstrap(new FakePlatformCommon());
PyBootstrap pyBootstrap =
new PyBootstrap(new FakePyInfoProvider(), new FakePyRuntimeInfoProvider());
- RepositoryBootstrap repositoryBootstrap = new RepositoryBootstrap(new FakeRepositoryModule());
+ RepositoryBootstrap repositoryBootstrap =
+ new RepositoryBootstrap(new FakeRepositoryModule(Lists.newArrayList()));
TestingBootstrap testingBootstrap =
new TestingBootstrap(
new FakeTestingModule(),
diff --git a/src/main/java/com/google/devtools/build/skydoc/SkydocMain.java b/src/main/java/com/google/devtools/build/skydoc/SkydocMain.java
index 5fbe391..1c93b24 100644
--- a/src/main/java/com/google/devtools/build/skydoc/SkydocMain.java
+++ b/src/main/java/com/google/devtools/build/skydoc/SkydocMain.java
@@ -512,7 +512,8 @@
ProtoBootstrap protoBootstrap = new ProtoBootstrap(new FakeProtoInfoApiProvider());
PyBootstrap pyBootstrap =
new PyBootstrap(new FakePyInfoProvider(), new FakePyRuntimeInfoProvider());
- RepositoryBootstrap repositoryBootstrap = new RepositoryBootstrap(new FakeRepositoryModule());
+ RepositoryBootstrap repositoryBootstrap =
+ new RepositoryBootstrap(new FakeRepositoryModule(ruleInfoList));
TestingBootstrap testingBootstrap =
new TestingBootstrap(
new FakeTestingModule(),
diff --git a/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeSkylarkRuleFunctionsApi.java b/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeSkylarkRuleFunctionsApi.java
index 9690686..875b3ca 100644
--- a/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeSkylarkRuleFunctionsApi.java
+++ b/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeSkylarkRuleFunctionsApi.java
@@ -206,7 +206,7 @@
* A comparator for {@link AttributeInfo} objects which sorts by attribute name alphabetically,
* except that any attribute named "name" is placed first.
*/
- private static class AttributeNameComparator implements Comparator<AttributeInfo> {
+ public static class AttributeNameComparator implements Comparator<AttributeInfo> {
@Override
public int compare(AttributeInfo o1, AttributeInfo o2) {
diff --git a/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/repository/BUILD b/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/repository/BUILD
index 47b87fa..9c349e9 100644
--- a/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/repository/BUILD
+++ b/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/repository/BUILD
@@ -16,5 +16,9 @@
"//src/main/java/com/google/devtools/build/lib:skylarkinterface",
"//src/main/java/com/google/devtools/build/lib:syntax",
"//src/main/java/com/google/devtools/build/lib/skylarkbuildapi/repository",
+ "//src/main/java/com/google/devtools/build/skydoc/fakebuildapi",
+ "//src/main/java/com/google/devtools/build/skydoc/rendering",
+ "//third_party:guava",
+ "//third_party:jsr305",
],
)
diff --git a/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/repository/FakeRepositoryModule.java b/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/repository/FakeRepositoryModule.java
index 50256e0..82ec124 100644
--- a/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/repository/FakeRepositoryModule.java
+++ b/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/repository/FakeRepositoryModule.java
@@ -14,16 +14,36 @@
package com.google.devtools.build.skydoc.fakebuildapi.repository;
+import com.google.common.collect.ImmutableMap;
import com.google.devtools.build.lib.skylarkbuildapi.repository.RepositoryModuleApi;
import com.google.devtools.build.lib.syntax.BaseFunction;
import com.google.devtools.build.lib.syntax.Environment;
+import com.google.devtools.build.lib.syntax.EvalException;
import com.google.devtools.build.lib.syntax.FuncallExpression;
+import com.google.devtools.build.lib.syntax.Runtime;
+import com.google.devtools.build.lib.syntax.SkylarkDict;
import com.google.devtools.build.lib.syntax.SkylarkList;
+import com.google.devtools.build.skydoc.fakebuildapi.FakeDescriptor;
+import com.google.devtools.build.skydoc.fakebuildapi.FakeSkylarkRuleFunctionsApi.AttributeNameComparator;
+import com.google.devtools.build.skydoc.rendering.AttributeInfo;
+import com.google.devtools.build.skydoc.rendering.AttributeInfo.Type;
+import com.google.devtools.build.skydoc.rendering.RuleInfo;
+import java.util.List;
+import java.util.stream.Collectors;
+import javax.annotation.Nullable;
/**
* Fake implementation of {@link RepositoryModuleApi}.
*/
public class FakeRepositoryModule implements RepositoryModuleApi {
+ private static final FakeDescriptor IMPLICIT_NAME_ATTRIBUTE_DESCRIPTOR =
+ new FakeDescriptor(Type.NAME, "A unique name for this repository.", true);
+
+ private final List<RuleInfo> ruleInfoList;
+
+ public FakeRepositoryModule(List<RuleInfo> ruleInfoList) {
+ this.ruleInfoList = ruleInfoList;
+ }
@Override
public BaseFunction repositoryRule(
@@ -33,7 +53,55 @@
SkylarkList<String> environ,
String doc,
FuncallExpression ast,
- Environment env) {
- return implementation;
+ Environment env)
+ throws EvalException {
+ List<AttributeInfo> attrInfos;
+ ImmutableMap.Builder<String, FakeDescriptor> attrsMapBuilder = ImmutableMap.builder();
+ if (attrs != null && attrs != Runtime.NONE) {
+ SkylarkDict<?, ?> attrsDict = (SkylarkDict<?, ?>) attrs;
+ attrsMapBuilder.putAll(attrsDict.getContents(String.class, FakeDescriptor.class, "attrs"));
+ }
+
+ attrsMapBuilder.put("name", IMPLICIT_NAME_ATTRIBUTE_DESCRIPTOR);
+ attrInfos =
+ attrsMapBuilder.build().entrySet().stream()
+ .filter(entry -> !entry.getKey().startsWith("_"))
+ .map(
+ entry ->
+ new AttributeInfo(
+ entry.getKey(),
+ entry.getValue().getDocString(),
+ entry.getValue().getType(),
+ entry.getValue().isMandatory()))
+ .collect(Collectors.toList());
+ attrInfos.sort(new AttributeNameComparator());
+
+ RepositoryRuleDefinitionIdentifier functionIdentifier =
+ new RepositoryRuleDefinitionIdentifier();
+
+ ruleInfoList.add(new RuleInfo(functionIdentifier, ast.getLocation(), doc, attrInfos));
+ return functionIdentifier;
+ }
+
+ /**
+ * A fake {@link BaseFunction} implementation which serves as an identifier for a rule definition.
+ * A skylark invocation of 'rule()' should spawn a unique instance of this class and return it.
+ * Thus, skylark code such as 'foo = rule()' will result in 'foo' being assigned to a unique
+ * identifier, which can later be matched to a registered rule() invocation saved by the fake
+ * build API implementation.
+ */
+ private static class RepositoryRuleDefinitionIdentifier extends BaseFunction {
+
+ private static int idCounter = 0;
+
+ public RepositoryRuleDefinitionIdentifier() {
+ super("RepositoryRuleDefinitionIdentifier" + idCounter++);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object other) {
+ // Use exact object matching.
+ return this == other;
+ }
}
}
diff --git a/src/test/java/com/google/devtools/build/skydoc/BUILD b/src/test/java/com/google/devtools/build/skydoc/BUILD
index 2cf576d..bcfc620 100644
--- a/src/test/java/com/google/devtools/build/skydoc/BUILD
+++ b/src/test/java/com/google/devtools/build/skydoc/BUILD
@@ -45,6 +45,13 @@
)
skydoc_test(
+ name = "repo_rule_test",
+ golden_file = "testdata/repo_rules_test/golden.txt",
+ input_file = "testdata/repo_rules_test/input.bzl",
+ skydoc = "//src/main/java/com/google/devtools/build/skydoc",
+)
+
+skydoc_test(
name = "unknown_name",
golden_file = "testdata/unknown_name_test/golden.txt",
input_file = "testdata/unknown_name_test/input.bzl",
diff --git a/src/test/java/com/google/devtools/build/skydoc/testdata/repo_rules_test/golden.txt b/src/test/java/com/google/devtools/build/skydoc/testdata/repo_rules_test/golden.txt
new file mode 100644
index 0000000..9bd9112
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/skydoc/testdata/repo_rules_test/golden.txt
@@ -0,0 +1,39 @@
+<a name="#my_repo"></a>
+## my_repo
+
+<pre>
+my_repo(<a href="#my_repo-name">name</a>, <a href="#my_repo-useless">useless</a>)
+</pre>
+
+Minimal example of a repository rule.
+
+### Attributes
+
+<table class="params-table">
+ <colgroup>
+ <col class="col-param" />
+ <col class="col-description" />
+ </colgroup>
+ <tbody>
+ <tr id="my_repo-name">
+ <td><code>name</code></td>
+ <td>
+ <a href="https://bazel.build/docs/build-ref.html#name">Name</a>; required
+ <p>
+ A unique name for this repository.
+ </p>
+ </td>
+ </tr>
+ <tr id="my_repo-useless">
+ <td><code>useless</code></td>
+ <td>
+ String; optional
+ <p>
+ This argument will be ingored. You don't have to specify it, but you may.
+ </p>
+ </td>
+ </tr>
+ </tbody>
+</table>
+
+
diff --git a/src/test/java/com/google/devtools/build/skydoc/testdata/repo_rules_test/input.bzl b/src/test/java/com/google/devtools/build/skydoc/testdata/repo_rules_test/input.bzl
new file mode 100644
index 0000000..bac2323
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/skydoc/testdata/repo_rules_test/input.bzl
@@ -0,0 +1,13 @@
+def _repo_rule_impl(ctx):
+ ctx.file("BUILD", "")
+
+my_repo = repository_rule(
+ implementation = _repo_rule_impl,
+ doc = "Minimal example of a repository rule.",
+ attrs = {
+ "useless" : attr.string(
+ doc = "This argument will be ingored. You don't have to specify it, but you may.",
+ default = "ignoreme",
+ ),
+ },
+)