Action generation on the toolchain (#762)

* Action generation on the toolchain

This separates the action generating code (mostly) from the rule
implementations.
Action generators are now registered with the toolchain, which means toolchains
can swap out the implementation of the actions at will.
This required a lot of breaking up of files, and to make it easy to understand
and reason about, the rule parts were put in a separate folder from the action
parts.
The only thing that should depend on things in actions/ is the toolchain
constructor, all access from rules go through the toolchain.

This should not be a functional difference, but it does enable a bunch of new
things. This also an example of what I would like the cc toolchain to look like
so we can spit out compile and link actions inside go_library.

* Review feedback
diff --git a/BUILD b/BUILD
index 0d6586f..3cf19d3 100644
--- a/BUILD
+++ b/BUILD
@@ -1,5 +1,5 @@
 load("@io_bazel_rules_go//go:def.bzl", "gazelle", "go_prefix", "go_path", "go_vet_test")
-load("@io_bazel_rules_go//go/private:lines_sorted_test.bzl", "lines_sorted_test")
+load("@io_bazel_rules_go//go/private:tools/lines_sorted_test.bzl", "lines_sorted_test")
 load("@io_bazel_rules_go//go/private:info.bzl", "go_info")
 load("@io_bazel_rules_go//proto:go_proto_library.bzl", "go_google_protobuf")
 
diff --git a/go/def.bzl b/go/def.bzl
index 0da44ca..68a6d99 100644
--- a/go/def.bzl
+++ b/go/def.bzl
@@ -12,25 +12,36 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+load("@io_bazel_rules_go//go/private:go_repository.bzl",
+    "go_repository",
+)
 load("@io_bazel_rules_go//go/private:providers.bzl",
     _GoLibrary = "GoLibrary",
     _GoBinary = "GoBinary",
 )
-load("@io_bazel_rules_go//go/private:repositories.bzl", "go_rules_dependencies", "go_register_toolchains")
-load("@io_bazel_rules_go//go/private:go_repository.bzl", "go_repository")
-load("@io_bazel_rules_go//go/private:go_prefix.bzl", "go_prefix")
-load("@io_bazel_rules_go//go/private:embed_data.bzl", "go_embed_data")
-load("@io_bazel_rules_go//go/private:gazelle.bzl", "gazelle")
-load("@io_bazel_rules_go//go/private:wrappers.bzl",
+load("@io_bazel_rules_go//go/private:repositories.bzl",
+    "go_rules_dependencies",
+    "go_register_toolchains",
+)
+load("@io_bazel_rules_go//go/private:rules/prefix.bzl", 
+    "go_prefix",
+)
+load("@io_bazel_rules_go//go/private:rules/wrappers.bzl",
     _go_library_macro = "go_library_macro",
     _go_binary_macro = "go_binary_macro",
     _go_test_macro = "go_test_macro",
 )
-load("@io_bazel_rules_go//go/private/tools:path.bzl", 
-  _go_path = "go_path",
+load("@io_bazel_rules_go//go/private:tools/embed_data.bzl", 
+    "go_embed_data",
 )
-load("@io_bazel_rules_go//go/private/tools:vet.bzl", 
-  _go_vet_test = "go_vet_test",
+load("@io_bazel_rules_go//go/private:tools/gazelle.bzl", 
+    "gazelle",
+)
+load("@io_bazel_rules_go//go/private:tools/path.bzl", 
+    _go_path = "go_path",
+)
+load("@io_bazel_rules_go//go/private:tools/vet.bzl", 
+    _go_vet_test = "go_vet_test",
 )
 
 GoLibrary = _GoLibrary
diff --git a/go/private/BUILD b/go/private/BUILD
index 17b1fb4..6c436b3 100644
--- a/go/private/BUILD
+++ b/go/private/BUILD
@@ -2,7 +2,7 @@
 
 filegroup(
     name = "all_rules",
-    srcs = glob(["*.bzl"]) + ["//go/private/tools:all_rules"],
+    srcs = glob(["**/*.bzl"]),
     visibility = ["//visibility:public"],
 )
 
diff --git a/go/private/BUILD.sdk.bazel b/go/private/BUILD.sdk.bazel
index 1915954..4c9e12a 100644
--- a/go/private/BUILD.sdk.bazel
+++ b/go/private/BUILD.sdk.bazel
@@ -1,4 +1,4 @@
-load("@io_bazel_rules_go//go/private:go_root.bzl", "go_root")
+load("@io_bazel_rules_go//go/private:rules/root.bzl", "go_root")
 
 package(default_visibility = [ "//visibility:public" ])
 
diff --git a/go/private/asm.bzl b/go/private/actions/asm.bzl
similarity index 95%
rename from go/private/asm.bzl
rename to go/private/actions/asm.bzl
index 556735e..f68ad63 100644
--- a/go/private/asm.bzl
+++ b/go/private/actions/asm.bzl
@@ -12,7 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-def emit_go_asm_action(ctx, go_toolchain, source, hdrs, out_obj):
+def emit_asm(ctx, go_toolchain, source, hdrs, out_obj):
   """Construct the command line for compiling Go Assembly code.
   Constructs a symlink tree to accomodate for workspace name.
   Args:
diff --git a/go/private/actions/compile.bzl b/go/private/actions/compile.bzl
new file mode 100644
index 0000000..7b1e279
--- /dev/null
+++ b/go/private/actions/compile.bzl
@@ -0,0 +1,60 @@
+# 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:common.bzl", 
+    "RACE_MODE",
+)
+load("@io_bazel_rules_go//go/private:providers.bzl",
+    "get_library",
+    "get_searchpath",
+)
+
+def emit_compile(ctx, go_toolchain, sources, golibs, mode, out_object, gc_goopts):
+  """Construct the command line for compiling Go code.
+
+  Args:
+    ctx: The skylark Context.
+    sources: an iterable of source code artifacts (or CTs? or labels?)
+    golibs: a depset of representing all imported libraries.
+    mode: Controls the compilation setup affecting things like enabling profilers and sanitizers.
+      This must be one of the values in common.bzl#compile_modes
+    out_object: the object file that should be produced
+    gc_goopts: additional flags to pass to the compiler.
+  """
+
+  # Add in any mode specific behaviours
+  if mode == RACE_MODE:
+    gc_goopts = gc_goopts + ("-race",)
+
+  gc_goopts = [ctx.expand_make_variables("gc_goopts", f, {}) for f in gc_goopts]
+  inputs = depset([go_toolchain.tools.go]) + sources
+  go_sources = [s.path for s in sources if not s.basename.startswith("_cgo")]
+  cgo_sources = [s.path for s in sources if s.basename.startswith("_cgo")]
+  args = [go_toolchain.tools.go.path]
+  for src in go_sources:
+    args += ["-src", src]
+  for golib in golibs:
+    inputs += [get_library(golib, mode)]
+    args += ["-dep", golib.importpath]
+    args += ["-I", get_searchpath(golib,mode)]
+  args += ["-o", out_object.path, "-trimpath", ".", "-I", "."]
+  args += ["--"] + gc_goopts + go_toolchain.flags.compile + cgo_sources
+  ctx.action(
+      inputs = list(inputs),
+      outputs = [out_object],
+      mnemonic = "GoCompile",
+      executable = go_toolchain.tools.compile,
+      arguments = args,
+      env = go_toolchain.env,
+  )
diff --git a/go/private/actions/cover.bzl b/go/private/actions/cover.bzl
new file mode 100644
index 0000000..a3cc9f8
--- /dev/null
+++ b/go/private/actions/cover.bzl
@@ -0,0 +1,51 @@
+# 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.
+
+def emit_cover(ctx, go_toolchain, sources):
+  """Construct the command line for test coverage instrument.
+
+  Args:
+    ctx: The skylark Context.
+    out_object: the object file for the library being compiled. Used to name
+      cover files.
+    sources: an iterable of Go source files.
+
+  Returns:
+    A list of Go source code files which might be coverage instrumented.
+  """
+  outputs = []
+  # TODO(linuxerwang): make the mode configurable.
+  cover_vars = []
+
+  for src in sources:
+    if (not src.basename.endswith(".go") or 
+        src.basename.endswith("_test.go") or 
+        src.basename.endswith(".cover.go")):
+      outputs += [src]
+      continue
+
+    cover_var = "Cover_" + src.basename[:-3].replace("-", "_").replace(".", "_")
+    cover_vars += ["{}={}".format(cover_var,src.short_path)]
+    out = ctx.new_file(cover_var + '.cover.go')
+    outputs += [out]
+    ctx.action(
+        inputs = [src] + go_toolchain.data.tools,
+        outputs = [out],
+        mnemonic = "GoCover",
+        executable = go_toolchain.tools.go,
+        arguments = ["tool", "cover", "--mode=set", "-var=%s" % cover_var, "-o", out.path, src.path],
+        env = go_toolchain.env,
+    )
+
+  return outputs, tuple(cover_vars)
diff --git a/go/private/actions/library.bzl b/go/private/actions/library.bzl
new file mode 100644
index 0000000..c847bd0
--- /dev/null
+++ b/go/private/actions/library.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:common.bzl", 
+    "dict_of",
+    "split_srcs",
+    "join_srcs",
+    "compile_modes",
+)
+load("@io_bazel_rules_go//go/private:providers.bzl", 
+    "GoLibrary", 
+    "CgoLibrary",
+    "library_attr",
+    "searchpath_attr",
+)
+
+def emit_library(ctx, go_toolchain, srcs, deps, cgo_object, library, want_coverage, importpath, golibs=[]):
+  dep_runfiles = [d.data_runfiles for d in deps]
+  direct = depset(golibs)
+  gc_goopts = tuple(ctx.attr.gc_goopts)
+  cgo_deps = depset()
+  cover_vars = ()
+  if library:
+    golib = library[GoLibrary]
+    cgolib = library[CgoLibrary]
+    srcs = golib.transformed + srcs
+    cover_vars += golib.cover_vars
+    direct += golib.direct
+    dep_runfiles += [library.data_runfiles]
+    gc_goopts += golib.gc_goopts
+    cgo_deps += golib.cgo_deps
+    if cgolib.object:
+      if cgo_object:
+        fail("go_library %s cannot have cgo_object because the package " +
+             "already has cgo_object in %s" % (ctx.label.name,
+                                               golib.name))
+      cgo_object = cgolib.object
+  source = split_srcs(srcs)
+  if source.c:
+    fail("c sources in non cgo rule")
+  if not source.go:
+    fail("no go sources")
+
+  if cgo_object:
+    dep_runfiles += [cgo_object.data_runfiles]
+    cgo_deps += cgo_object.cgo_deps
+
+  extra_objects = [cgo_object.cgo_obj] if cgo_object else []
+  for src in source.asm:
+    obj = ctx.new_file(src, "%s.dir/%s.o" % (ctx.label.name, src.basename[:-2]))
+    go_toolchain.actions.asm(ctx, go_toolchain, src, source.headers, obj)
+    extra_objects += [obj]
+
+  for dep in deps:
+    direct += [dep[GoLibrary]]
+
+  transitive = depset()
+  for golib in direct:
+    transitive += [golib]
+    transitive += golib.transitive
+
+  go_srcs = source.go
+  if want_coverage:
+    go_srcs, cvars = go_toolchain.actions.cover(ctx, go_toolchain, go_srcs)
+    cover_vars += cvars
+
+  lib_name = importpath + ".a"
+  mode_fields = {} # These are added to the GoLibrary provider directly
+  for mode in compile_modes:
+    out_lib = ctx.new_file("~{}~{}~/{}".format(mode, ctx.label.name, lib_name))
+    out_object = ctx.new_file("~{}~{}~/{}.o".format(mode, ctx.label.name, importpath))
+    searchpath = out_lib.path[:-len(lib_name)]
+    mode_fields[library_attr(mode)] = out_lib
+    mode_fields[searchpath_attr(mode)] = searchpath
+    go_toolchain.actions.compile(ctx,
+        go_toolchain = go_toolchain,
+        sources = go_srcs,
+        golibs = direct,
+        mode = mode,
+        out_object = out_object,
+        gc_goopts = gc_goopts,
+    )
+    go_toolchain.actions.pack(ctx, go_toolchain, out_lib, [out_object] + extra_objects)
+
+  dylibs = []
+  if cgo_object:
+    dylibs += [d for d in cgo_object.cgo_deps if d.path.endswith(".so")]
+
+  runfiles = ctx.runfiles(files = dylibs, collect_data = True)
+  for d in dep_runfiles:
+    runfiles = runfiles.merge(d)
+
+  transformed = dict_of(source)
+  transformed["go"] = go_srcs
+
+  return [
+      GoLibrary(
+          label = ctx.label,
+          importpath = importpath, # The import path for this library
+          direct = direct, # The direct depencancies of the library
+          transitive = transitive, # The transitive set of go libraries depended on
+          srcs = depset(srcs), # The original sources
+          transformed = join_srcs(struct(**transformed)), # The transformed sources actually compiled
+          cgo_deps = cgo_deps, # The direct cgo dependencies of this library
+          gc_goopts = gc_goopts, # The options this library was compiled with
+          runfiles = runfiles, # The runfiles needed for things including this library
+          cover_vars = cover_vars, # The cover variables for this library
+          **mode_fields
+      ),
+      CgoLibrary(
+          object = cgo_object,
+      ),
+  ]
diff --git a/go/private/actions/link.bzl b/go/private/actions/link.bzl
new file mode 100644
index 0000000..8ff3188
--- /dev/null
+++ b/go/private/actions/link.bzl
@@ -0,0 +1,132 @@
+# 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:common.bzl",
+    "RACE_MODE",
+)
+load("@io_bazel_rules_go//go/private:providers.bzl",
+    "get_library",
+    "get_searchpath",
+)
+
+def emit_link(ctx, go_toolchain, library, mode, executable, gc_linkopts, x_defs):
+  """Adds an action to link the supplied library in the given mode, producing the executable.
+  Args:
+    ctx: The skylark Context.
+    library: The library to link.
+    mode: Controls the linking setup affecting things like enabling profilers and sanitizers.
+      This must be one of the values in common.bzl#compile_modes
+    executable: The binary to produce.
+    gc_linkopts: basic link options, these may be adjusted by the mode.
+    x_defs: link defines, including build stamping ones
+  """
+
+  # Add in any mode specific behaviours
+  if mode == RACE_MODE:
+    gc_linkopts += ["-race"]
+
+  config_strip = len(ctx.configuration.bin_dir.path) + 1
+  pkg_depth = executable.dirname[config_strip:].count('/') + 1
+
+  ld = None
+  extldflags = []
+  if go_toolchain.external_linker:
+    ld = go_toolchain.external_linker.compiler_executable
+    extldflags = go_toolchain.external_linker.options
+  extldflags += ["-Wl,-rpath,$ORIGIN/" + ("../" * pkg_depth)]
+
+  gc_linkopts, extldflags = _extract_extldflags(gc_linkopts, extldflags)
+
+  link_opts = ["-L", "."]
+  libs = depset()
+  cgo_deps = depset()
+  for golib in depset([library]) + library.transitive:
+    libs += [get_library(golib, mode)]
+    link_opts += ["-L", get_searchpath(golib, mode)]
+    cgo_deps += golib.cgo_deps
+
+  for d in cgo_deps:
+    if d.basename.endswith('.so'):
+      short_dir = d.dirname[len(d.root.path):]
+      extldflags += ["-Wl,-rpath,$ORIGIN/" + ("../" * pkg_depth) + short_dir]
+
+  link_opts += ["-o", executable.path] + gc_linkopts
+
+  # Process x_defs, either adding them directly to linker options, or
+  # saving them to process through stamping support.
+  stamp_x_defs = {}
+  for k, v in x_defs.items():
+    if v.startswith("{") and v.endswith("}"):
+      stamp_x_defs[k] = v[1:-1]
+    else:
+      link_opts += ["-X", "%s=%s" % (k, v)]
+
+  link_opts += go_toolchain.flags.link
+  if ld: 
+    link_opts += [
+        "-extld", ld,
+        "-extldflags", " ".join(extldflags),
+    ]
+  link_opts += [get_library(golib, mode).path]
+  link_args = [go_toolchain.tools.go.path]
+  # Stamping support
+  stamp_inputs = []
+  if stamp_x_defs or ctx.attr.linkstamp:
+    stamp_inputs = [ctx.info_file, ctx.version_file]
+    for f in stamp_inputs:
+      link_args += ["-stamp", f.path]
+    for k,v in stamp_x_defs.items():
+      link_args += ["-X", "%s=%s" % (k, v)]
+    # linkstamp option support: read workspace status files,
+    # converting "KEY value" lines to "-X $linkstamp.KEY=value" arguments
+    # to the go linker.
+    if ctx.attr.linkstamp:
+      link_args += ["-linkstamp", ctx.attr.linkstamp]
+
+  link_args += ["--"] + link_opts
+
+  ctx.action(
+      inputs = list(libs + cgo_deps +
+                go_toolchain.data.tools + go_toolchain.data.crosstool + stamp_inputs),
+      outputs = [executable],
+      mnemonic = "GoLink",
+      executable = go_toolchain.tools.link,
+      arguments = link_args,
+      env = go_toolchain.env,
+  )
+
+def _extract_extldflags(gc_linkopts, extldflags):
+  """Extracts -extldflags from gc_linkopts and combines them into a single list.
+
+  Args:
+    gc_linkopts: a list of flags passed in through the gc_linkopts attributes.
+      ctx.expand_make_variables should have already been applied.
+    extldflags: a list of flags to be passed to the external linker.
+
+  Return:
+    A tuple containing the filtered gc_linkopts with external flags removed,
+    and a combined list of external flags.
+  """
+  filtered_gc_linkopts = []
+  is_extldflags = False
+  for opt in gc_linkopts:
+    if is_extldflags:
+      is_extldflags = False
+      extldflags += [opt]
+    elif opt == "-extldflags":
+      is_extldflags = True
+    else:
+      filtered_gc_linkopts += [opt]
+  return filtered_gc_linkopts, extldflags
+
diff --git a/go/private/actions/pack.bzl b/go/private/actions/pack.bzl
new file mode 100644
index 0000000..042e76e
--- /dev/null
+++ b/go/private/actions/pack.bzl
@@ -0,0 +1,31 @@
+# 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.
+
+def emit_pack(ctx, go_toolchain, out_lib, objects):
+  """Construct the command line for packing objects together.
+
+  Args:
+    ctx: The skylark Context.
+    out_lib: the archive that should be produced
+    objects: an iterable of object files to be added to the output archive file.
+  """
+  ctx.action(
+      inputs = objects + go_toolchain.data.tools,
+      outputs = [out_lib],
+      mnemonic = "GoPack",
+      executable = go_toolchain.tools.go,
+      arguments = ["tool", "pack", "c", out_lib.path] + [a.path for a in objects],
+      env = go_toolchain.env,
+  )
+
diff --git a/go/private/binary.bzl b/go/private/binary.bzl
deleted file mode 100644
index 138c6ca..0000000
--- a/go/private/binary.bzl
+++ /dev/null
@@ -1,226 +0,0 @@
-# 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:common.bzl",
-    "NORMAL_MODE",
-    "RACE_MODE",
-    "compile_modes",
-    "go_filetype",
-)
-load("@io_bazel_rules_go//go/private:library.bzl",
-    "emit_library_actions",
-    "get_library",
-    "get_searchpath",
-    "go_importpath",
-    "go_prefix_default",
-)
-load("@io_bazel_rules_go//go/private:providers.bzl", "GoLibrary", "GoBinary")
-
-def _go_binary_impl(ctx):
-  """go_binary_impl emits actions for compiling and linking a go executable."""
-  go_toolchain = ctx.toolchains["@io_bazel_rules_go//go:toolchain"]
-  golib, _ = emit_library_actions(ctx,
-      go_toolchain = go_toolchain,
-      srcs = ctx.files.srcs,
-      deps = ctx.attr.deps,
-      cgo_object = None,
-      library = ctx.attr.library,
-      want_coverage = False,
-      importpath = go_importpath(ctx),
-  )
-
-  # Default (dynamic) linking
-  race_executable = ctx.new_file(ctx.attr.name + ".race")
-  for mode in compile_modes:
-    executable = ctx.outputs.executable
-    if mode == RACE_MODE:
-      executable = race_executable
-    emit_go_link_action(
-        ctx,
-        go_toolchain = go_toolchain,
-        library=golib,
-        mode=mode,
-        executable=executable,
-        gc_linkopts=gc_linkopts(ctx),
-        x_defs=ctx.attr.x_defs,
-    )
-
-  # Static linking (in the 'static' output group)
-  static_linkopts = [
-      "-linkmode", "external",
-      "-extldflags", "-static",
-  ]
-  static_executable = ctx.new_file(ctx.attr.name + ".static")
-  emit_go_link_action(
-      ctx,
-      go_toolchain = go_toolchain,
-      library=golib,
-      mode=NORMAL_MODE,
-      executable=static_executable,
-      gc_linkopts=gc_linkopts(ctx) + static_linkopts,
-      x_defs=ctx.attr.x_defs,
-  )
-
-  return [
-      golib,
-      GoBinary(
-          executable = ctx.outputs.executable,
-          static = static_executable,
-          race = race_executable,
-      ),
-      DefaultInfo(
-          files = depset([ctx.outputs.executable]),
-          runfiles = golib.runfiles,
-      ),
-      OutputGroupInfo(
-          static = depset([static_executable]),
-          race = depset([race_executable]),
-      ),
-  ]
-
-go_binary = rule(
-    _go_binary_impl,
-    attrs = {
-        "data": attr.label_list(
-            allow_files = True,
-            cfg = "data",
-        ),
-        "srcs": attr.label_list(allow_files = go_filetype),
-        "deps": attr.label_list(providers = [GoLibrary]),
-        "importpath": attr.string(),
-        "library": attr.label(providers = [GoLibrary]),
-        "gc_goopts": attr.string_list(),
-        "gc_linkopts": attr.string_list(),
-        "linkstamp": attr.string(),
-        "x_defs": attr.string_dict(),
-        "_go_prefix": attr.label(default = go_prefix_default),
-    },
-    executable = True,
-    toolchains = ["@io_bazel_rules_go//go:toolchain"],
-)
-
-def gc_linkopts(ctx):
-  gc_linkopts = [ctx.expand_make_variables("gc_linkopts", f, {})
-                 for f in ctx.attr.gc_linkopts]
-  return gc_linkopts
-
-def _extract_extldflags(gc_linkopts, extldflags):
-  """Extracts -extldflags from gc_linkopts and combines them into a single list.
-
-  Args:
-    gc_linkopts: a list of flags passed in through the gc_linkopts attributes.
-      ctx.expand_make_variables should have already been applied.
-    extldflags: a list of flags to be passed to the external linker.
-
-  Return:
-    A tuple containing the filtered gc_linkopts with external flags removed,
-    and a combined list of external flags.
-  """
-  filtered_gc_linkopts = []
-  is_extldflags = False
-  for opt in gc_linkopts:
-    if is_extldflags:
-      is_extldflags = False
-      extldflags += [opt]
-    elif opt == "-extldflags":
-      is_extldflags = True
-    else:
-      filtered_gc_linkopts += [opt]
-  return filtered_gc_linkopts, extldflags
-
-def emit_go_link_action(ctx, go_toolchain, library, mode, executable, gc_linkopts, x_defs):
-  """Adds an action to link the supplied library in the given mode, producing the executable.
-  Args:
-    ctx: The skylark Context.
-    library: The library to link.
-    mode: Controls the linking setup affecting things like enabling profilers and sanitizers.
-      This must be one of the values in common.bzl#compile_modes
-    executable: The binary to produce.
-    gc_linkopts: basic link options, these may be adjusted by the mode.
-    x_defs: link defines, including build stamping ones
-  """
-
-  # Add in any mode specific behaviours
-  if mode == RACE_MODE:
-    gc_linkopts += ["-race"]
-
-  config_strip = len(ctx.configuration.bin_dir.path) + 1
-  pkg_depth = executable.dirname[config_strip:].count('/') + 1
-
-  ld = None
-  extldflags = []
-  if go_toolchain.external_linker:
-    ld = go_toolchain.external_linker.compiler_executable
-    extldflags = go_toolchain.external_linker.options
-  extldflags += ["-Wl,-rpath,$ORIGIN/" + ("../" * pkg_depth)]
-
-  gc_linkopts, extldflags = _extract_extldflags(gc_linkopts, extldflags)
-
-  link_opts = ["-L", "."]
-  libs = depset()
-  cgo_deps = depset()
-  for golib in depset([library]) + library.transitive:
-    libs += [get_library(golib, mode)]
-    link_opts += ["-L", get_searchpath(golib, mode)]
-    cgo_deps += golib.cgo_deps
-
-  for d in cgo_deps:
-    if d.basename.endswith('.so'):
-      short_dir = d.dirname[len(d.root.path):]
-      extldflags += ["-Wl,-rpath,$ORIGIN/" + ("../" * pkg_depth) + short_dir]
-
-  link_opts += ["-o", executable.path] + gc_linkopts
-
-  # Process x_defs, either adding them directly to linker options, or
-  # saving them to process through stamping support.
-  stamp_x_defs = {}
-  for k, v in x_defs.items():
-    if v.startswith("{") and v.endswith("}"):
-      stamp_x_defs[k] = v[1:-1]
-    else:
-      link_opts += ["-X", "%s=%s" % (k, v)]
-
-  link_opts += go_toolchain.flags.link
-  if ld: 
-    link_opts += [
-        "-extld", ld,
-        "-extldflags", " ".join(extldflags),
-    ]
-  link_opts += [get_library(golib, mode).path]
-  link_args = [go_toolchain.tools.go.path]
-  # Stamping support
-  stamp_inputs = []
-  if stamp_x_defs or ctx.attr.linkstamp:
-    stamp_inputs = [ctx.info_file, ctx.version_file]
-    for f in stamp_inputs:
-      link_args += ["-stamp", f.path]
-    for k,v in stamp_x_defs.items():
-      link_args += ["-X", "%s=%s" % (k, v)]
-    # linkstamp option support: read workspace status files,
-    # converting "KEY value" lines to "-X $linkstamp.KEY=value" arguments
-    # to the go linker.
-    if ctx.attr.linkstamp:
-      link_args += ["-linkstamp", ctx.attr.linkstamp]
-
-  link_args += ["--"] + link_opts
-
-  ctx.action(
-      inputs = list(libs + cgo_deps +
-                go_toolchain.data.tools + go_toolchain.data.crosstool + stamp_inputs),
-      outputs = [executable],
-      mnemonic = "GoLink",
-      executable = go_toolchain.tools.link,
-      arguments = link_args,
-      env = go_toolchain.env,
-  )
diff --git a/go/private/common.bzl b/go/private/common.bzl
index 0973c6a..cfda114 100644
--- a/go/private/common.bzl
+++ b/go/private/common.bzl
@@ -12,6 +12,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+load("@io_bazel_rules_go//go/private:providers.bzl", "GoLibrary")
+
 DEFAULT_LIB = "go_default_library"
 VENDOR_PREFIX = "/vendor/"
 
@@ -103,3 +105,34 @@
 
 def join_srcs(source):
   return depset() + source.go + source.headers + source.asm + source.c
+
+
+def go_importpath(ctx):
+  """Returns the expected importpath of the go_library being built.
+
+  Args:
+    ctx: The skylark Context
+
+  Returns:
+    Go importpath of the library
+  """
+  path = ctx.attr.importpath
+  if path != "":
+    return path
+  if getattr(ctx.attr, "library", None):
+     path = ctx.attr.library[GoLibrary].importpath
+     if path:
+       return path
+  path = ctx.attr._go_prefix.go_prefix
+  if path.endswith("/"):
+    path = path[:-1]
+  if ctx.label.package:
+    path += "/" + ctx.label.package
+  if ctx.label.name != DEFAULT_LIB and not path.endswith(ctx.label.name):
+    path += "/" + ctx.label.name
+  if path.rfind(VENDOR_PREFIX) != -1:
+    path = path[len(VENDOR_PREFIX) + path.rfind(VENDOR_PREFIX):]
+  if path[0] == "/":
+    path = path[1:]
+  return path
+
diff --git a/go/private/go_toolchain.bzl b/go/private/go_toolchain.bzl
index b71e4c5..87e5f2a 100644
--- a/go/private/go_toolchain.bzl
+++ b/go/private/go_toolchain.bzl
@@ -14,10 +14,12 @@
 """
 Toolchain rules used by go.
 """
-
-#TODO: Remove this once all users (kubernetes) no longer use it
-def get_go_toolchain(ctx):
-    return ctx.toolchains["@io_bazel_rules_go//go:toolchain"]
+load("@io_bazel_rules_go//go/private:actions/asm.bzl", "emit_asm")
+load("@io_bazel_rules_go//go/private:actions/compile.bzl", "emit_compile")
+load("@io_bazel_rules_go//go/private:actions/cover.bzl", "emit_cover")
+load("@io_bazel_rules_go//go/private:actions/library.bzl", "emit_library")
+load("@io_bazel_rules_go//go/private:actions/link.bzl", "emit_link")
+load("@io_bazel_rules_go//go/private:actions/pack.bzl", "emit_pack")
 
 def _go_toolchain_impl(ctx):
   return [platform_common.ToolchainInfo(
@@ -28,6 +30,14 @@
           "GOOS": ctx.attr.goos,
           "GOARCH": ctx.attr.goarch,
       },
+      actions = struct(
+          asm = emit_asm,
+          compile = emit_compile,
+          cover = emit_cover,
+          library = emit_library,
+          link = emit_link,
+          pack = emit_pack,
+      ),
       paths = struct(
         root = ctx.attr.root,
       ),
@@ -118,11 +128,6 @@
 )
 """Declares a go toolchain for use.
 This is used when porting the rules_go to a new platform.
-Args:
-  name: The name of the toolchain instance.
-  exec_compatible_with: The set of constraints this toolchain requires to execute.
-  target_compatible_with: The set of constraints for the outputs built with this toolchain.
-  go: The location of the `go` binary.
 """
 
 def _go_toolchain_flags(ctx):
diff --git a/go/private/library.bzl b/go/private/library.bzl
deleted file mode 100644
index eb98cbe..0000000
--- a/go/private/library.bzl
+++ /dev/null
@@ -1,311 +0,0 @@
-# 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:common.bzl", 
-  "DEFAULT_LIB",
-  "VENDOR_PREFIX",
-  "go_filetype",
-  "dict_of",
-  "split_srcs",
-  "join_srcs",
-  "RACE_MODE",
-  "NORMAL_MODE",
-  "compile_modes",
-)
-load("@io_bazel_rules_go//go/private:asm.bzl", "emit_go_asm_action")
-load("@io_bazel_rules_go//go/private:providers.bzl", "GoLibrary", "CgoLibrary")
-
-def emit_library_actions(ctx, go_toolchain, srcs, deps, cgo_object, library, want_coverage, importpath, golibs=[]):
-  dep_runfiles = [d.data_runfiles for d in deps]
-  direct = depset(golibs)
-  gc_goopts = tuple(ctx.attr.gc_goopts)
-  cgo_deps = depset()
-  cover_vars = ()
-  if library:
-    golib = library[GoLibrary]
-    cgolib = library[CgoLibrary]
-    srcs = golib.transformed + srcs
-    cover_vars += golib.cover_vars
-    direct += golib.direct
-    dep_runfiles += [library.data_runfiles]
-    gc_goopts += golib.gc_goopts
-    cgo_deps += golib.cgo_deps
-    if cgolib.object:
-      if cgo_object:
-        fail("go_library %s cannot have cgo_object because the package " +
-             "already has cgo_object in %s" % (ctx.label.name,
-                                               golib.name))
-      cgo_object = cgolib.object
-  source = split_srcs(srcs)
-  if source.c:
-    fail("c sources in non cgo rule")
-  if not source.go:
-    fail("no go sources")
-
-  if cgo_object:
-    dep_runfiles += [cgo_object.data_runfiles]
-    cgo_deps += cgo_object.cgo_deps
-
-  extra_objects = [cgo_object.cgo_obj] if cgo_object else []
-  for src in source.asm:
-    obj = ctx.new_file(src, "%s.dir/%s.o" % (ctx.label.name, src.basename[:-2]))
-    emit_go_asm_action(ctx, go_toolchain, src, source.headers, obj)
-    extra_objects += [obj]
-
-  for dep in deps:
-    direct += [dep[GoLibrary]]
-
-  transitive = depset()
-  for golib in direct:
-    transitive += [golib]
-    transitive += golib.transitive
-
-  go_srcs = source.go
-  if want_coverage:
-    go_srcs, cvars = _emit_go_cover_action(ctx, go_toolchain, go_srcs)
-    cover_vars += cvars
-
-  lib_name = importpath + ".a"
-  mode_fields = {} # These are added to the GoLibrary provider directly
-  for mode in compile_modes:
-    out_lib = ctx.new_file("~{}~{}~/{}".format(mode, ctx.label.name, lib_name))
-    out_object = ctx.new_file("~{}~{}~/{}.o".format(mode, ctx.label.name, importpath))
-    searchpath = out_lib.path[:-len(lib_name)]
-    mode_fields[mode+"_library"] = out_lib
-    mode_fields[mode+"_searchpath"] = searchpath
-    emit_go_compile_action(ctx,
-        go_toolchain = go_toolchain,
-        sources = go_srcs,
-        golibs = direct,
-        mode = mode,
-        out_object = out_object,
-        gc_goopts = gc_goopts,
-    )
-    emit_go_pack_action(ctx, go_toolchain, out_lib, [out_object] + extra_objects)
-
-  dylibs = []
-  if cgo_object:
-    dylibs += [d for d in cgo_object.cgo_deps if d.path.endswith(".so")]
-
-  runfiles = ctx.runfiles(files = dylibs, collect_data = True)
-  for d in dep_runfiles:
-    runfiles = runfiles.merge(d)
-
-  transformed = dict_of(source)
-  transformed["go"] = go_srcs
-
-  return [
-      GoLibrary(
-          label = ctx.label,
-          importpath = importpath, # The import path for this library
-          direct = direct, # The direct depencancies of the library
-          transitive = transitive, # The transitive set of go libraries depended on
-          srcs = depset(srcs), # The original sources
-          transformed = join_srcs(struct(**transformed)), # The transformed sources actually compiled
-          cgo_deps = cgo_deps, # The direct cgo dependencies of this library
-          gc_goopts = gc_goopts, # The options this library was compiled with
-          runfiles = runfiles, # The runfiles needed for things including this library
-          cover_vars = cover_vars, # The cover variables for this library
-          **mode_fields
-      ),
-      CgoLibrary(
-          object = cgo_object,
-      ),
-  ]
-
-def get_library(golib, mode):
-  """Returns the compiled library for the given mode"""
-  # The attribute name must match the one assigned in emit_library_actions 
-  return getattr(golib, mode+"_library")
-
-def get_searchpath(golib, mode):
-  """Returns the search path for the given mode"""
-  # The attribute name must match the one assigned in emit_library_actions 
-  return getattr(golib, mode+"_searchpath")
-
-def _go_library_impl(ctx):
-  """Implements the go_library() rule."""
-  go_toolchain = ctx.toolchains["@io_bazel_rules_go//go:toolchain"]
-  cgo_object = None
-  if hasattr(ctx.attr, "cgo_object"):
-    cgo_object = ctx.attr.cgo_object
-  golib, cgolib = emit_library_actions(ctx,
-      go_toolchain = go_toolchain,
-      srcs = ctx.files.srcs,
-      deps = ctx.attr.deps,
-      cgo_object = cgo_object,
-      library = ctx.attr.library,
-      want_coverage = ctx.coverage_instrumented(),
-      importpath = go_importpath(ctx),
-  )
-
-  return [
-      golib,
-      cgolib,
-      DefaultInfo(
-          files = depset([get_library(golib, NORMAL_MODE)]),
-          runfiles = golib.runfiles,
-      ),
-      OutputGroupInfo(
-          race = depset([get_library(golib, RACE_MODE)]),
-      ),
-  ]
-
-def go_prefix_default(importpath):
-  return (None
-          if importpath
-          else Label("//:go_prefix", relative_to_caller_repository = True))
-
-go_library = rule(
-    _go_library_impl,
-    attrs = {
-        "data": attr.label_list(allow_files = True, cfg = "data"),
-        "srcs": attr.label_list(allow_files = go_filetype),
-        "deps": attr.label_list(providers = [GoLibrary]),
-        "importpath": attr.string(),
-        "library": attr.label(providers = [GoLibrary]),
-        "gc_goopts": attr.string_list(),
-        "cgo_object": attr.label(
-            providers = [
-                "cgo_obj",
-                "cgo_deps",
-            ],
-        ),
-        "_go_prefix": attr.label(default = go_prefix_default),
-    },
-    toolchains = ["@io_bazel_rules_go//go:toolchain"],
-)
-
-def go_importpath(ctx):
-  """Returns the expected importpath of the go_library being built.
-
-  Args:
-    ctx: The skylark Context
-
-  Returns:
-    Go importpath of the library
-  """
-  path = ctx.attr.importpath
-  if path != "":
-    return path
-  if getattr(ctx.attr, "library", None):
-    path = ctx.attr.library[GoLibrary].importpath
-    if path:
-      return path
-  path = ctx.attr._go_prefix.go_prefix
-  if path.endswith("/"):
-    path = path[:-1]
-  if ctx.label.package:
-    path += "/" + ctx.label.package
-  if ctx.label.name != DEFAULT_LIB and not path.endswith(ctx.label.name):
-    path += "/" + ctx.label.name
-  if path.rfind(VENDOR_PREFIX) != -1:
-    path = path[len(VENDOR_PREFIX) + path.rfind(VENDOR_PREFIX):]
-  if path[0] == "/":
-    path = path[1:]
-  return path
-
-def emit_go_compile_action(ctx, go_toolchain, sources, golibs, mode, out_object, gc_goopts):
-  """Construct the command line for compiling Go code.
-
-  Args:
-    ctx: The skylark Context.
-    sources: an iterable of source code artifacts (or CTs? or labels?)
-    golibs: a depset of representing all imported libraries.
-    mode: Controls the compilation setup affecting things like enabling profilers and sanitizers.
-      This must be one of the values in common.bzl#compile_modes
-    out_object: the object file that should be produced
-    gc_goopts: additional flags to pass to the compiler.
-  """
-
-  # Add in any mode specific behaviours
-  if mode == RACE_MODE:
-    gc_goopts = gc_goopts + ("-race",)
-
-  gc_goopts = [ctx.expand_make_variables("gc_goopts", f, {}) for f in gc_goopts]
-  inputs = depset([go_toolchain.tools.go]) + sources
-  go_sources = [s.path for s in sources if not s.basename.startswith("_cgo")]
-  cgo_sources = [s.path for s in sources if s.basename.startswith("_cgo")]
-  args = [go_toolchain.tools.go.path]
-  for src in go_sources:
-    args += ["-src", src]
-  for golib in golibs:
-    inputs += [get_library(golib, mode)]
-    args += ["-dep", golib.importpath]
-    args += ["-I", get_searchpath(golib,mode)]
-  args += ["-o", out_object.path, "-trimpath", ".", "-I", "."]
-  args += ["--"] + gc_goopts + go_toolchain.flags.compile + cgo_sources
-  ctx.action(
-      inputs = list(inputs),
-      outputs = [out_object],
-      mnemonic = "GoCompile",
-      executable = go_toolchain.tools.compile,
-      arguments = args,
-      env = go_toolchain.env,
-  )
-
-def emit_go_pack_action(ctx, go_toolchain, out_lib, objects):
-  """Construct the command line for packing objects together.
-
-  Args:
-    ctx: The skylark Context.
-    out_lib: the archive that should be produced
-    objects: an iterable of object files to be added to the output archive file.
-  """
-  ctx.action(
-      inputs = objects + go_toolchain.data.tools,
-      outputs = [out_lib],
-      mnemonic = "GoPack",
-      executable = go_toolchain.tools.go,
-      arguments = ["tool", "pack", "c", out_lib.path] + [a.path for a in objects],
-      env = go_toolchain.env,
-  )
-
-def _emit_go_cover_action(ctx, go_toolchain, sources):
-  """Construct the command line for test coverage instrument.
-
-  Args:
-    ctx: The skylark Context.
-    out_object: the object file for the library being compiled. Used to name
-      cover files.
-    sources: an iterable of Go source files.
-
-  Returns:
-    A list of Go source code files which might be coverage instrumented.
-  """
-  outputs = []
-  # TODO(linuxerwang): make the mode configurable.
-  cover_vars = []
-
-  for src in sources:
-    if (not src.basename.endswith(".go") or 
-        src.basename.endswith("_test.go") or 
-        src.basename.endswith(".cover.go")):
-      outputs += [src]
-      continue
-
-    cover_var = "Cover_" + src.basename[:-3].replace("-", "_").replace(".", "_")
-    cover_vars += ["{}={}".format(cover_var,src.short_path)]
-    out = ctx.new_file(cover_var + '.cover.go')
-    outputs += [out]
-    ctx.action(
-        inputs = [src] + go_toolchain.data.tools,
-        outputs = [out],
-        mnemonic = "GoCover",
-        executable = go_toolchain.tools.go,
-        arguments = ["tool", "cover", "--mode=set", "-var=%s" % cover_var, "-o", out.path, src.path],
-        env = go_toolchain.env,
-    )
-
-  return outputs, tuple(cover_vars)
diff --git a/go/private/providers.bzl b/go/private/providers.bzl
index 100b490..017104c 100644
--- a/go/private/providers.bzl
+++ b/go/private/providers.bzl
@@ -15,4 +15,36 @@
 GoLibrary = provider()
 GoBinary = provider()
 CgoLibrary = provider()
-GoPath = provider()
\ No newline at end of file
+GoPath = provider()
+
+def library_attr(mode):
+  """Returns the attribute name for the library of the given mode.
+  
+  mode must be one of the common.bzl#compile_modes
+  """
+  return mode+"_library"
+
+def get_library(golib, mode):
+  """Returns the compiled library for the given mode
+  
+  golib must be a GoLibrary
+  mode must be one of the common.bzl#compile_modes
+  """
+  return getattr(golib, library_attr(mode))
+
+def searchpath_attr(mode):
+  """Returns the search path for the given mode
+  
+  mode must be one of the common.bzl#compile_modes
+  """
+  return mode+"_searchpath"
+
+def get_searchpath(golib, mode):
+  """Returns the search path for the given mode
+  
+  golib must be a GoLibrary
+  mode must be one of the common.bzl#compile_modes
+  """
+  return getattr(golib, searchpath_attr(mode))
+
+
diff --git a/go/private/rules/binary.bzl b/go/private/rules/binary.bzl
new file mode 100644
index 0000000..30e9ddd
--- /dev/null
+++ b/go/private/rules/binary.bzl
@@ -0,0 +1,113 @@
+# 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:common.bzl",
+    "compile_modes",
+    "go_filetype",
+    "go_importpath",
+    "NORMAL_MODE",
+    "RACE_MODE",
+)
+load("@io_bazel_rules_go//go/private:rules/prefix.bzl",
+    "go_prefix_default",
+)
+load("@io_bazel_rules_go//go/private:providers.bzl", "GoLibrary", "GoBinary")
+
+def _go_binary_impl(ctx):
+  """go_binary_impl emits actions for compiling and linking a go executable."""
+  go_toolchain = ctx.toolchains["@io_bazel_rules_go//go:toolchain"]
+  golib, _ = go_toolchain.actions.library(ctx,
+      go_toolchain = go_toolchain,
+      srcs = ctx.files.srcs,
+      deps = ctx.attr.deps,
+      cgo_object = None,
+      library = ctx.attr.library,
+      want_coverage = False,
+      importpath = go_importpath(ctx),
+  )
+
+  # Default (dynamic) linking
+  race_executable = ctx.new_file(ctx.attr.name + ".race")
+  for mode in compile_modes:
+    executable = ctx.outputs.executable
+    if mode == RACE_MODE:
+      executable = race_executable
+    go_toolchain.actions.link(
+        ctx,
+        go_toolchain = go_toolchain,
+        library=golib,
+        mode=mode,
+        executable=executable,
+        gc_linkopts=gc_linkopts(ctx),
+        x_defs=ctx.attr.x_defs,
+    )
+
+  # Static linking (in the 'static' output group)
+  static_linkopts = [
+      "-linkmode", "external",
+      "-extldflags", "-static",
+  ]
+  static_executable = ctx.new_file(ctx.attr.name + ".static")
+  go_toolchain.actions.link(
+      ctx,
+      go_toolchain = go_toolchain,
+      library=golib,
+      mode=NORMAL_MODE,
+      executable=static_executable,
+      gc_linkopts=gc_linkopts(ctx) + static_linkopts,
+      x_defs=ctx.attr.x_defs,
+  )
+
+  return [
+      golib,
+      GoBinary(
+          executable = ctx.outputs.executable,
+          static = static_executable,
+          race = race_executable,
+      ),
+      DefaultInfo(
+          files = depset([ctx.outputs.executable]),
+          runfiles = golib.runfiles,
+      ),
+      OutputGroupInfo(
+          static = depset([static_executable]),
+          race = depset([race_executable]),
+      ),
+  ]
+
+go_binary = rule(
+    _go_binary_impl,
+    attrs = {
+        "data": attr.label_list(
+            allow_files = True,
+            cfg = "data",
+        ),
+        "srcs": attr.label_list(allow_files = go_filetype),
+        "deps": attr.label_list(providers = [GoLibrary]),
+        "importpath": attr.string(),
+        "library": attr.label(providers = [GoLibrary]),
+        "gc_goopts": attr.string_list(),
+        "gc_linkopts": attr.string_list(),
+        "linkstamp": attr.string(),
+        "x_defs": attr.string_dict(),
+        "_go_prefix": attr.label(default = go_prefix_default),
+    },
+    executable = True,
+    toolchains = ["@io_bazel_rules_go//go:toolchain"],
+)
+
+def gc_linkopts(ctx):
+  gc_linkopts = [ctx.expand_make_variables("gc_linkopts", f, {})
+                 for f in ctx.attr.gc_linkopts]
+  return gc_linkopts
diff --git a/go/private/cgo.bzl b/go/private/rules/cgo.bzl
similarity index 99%
rename from go/private/cgo.bzl
rename to go/private/rules/cgo.bzl
index 7c10589..f4fbf28 100644
--- a/go/private/cgo.bzl
+++ b/go/private/rules/cgo.bzl
@@ -13,7 +13,6 @@
 # limitations under the License.
 
 load("@io_bazel_rules_go//go/private:common.bzl", "dict_of", "split_srcs", "join_srcs", "pkg_dir")
-load("@io_bazel_rules_go//go/private:library.bzl", "go_library")
 load("@io_bazel_rules_go//go/private:providers.bzl", "GoLibrary")
 
 def _cgo_select_go_files_impl(ctx):
diff --git a/go/private/rules/library.bzl b/go/private/rules/library.bzl
new file mode 100644
index 0000000..3f2b0f7
--- /dev/null
+++ b/go/private/rules/library.bzl
@@ -0,0 +1,75 @@
+# 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:common.bzl", 
+    "go_filetype",
+    "go_importpath",
+    "RACE_MODE",
+    "NORMAL_MODE",
+)
+load("@io_bazel_rules_go//go/private:providers.bzl", 
+    "GoLibrary", 
+    "get_library",
+)
+load("@io_bazel_rules_go//go/private:rules/prefix.bzl",
+    "go_prefix_default",
+)
+
+def _go_library_impl(ctx):
+  """Implements the go_library() rule."""
+  go_toolchain = ctx.toolchains["@io_bazel_rules_go//go:toolchain"]
+  cgo_object = None
+  if hasattr(ctx.attr, "cgo_object"):
+    cgo_object = ctx.attr.cgo_object
+  golib, cgolib = go_toolchain.actions.library(ctx,
+      go_toolchain = go_toolchain,
+      srcs = ctx.files.srcs,
+      deps = ctx.attr.deps,
+      cgo_object = cgo_object,
+      library = ctx.attr.library,
+      want_coverage = ctx.coverage_instrumented(),
+      importpath = go_importpath(ctx),
+  )
+
+  return [
+      golib,
+      cgolib,
+      DefaultInfo(
+          files = depset([get_library(golib, NORMAL_MODE)]),
+          runfiles = golib.runfiles,
+      ),
+      OutputGroupInfo(
+          race = depset([get_library(golib, RACE_MODE)]),
+      ),
+  ]
+
+go_library = rule(
+    _go_library_impl,
+    attrs = {
+        "data": attr.label_list(allow_files = True, cfg = "data"),
+        "srcs": attr.label_list(allow_files = go_filetype),
+        "deps": attr.label_list(providers = [GoLibrary]),
+        "importpath": attr.string(),
+        "library": attr.label(providers = [GoLibrary]),
+        "gc_goopts": attr.string_list(),
+        "cgo_object": attr.label(
+            providers = [
+                "cgo_obj",
+                "cgo_deps",
+            ],
+        ),
+        "_go_prefix": attr.label(default = go_prefix_default),
+    },
+    toolchains = ["@io_bazel_rules_go//go:toolchain"],
+)
diff --git a/go/private/go_prefix.bzl b/go/private/rules/prefix.bzl
similarity index 90%
rename from go/private/go_prefix.bzl
rename to go/private/rules/prefix.bzl
index 5ae84b6..5035b7c 100644
--- a/go/private/go_prefix.bzl
+++ b/go/private/rules/prefix.bzl
@@ -37,3 +37,9 @@
     prefix = prefix,
     visibility = ["//visibility:public" ]
   )
+
+def go_prefix_default(importpath):
+  return (None
+          if importpath
+          else Label("//:go_prefix", relative_to_caller_repository = True))
+
diff --git a/go/private/go_root.bzl b/go/private/rules/root.bzl
similarity index 100%
rename from go/private/go_root.bzl
rename to go/private/rules/root.bzl
diff --git a/go/private/test.bzl b/go/private/rules/test.bzl
similarity index 92%
rename from go/private/test.bzl
rename to go/private/rules/test.bzl
index e11a017..32b398f 100644
--- a/go/private/test.bzl
+++ b/go/private/rules/test.bzl
@@ -14,19 +14,16 @@
 
 load("@io_bazel_rules_go//go/private:common.bzl",
     "go_filetype",
+    "go_importpath",
     "split_srcs",
     "pkg_dir",
     "NORMAL_MODE",
     "RACE_MODE",
 )
-load("@io_bazel_rules_go//go/private:library.bzl",
-    "emit_go_compile_action",
-    "emit_go_pack_action",
-    "emit_library_actions",
-    "go_importpath",
+load("@io_bazel_rules_go//go/private:rules/prefix.bzl",
     "go_prefix_default",
 )
-load("@io_bazel_rules_go//go/private:binary.bzl", "emit_go_link_action", "gc_linkopts")
+load("@io_bazel_rules_go//go/private:rules/binary.bzl", "gc_linkopts")
 load("@io_bazel_rules_go//go/private:providers.bzl", "GoLibrary", "GoBinary")
 
 def _go_test_impl(ctx):
@@ -36,7 +33,7 @@
   test into a binary."""
 
   go_toolchain = ctx.toolchains["@io_bazel_rules_go//go:toolchain"]
-  golib, _ = emit_library_actions(ctx,
+  golib, _ = go_toolchain.actions.library(ctx,
       go_toolchain = go_toolchain,
       srcs = ctx.files.srcs,
       deps = ctx.attr.deps,
@@ -81,7 +78,7 @@
       env = dict(go_toolchain.env, RUNDIR=ctx.label.package)
   )
 
-  main_lib, _ = emit_library_actions(ctx,
+  main_lib, _ = go_toolchain.actions.library(ctx,
       go_toolchain = go_toolchain,
       srcs = [main_go],
       deps = [],
@@ -97,7 +94,7 @@
   if "race" in ctx.features:
     mode = RACE_MODE
 
-  emit_go_link_action(
+  go_toolchain.actions.link(
       ctx,
       go_toolchain = go_toolchain,
       library=main_lib,
diff --git a/go/private/wrappers.bzl b/go/private/rules/wrappers.bzl
similarity index 87%
rename from go/private/wrappers.bzl
rename to go/private/rules/wrappers.bzl
index e8d510b..f925259 100644
--- a/go/private/wrappers.bzl
+++ b/go/private/rules/wrappers.bzl
@@ -12,10 +12,10 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-load("@io_bazel_rules_go//go/private:binary.bzl", "go_binary")
-load("@io_bazel_rules_go//go/private:library.bzl", "go_library")
-load("@io_bazel_rules_go//go/private:test.bzl", "go_test")
-load("@io_bazel_rules_go//go/private:cgo.bzl", "setup_cgo_library")
+load("@io_bazel_rules_go//go/private:rules/binary.bzl", "go_binary")
+load("@io_bazel_rules_go//go/private:rules/library.bzl", "go_library")
+load("@io_bazel_rules_go//go/private:rules/test.bzl", "go_test")
+load("@io_bazel_rules_go//go/private:rules/cgo.bzl", "setup_cgo_library")
 
 def go_library_macro(name, srcs=None, cgo=False, cdeps=[], copts=[], clinkopts=[], **kwargs):
   cgo_object = None
diff --git a/go/private/tools/BUILD b/go/private/tools/BUILD
deleted file mode 100644
index 72593b4..0000000
--- a/go/private/tools/BUILD
+++ /dev/null
@@ -1,5 +0,0 @@
-filegroup(
-    name = "all_rules",
-    srcs = glob(["*.bzl"]),
-    visibility = ["//visibility:public"],
-)
diff --git a/go/private/embed_data.bzl b/go/private/tools/embed_data.bzl
similarity index 100%
rename from go/private/embed_data.bzl
rename to go/private/tools/embed_data.bzl
diff --git a/go/private/files_equal_test.bzl b/go/private/tools/files_equal_test.bzl
similarity index 100%
rename from go/private/files_equal_test.bzl
rename to go/private/tools/files_equal_test.bzl
diff --git a/go/private/gazelle.bzl b/go/private/tools/gazelle.bzl
similarity index 100%
rename from go/private/gazelle.bzl
rename to go/private/tools/gazelle.bzl
diff --git a/go/private/go_tool_binary.bzl b/go/private/tools/go_tool_binary.bzl
similarity index 96%
rename from go/private/go_tool_binary.bzl
rename to go/private/tools/go_tool_binary.bzl
index ab492f7..61f89a4 100644
--- a/go/private/go_tool_binary.bzl
+++ b/go/private/tools/go_tool_binary.bzl
@@ -13,7 +13,7 @@
 # limitations under the License.
 
 load("@io_bazel_rules_go//go/private:providers.bzl", "GoLibrary")
-load("@io_bazel_rules_go//go/private:library.bzl", "go_importpath")
+load("@io_bazel_rules_go//go/private:common.bzl", "go_importpath")
 
 def _go_tool_binary_impl(ctx):
   toolchain = ctx.toolchains["@io_bazel_rules_go//go:bootstrap_toolchain"]
diff --git a/go/private/lines_sorted_test.bzl b/go/private/tools/lines_sorted_test.bzl
similarity index 93%
rename from go/private/lines_sorted_test.bzl
rename to go/private/tools/lines_sorted_test.bzl
index bb6ced2..c7f7f2b 100644
--- a/go/private/lines_sorted_test.bzl
+++ b/go/private/tools/lines_sorted_test.bzl
@@ -13,7 +13,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-load("@io_bazel_rules_go//go/private:files_equal_test.bzl", "files_equal_test")
+load("@io_bazel_rules_go//go/private:tools/files_equal_test.bzl", "files_equal_test")
 
 def lines_sorted_test(name, file, cmd="cat $< >$@", visibility=None, **kwargs):
   """Tests that lines within a file are sorted."""
diff --git a/go/private/single_output_test.bzl b/go/private/tools/single_output_test.bzl
similarity index 100%
rename from go/private/single_output_test.bzl
rename to go/private/tools/single_output_test.bzl
diff --git a/go/tools/builders/BUILD b/go/tools/builders/BUILD
index 185b7b4..bc43c7b 100644
--- a/go/tools/builders/BUILD
+++ b/go/tools/builders/BUILD
@@ -1,4 +1,4 @@
-load("@io_bazel_rules_go//go/private:go_tool_binary.bzl", "go_tool_binary")
+load("@io_bazel_rules_go//go/private:tools/go_tool_binary.bzl", "go_tool_binary")
 load("@io_bazel_rules_go//go:def.bzl", "go_test")
 
 go_test(
diff --git a/go/tools/extract_package/BUILD b/go/tools/extract_package/BUILD
index e0372da..a88d192 100644
--- a/go/tools/extract_package/BUILD
+++ b/go/tools/extract_package/BUILD
@@ -1,4 +1,4 @@
-load("@io_bazel_rules_go//go/private:go_tool_binary.bzl", "go_tool_binary")
+load("@io_bazel_rules_go//go/private:tools/go_tool_binary.bzl", "go_tool_binary")
 load("@io_bazel_rules_go//go:def.bzl", "go_test")
 
 go_tool_binary(
diff --git a/tests/binary_test_outputs/BUILD b/tests/binary_test_outputs/BUILD
index d27928f..d966a2b 100644
--- a/tests/binary_test_outputs/BUILD
+++ b/tests/binary_test_outputs/BUILD
@@ -2,7 +2,7 @@
 # See documentation in single_output_test.bzl.
 
 load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_test")
-load("@io_bazel_rules_go//go/private:single_output_test.bzl", "single_output_test")
+load("@io_bazel_rules_go//go/private:tools/single_output_test.bzl", "single_output_test")
 
 single_output_test(
     name = "binary_single_output_test",