Add go_sdk rule and GoSDK provider (#1606)

go_sdk is a new rule that gathers information about an SDK and returns
a GoSDK provider which will get wired into the toolchain.

package_list is a new rule that generates a list of importable
packages from the sources in the SDK (previously, we invoked go list,
which is slower).
diff --git a/go/def.bzl b/go/def.bzl
index 24c5cc5..b0c82e3 100644
--- a/go/def.bzl
+++ b/go/def.bzl
@@ -23,6 +23,7 @@
     _GoLibrary = "GoLibrary",
     _GoPath = "GoPath",
     _GoSource = "GoSource",
+    _GoSDK = "GoSDK",
 )
 load(
     "@io_bazel_rules_go//go/private:repositories.bzl",
@@ -36,6 +37,10 @@
     "go_local_sdk",
 )
 load(
+    "@io_bazel_rules_go//go/private:rules/sdk.bzl",
+    "go_sdk",
+)
+load(
     "@io_bazel_rules_go//go/private:go_toolchain.bzl",
     "go_toolchain",
 )
@@ -89,6 +94,9 @@
 GoArchiveData = _GoArchiveData
 """See go/providers.rst#GoArchiveData for full documentation."""
 
+GoSDK = _GoSDK
+"""See go/providers.rst#GoSDK for full documentation."""
+
 go_library = _go_library_macro
 """See go/core.rst#go_library for full documentation."""
 
diff --git a/go/private/providers.bzl b/go/private/providers.bzl
index 15e01fc..9a5a3a3 100644
--- a/go/private/providers.bzl
+++ b/go/private/providers.bzl
@@ -48,6 +48,27 @@
 
 GoPath = provider()
 
+GoSDK = provider(
+    doc = "Contains information about the Go SDK used in the toolchain",
+    fields = {
+        "goos": "The host OS the SDK was built for.",
+        "goarch": "The host architecture the SDK was built for.",
+        "root_file": "A file in the SDK root directory",
+        "libs": ("List of pre-compiled .a files for the standard library " +
+                 "built for the execution platform."),
+        "headers": ("List of .h files from pkg/include that may be included " +
+                    "in assembly sources."),
+        "srcs": ("List of source files for importable packages in the " +
+                 "standard library. Internal, vendored, and tool packages " +
+                 "may not be included."),
+        "package_list": ("A file containing a list of importable packages " +
+                         "in the standard library."),
+        "tools": ("List of executable files from pkg/tool " +
+                  "built for the execution platform."),
+        "go": "The go binary file",
+    },
+)
+
 GoStdLib = provider()
 
 GoBuilders = provider()
diff --git a/go/private/rules/sdk.bzl b/go/private/rules/sdk.bzl
new file mode 100644
index 0000000..3995080
--- /dev/null
+++ b/go/private/rules/sdk.bzl
@@ -0,0 +1,124 @@
+# Copyright 2014 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.
+
+load(
+    "@io_bazel_rules_go//go/private:providers.bzl",
+    "GoSDK",
+)
+
+def _go_sdk_impl(ctx):
+    return [GoSDK(
+        goos = ctx.attr.goos,
+        goarch = ctx.attr.goarch,
+        root_file = ctx.file.root_file,
+        package_list = ctx.file.package_list,
+        libs = ctx.files.libs,
+        headers = ctx.files.headers,
+        srcs = ctx.files.srcs,
+        tools = ctx.files.tools,
+        go = ctx.file.go,
+    )]
+
+go_sdk = rule(
+    _go_sdk_impl,
+    attrs = {
+        "goos": attr.string(
+            mandatory = True,
+            doc = "The host OS the SDK was built for",
+        ),
+        "goarch": attr.string(
+            mandatory = True,
+            doc = "The host architecture the SDK was built for",
+        ),
+        "root_file": attr.label(
+            mandatory = True,
+            allow_single_file = True,
+            doc = "A file in the SDK root directory. Used to determine GOROOT.",
+        ),
+        "package_list": attr.label(
+            mandatory = True,
+            allow_single_file = True,
+            doc = ("A text file containing a list of packages in the " +
+                   "standard library that may be imported."),
+        ),
+        "libs": attr.label_list(
+            allow_files = [".a"],
+            doc = ("Pre-compiled .a files for the standard library, " +
+                   "built for the execution platform"),
+        ),
+        "headers": attr.label_list(
+            allow_files = [".h"],
+            doc = (".h files from pkg/include that may be included in " +
+                   "assembly sources"),
+        ),
+        "srcs": attr.label_list(
+            allow_files = True,
+            doc = "Source files for packages in the standard library",
+        ),
+        "tools": attr.label_list(
+            allow_files = True,
+            cfg = "host",
+            doc = ("List of executable files from pkg/tool " +
+                   "built for the execution platform"),
+        ),
+        "go": attr.label(
+            mandatory = True,
+            allow_single_file = True,
+            executable = True,
+            cfg = "host",
+            doc = "The go binary",
+        ),
+    },
+    doc = ("Collects information about a Go SDK. The SDK must have a normal " +
+           "GOROOT directory structure."),
+    provides = [GoSDK],
+)
+
+def _package_list_impl(ctx):
+    packages = {}
+    src_dir = ctx.file.root_file.dirname + "/src/"
+    for src in ctx.files.srcs:
+        pkg_src_dir = src.dirname
+        if not pkg_src_dir.startswith(src_dir):
+            continue
+        pkg_name = pkg_src_dir[len(src_dir):]
+        if any([prefix in pkg_name for prefix in ("vendor/", "cmd/")]):
+            continue
+        packages[pkg_name] = None
+    content = "\n".join(sorted(packages.keys())) + "\n"
+    ctx.actions.write(ctx.outputs.out, content)
+    return [DefaultInfo(files = depset([ctx.outputs.out]))]
+
+package_list = rule(
+    _package_list_impl,
+    attrs = {
+        "srcs": attr.label_list(
+            allow_files = True,
+            doc = "Source files for packages in the standard library",
+        ),
+        "root_file": attr.label(
+            mandatory = True,
+            allow_single_file = True,
+            doc = "A file in the SDK root directory. Used to determine GOROOT.",
+        ),
+        "out": attr.output(
+            mandatory = True,
+            doc = "File to write. Must be 'packages.txt'.",
+            # Gazelle depends on this file directly. It has to be an output
+            # attribute because Bazel has no other way of knowing what rule
+            # produces this file.
+            # TODO(jayconrod): Update Gazelle and simplify this.
+        ),
+    },
+)