Migrate rules_rust to the new Starlark C++ toolchain API (#133)

This is needed for rules_rust to be forward compatible with Bazel 0.20.
Tracking issues for migration:

* https://github.com/bazelbuild/bazel/issues/6380
* https://github.com/bazelbuild/bazel/issues/6434

Fixes #131.
diff --git a/WORKSPACE b/WORKSPACE
index cd69e6f..8e259c5 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -59,3 +59,13 @@
         "https://github.com/bazelbuild/bazel-toolchains/archive/cdea5b8675914d0a354d89f108de5d28e54e0edc.tar.gz",
     ],
 )
+
+http_archive(
+    name = "bazel_skylib",
+    url = "https://github.com/bazelbuild/bazel-skylib/archive/0.5.0.tar.gz",
+    sha256 = "b5f6abe419da897b7901f90cbab08af958b97a8f3575b0d3dd062ac7ce78541f",
+    strip_prefix = "bazel-skylib-0.5.0"
+)
+
+load(":workspace.bzl", "bazel_version")
+bazel_version(name = "bazel_version")
diff --git a/rust/private/rust.bzl b/rust/private/rust.bzl
index e05f105..952b67e 100644
--- a/rust/private/rust.bzl
+++ b/rust/private/rust.bzl
@@ -192,6 +192,7 @@
         ],
         single_file = True,
     ),
+    "_cc_toolchain": attr.label(default = "@bazel_tools//tools/cpp:current_cc_toolchain"),
 }
 
 _rust_library_attrs = {
@@ -202,6 +203,7 @@
     _rust_library_impl,
     attrs = dict(_rust_common_attrs.items() +
                  _rust_library_attrs.items()),
+    fragments = ["cpp"],
     host_fragments = ["cpp"],
     toolchains = ["@io_bazel_rules_rust//rust:toolchain"],
 )
@@ -318,6 +320,7 @@
     _rust_binary_impl,
     attrs = _rust_common_attrs,
     executable = True,
+    fragments = ["cpp"],
     host_fragments = ["cpp"],
     toolchains = ["@io_bazel_rules_rust//rust:toolchain"],
 )
@@ -449,6 +452,7 @@
     _rust_test_impl,
     attrs = _rust_common_attrs,
     executable = True,
+    fragments = ["cpp"],
     host_fragments = ["cpp"],
     test = True,
     toolchains = ["@io_bazel_rules_rust//rust:toolchain"],
@@ -618,6 +622,7 @@
     _rust_benchmark_impl,
     attrs = _rust_common_attrs,
     executable = True,
+    fragments = ["cpp"],
     host_fragments = ["cpp"],
     toolchains = ["@io_bazel_rules_rust//rust:toolchain"],
 )
diff --git a/rust/private/rustc.bzl b/rust/private/rustc.bzl
index 80acea5..0bd7c44 100644
--- a/rust/private/rustc.bzl
+++ b/rust/private/rustc.bzl
@@ -13,6 +13,16 @@
 # limitations under the License.
 
 load(":private/utils.bzl", "find_toolchain", "relative_path")
+load(
+    "@bazel_tools//tools/build_defs/cc:action_names.bzl",
+    "CPP_LINK_EXECUTABLE_ACTION_NAME",
+)
+load(
+    "@bazel_tools//tools/cpp:toolchain_utils.bzl",
+    "find_cpp_toolchain",
+)
+load("@bazel_skylib//lib:versions.bzl", "versions")
+load("@bazel_version//:def.bzl", "BAZEL_VERSION")
 
 CrateInfo = provider(
     fields = {
@@ -178,20 +188,37 @@
     if ctx.file.out_dir_tar:
         compile_inputs.append(ctx.file.out_dir_tar)
 
+    rpaths = _compute_rpaths(toolchain, output_dir, depinfo)
+
+    if versions.is_at_least("0.18.0", BAZEL_VERSION):
+        user_link_flags = ctx.fragments.cpp.linkopts
+    else:
+        user_link_flags = depset(ctx.fragments.cpp.linkopts)
+
     # Paths to cc (for linker) and ar
     cpp_fragment = ctx.host_fragments.cpp
-    cc = cpp_fragment.compiler_executable
-    ar = cpp_fragment.ar_executable
-
-    # Currently, the CROSSTOOL config for darwin sets ar to "libtool". Because
-    # rust uses ar-specific flags, use /usr/bin/ar in this case.
-    # TODO(dzc): This is not ideal. Remove this workaround once ar_executable
-    # always points to an ar binary.
-    ar_str = "%s" % ar
-    if ar_str.find("libtool", 0) != -1:
-        ar = "/usr/bin/ar"
-
-    rpaths = _compute_rpaths(toolchain, output_dir, depinfo)
+    cc_toolchain = find_cpp_toolchain(ctx)
+    feature_configuration = cc_common.configure_features(
+        cc_toolchain = cc_toolchain,
+        requested_features = ctx.features,
+        unsupported_features = ctx.disabled_features,
+    )
+    ld = cc_common.get_tool_for_action(
+        feature_configuration = feature_configuration,
+        action_name = CPP_LINK_EXECUTABLE_ACTION_NAME,
+    )
+    link_variables = cc_common.create_link_variables(
+        feature_configuration = feature_configuration,
+        cc_toolchain = cc_toolchain,
+        is_linking_dynamic_library = False,
+        runtime_library_search_directories = rpaths,
+        user_link_flags = user_link_flags,
+    )
+    link_options = cc_common.get_memory_inefficient_command_line(
+        feature_configuration = feature_configuration,
+        action_name = CPP_LINK_EXECUTABLE_ACTION_NAME,
+        variables = link_variables,
+    )
 
     # Construct features flags
     features_flags = _get_features_flags(ctx.attr.crate_features)
@@ -222,9 +249,8 @@
             # Mangle symbols to disambiguate crates with the same name
             "--codegen metadata=%s" % extra_filename,
             "--codegen extra-filename='%s'" % extra_filename,
-            "--codegen ar=%s" % ar,
-            "--codegen linker=%s" % cc,
-            "--codegen link-args='%s'" % " ".join(cpp_fragment.link_options),
+            "--codegen linker=%s" % ld,
+            "--codegen link-args='%s'" % " ".join(link_options),
             "--remap-path-prefix {}={}".format("$(pwd)", "__bazel_redacted_pwd"),
             "--out-dir",
             output_dir,
@@ -232,7 +258,6 @@
             "--color always",
             "--target=" + toolchain.target_triple,
         ] +
-        ["--codegen link-arg='-Wl,-rpath={}'".format(rpath) for rpath in rpaths] +
         features_flags +
         rust_flags +
         depinfo.link_search_flags +
@@ -268,7 +293,7 @@
     for runtime linking of shared libraries.
     """
     if not depinfo.transitive_dylibs:
-        return []
+        return depset([])
     if toolchain.os != "linux":
         fail("Runtime linking is not supported on {}, but found {}".format(
             toolchain.os,
@@ -276,10 +301,10 @@
         ))
 
     # Multiple dylibs can be present in the same directory, so deduplicate them.
-    return [
-        "$ORIGIN/" + relative_path(output_dir, lib_dir)
+    return depset([
+        relative_path(output_dir, lib_dir)
         for lib_dir in _get_dir_names(depinfo.transitive_dylibs)
-    ]
+    ])
 
 def _get_features_flags(features):
     """
diff --git a/workspace.bzl b/workspace.bzl
new file mode 100644
index 0000000..773a41d
--- /dev/null
+++ b/workspace.bzl
@@ -0,0 +1,12 @@
+load("@bazel_skylib//lib:versions.bzl", "versions")
+
+def _store_bazel_version(repository_ctx):
+    bazel_version = versions.get();
+    if versions.is_at_most("0.17.0", bazel_version):
+        fail("Bazel %s is too old to use with rules_rust, please use at least Bazel 0.17.1, preferably newer." % bazel_version)
+    repository_ctx.file("BUILD", "exports_files(['def.bzl'])")
+    repository_ctx.file("def.bzl", "BAZEL_VERSION='" + bazel_version + "'")
+
+bazel_version = repository_rule(
+    implementation = _store_bazel_version,
+)