Rust GN target generation

Logic for constructing Rust targets from a GN file. This adds support
for the `rust_library` function, and allows the BinaryTargetGenerator to
redirect itself into the RustBinaryTargetGenerator if Rust source files
are used.

Change-Id: I987cb161c4851deb4a0e130918d90144e7af90bc
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/4884
Commit-Queue: Julie Hockett <juliehockett@google.com>
Reviewed-by: Brett Wilson <brettw@google.com>
diff --git a/build/gen.py b/build/gen.py
index 3c7c858..bda1653 100755
--- a/build/gen.py
+++ b/build/gen.py
@@ -516,6 +516,7 @@
         'tools/gn/qt_creator_writer.cc',
         'tools/gn/runtime_deps.cc',
         'tools/gn/rust_substitution_type.cc',
+        'tools/gn/rust_target_generator.cc',
         'tools/gn/rust_tool.cc',
         'tools/gn/rust_values.cc',
         'tools/gn/rust_variables.cc',
@@ -587,6 +588,7 @@
         'tools/gn/function_toolchain_unittest.cc',
         'tools/gn/function_write_file_unittest.cc',
         'tools/gn/functions_target_unittest.cc',
+        'tools/gn/functions_target_rust_unittest.cc',
         'tools/gn/functions_unittest.cc',
         'tools/gn/header_checker_unittest.cc',
         'tools/gn/inherited_libraries_unittest.cc',
diff --git a/tools/gn/binary_target_generator.cc b/tools/gn/binary_target_generator.cc
index 5b01b62..d307342 100644
--- a/tools/gn/binary_target_generator.cc
+++ b/tools/gn/binary_target_generator.cc
@@ -10,6 +10,8 @@
 #include "tools/gn/filesystem_utils.h"
 #include "tools/gn/functions.h"
 #include "tools/gn/parse_tree.h"
+#include "tools/gn/rust_target_generator.h"
+#include "tools/gn/rust_variables.h"
 #include "tools/gn/scope.h"
 #include "tools/gn/settings.h"
 #include "tools/gn/value_extractors.h"
@@ -64,6 +66,13 @@
   if (!ValidateSources())
     return;
 
+  if (target_->source_types_used().RustSourceUsed()) {
+    RustTargetGenerator rustgen(target_, scope_, function_call_, err_);
+    rustgen.Run();
+    if (err_->has_error())
+      return;
+  }
+
   // Config values (compiler flags, etc.) set directly on this target.
   ConfigValuesGenerator gen(&target_->config_values(), scope_,
                             scope_->GetSourceDir(), err_);
@@ -170,16 +179,6 @@
   return true;
 }
 
-bool BinaryTargetGenerator::FillOutputExtension() {
-  const Value* value = scope_->GetValue(variables::kOutputExtension, true);
-  if (!value)
-    return true;
-  if (!value->VerifyTypeIs(Value::STRING, err_))
-    return false;
-  target_->set_output_extension(value->string_value());
-  return true;
-}
-
 bool BinaryTargetGenerator::FillAllowCircularIncludesFrom() {
   const Value* value =
       scope_->GetValue(variables::kAllowCircularIncludesFrom, true);
@@ -219,6 +218,12 @@
 }
 
 bool BinaryTargetGenerator::ValidateSources() {
+  // For Rust targets, if the only source file is the root `sources` can be
+  // omitted/empty.
+  if (scope_->GetValue(variables::kRustCrateRoot, false)) {
+    target_->source_types_used().Set(SourceFile::SOURCE_RS);
+  }
+
   if (target_->source_types_used().MixedSourceUsed()) {
     *err_ =
         Err(function_call_, "More than one language used in target sources.",
diff --git a/tools/gn/binary_target_generator.h b/tools/gn/binary_target_generator.h
index e0700dd..b88da50 100644
--- a/tools/gn/binary_target_generator.h
+++ b/tools/gn/binary_target_generator.h
@@ -30,7 +30,6 @@
   bool FillOutputName();
   bool FillOutputPrefixOverride();
   bool FillOutputDir();
-  bool FillOutputExtension();
   bool FillAllowCircularIncludesFrom();
   bool ValidateSources();
 
diff --git a/tools/gn/config_values_generator.cc b/tools/gn/config_values_generator.cc
index ff4e4df..5947274 100644
--- a/tools/gn/config_values_generator.cc
+++ b/tools/gn/config_values_generator.cc
@@ -73,6 +73,8 @@
   FILL_DIR_CONFIG_VALUE(include_dirs)
   FILL_STRING_CONFIG_VALUE(ldflags)
   FILL_DIR_CONFIG_VALUE(lib_dirs)
+  FILL_STRING_CONFIG_VALUE(rustflags)
+  FILL_STRING_CONFIG_VALUE(rustenv)
 
 #undef FILL_STRING_CONFIG_VALUE
 #undef FILL_DIR_CONFIG_VALUE
diff --git a/tools/gn/config_values_generator.h b/tools/gn/config_values_generator.h
index 165f41a..e663991 100644
--- a/tools/gn/config_values_generator.h
+++ b/tools/gn/config_values_generator.h
@@ -40,6 +40,7 @@
 #define CONFIG_VALUES_VARS_HELP                                            \
   "  Flags: cflags, cflags_c, cflags_cc, cflags_objc, cflags_objcc,\n"     \
   "         asmflags, defines, include_dirs, inputs, ldflags, lib_dirs,\n" \
-  "         libs, precompiled_header, precompiled_source\n"
+  "         libs, precompiled_header, precompiled_source, rustflags,\n"    \
+  "         rustenv\n"
 
 #endif  // TOOLS_GN_CONFIG_VALUES_GENERATOR_H_
diff --git a/tools/gn/functions.cc b/tools/gn/functions.cc
index 34d539c..ad4a1b6 100644
--- a/tools/gn/functions.cc
+++ b/tools/gn/functions.cc
@@ -1269,6 +1269,7 @@
     INSERT_FUNCTION(StaticLibrary, true)
     INSERT_FUNCTION(Target, true)
     INSERT_FUNCTION(GeneratedFile, true)
+    INSERT_FUNCTION(RustLibrary, true)
 
     INSERT_FUNCTION(Assert, false)
     INSERT_FUNCTION(Config, false)
diff --git a/tools/gn/functions.h b/tools/gn/functions.h
index 97f5b0d..9dc2cdc 100644
--- a/tools/gn/functions.h
+++ b/tools/gn/functions.h
@@ -278,6 +278,15 @@
                     const std::vector<Value>& args,
                     Err* err);
 
+extern const char kRustLibrary[];
+extern const char kRustLibrary_HelpShort[];
+extern const char kRustLibrary_Help[];
+Value RunRustLibrary(Scope* scope,
+                     const FunctionCallNode* function,
+                     const std::vector<Value>& args,
+                     BlockNode* block,
+                     Err* err);
+
 extern const char kSetDefaults[];
 extern const char kSetDefaults_HelpShort[];
 extern const char kSetDefaults_Help[];
diff --git a/tools/gn/functions_target.cc b/tools/gn/functions_target.cc
index a0690ab..c20c1e6 100644
--- a/tools/gn/functions_target.cc
+++ b/tools/gn/functions_target.cc
@@ -20,6 +20,11 @@
   "  General: check_includes, configs, data, friend, inputs, metadata,\n"  \
   "           output_name, output_extension, public, sources, testonly,\n" \
   "           visibility\n"
+#define RUST_VARS \
+  "  Rust variables: aliased_deps, crate_root, crate_name, edition\n"
+#define RUST_SHARED_VARS                                                 \
+  "  Rust variables: aliased_deps, crate_root, crate_name, crate_type, " \
+  "edition\n"
 
 namespace functions {
 
@@ -87,6 +92,15 @@
   "  action are started. This can give additional parallelism in the build\n" \
   "  for runtime-only dependencies.\n"
 
+// Common help paragraph on targets that can use different languages.
+#define LANGUAGE_HELP                                                     \
+  "\n"                                                                    \
+  "  The tools and commands used to create this target type will be\n"    \
+  "  determined by the source files in its sources. Targets containing\n" \
+  "  multiple compiler-incompatible languages are not allowed (e.g. a\n"  \
+  "  target containing both C and C++ sources is acceptable, but a\n"     \
+  "  target containing C and Rust sources is not).\n"
+
 const char kAction[] = "action";
 const char kAction_HelpShort[] =
     "action: Declare a target that runs a script a single time.";
@@ -532,9 +546,14 @@
 const char kExecutable_Help[] =
     R"(executable: Declare an executable target.
 
+Language and compilation
+)" LANGUAGE_HELP
+    R"(
+
 Variables
 
-)" CONFIG_VALUES_VARS_HELP DEPS_VARS DEPENDENT_CONFIG_VARS GENERAL_TARGET_VARS;
+)" CONFIG_VALUES_VARS_HELP DEPS_VARS DEPENDENT_CONFIG_VARS GENERAL_TARGET_VARS
+        RUST_VARS;
 
 Value RunExecutable(Scope* scope,
                     const FunctionCallNode* function,
@@ -596,9 +615,14 @@
   to dynamically load the library at runtime), then you should use a
   "shared_library" target type instead.
 
+Language and compilation
+)" LANGUAGE_HELP
+    R"(
+
 Variables
 
-)" CONFIG_VALUES_VARS_HELP DEPS_VARS DEPENDENT_CONFIG_VARS GENERAL_TARGET_VARS;
+)" CONFIG_VALUES_VARS_HELP DEPS_VARS DEPENDENT_CONFIG_VARS GENERAL_TARGET_VARS
+        RUST_SHARED_VARS;
 
 Value RunLoadableModule(Scope* scope,
                         const FunctionCallNode* function,
@@ -609,6 +633,35 @@
                               block, err);
 }
 
+// rust_library ----------------------------------------------------------------
+
+const char kRustLibrary[] = "rust_library";
+const char kRustLibrary_HelpShort[] =
+    "rust_library: Declare a Rust library target.";
+const char kRustLibrary_Help[] =
+    R"(rust_library: Declare a Rust library target.
+
+  A Rust library is an archive containing additional rust-c provided metadata.
+  These are the files produced by the rustc compiler with the `.rlib`
+  extension, and are the intermediate step for most Rust-based binaries.
+
+Language and compilation
+)" LANGUAGE_HELP
+    R"(
+
+Variables
+
+)" CONFIG_VALUES_VARS_HELP DEPS_VARS DEPENDENT_CONFIG_VARS GENERAL_TARGET_VARS
+        RUST_VARS;
+Value RunRustLibrary(Scope* scope,
+                     const FunctionCallNode* function,
+                     const std::vector<Value>& args,
+                     BlockNode* block,
+                     Err* err) {
+  return ExecuteGenericTarget(functions::kRustLibrary, scope, function, args,
+                              block, err);
+}
+
 // shared_library --------------------------------------------------------------
 
 const char kSharedLibrary[] = "shared_library";
@@ -623,9 +676,14 @@
   via "data_deps" or, on Darwin platforms, use a "loadable_module" target type
   instead.
 
+Language and compilation
+)" LANGUAGE_HELP
+    R"(
+
 Variables
 
-)" CONFIG_VALUES_VARS_HELP DEPS_VARS DEPENDENT_CONFIG_VARS GENERAL_TARGET_VARS;
+)" CONFIG_VALUES_VARS_HELP DEPS_VARS DEPENDENT_CONFIG_VARS GENERAL_TARGET_VARS
+        RUST_SHARED_VARS;
 
 Value RunSharedLibrary(Scope* scope,
                        const FunctionCallNode* function,
@@ -643,6 +701,11 @@
 const char kSourceSet_Help[] =
     R"(source_set: Declare a source set target.
 
+  The language of a source_set target is determined by the extensions present
+  in its sources.
+
+C-language source_sets
+
   A source set is a collection of sources that get compiled, but are not linked
   to produce any kind of library. Instead, the resulting object files are
   implicitly added to the linker line of all targets that depend on the source
@@ -664,6 +727,13 @@
   not from the intermediate targets." There is no way to express this concept
   when linking multiple static libraries into a shared library.
 
+Rust-language source_sets
+
+  A Rust source set is a collection of sources that get passed along to the
+  final target that depends on it. No compilation is performed, and the source
+  files are simply added as dependencies on the eventual rustc invocation that
+  would produce a binary.
+
 Variables
 
 )" CONFIG_VALUES_VARS_HELP DEPS_VARS DEPENDENT_CONFIG_VARS GENERAL_TARGET_VARS;
@@ -694,7 +764,8 @@
 Variables
 
   complete_static_lib
-)" CONFIG_VALUES_VARS_HELP DEPS_VARS DEPENDENT_CONFIG_VARS GENERAL_TARGET_VARS;
+)" CONFIG_VALUES_VARS_HELP DEPS_VARS DEPENDENT_CONFIG_VARS GENERAL_TARGET_VARS
+        RUST_VARS LANGUAGE_HELP;
 
 Value RunStaticLibrary(Scope* scope,
                        const FunctionCallNode* function,
diff --git a/tools/gn/functions_target_rust_unittest.cc b/tools/gn/functions_target_rust_unittest.cc
new file mode 100644
index 0000000..a8da82d
--- /dev/null
+++ b/tools/gn/functions_target_rust_unittest.cc
@@ -0,0 +1,352 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "tools/gn/config.h"
+#include "tools/gn/scheduler.h"
+#include "tools/gn/scope.h"
+#include "tools/gn/test_with_scheduler.h"
+#include "tools/gn/test_with_scope.h"
+#include "util/test/test.h"
+
+using RustFunctionsTarget = TestWithScheduler;
+
+// Checks that the appropriate crate type is used.
+TEST_F(RustFunctionsTarget, CrateName) {
+  TestWithScope setup;
+
+  // The target generator needs a place to put the targets or it will fail.
+  Scope::ItemVector item_collector;
+  setup.scope()->set_item_collector(&item_collector);
+  setup.scope()->set_source_dir(SourceDir("/"));
+
+  TestParseInput exe_input(
+      "executable(\"foo\") {\n"
+      "  crate_name = \"foo_crate\"\n"
+      "  sources = [ \"foo.rs\", \"lib.rs\", \"main.rs\" ]\n"
+      "  edition = \"2018\""
+      "}\n");
+  ASSERT_FALSE(exe_input.has_error());
+  Err err;
+  exe_input.parsed()->Execute(setup.scope(), &err);
+  ASSERT_FALSE(err.has_error()) << err.message();
+  ASSERT_EQ(item_collector.back()->AsTarget()->rust_values().crate_name(),
+            "foo_crate");
+
+  TestParseInput lib_input(
+      "executable(\"foo\") {\n"
+      "  sources = [ \"lib.rs\" ]\n"
+      "  edition = \"2018\""
+      "}\n");
+  ASSERT_FALSE(lib_input.has_error());
+  err = Err();
+  lib_input.parsed()->Execute(setup.scope(), &err);
+  ASSERT_FALSE(err.has_error()) << err.message();
+  ASSERT_EQ(item_collector.back()->AsTarget()->rust_values().crate_name(),
+            "foo")
+      << item_collector.back()->AsTarget()->rust_values().crate_name();
+}
+
+// Checks that the appropriate crate root is found.
+TEST_F(RustFunctionsTarget, CrateRootFind) {
+  TestWithScope setup;
+
+  // The target generator needs a place to put the targets or it will fail.
+  Scope::ItemVector item_collector;
+  setup.scope()->set_item_collector(&item_collector);
+  setup.scope()->set_source_dir(SourceDir("/"));
+
+  TestParseInput normal_input(
+      "executable(\"foo\") {\n"
+      "  crate_root = \"foo.rs\""
+      "  sources = [ \"main.rs\" ]\n"
+      "  edition = \"2018\""
+      "}\n");
+  ASSERT_FALSE(normal_input.has_error());
+  Err err;
+  normal_input.parsed()->Execute(setup.scope(), &err);
+  ASSERT_FALSE(err.has_error()) << err.message();
+  ASSERT_EQ(
+      item_collector.back()->AsTarget()->rust_values().crate_root().value(),
+      "/foo.rs");
+
+  TestParseInput normal_shlib_input(
+      "shared_library(\"foo\") {\n"
+      "  crate_root = \"foo.rs\""
+      "  crate_type = \"dylib\"\n"
+      "  sources = [ \"main.rs\" ]\n"
+      "  edition = \"2018\""
+      "}\n");
+  ASSERT_FALSE(normal_shlib_input.has_error());
+  err = Err();
+  normal_shlib_input.parsed()->Execute(setup.scope(), &err);
+  ASSERT_FALSE(err.has_error()) << err.message();
+  ASSERT_EQ(
+      item_collector.back()->AsTarget()->rust_values().crate_root().value(),
+      "/foo.rs");
+
+  TestParseInput exe_input(
+      "executable(\"foo\") {\n"
+      "  sources = [ \"foo.rs\", \"lib.rs\", \"main.rs\" ]\n"
+      "  edition = \"2018\""
+      "}\n");
+  ASSERT_FALSE(exe_input.has_error());
+  err = Err();
+  exe_input.parsed()->Execute(setup.scope(), &err);
+  ASSERT_FALSE(err.has_error()) << err.message();
+  ASSERT_EQ(
+      item_collector.back()->AsTarget()->rust_values().crate_root().value(),
+      "/main.rs");
+
+  TestParseInput lib_input(
+      "rust_library(\"libfoo\") {\n"
+      "  sources = [ \"foo.rs\", \"lib.rs\", \"main.rs\" ]\n"
+      "  edition = \"2018\""
+      "}\n");
+  ASSERT_FALSE(lib_input.has_error());
+  err = Err();
+  lib_input.parsed()->Execute(setup.scope(), &err);
+  ASSERT_FALSE(err.has_error()) << err.message();
+  ASSERT_EQ(
+      item_collector.back()->AsTarget()->rust_values().crate_root().value(),
+      "/lib.rs");
+
+  TestParseInput singlesource_input(
+      "executable(\"bar\") {\n"
+      "  sources = [ \"bar.rs\" ]\n"
+      "  edition = \"2018\""
+      "}\n");
+  ASSERT_FALSE(singlesource_input.has_error());
+  err = Err();
+  singlesource_input.parsed()->Execute(setup.scope(), &err);
+  ASSERT_FALSE(err.has_error()) << err.message();
+  ASSERT_EQ(
+      item_collector.back()->AsTarget()->rust_values().crate_root().value(),
+      "/bar.rs");
+
+  TestParseInput error_input(
+      "rust_library(\"foo\") {\n"
+      "  sources = [ \"foo.rs\", \"main.rs\" ]\n"
+      "  edition = \"2018\""
+      "}\n");
+  ASSERT_FALSE(error_input.has_error());
+  err = Err();
+  error_input.parsed()->Execute(setup.scope(), &err);
+  ASSERT_TRUE(err.has_error());
+  EXPECT_EQ("Missing \"crate_root\" and missing \"lib.rs\" in sources.",
+            err.message());
+
+  TestParseInput nosources_input(
+      "executable(\"bar\") {\n"
+      "  crate_root = \"bar.rs\"\n"
+      "  edition = \"2018\""
+      "}\n");
+  ASSERT_FALSE(nosources_input.has_error());
+  err = Err();
+  nosources_input.parsed()->Execute(setup.scope(), &err);
+  ASSERT_FALSE(err.has_error()) << err.message();
+  ASSERT_EQ(
+      item_collector.back()->AsTarget()->rust_values().crate_root().value(),
+      "/bar.rs");
+}
+
+// Checks that the appropriate crate type is used.
+TEST_F(RustFunctionsTarget, CrateTypeSelection) {
+  TestWithScope setup;
+
+  // The target generator needs a place to put the targets or it will fail.
+  Scope::ItemVector item_collector;
+  setup.scope()->set_item_collector(&item_collector);
+  setup.scope()->set_source_dir(SourceDir("/"));
+
+  TestParseInput lib_input(
+      "shared_library(\"libfoo\") {\n"
+      "  crate_type = \"dylib\"\n"
+      "  sources = [ \"lib.rs\" ]\n"
+      "  edition = \"2018\""
+      "}\n");
+  ASSERT_FALSE(lib_input.has_error());
+  Err err;
+  lib_input.parsed()->Execute(setup.scope(), &err);
+  ASSERT_FALSE(err.has_error()) << err.message();
+  ASSERT_EQ(item_collector.back()->AsTarget()->rust_values().crate_type(),
+            RustValues::CRATE_DYLIB);
+
+  TestParseInput exe_error_input(
+      "executable(\"foo\") {\n"
+      "  crate_type = \"bin\"\n"
+      "  sources = [ \"main.rs\" ]\n"
+      "  edition = \"2018\""
+      "}\n");
+  ASSERT_FALSE(exe_error_input.has_error());
+  err = Err();
+  exe_error_input.parsed()->Execute(setup.scope(), &err);
+  ASSERT_TRUE(err.has_error());
+  EXPECT_EQ(
+      "\"crate_type\" automatically inferred for non-shared Rust targets.",
+      err.message())
+      << err.message();
+
+  TestParseInput lib_error_input(
+      "shared_library(\"foo\") {\n"
+      "  crate_type = \"bad\"\n"
+      "  sources = [ \"lib.rs\" ]\n"
+      "  edition = \"2018\""
+      "}\n");
+  ASSERT_FALSE(lib_error_input.has_error());
+  err = Err();
+  lib_error_input.parsed()->Execute(setup.scope(), &err);
+  ASSERT_TRUE(err.has_error());
+  EXPECT_EQ("Inadmissible crate type \"bad\".", err.message()) << err.message();
+
+  TestParseInput lib_missing_error_input(
+      "shared_library(\"foo\") {\n"
+      "  sources = [ \"lib.rs\" ]\n"
+      "  edition = \"2018\""
+      "}\n");
+  ASSERT_FALSE(lib_missing_error_input.has_error());
+  err = Err();
+  lib_missing_error_input.parsed()->Execute(setup.scope(), &err);
+  ASSERT_TRUE(err.has_error());
+  EXPECT_EQ("Must set \"crate_type\" on a Rust \"shared_library\".",
+            err.message());
+}
+
+// Checks that the appropriate config values are propagated.
+TEST_F(RustFunctionsTarget, ConfigValues) {
+  TestWithScope setup;
+
+  // The target generator needs a place to put the targets or it will fail.
+  Scope::ItemVector item_collector;
+  setup.scope()->set_item_collector(&item_collector);
+  setup.scope()->set_source_dir(SourceDir("/"));
+
+  TestParseInput exe_input(
+      "config(\"foo\") {\n"
+      "  rustflags = [ \"-Cdebuginfo=2\" ]\n"
+      "  rustenv = [ \"RUST_BACKTRACE=1\" ]"
+      "}\n");
+  ASSERT_FALSE(exe_input.has_error());
+  Err err;
+  exe_input.parsed()->Execute(setup.scope(), &err);
+  ASSERT_FALSE(err.has_error()) << err.message();
+
+  EXPECT_EQ(item_collector.back()->AsConfig()->own_values().rustflags().size(),
+            1U);
+  EXPECT_EQ(item_collector.back()->AsConfig()->own_values().rustflags()[0],
+            "-Cdebuginfo=2");
+  EXPECT_EQ(item_collector.back()->AsConfig()->own_values().rustenv().size(),
+            1U);
+  EXPECT_EQ(item_collector.back()->AsConfig()->own_values().rustenv()[0],
+            "RUST_BACKTRACE=1");
+}
+
+// Checks that set_defaults works properly.
+TEST_F(RustFunctionsTarget, SetDefaults) {
+  TestWithScope setup;
+
+  // The target generator needs a place to put the targets or it will fail.
+  Scope::ItemVector item_collector;
+  setup.scope()->set_item_collector(&item_collector);
+  setup.scope()->set_source_dir(SourceDir("/"));
+
+  TestParseInput exe_input(
+      "config(\"foo\") {\n"
+      "  rustflags = [ \"-Cdebuginfo=2\" ]\n"
+      "  rustenv = [ \"RUST_BACKTRACE=1\" ]"
+      "}\n"
+      "set_defaults(\"rust_library\") {\n"
+      "  configs = [ \":foo\" ]\n"
+      "}\n");
+  ASSERT_FALSE(exe_input.has_error());
+  Err err;
+  exe_input.parsed()->Execute(setup.scope(), &err);
+  ASSERT_FALSE(err.has_error()) << err.message() << err.message();
+
+  EXPECT_EQ(setup.scope()
+                ->GetTargetDefaults("rust_library")
+                ->GetValue("configs")
+                ->type(),
+            Value::LIST);
+  EXPECT_EQ(setup.scope()
+                ->GetTargetDefaults("rust_library")
+                ->GetValue("configs")
+                ->list_value()
+                .size(),
+            1U);
+  EXPECT_EQ(setup.scope()
+                ->GetTargetDefaults("rust_library")
+                ->GetValue("configs")
+                ->list_value()[0]
+                .type(),
+            Value::STRING);
+  EXPECT_EQ(setup.scope()
+                ->GetTargetDefaults("rust_library")
+                ->GetValue("configs")
+                ->list_value()[0]
+                .string_value(),
+            ":foo");
+}
+
+// Checks that the dition gets propagated correctly.
+TEST_F(RustFunctionsTarget, Edition) {
+  TestWithScope setup;
+
+  // The target generator needs a place to put the targets or it will fail.
+  Scope::ItemVector item_collector;
+  setup.scope()->set_item_collector(&item_collector);
+  setup.scope()->set_source_dir(SourceDir("/"));
+
+  TestParseInput lib_input(
+      "shared_library(\"libfoo\") {\n"
+      "  crate_type = \"dylib\"\n"
+      "  sources = [ \"lib.rs\" ]\n"
+      "  edition = \"2018\""
+      "}\n");
+  ASSERT_FALSE(lib_input.has_error());
+  Err err;
+  lib_input.parsed()->Execute(setup.scope(), &err);
+  ASSERT_FALSE(err.has_error()) << err.message();
+  ASSERT_EQ(item_collector.back()->AsTarget()->rust_values().edition(), "2018");
+
+  TestParseInput error_input(
+      "shared_library(\"foo\") {\n"
+      "  crate_type = \"dylib\"\n"
+      "  sources = [ \"lib.rs\" ]\n"
+      "}\n");
+  ASSERT_FALSE(error_input.has_error());
+  err = Err();
+  error_input.parsed()->Execute(setup.scope(), &err);
+  ASSERT_TRUE(err.has_error());
+  EXPECT_EQ("Missing \"edition\" in Rust target.", err.message())
+      << err.message();
+}
+
+// Checks aliased_deps parsing.
+TEST_F(RustFunctionsTarget, AliasedDeps) {
+  TestWithScope setup;
+
+  // The target generator needs a place to put the targets or it will fail.
+  Scope::ItemVector item_collector;
+  setup.scope()->set_item_collector(&item_collector);
+  setup.scope()->set_source_dir(SourceDir("/"));
+
+  TestParseInput exe_input(
+      "executable(\"foo\") {\n"
+      "  sources = [ \"main.rs\" ]\n"
+      "  deps = [ \"//bar\", \"//baz\" ]\n"
+      "  edition = \"2018\""
+      "  aliased_deps = {\n"
+      "    bar_renamed = \"//bar\"\n"
+      "    baz_renamed = \"//baz:baz\"\n"
+      "  }\n"
+      "}\n");
+  ASSERT_FALSE(exe_input.has_error());
+  Err err;
+  exe_input.parsed()->Execute(setup.scope(), &err);
+  ASSERT_FALSE(err.has_error()) << err.message();
+
+  EXPECT_EQ(
+      item_collector.back()->AsTarget()->rust_values().aliased_deps().size(),
+      2U);
+}
diff --git a/tools/gn/rust_target_generator.cc b/tools/gn/rust_target_generator.cc
new file mode 100644
index 0000000..1dad154
--- /dev/null
+++ b/tools/gn/rust_target_generator.cc
@@ -0,0 +1,197 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "tools/gn/rust_target_generator.h"
+
+#include "tools/gn/config_values_generator.h"
+#include "tools/gn/err.h"
+#include "tools/gn/filesystem_utils.h"
+#include "tools/gn/functions.h"
+#include "tools/gn/parse_tree.h"
+#include "tools/gn/rust_variables.h"
+#include "tools/gn/scope.h"
+#include "tools/gn/target.h"
+#include "tools/gn/value_extractors.h"
+
+RustTargetGenerator::RustTargetGenerator(Target* target,
+                                         Scope* scope,
+                                         const FunctionCallNode* function_call,
+                                         Err* err)
+    : TargetGenerator(target, scope, function_call, err) {}
+
+RustTargetGenerator::~RustTargetGenerator() = default;
+
+void RustTargetGenerator::DoRun() {
+  // source_set targets don't need any special Rust handling.
+  if (target_->output_type() == Target::SOURCE_SET)
+    return;
+
+  // Check that this type of target is Rust-supported.
+  if (target_->output_type() != Target::EXECUTABLE &&
+      target_->output_type() != Target::SHARED_LIBRARY &&
+      target_->output_type() != Target::RUST_LIBRARY &&
+      target_->output_type() != Target::STATIC_LIBRARY &&
+      target_->output_type() != Target::LOADABLE_MODULE) {
+    // Only valid rust output types.
+    *err_ = Err(function_call_,
+                "Target type \"" +
+                    std::string(Target::GetStringForOutputType(
+                        target_->output_type())) +
+                    "\" is not supported for Rust compilation.",
+                "Supported target types are \"executable\", \"loadable_module\""
+                "\"shared_library\", \"static_library\", or \"source_set\".");
+    return;
+  }
+
+  if (!FillCrateName())
+    return;
+
+  if (!FillCrateType())
+    return;
+
+  if (!FillCrateRoot())
+    return;
+
+  if (!FillEdition())
+    return;
+
+  if (!FillAliasedDeps())
+    return;
+}
+
+bool RustTargetGenerator::FillCrateName() {
+  const Value* value = scope_->GetValue(variables::kRustCrateName, true);
+  if (!value) {
+    // The target name will be used.
+    target_->rust_values().crate_name() = target_->label().name();
+    return true;
+  }
+  if (!value->VerifyTypeIs(Value::STRING, err_))
+    return false;
+
+  target_->rust_values().crate_name() = std::move(value->string_value());
+  return true;
+}
+
+bool RustTargetGenerator::FillCrateType() {
+  const Value* value = scope_->GetValue(variables::kRustCrateType, true);
+  if (!value) {
+    // Non-shared_library targets shouldn't set this, so that's okay.
+    if (target_->output_type() != Target::SHARED_LIBRARY &&
+        target_->output_type() != Target::LOADABLE_MODULE)
+      return true;
+    // But require shared_library and loadable_module targets to tell us what
+    // they want.
+    *err_ = Err(function_call_,
+                "Must set \"crate_type\" on a Rust \"shared_library\".",
+                "\"crate_type\" must be one of \"dylib\", \"cdylib\", or "
+                "\"proc-macro\".");
+    return false;
+  }
+
+  if (target_->output_type() != Target::SHARED_LIBRARY &&
+      target_->output_type() != Target::LOADABLE_MODULE) {
+    *err_ = Err(
+        value->origin(),
+        "\"crate_type\" automatically inferred for non-shared Rust targets.",
+        "Setting it here has no effect.");
+    return false;
+  }
+
+  if (!value->VerifyTypeIs(Value::STRING, err_))
+    return false;
+
+  if (value->string_value() == "dylib") {
+    target_->rust_values().set_crate_type(RustValues::CRATE_DYLIB);
+    return true;
+  }
+  if (value->string_value() == "cdylib") {
+    target_->rust_values().set_crate_type(RustValues::CRATE_CDYLIB);
+    return true;
+  }
+  if (value->string_value() == "proc-macro") {
+    target_->rust_values().set_crate_type(RustValues::CRATE_PROC_MACRO);
+    return true;
+  }
+
+  *err_ = Err(value->origin(),
+              "Inadmissible crate type \"" + value->string_value() + "\".",
+              "\"crate_type\" must be one of \"dylib\", \"cdylib\", or "
+              "\"proc-macro\" for a \"shared_library\".");
+  return false;
+}
+
+bool RustTargetGenerator::FillCrateRoot() {
+  const Value* value = scope_->GetValue(variables::kRustCrateRoot, true);
+  if (!value) {
+    // If there's only one source, use that.
+    if (target_->sources().size() == 1) {
+      target_->rust_values().set_crate_root(target_->sources()[0]);
+      return true;
+    }
+    // Otherwise, see if "lib.rs" or "main.rs" (as relevant) are in sources.
+    std::string to_find =
+        target_->output_type() == Target::EXECUTABLE ? "main.rs" : "lib.rs";
+    for (auto& source : target_->sources()) {
+      if (source.GetName() == to_find) {
+        target_->rust_values().set_crate_root(source);
+        return true;
+      }
+    }
+    *err_ = Err(function_call_, "Missing \"crate_root\" and missing \"" +
+                                    to_find + "\" in sources.");
+    return false;
+  }
+
+  if (!value->VerifyTypeIs(Value::STRING, err_))
+    return false;
+
+  SourceFile dest;
+  if (!ExtractRelativeFile(scope_->settings()->build_settings(), *value,
+                           scope_->GetSourceDir(), &dest, err_))
+    return false;
+
+  target_->rust_values().set_crate_root(dest);
+  return true;
+}
+
+bool RustTargetGenerator::FillEdition() {
+  const Value* value = scope_->GetValue(variables::kRustEdition, true);
+  if (!value) {
+    *err_ = Err(function_call_, "Missing \"edition\" in Rust target.");
+    return false;
+  }
+
+  if (!value->VerifyTypeIs(Value::STRING, err_))
+    return false;
+
+  target_->rust_values().edition() = std::move(value->string_value());
+  return true;
+}
+
+bool RustTargetGenerator::FillAliasedDeps() {
+  const Value* value = scope_->GetValue(variables::kRustAliasedDeps, true);
+  if (!value)
+    return true;
+
+  if (!value->VerifyTypeIs(Value::SCOPE, err_))
+    return false;
+
+  Scope::KeyValueMap aliased_deps;
+  value->scope_value()->GetCurrentScopeValues(&aliased_deps);
+  for (const auto& pair : aliased_deps) {
+    Label dep_label =
+        Label::Resolve(scope_->GetSourceDir(), ToolchainLabelForScope(scope_),
+                       pair.second, err_);
+
+    if (err_->has_error())
+      return false;
+
+    // Insert into the aliased_deps map.
+    target_->rust_values().aliased_deps().insert(std::pair<Label, std::string>(
+        std::move(dep_label), std::move(pair.first)));
+  }
+
+  return true;
+}
diff --git a/tools/gn/rust_target_generator.h b/tools/gn/rust_target_generator.h
new file mode 100644
index 0000000..ae6a365
--- /dev/null
+++ b/tools/gn/rust_target_generator.h
@@ -0,0 +1,34 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TOOLS_GN_RUST_TARGET_GENERATOR_H_
+#define TOOLS_GN_RUST_TARGET_GENERATOR_H_
+
+#include "base/macros.h"
+#include "tools/gn/target.h"
+#include "tools/gn/target_generator.h"
+
+// Collects and writes specified data.
+class RustTargetGenerator : public TargetGenerator {
+ public:
+  RustTargetGenerator(Target* target,
+                      Scope* scope,
+                      const FunctionCallNode* function_call,
+                      Err* err);
+  ~RustTargetGenerator() override;
+
+ protected:
+  void DoRun() override;
+
+ private:
+  bool FillCrateName();
+  bool FillCrateRoot();
+  bool FillCrateType();
+  bool FillEdition();
+  bool FillAliasedDeps();
+
+  DISALLOW_COPY_AND_ASSIGN(RustTargetGenerator);
+};
+
+#endif  // TOOLS_GN_GENERATED_FILE_TARGET_GENERATOR_H_
diff --git a/tools/gn/substitution_writer.cc b/tools/gn/substitution_writer.cc
index b26579b..8ca261e 100644
--- a/tools/gn/substitution_writer.cc
+++ b/tools/gn/substitution_writer.cc
@@ -9,6 +9,8 @@
 #include "tools/gn/escape.h"
 #include "tools/gn/filesystem_utils.h"
 #include "tools/gn/output_file.h"
+#include "tools/gn/rust_substitution_type.h"
+#include "tools/gn/rust_tool.h"
 #include "tools/gn/settings.h"
 #include "tools/gn/source_file.h"
 #include "tools/gn/string_utils.h"
@@ -569,7 +571,26 @@
     if (target->output_extension().empty())
       return std::string();  // Explicitly set to no extension.
     return std::string(".") + target->output_extension();
-
+  } else if (type == &kRustSubstitutionCrateName) {
+    // Only include the toolchain for non-default toolchains.
+    return target->rust_values().crate_name();
+  } else if (type == &kRustSubstitutionOutputPrefix) {
+    // Rustc expects specific output prefixes, so make sure we provide it if
+    // necessary.
+    if (target->output_type() == Target::RUST_LIBRARY ||
+        target->output_type() == Target::SHARED_LIBRARY ||
+        target->output_type() == Target::LOADABLE_MODULE)
+      return "lib";
+    return "";
+  } else if (type == &kRustSubstitutionOutputExtension) {
+    if (!target->output_extension_set()) {
+      DCHECK(tool->AsRust());
+      return tool->AsRust()->rustc_output_extension(
+          target->output_type(), target->rust_values().crate_type());
+    }
+    if (target->output_extension().empty())
+      return std::string();  // Explicitly set to no extension.
+    return std::string(".") + target->output_extension();
   } else {
     NOTREACHED();
     return std::string();
diff --git a/tools/gn/target_generator.cc b/tools/gn/target_generator.cc
index a468426..cfcc4e3 100644
--- a/tools/gn/target_generator.cc
+++ b/tools/gn/target_generator.cc
@@ -144,6 +144,10 @@
     GeneratedFileTargetGenerator generator(target.get(), scope, function_call,
                                            Target::GENERATED_FILE, err);
     generator.Run();
+  } else if (output_type == functions::kRustLibrary) {
+    BinaryTargetGenerator generator(target.get(), scope, function_call,
+                                    Target::RUST_LIBRARY, err);
+    generator.Run();
   } else {
     *err = Err(function_call, "Not a known target type",
                "I am very confused by the target type \"" + output_type + "\"");
@@ -359,6 +363,16 @@
   return true;
 }
 
+bool TargetGenerator::FillOutputExtension() {
+  const Value* value = scope_->GetValue(variables::kOutputExtension, true);
+  if (!value)
+    return true;
+  if (!value->VerifyTypeIs(Value::STRING, err_))
+    return false;
+  target_->set_output_extension(value->string_value());
+  return true;
+}
+
 bool TargetGenerator::EnsureSubstitutionIsInOutputDir(
     const SubstitutionPattern& pattern,
     const Value& original_value) {
diff --git a/tools/gn/target_generator.h b/tools/gn/target_generator.h
index 8795cbf..ae54882 100644
--- a/tools/gn/target_generator.h
+++ b/tools/gn/target_generator.h
@@ -52,6 +52,7 @@
   bool FillConfigs();
   bool FillOutputs(bool allow_substitutions);
   bool FillCheckIncludes();
+  bool FillOutputExtension();
 
   // Rrturns true if the given pattern will expand to a file in the output
   // directory. If not, returns false and sets the error, blaming the given