Internal change

PiperOrigin-RevId: 210729402
diff --git a/src/main/java/com/google/devtools/build/docgen/ApiExporter.java b/src/main/java/com/google/devtools/build/docgen/ApiExporter.java
index 4091322..de98f0d 100644
--- a/src/main/java/com/google/devtools/build/docgen/ApiExporter.java
+++ b/src/main/java/com/google/devtools/build/docgen/ApiExporter.java
@@ -22,17 +22,30 @@
 import com.google.devtools.build.docgen.skylark.SkylarkMethodDoc;
 import com.google.devtools.build.docgen.skylark.SkylarkModuleDoc;
 import com.google.devtools.build.docgen.skylark.SkylarkParamDoc;
+import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider;
 import com.google.devtools.build.lib.util.Classpath.ClassPathException;
+import com.google.devtools.common.options.OptionsParser;
 import java.io.BufferedOutputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Collections;
 import java.util.Map;
 
 /** The main class for the Skylark documentation generator. */
 public class ApiExporter {
+  private static ConfiguredRuleClassProvider createRuleClassProvider(String classProvider)
+      throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException,
+          IllegalAccessException {
+    Class<?> providerClass = Class.forName(classProvider);
+    Method createMethod = providerClass.getMethod("create");
+    return (ConfiguredRuleClassProvider) createMethod.invoke(null);
+  }
 
-  private static void appendBuiltins(String builtinsFile) {
-    try (BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(builtinsFile))) {
+  private static void appendBuiltins(
+      ProtoFileBuildEncyclopediaProcessor processor, String filename) {
+    try (BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(filename))) {
       Builtins.Builder builtins = Builtins.newBuilder();
 
       Map<String, SkylarkModuleDoc> allTypes = SkylarkDocumentationCollector.collectModules();
@@ -58,6 +71,12 @@
             type.addField(collectFieldInfo(meth));
           }
         }
+        // Add native rules to the native type.
+        if (mod.getName().equals("native")) {
+          for (Value.Builder rule : processor.getNativeRules()) {
+            type.addField(rule);
+          }
+        }
         builtins.addType(type);
 
         // Include SkylarkModuleDoc in Builtins as a Value.
@@ -96,15 +115,46 @@
     return field;
   }
 
+  private static void printUsage(OptionsParser parser) {
+    System.err.println(
+        "Usage: api_exporter_bin -n product_name -p rule_class_provider (-i input_dir)+\n"
+            + "   -f outputFile [-b blacklist] [-h]\n\n"
+            + "Exports all Starlark builtins to a file including the embedded native rules.\n"
+            + "The product name (-n), rule class provider (-p), output file (-f) and at least \n"
+            + " one input_dir (-i) must be specified.\n");
+    System.err.println(
+        parser.describeOptionsWithDeprecatedCategories(
+            Collections.<String, String>emptyMap(), OptionsParser.HelpVerbosity.LONG));
+  }
+
   public static void main(String[] args) {
-    if (args.length != 1) {
-      throw new IllegalArgumentException(
-          "Expected one argument. Usage:\n" + "{api_exporter_bin} {builtin_output_file}");
+    OptionsParser parser = OptionsParser.newOptionsParser(BuildEncyclopediaOptions.class);
+    parser.parseAndExitUponError(args);
+    BuildEncyclopediaOptions options = parser.getOptions(BuildEncyclopediaOptions.class);
+
+    if (options.help) {
+      printUsage(parser);
+      Runtime.getRuntime().exit(0);
     }
 
-    String builtinsProtoFile = args[0];
+    if (options.productName.isEmpty()
+        || options.inputDirs.isEmpty()
+        || options.provider.isEmpty()
+        || options.outputFile.isEmpty()) {
+      printUsage(parser);
+      Runtime.getRuntime().exit(1);
+    }
 
-    appendBuiltins(builtinsProtoFile);
+    try {
+      ProtoFileBuildEncyclopediaProcessor processor =
+          new ProtoFileBuildEncyclopediaProcessor(
+              options.productName, createRuleClassProvider(options.provider));
+      processor.generateDocumentation(options.inputDirs, options.outputFile, options.blacklist);
+
+      appendBuiltins(processor, options.outputFile);
+    } catch (Throwable e) {
+      System.err.println("ERROR: " + e.getMessage());
+    }
   }
 }
 
diff --git a/src/main/java/com/google/devtools/build/docgen/BuildEncyclopediaOptions.java b/src/main/java/com/google/devtools/build/docgen/BuildEncyclopediaOptions.java
index 3c68ccb..bfd5840 100644
--- a/src/main/java/com/google/devtools/build/docgen/BuildEncyclopediaOptions.java
+++ b/src/main/java/com/google/devtools/build/docgen/BuildEncyclopediaOptions.java
@@ -55,6 +55,15 @@
   public String provider;
 
   @Option(
+      name = "output_file",
+      abbrev = 'f',
+      defaultValue = "",
+      documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+      effectTags = {OptionEffectTag.UNKNOWN},
+      help = "An output file.")
+  public String outputFile;
+
+  @Option(
     name = "output_dir",
     abbrev = 'o',
     defaultValue = ".",
diff --git a/src/main/java/com/google/devtools/build/docgen/ProtoFileBuildEncyclopediaProcessor.java b/src/main/java/com/google/devtools/build/docgen/ProtoFileBuildEncyclopediaProcessor.java
new file mode 100644
index 0000000..2d75605
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/docgen/ProtoFileBuildEncyclopediaProcessor.java
@@ -0,0 +1,65 @@
+// Copyright 2018 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.build.docgen;
+
+import com.google.devtools.build.docgen.builtin.BuiltinProtos.Callable;
+import com.google.devtools.build.docgen.builtin.BuiltinProtos.Param;
+import com.google.devtools.build.docgen.builtin.BuiltinProtos.Value;
+import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/** Assembles a list of native rules that can be exported to a builtin.proto file. */
+public class ProtoFileBuildEncyclopediaProcessor extends BuildEncyclopediaProcessor {
+  private List<Value.Builder> nativeRules = null;
+
+  public ProtoFileBuildEncyclopediaProcessor(
+      String productName, ConfiguredRuleClassProvider ruleClassProvider) {
+    super(productName, ruleClassProvider);
+    nativeRules = new ArrayList<>();
+  }
+
+  @Override
+  public void generateDocumentation(List<String> inputDirs, String outputFile, String blackList)
+      throws BuildEncyclopediaDocException, IOException {
+    BuildDocCollector collector = new BuildDocCollector(productName, ruleClassProvider, false);
+    RuleLinkExpander expander = new RuleLinkExpander(productName, true);
+    Map<String, RuleDocumentation> ruleDocEntries =
+        collector.collect(inputDirs, blackList, expander);
+    RuleFamilies ruleFamilies = assembleRuleFamilies(ruleDocEntries.values());
+
+    for (RuleFamily entry : ruleFamilies.all) {
+      for (RuleDocumentation doc : entry.getRules()) {
+        Value.Builder rule = Value.newBuilder();
+        rule.setName(doc.getRuleName());
+        rule.setDoc(doc.getHtmlDocumentation());
+        Callable.Builder callable = Callable.newBuilder();
+        for (RuleDocumentationAttribute attr : doc.getAttributes()) {
+          Param.Builder param = Param.newBuilder();
+          param.setName(attr.getAttributeName());
+          callable.addParam(param);
+        }
+        rule.setCallable(callable);
+        nativeRules.add(rule);
+      }
+    }
+  }
+
+  public List<Value.Builder> getNativeRules() {
+    return nativeRules;
+  }
+}