Generate Stardoc documentation for modules of functions Thus, if a Starlark file exposes a struct which contains functions (as a way of namespacing these functions, such as math.min()), Starlark generates documentation for these functions in the correct namespace. This also changes Stardoc to avoid documenting private symbols (prefixed with an underscore) by default. Progress toward https://github.com/bazelbuild/skydoc/issues/161. RELNOTES: None. PiperOrigin-RevId: 235565344
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 bf7a026..baf1376 100644 --- a/src/main/java/com/google/devtools/build/skydoc/SkydocMain.java +++ b/src/main/java/com/google/devtools/build/skydoc/SkydocMain.java
@@ -58,6 +58,7 @@ import com.google.devtools.build.lib.syntax.Environment; import com.google.devtools.build.lib.syntax.Environment.Extension; import com.google.devtools.build.lib.syntax.Environment.GlobalFrame; +import com.google.devtools.build.lib.syntax.EvalException; import com.google.devtools.build.lib.syntax.MethodLibrary; import com.google.devtools.build.lib.syntax.Mutability; import com.google.devtools.build.lib.syntax.ParserInputSource; @@ -74,6 +75,7 @@ import com.google.devtools.build.skydoc.fakebuildapi.FakeSkylarkCommandLineApi; import com.google.devtools.build.skydoc.fakebuildapi.FakeSkylarkNativeModuleApi; import com.google.devtools.build.skydoc.fakebuildapi.FakeSkylarkRuleFunctionsApi; +import com.google.devtools.build.skydoc.fakebuildapi.FakeStructApi; import com.google.devtools.build.skydoc.fakebuildapi.FakeStructApi.FakeStructProviderApi; import com.google.devtools.build.skydoc.fakebuildapi.android.FakeAndroidDeviceBrokerInfo.FakeAndroidDeviceBrokerInfoProvider; import com.google.devtools.build.skydoc.fakebuildapi.android.FakeAndroidInstrumentationInfo.FakeAndroidInstrumentationInfoProvider; @@ -157,7 +159,7 @@ } public static void main(String[] args) - throws IOException, InterruptedException, LabelSyntaxException { + throws IOException, InterruptedException, LabelSyntaxException, EvalException { OptionsParser parser = OptionsParser.newOptionsParser(StarlarkSemanticsOptions.class, SkydocOptions.class); parser.parseAndExitUponError(args); @@ -209,33 +211,38 @@ MarkdownRenderer renderer = new MarkdownRenderer(); - if (symbolNames.isEmpty()) { - try (PrintWriter printWriter = new PrintWriter(outputPath, "UTF-8")) { - printRuleInfos(printWriter, renderer, ruleInfoMap.build(), unknownNamedRules.build()); - printProviderInfos(printWriter, renderer, providerInfoMap.build()); - printUserDefinedFunctions(printWriter, renderer, userDefinedFunctions.build()); - } - } else { - Map<String, RuleInfo> filteredRuleInfos = - ruleInfoMap.build().entrySet().stream() - .filter(entry -> symbolNames.contains(entry.getKey())) - .collect(ImmutableMap.toImmutableMap(Entry::getKey, Entry::getValue)); - Map<String, ProviderInfo> filteredProviderInfos = - providerInfoMap.build().entrySet().stream() - .filter(entry -> symbolNames.contains(entry.getKey())) - .collect(ImmutableMap.toImmutableMap(Entry::getKey, Entry::getValue)); - Map<String, UserDefinedFunction> filteredUserDefinedFunctions = - userDefinedFunctions.build().entrySet().stream() - .filter(entry -> symbolNames.contains(entry.getKey())) - .collect(ImmutableMap.toImmutableMap(Entry::getKey, Entry::getValue)); - try (PrintWriter printWriter = new PrintWriter(outputPath, "UTF-8")) { - printRuleInfos(printWriter, renderer, filteredRuleInfos, ImmutableList.of()); - printProviderInfos(printWriter, renderer, filteredProviderInfos); - printUserDefinedFunctions(printWriter, renderer, filteredUserDefinedFunctions); - } + Map<String, RuleInfo> filteredRuleInfos = + ruleInfoMap.build().entrySet().stream() + .filter(entry -> validSymbolName(symbolNames, entry.getKey())) + .collect(ImmutableMap.toImmutableMap(Entry::getKey, Entry::getValue)); + Map<String, ProviderInfo> filteredProviderInfos = + providerInfoMap.build().entrySet().stream() + .filter(entry -> validSymbolName(symbolNames, entry.getKey())) + .collect(ImmutableMap.toImmutableMap(Entry::getKey, Entry::getValue)); + Map<String, UserDefinedFunction> filteredUserDefinedFunctions = + userDefinedFunctions.build().entrySet().stream() + .filter(entry -> validSymbolName(symbolNames, entry.getKey())) + .collect(ImmutableMap.toImmutableMap(Entry::getKey, Entry::getValue)); + try (PrintWriter printWriter = new PrintWriter(outputPath, "UTF-8")) { + printRuleInfos(printWriter, renderer, filteredRuleInfos, ImmutableList.of()); + printProviderInfos(printWriter, renderer, filteredProviderInfos); + printUserDefinedFunctions(printWriter, renderer, filteredUserDefinedFunctions); } } + private static boolean validSymbolName(ImmutableSet<String> symbolNames, String symbolName) { + if (symbolNames.isEmpty()) { + // Symbols prefixed with an underscore are private, and thus, by default, documentation + // should not be generated for them. + return !symbolName.startsWith("_"); + } else if (symbolNames.contains(symbolName)) { + return true; + } else if (symbolName.contains(".")) { + return symbolNames.contains(symbolName.substring(0, symbolName.indexOf('.'))); + } + return false; + } + private static ImmutableSet<String> getSymbolNames(List<String> args) { ImmutableSet.Builder<String> symbolNameSet = ImmutableSet.builder(); for (int argi = 2; argi < args.size(); argi++) { @@ -332,7 +339,7 @@ ImmutableList.Builder<RuleInfo> unknownNamedRules, ImmutableMap.Builder<String, ProviderInfo> providerInfoMap, ImmutableMap.Builder<String, UserDefinedFunction> userDefinedFunctionMap) - throws InterruptedException, IOException, LabelSyntaxException { + throws InterruptedException, IOException, LabelSyntaxException, EvalException { List<RuleInfo> ruleInfoList = new ArrayList<>(); List<ProviderInfo> providerInfoList = new ArrayList<>(); @@ -366,6 +373,15 @@ UserDefinedFunction userDefinedFunction = (UserDefinedFunction) envEntry.getValue(); userDefinedFunctionMap.put(envEntry.getKey(), userDefinedFunction); } + if (envEntry.getValue() instanceof FakeStructApi) { + FakeStructApi struct = (FakeStructApi) envEntry.getValue(); + for (String field : struct.getFieldNames()) { + if (struct.getValue(field) instanceof UserDefinedFunction) { + UserDefinedFunction userDefinedFunction = (UserDefinedFunction) struct.getValue(field); + userDefinedFunctionMap.put(envEntry.getKey() + "." + field, userDefinedFunction); + } + } + } } unknownNamedRules.addAll(ruleFunctions.values().stream()
diff --git a/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeStructApi.java b/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeStructApi.java index cd99533..82d8ec7 100644 --- a/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeStructApi.java +++ b/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeStructApi.java
@@ -64,7 +64,7 @@ @Override public ImmutableCollection<String> getFieldNames() throws EvalException { - return ImmutableList.of(); + return ImmutableList.copyOf(objects.keySet()); } @Nullable
diff --git a/src/test/java/com/google/devtools/build/skydoc/BUILD b/src/test/java/com/google/devtools/build/skydoc/BUILD index 05d9df3..56a28a2 100644 --- a/src/test/java/com/google/devtools/build/skydoc/BUILD +++ b/src/test/java/com/google/devtools/build/skydoc/BUILD
@@ -154,3 +154,20 @@ input_file = "testdata/function_basic_test/input.bzl", skydoc = "//src/main/java/com/google/devtools/build/skydoc", ) + +skydoc_test( + name = "module_test", + golden_file = "testdata/module_test/golden.txt", + input_file = "testdata/module_test/input.bzl", + skydoc = "//src/main/java/com/google/devtools/build/skydoc", +) + +skydoc_test( + name = "module_test_with_whitelist", + golden_file = "testdata/module_test/golden.txt", + input_file = "testdata/module_test/input.bzl", + skydoc = "//src/main/java/com/google/devtools/build/skydoc", + whitelisted_symbols = [ + "my_module", + ], +)
diff --git a/src/test/java/com/google/devtools/build/skydoc/testdata/config_apis_test/golden.txt b/src/test/java/com/google/devtools/build/skydoc/testdata/config_apis_test/golden.txt index cb1e448..b16c972 100644 --- a/src/test/java/com/google/devtools/build/skydoc/testdata/config_apis_test/golden.txt +++ b/src/test/java/com/google/devtools/build/skydoc/testdata/config_apis_test/golden.txt
@@ -58,32 +58,6 @@ </table> -## _build_setting_impl - -<pre> -_build_setting_impl(<a href="#_build_setting_impl-ctx">ctx</a>) -</pre> - - - -### Parameters - -<table class="params-table"> - <colgroup> - <col class="col-param" /> - <col class="col-description" /> - </colgroup> - <tbody> - <tr id="_build_setting_impl-ctx"> - <td><code>ctx</code></td> - <td> - required. - </td> - </tr> - </tbody> -</table> - - ## exercise_the_api <pre>
diff --git a/src/test/java/com/google/devtools/build/skydoc/testdata/module_test/golden.txt b/src/test/java/com/google/devtools/build/skydoc/testdata/module_test/golden.txt new file mode 100644 index 0000000..03f0a35 --- /dev/null +++ b/src/test/java/com/google/devtools/build/skydoc/testdata/module_test/golden.txt
@@ -0,0 +1,105 @@ +## my_module.assert_non_empty + +<pre> +my_module.assert_non_empty(<a href="#my_module.assert_non_empty-some_list">some_list</a>, <a href="#my_module.assert_non_empty-other_list">other_list</a>) +</pre> + +Asserts the two given lists are not empty. + +### Parameters + +<table class="params-table"> + <colgroup> + <col class="col-param" /> + <col class="col-description" /> + </colgroup> + <tbody> + <tr id="my_module.assert_non_empty-some_list"> + <td><code>some_list</code></td> + <td> + required. + <p> + The first list + </p> + </td> + </tr> + <tr id="my_module.assert_non_empty-other_list"> + <td><code>other_list</code></td> + <td> + required. + <p> + The second list + </p> + </td> + </tr> + </tbody> +</table> + + +## my_module.min + +<pre> +my_module.min(<a href="#my_module.min-integers">integers</a>) +</pre> + +Returns the minimum of given elements. + +### Parameters + +<table class="params-table"> + <colgroup> + <col class="col-param" /> + <col class="col-description" /> + </colgroup> + <tbody> + <tr id="my_module.min-integers"> + <td><code>integers</code></td> + <td> + required. + <p> + A list of integers. Must not be empty. + </p> + </td> + </tr> + </tbody> +</table> + + +## my_module.join_strings + +<pre> +my_module.join_strings(<a href="#my_module.join_strings-strings">strings</a>, <a href="#my_module.join_strings-delimiter">delimiter</a>) +</pre> + +Joins the given strings with a delimiter. + +### Parameters + +<table class="params-table"> + <colgroup> + <col class="col-param" /> + <col class="col-description" /> + </colgroup> + <tbody> + <tr id="my_module.join_strings-strings"> + <td><code>strings</code></td> + <td> + required. + <p> + A list of strings to join. + </p> + </td> + </tr> + <tr id="my_module.join_strings-delimiter"> + <td><code>delimiter</code></td> + <td> + optional. default is <code>", "</code> + <p> + The delimiter to use + </p> + </td> + </tr> + </tbody> +</table> + +
diff --git a/src/test/java/com/google/devtools/build/skydoc/testdata/module_test/input.bzl b/src/test/java/com/google/devtools/build/skydoc/testdata/module_test/input.bzl new file mode 100644 index 0000000..d1b04b3 --- /dev/null +++ b/src/test/java/com/google/devtools/build/skydoc/testdata/module_test/input.bzl
@@ -0,0 +1,43 @@ +"""A test that verifies documenting a module of functions.""" + +def _min(integers): + """Returns the minimum of given elements. + + Args: + integers: A list of integers. Must not be empty. + + Returns: + The minimum integer in the given list. + """ + _ignore = [integers] + return 42 + +def _assert_non_empty(some_list, other_list): + """Asserts the two given lists are not empty. + + Args: + some_list: The first list + other_list: The second list + """ + _ignore = [some_list, other_list] + fail("Not implemented") + +def _join_strings(strings, delimiter = ", "): + """Joins the given strings with a delimiter. + + Args: + strings: A list of strings to join. + delimiter: The delimiter to use + + Returns: + The joined string. + """ + _ignore = [strings, delimiter] + return "" + +my_module = struct( + dropped_field = "Note this field should not be documented", + assert_non_empty = _assert_non_empty, + min = _min, + join_strings = _join_strings, +)
diff --git a/src/test/java/com/google/devtools/build/skydoc/testdata/unknown_name_test/golden.txt b/src/test/java/com/google/devtools/build/skydoc/testdata/unknown_name_test/golden.txt index 3db7875..3e9ae4a 100644 --- a/src/test/java/com/google/devtools/build/skydoc/testdata/unknown_name_test/golden.txt +++ b/src/test/java/com/google/devtools/build/skydoc/testdata/unknown_name_test/golden.txt
@@ -1,57 +1,3 @@ -<a name="#<unknown name>"></a> -## <unknown name> - -<pre> -<unknown name>(<a href="#<unknown name>-name">name</a>, <a href="#<unknown name>-first">first</a>, <a href="#<unknown name>-fourth">fourth</a>, <a href="#<unknown name>-second">second</a>, <a href="#<unknown name>-third">third</a>) -</pre> - - - -### Attributes - -<table class="params-table"> - <colgroup> - <col class="col-param" /> - <col class="col-description" /> - </colgroup> - <tbody> - <tr id="<unknown name>-name"> - <td><code>name</code></td> - <td> - <a href="https://bazel.build/docs/build-ref.html#name">Name</a>; required - <p> - A unique name for this target. - </p> - </td> - </tr> - <tr id="<unknown name>-first"> - <td><code>first</code></td> - <td> - <a href="https://bazel.build/docs/build-ref.html#labels">Label</a>; required - </td> - </tr> - <tr id="<unknown name>-fourth"> - <td><code>fourth</code></td> - <td> - Boolean; optional - </td> - </tr> - <tr id="<unknown name>-second"> - <td><code>second</code></td> - <td> - <a href="https://bazel.build/docs/skylark/lib/dict.html">Dictionary: String -> String</a>; required - </td> - </tr> - <tr id="<unknown name>-third"> - <td><code>third</code></td> - <td> - <a href="https://bazel.build/docs/build-ref.html#labels">Label</a>; required - </td> - </tr> - </tbody> -</table> - - ## my_rule_impl <pre>