From 547aa486608be0e3eec682759d53c26948996f1f Mon Sep 17 00:00:00 2001 From: Klaus Aehlig Date: Mon, 18 Mar 2019 11:44:11 -0700 Subject: [PATCH] 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 --- .../devtools/build/docgen/SymbolFamilies.java | 3 +- .../devtools/build/skydoc/SkydocMain.java | 3 +- .../FakeSkylarkRuleFunctionsApi.java | 2 +- .../skydoc/fakebuildapi/repository/BUILD | 4 ++ .../repository/FakeRepositoryModule.java | 72 ++++++++++++++++++- .../com/google/devtools/build/skydoc/BUILD | 7 ++ .../testdata/repo_rules_test/golden.txt | 39 ++++++++++ .../skydoc/testdata/repo_rules_test/input.bzl | 13 ++++ 8 files changed, 138 insertions(+), 5 deletions(-) create mode 100644 src/test/java/com/google/devtools/build/skydoc/testdata/repo_rules_test/golden.txt create mode 100644 src/test/java/com/google/devtools/build/skydoc/testdata/repo_rules_test/input.bzl 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 e3ef9ea2547e24..f90f5660ccf0cd 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 @@ private Map collectBzlGlobals() { 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 5fbe391d889b45..1c93b24aa95e4b 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 @@ private static GlobalFrame globalFrame(List ruleInfoList, 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 96906864a5352f..875b3cabc3e5c2 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 @@ public boolean equals(@Nullable Object other) { * 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 { + public static class AttributeNameComparator implements Comparator { @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 47b87fa6ffb05d..9c349e942511f9 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 @@ java_library( "//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 50256e0fe222cf..82ec124c0ad612 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 ruleInfoList; + + public FakeRepositoryModule(List ruleInfoList) { + this.ruleInfoList = ruleInfoList; + } @Override public BaseFunction repositoryRule( @@ -33,7 +53,55 @@ public BaseFunction repositoryRule( SkylarkList environ, String doc, FuncallExpression ast, - Environment env) { - return implementation; + Environment env) + throws EvalException { + List attrInfos; + ImmutableMap.Builder 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 2cf576d5e7091f..bcfc620675158c 100644 --- a/src/test/java/com/google/devtools/build/skydoc/BUILD +++ b/src/test/java/com/google/devtools/build/skydoc/BUILD @@ -44,6 +44,13 @@ skydoc_test( whitelisted_symbols = ["my_rule"], ) +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", 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 00000000000000..9bd9112d31b8c4 --- /dev/null +++ b/src/test/java/com/google/devtools/build/skydoc/testdata/repo_rules_test/golden.txt @@ -0,0 +1,39 @@ + +## my_repo + +
+my_repo(name, useless)
+
+ +Minimal example of a repository rule. + +### Attributes + + + + + + + + + + + + + + + + +
name + Name; required +

+ A unique name for this repository. +

+
useless + String; optional +

+ This argument will be ingored. You don't have to specify it, but you may. +

+
+ + 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 00000000000000..bac2323b157b13 --- /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", + ), + }, +)