[rust project] Extract compiler target

Extract the compiler arguments, and then from them, the compilation
target triple that is being passed to rustc.

This is also a step towards deduplicating targets by compilation
target and options (combining test and non-test, and preferring
cross-compilation targets over the host (default toolchain over
a separate toolchain, if there are multiple listings of the same
rlib/binary/test.

Change-Id: I1af0ef2da5dca41729e9367389048a068efd7959
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/8840
Commit-Queue: Aaron Wood <aaronwood@google.com>
Reviewed-by: Tyler Mandry <tmandry@google.com>
diff --git a/src/gn/rust_project_writer.cc b/src/gn/rust_project_writer.cc
index 21ac261..7bd0561 100644
--- a/src/gn/rust_project_writer.cc
+++ b/src/gn/rust_project_writer.cc
@@ -5,6 +5,7 @@
 #include "gn/rust_project_writer.h"
 
 #include <fstream>
+#include <optional>
 #include <sstream>
 #include <tuple>
 
@@ -44,7 +45,9 @@
 //              "unix", // "atomic" value config options
 //              "rust_panic=\"abort\""", // key="value" config options
 //            ]
-//            "root_module": "absolute path to crate"
+//            "root_module": "absolute path to crate",
+//            "label": "//path/target:value", // GN target for the crate
+//            "target": "x86_64-unknown-linux" // optional rustc target
 //        },
 // }
 //
@@ -105,8 +108,62 @@
   return deps;
 }
 
-// TODO(bwb) Parse sysroot structure from toml files. This is fragile and might
-// break if upstream changes the dependency structure.
+std::vector<std::string> ExtractCompilerArgs(const Target* target) {
+  std::vector<std::string> args;
+  for (ConfigValuesIterator iter(target); !iter.done(); iter.Next()) {
+    auto rustflags = iter.cur().rustflags();
+    for (auto flag_iter = rustflags.begin(); flag_iter != rustflags.end();
+         flag_iter++) {
+      args.push_back(*flag_iter);
+    }
+  }
+  return args;
+}
+
+std::optional<std::string> FindArgValue(const char* arg,
+                                        const std::vector<std::string>& args) {
+  for (auto current = args.begin(); current != args.end();) {
+    // capture the current value
+    auto previous = *current;
+    // and increment
+    current++;
+
+    // if the previous argument matches `arg`, and after the above increment the
+    // end hasn't been reached, this current argument is the desired value.
+    if (previous == arg && current != args.end()) {
+      return std::make_optional(*current);
+    }
+  }
+  return std::nullopt;
+}
+
+std::optional<std::string> FindArgValueAfterPrefix(
+    const std::string& prefix,
+    const std::vector<std::string>& args) {
+  for (auto arg : args) {
+    if (!arg.compare(0, prefix.size(), prefix)) {
+      auto value = arg.substr(prefix.size());
+      return std::make_optional(value);
+    }
+  }
+  return std::nullopt;
+}
+
+std::vector<std::string> FindAllArgValuesAfterPrefix(
+    const std::string& prefix,
+    const std::vector<std::string>& args) {
+  std::vector<std::string> values;
+  for (auto arg : args) {
+    if (!arg.compare(0, prefix.size(), prefix)) {
+      auto value = arg.substr(prefix.size());
+      values.push_back(value);
+    }
+  }
+  return values;
+}
+
+// TODO(bwb) Parse sysroot structure from toml files. This is fragile and
+// might break if upstream changes the dependency structure.
 const std::string_view sysroot_crates[] = {"std",
                                            "core",
                                            "alloc",
@@ -218,6 +275,9 @@
     return;
   }
 
+  auto compiler_args = ExtractCompilerArgs(target);
+  auto compiler_target = FindArgValue("--target", compiler_args);
+
   // Check what sysroot this target needs.  Add it to the crate list if it
   // hasn't already been added.
   auto rust_tool =
@@ -243,28 +303,21 @@
   SourceFile crate_root = target->rust_values().crate_root();
   std::string crate_label = target->label().GetUserVisibleName(false);
 
-  std::string cfg_prefix("--cfg=");
-  std::string edition_prefix("--edition=");
-  ConfigList cfgs;
-
-  std::string edition = "2015";  // default to be overridden as needed
-
-  for (ConfigValuesIterator iter(target); !iter.done(); iter.Next()) {
-    for (const auto& flag : iter.cur().rustflags()) {
-      // extract the edition of this target
-      if (!flag.compare(0, edition_prefix.size(), edition_prefix)) {
-        edition = flag.substr(edition_prefix.size());
-      }
-      if (!flag.compare(0, cfg_prefix.size(), cfg_prefix)) {
-        auto cfg = flag.substr(cfg_prefix.size());
-        std::string escaped_config;
-        base::EscapeJSONString(cfg, false, &escaped_config);
-        cfgs.push_back(escaped_config);
-      }
-    }
+  auto edition =
+      FindArgValueAfterPrefix(std::string("--edition="), compiler_args);
+  if (!edition.has_value()) {
+    edition = FindArgValue("--edition", compiler_args);
   }
 
-  Crate crate = Crate(crate_root, crate_id, crate_label, edition);
+  Crate crate =
+      Crate(crate_root, crate_id, crate_label, edition.value_or("2015"));
+
+  crate.SetCompilerArgs(compiler_args);
+  if (compiler_target.has_value())
+    crate.SetCompilerTarget(compiler_target.value());
+
+  ConfigList cfgs =
+      FindAllArgValuesAfterPrefix(std::string("--cfg="), compiler_args);
 
   crate.AddConfigItem("test");
   crate.AddConfigItem("debug_assertions");
@@ -293,17 +346,22 @@
 void WriteCrates(const BuildSettings* build_settings,
                  CrateList& crate_list,
                  std::ostream& rust_project) {
+  // produce a de-duplicated set of source roots:
+  std::set<std::string> roots;
+  for (auto& crate : crate_list) {
+    roots.insert(
+        FilePathToUTF8(build_settings->GetFullPath(crate.root().GetDir())));
+  }
+
   rust_project << "{" NEWLINE;
   rust_project << "  \"roots\": [";
   bool first_root = true;
-  for (auto& crate : crate_list) {
+  for (auto& root : roots) {
     if (!first_root)
       rust_project << ",";
     first_root = false;
 
-    std::string root_dir =
-        FilePathToUTF8(build_settings->GetFullPath(crate.root().GetDir()));
-    rust_project << NEWLINE "    \"" << root_dir << "\"";
+    rust_project << NEWLINE "    \"" << root << "\"";
   }
   rust_project << NEWLINE "  ]," NEWLINE;
   rust_project << "  \"crates\": [";
@@ -313,14 +371,38 @@
       rust_project << ",";
     first_crate = false;
 
-    auto crate_root = FilePathToUTF8(build_settings->GetFullPath(crate.root()));
+    auto crate_module =
+        FilePathToUTF8(build_settings->GetFullPath(crate.root()));
 
     rust_project << NEWLINE << "    {" NEWLINE
                  << "      \"crate_id\": " << crate.index() << "," NEWLINE
-                 << "      \"root_module\": \"" << crate_root << "\"," NEWLINE
-                 << "      \"label\": \"" << crate.label() << "\"," NEWLINE
-                 << "      \"deps\": [";
+                 << "      \"root_module\": \"" << crate_module << "\"," NEWLINE
+                 << "      \"label\": \"" << crate.label() << "\"," NEWLINE;
 
+    auto compiler_target = crate.CompilerTarget();
+    if (compiler_target.has_value()) {
+      rust_project << "      \"target\": \"" << compiler_target.value()
+                   << "\"," NEWLINE;
+    }
+
+    auto compiler_args = crate.CompilerArgs();
+    if (!compiler_args.empty()) {
+      rust_project << "      \"compiler_args\": [";
+      bool first_arg = true;
+      for (auto& arg : crate.CompilerArgs()) {
+        if (!first_arg)
+          rust_project << ", ";
+        first_arg = false;
+
+        std::string escaped_arg;
+        base::EscapeJSONString(arg, false, &escaped_arg);
+
+        rust_project << "\"" << escaped_arg << "\"";
+      }
+      rust_project << "]," << NEWLINE;
+    }
+
+    rust_project << "      \"deps\": [";
     bool first_dep = true;
     for (auto& dep : crate.dependencies()) {
       if (!first_dep)
@@ -343,8 +425,11 @@
         rust_project << ",";
       first_cfg = false;
 
+      std::string escaped_config;
+      base::EscapeJSONString(cfg, false, &escaped_config);
+
       rust_project << NEWLINE;
-      rust_project << "        \"" << cfg << "\"";
+      rust_project << "        \"" << escaped_config << "\"";
     }
     rust_project << NEWLINE;
     rust_project << "      ]" NEWLINE;  // end cfgs
diff --git a/src/gn/rust_project_writer_helpers.h b/src/gn/rust_project_writer_helpers.h
index 25340b3..0020e2c 100644
--- a/src/gn/rust_project_writer_helpers.h
+++ b/src/gn/rust_project_writer_helpers.h
@@ -6,6 +6,7 @@
 #define TOOLS_GN_RUST_PROJECT_WRITER_HELPERS_H_
 
 #include <fstream>
+#include <optional>
 #include <sstream>
 #include <string>
 #include <tuple>
@@ -47,6 +48,12 @@
     deps_.push_back(std::make_pair(index, name));
   }
 
+  // Set the compiler arguments used to invoke the compilation of this crate
+  void SetCompilerArgs(std::vector<std::string> args) { compiler_args_ = args; }
+
+  // Set the compiler target ("e.g. x86_64-linux-kernel")
+  void SetCompilerTarget(std::string target) { compiler_target_ = target; }
+
   // Returns the root file for the crate.
   SourceFile& root() { return root_; }
 
@@ -65,6 +72,14 @@
   // Return the set of dependencies for this crate.
   DependencyList& dependencies() { return deps_; }
 
+  // Return the compiler arguments used to invoke the compilation of this crate
+  const std::vector<std::string>& CompilerArgs() { return compiler_args_; }
+
+  // Return the compiler target "triple" from the compiler args
+  const std::optional<std::string>& CompilerTarget() {
+    return compiler_target_;
+  }
+
  private:
   SourceFile root_;
   CrateIndex index_;
@@ -72,6 +87,8 @@
   std::string edition_;
   ConfigList configs_;
   DependencyList deps_;
+  std::optional<std::string> compiler_target_;
+  std::vector<std::string> compiler_args_;
 };
 
 using CrateList = std::vector<Crate>;
@@ -97,4 +114,27 @@
                  CrateList& crate_list,
                  std::ostream& rust_project);
 
+// Assemble the compiler arguments for the given GN Target.
+std::vector<std::string> ExtractCompilerArgs(const Target* target);
+
+// Find the value of an argument that's passed to the compiler as two
+// consecutive strings in the list of arguments:  ["arg", "value"]
+std::optional<std::string> FindArgValue(const char* arg,
+                                        const std::vector<std::string>& args);
+
+// Find the first argument that matches the prefix, returning the value after
+// the prefix.  e.g. ˝--arg=value", is returned as "value" if the prefix
+// "--arg=" is used.
+std::optional<std::string> FindArgValueAfterPrefix(
+    const std::string& prefix,
+    const std::vector<std::string>& args);
+
+// Find all arguments that match the given prefix, returning the value after
+// the prefix for each one.  e.g. "--cfg=value" is returned as "value" if the
+// prefix "--cfg=" is used.
+std::vector<std::string> FindAllArgValuesAfterPrefix(
+    const std::string& prefix,
+    const std::vector<std::string>& args);
+
+
 #endif  // TOOLS_GN_RUST_PROJECT_WRITER_HELPERS_H_
diff --git a/src/gn/rust_project_writer_helpers_unittest.cc b/src/gn/rust_project_writer_helpers_unittest.cc
index 23657ee..c19e709 100644
--- a/src/gn/rust_project_writer_helpers_unittest.cc
+++ b/src/gn/rust_project_writer_helpers_unittest.cc
@@ -18,11 +18,13 @@
   TestWithScope setup;
 
   CrateList crates;
-  Crate dep = Crate(SourceFile("/root/tortoise/lib.rs"), 0, "//tortoise:bar", "2015");
-  Crate target = Crate(SourceFile("/root/hare/lib.rs"), 1, "//hare:bar", "2015");
+  Crate dep =
+      Crate(SourceFile("/root/tortoise/lib.rs"), 0, "//tortoise:bar", "2015");
+  Crate target =
+      Crate(SourceFile("/root/hare/lib.rs"), 1, "//hare:bar", "2015");
   target.AddDependency(0, "tortoise");
   target.AddConfigItem("unix");
-  target.AddConfigItem("feature=\\\"test\\\"");
+  target.AddConfigItem("feature=\"test\"");
 
   crates.push_back(dep);
   crates.push_back(target);
@@ -36,8 +38,8 @@
   const char expected_json[] =
       "{\n"
       "  \"roots\": [\n"
-      "    \"/root/tortoise/\",\n"
-      "    \"/root/hare/\"\n"
+      "    \"/root/hare/\",\n"
+      "    \"/root/tortoise/\"\n"
       "  ],\n"
       "  \"crates\": [\n"
       "    {\n"
@@ -88,297 +90,466 @@
   base::ReplaceSubstringsAfterOffset(&out, 0, "\r\n", "\n");
 #endif
 
-const char expected_json[] =
-    "{\n"
-    "  \"roots\": [\n"
-    "    \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libcore/\",\n"
-    "    \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/liballoc/\",\n"
-    "    \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libpanic_abort/\",\n"
-    "    \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libunwind/\",\n"
-    "    \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libstd/\",\n"
-    "    \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libcollections/\",\n"
-    "    \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/liblibc/\",\n"
-    "    \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libpanic_unwind/\",\n"
-    "    \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libproc_macro/\",\n"
-    "    \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/librustc_unicode/\",\n"
-    "    \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libstd_unicode/\",\n"
-    "    \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libtest/\",\n"
-    "    \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/liballoc_jemalloc/\",\n"
-    "    \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/liballoc_system/\",\n"
-    "    \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libcompiler_builtins/\",\n"
-    "    \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libgetopts/\",\n"
-    "    \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libbuild_helper/\",\n"
-    "    \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/librustc_asan/\",\n"
-    "    \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/librustc_lsan/\",\n"
-    "    \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/librustc_msan/\",\n"
-    "    \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/librustc_tsan/\",\n"
-    "    \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libsyntax/\"\n"
-    "  ],\n"
-    "  \"crates\": [\n"
-    "    {\n"
-    "      \"crate_id\": 0,\n"
-    "      \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libcore/lib.rs\",\n"
-    "      \"label\": \"core\",\n"
-    "      \"deps\": [\n"
-    "      ],\n"
-    "      \"edition\": \"2018\",\n"
-    "      \"cfg\": [\n"
-    "        \"debug_assertions\"\n"
-    "      ]\n"
-    "    },\n"
-    "    {\n"
-    "      \"crate_id\": 1,\n"
-    "      \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/liballoc/lib.rs\",\n"
-    "      \"label\": \"alloc\",\n"
-    "      \"deps\": [\n"
-    "        {\n"
-    "          \"crate\": 0,\n"
-    "          \"name\": \"core\"\n"
-    "        }\n"
-    "      ],\n"
-    "      \"edition\": \"2018\",\n"
-    "      \"cfg\": [\n"
-    "        \"debug_assertions\"\n"
-    "      ]\n"
-    "    },\n"
-    "    {\n"
-    "      \"crate_id\": 2,\n"
-    "      \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libpanic_abort/lib.rs\",\n"
-    "      \"label\": \"panic_abort\",\n"
-    "      \"deps\": [\n"
-    "      ],\n"
-    "      \"edition\": \"2018\",\n"
-    "      \"cfg\": [\n"
-    "        \"debug_assertions\"\n"
-    "      ]\n"
-    "    },\n"
-    "    {\n"
-    "      \"crate_id\": 3,\n"
-    "      \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libunwind/lib.rs\",\n"
-    "      \"label\": \"unwind\",\n"
-    "      \"deps\": [\n"
-    "      ],\n"
-    "      \"edition\": \"2018\",\n"
-    "      \"cfg\": [\n"
-    "        \"debug_assertions\"\n"
-    "      ]\n"
-    "    },\n"
-    "    {\n"
-    "      \"crate_id\": 4,\n"
-    "      \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libstd/lib.rs\",\n"
-    "      \"label\": \"std\",\n"
-    "      \"deps\": [\n"
-    "        {\n"
-    "          \"crate\": 1,\n"
-    "          \"name\": \"alloc\"\n"
-    "        },\n"
-    "        {\n"
-    "          \"crate\": 0,\n"
-    "          \"name\": \"core\"\n"
-    "        },\n"
-    "        {\n"
-    "          \"crate\": 2,\n"
-    "          \"name\": \"panic_abort\"\n"
-    "        },\n"
-    "        {\n"
-    "          \"crate\": 3,\n"
-    "          \"name\": \"unwind\"\n"
-    "        }\n"
-    "      ],\n"
-    "      \"edition\": \"2018\",\n"
-    "      \"cfg\": [\n"
-    "        \"debug_assertions\"\n"
-    "      ]\n"
-    "    },\n"
-    "    {\n"
-    "      \"crate_id\": 5,\n"
-    "      \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libcollections/lib.rs\",\n"
-    "      \"label\": \"collections\",\n"
-    "      \"deps\": [\n"
-    "      ],\n"
-    "      \"edition\": \"2018\",\n"
-    "      \"cfg\": [\n"
-    "        \"debug_assertions\"\n"
-    "      ]\n"
-    "    },\n"
-    "    {\n"
-    "      \"crate_id\": 6,\n"
-    "      \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/liblibc/lib.rs\",\n"
-    "      \"label\": \"libc\",\n"
-    "      \"deps\": [\n"
-    "      ],\n"
-    "      \"edition\": \"2018\",\n"
-    "      \"cfg\": [\n"
-    "        \"debug_assertions\"\n"
-    "      ]\n"
-    "    },\n"
-    "    {\n"
-    "      \"crate_id\": 7,\n"
-    "      \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libpanic_unwind/lib.rs\",\n"
-    "      \"label\": \"panic_unwind\",\n"
-    "      \"deps\": [\n"
-    "      ],\n"
-    "      \"edition\": \"2018\",\n"
-    "      \"cfg\": [\n"
-    "        \"debug_assertions\"\n"
-    "      ]\n"
-    "    },\n"
-    "    {\n"
-    "      \"crate_id\": 8,\n"
-    "      \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libproc_macro/lib.rs\",\n"
-    "      \"label\": \"proc_macro\",\n"
-    "      \"deps\": [\n"
-    "      ],\n"
-    "      \"edition\": \"2018\",\n"
-    "      \"cfg\": [\n"
-    "        \"debug_assertions\"\n"
-    "      ]\n"
-    "    },\n"
-    "    {\n"
-    "      \"crate_id\": 9,\n"
-    "      \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/librustc_unicode/lib.rs\",\n"
-    "      \"label\": \"rustc_unicode\",\n"
-    "      \"deps\": [\n"
-    "      ],\n"
-    "      \"edition\": \"2018\",\n"
-    "      \"cfg\": [\n"
-    "        \"debug_assertions\"\n"
-    "      ]\n"
-    "    },\n"
-    "    {\n"
-    "      \"crate_id\": 10,\n"
-    "      \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libstd_unicode/lib.rs\",\n"
-    "      \"label\": \"std_unicode\",\n"
-    "      \"deps\": [\n"
-    "      ],\n"
-    "      \"edition\": \"2018\",\n"
-    "      \"cfg\": [\n"
-    "        \"debug_assertions\"\n"
-    "      ]\n"
-    "    },\n"
-    "    {\n"
-    "      \"crate_id\": 11,\n"
-    "      \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libtest/lib.rs\",\n"
-    "      \"label\": \"test\",\n"
-    "      \"deps\": [\n"
-    "      ],\n"
-    "      \"edition\": \"2018\",\n"
-    "      \"cfg\": [\n"
-    "        \"debug_assertions\"\n"
-    "      ]\n"
-    "    },\n"
-    "    {\n"
-    "      \"crate_id\": 12,\n"
-    "      \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/liballoc_jemalloc/lib.rs\",\n"
-    "      \"label\": \"alloc_jemalloc\",\n"
-    "      \"deps\": [\n"
-    "      ],\n"
-    "      \"edition\": \"2018\",\n"
-    "      \"cfg\": [\n"
-    "        \"debug_assertions\"\n"
-    "      ]\n"
-    "    },\n"
-    "    {\n"
-    "      \"crate_id\": 13,\n"
-    "      \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/liballoc_system/lib.rs\",\n"
-    "      \"label\": \"alloc_system\",\n"
-    "      \"deps\": [\n"
-    "      ],\n"
-    "      \"edition\": \"2018\",\n"
-    "      \"cfg\": [\n"
-    "        \"debug_assertions\"\n"
-    "      ]\n"
-    "    },\n"
-    "    {\n"
-    "      \"crate_id\": 14,\n"
-    "      \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libcompiler_builtins/lib.rs\",\n"
-    "      \"label\": \"compiler_builtins\",\n"
-    "      \"deps\": [\n"
-    "      ],\n"
-    "      \"edition\": \"2018\",\n"
-    "      \"cfg\": [\n"
-    "        \"debug_assertions\"\n"
-    "      ]\n"
-    "    },\n"
-    "    {\n"
-    "      \"crate_id\": 15,\n"
-    "      \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libgetopts/lib.rs\",\n"
-    "      \"label\": \"getopts\",\n"
-    "      \"deps\": [\n"
-    "      ],\n"
-    "      \"edition\": \"2018\",\n"
-    "      \"cfg\": [\n"
-    "        \"debug_assertions\"\n"
-    "      ]\n"
-    "    },\n"
-    "    {\n"
-    "      \"crate_id\": 16,\n"
-    "      \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libbuild_helper/lib.rs\",\n"
-    "      \"label\": \"build_helper\",\n"
-    "      \"deps\": [\n"
-    "      ],\n"
-    "      \"edition\": \"2018\",\n"
-    "      \"cfg\": [\n"
-    "        \"debug_assertions\"\n"
-    "      ]\n"
-    "    },\n"
-    "    {\n"
-    "      \"crate_id\": 17,\n"
-    "      \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/librustc_asan/lib.rs\",\n"
-    "      \"label\": \"rustc_asan\",\n"
-    "      \"deps\": [\n"
-    "      ],\n"
-    "      \"edition\": \"2018\",\n"
-    "      \"cfg\": [\n"
-    "        \"debug_assertions\"\n"
-    "      ]\n"
-    "    },\n"
-    "    {\n"
-    "      \"crate_id\": 18,\n"
-    "      \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/librustc_lsan/lib.rs\",\n"
-    "      \"label\": \"rustc_lsan\",\n"
-    "      \"deps\": [\n"
-    "      ],\n"
-    "      \"edition\": \"2018\",\n"
-    "      \"cfg\": [\n"
-    "        \"debug_assertions\"\n"
-    "      ]\n"
-    "    },\n"
-    "    {\n"
-    "      \"crate_id\": 19,\n"
-    "      \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/librustc_msan/lib.rs\",\n"
-    "      \"label\": \"rustc_msan\",\n"
-    "      \"deps\": [\n"
-    "      ],\n"
-    "      \"edition\": \"2018\",\n"
-    "      \"cfg\": [\n"
-    "        \"debug_assertions\"\n"
-    "      ]\n"
-    "    },\n"
-    "    {\n"
-    "      \"crate_id\": 20,\n"
-    "      \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/librustc_tsan/lib.rs\",\n"
-    "      \"label\": \"rustc_tsan\",\n"
-    "      \"deps\": [\n"
-    "      ],\n"
-    "      \"edition\": \"2018\",\n"
-    "      \"cfg\": [\n"
-    "        \"debug_assertions\"\n"
-    "      ]\n"
-    "    },\n"
-    "    {\n"
-    "      \"crate_id\": 21,\n"
-    "      \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libsyntax/lib.rs\",\n"
-    "      \"label\": \"syntax\",\n"
-    "      \"deps\": [\n"
-    "      ],\n"
-    "      \"edition\": \"2018\",\n"
-    "      \"cfg\": [\n"
-    "        \"debug_assertions\"\n"
-    "      ]\n"
-    "    }\n"
-    "  ]\n"
-    "}\n";
-;
+  const char expected_json[] =
+      "{\n"
+      "  \"roots\": [\n"
+      "    \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/liballoc/\",\n"
+      "    \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/liballoc_jemalloc/\",\n"
+      "    \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/liballoc_system/\",\n"
+      "    \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libbuild_helper/\",\n"
+      "    \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libcollections/\",\n"
+      "    \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libcompiler_builtins/\",\n"
+      "    \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libcore/\",\n"
+      "    \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libgetopts/\",\n"
+      "    \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/liblibc/\",\n"
+      "    \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libpanic_abort/\",\n"
+      "    \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libpanic_unwind/\",\n"
+      "    \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libproc_macro/\",\n"
+      "    \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/librustc_asan/\",\n"
+      "    \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/librustc_lsan/\",\n"
+      "    \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/librustc_msan/\",\n"
+      "    \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/librustc_tsan/\",\n"
+      "    \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/librustc_unicode/\",\n"
+      "    \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libstd/\",\n"
+      "    \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libstd_unicode/\",\n"
+      "    \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libsyntax/\",\n"
+      "    \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libtest/\",\n"
+      "    \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libunwind/\"\n"
+      "  ],\n"
+      "  \"crates\": [\n"
+      "    {\n"
+      "      \"crate_id\": 0,\n"
+      "      \"root_module\": "
+      "\"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libcore/lib.rs\",\n"
+      "      \"label\": \"core\",\n"
+      "      \"deps\": [\n"
+      "      ],\n"
+      "      \"edition\": \"2018\",\n"
+      "      \"cfg\": [\n"
+      "        \"debug_assertions\"\n"
+      "      ]\n"
+      "    },\n"
+      "    {\n"
+      "      \"crate_id\": 1,\n"
+      "      \"root_module\": "
+      "\"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/liballoc/lib.rs\",\n"
+      "      \"label\": \"alloc\",\n"
+      "      \"deps\": [\n"
+      "        {\n"
+      "          \"crate\": 0,\n"
+      "          \"name\": \"core\"\n"
+      "        }\n"
+      "      ],\n"
+      "      \"edition\": \"2018\",\n"
+      "      \"cfg\": [\n"
+      "        \"debug_assertions\"\n"
+      "      ]\n"
+      "    },\n"
+      "    {\n"
+      "      \"crate_id\": 2,\n"
+      "      \"root_module\": "
+      "\"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libpanic_abort/"
+      "lib.rs\",\n"
+      "      \"label\": \"panic_abort\",\n"
+      "      \"deps\": [\n"
+      "      ],\n"
+      "      \"edition\": \"2018\",\n"
+      "      \"cfg\": [\n"
+      "        \"debug_assertions\"\n"
+      "      ]\n"
+      "    },\n"
+      "    {\n"
+      "      \"crate_id\": 3,\n"
+      "      \"root_module\": "
+      "\"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libunwind/lib.rs\",\n"
+      "      \"label\": \"unwind\",\n"
+      "      \"deps\": [\n"
+      "      ],\n"
+      "      \"edition\": \"2018\",\n"
+      "      \"cfg\": [\n"
+      "        \"debug_assertions\"\n"
+      "      ]\n"
+      "    },\n"
+      "    {\n"
+      "      \"crate_id\": 4,\n"
+      "      \"root_module\": "
+      "\"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libstd/lib.rs\",\n"
+      "      \"label\": \"std\",\n"
+      "      \"deps\": [\n"
+      "        {\n"
+      "          \"crate\": 1,\n"
+      "          \"name\": \"alloc\"\n"
+      "        },\n"
+      "        {\n"
+      "          \"crate\": 0,\n"
+      "          \"name\": \"core\"\n"
+      "        },\n"
+      "        {\n"
+      "          \"crate\": 2,\n"
+      "          \"name\": \"panic_abort\"\n"
+      "        },\n"
+      "        {\n"
+      "          \"crate\": 3,\n"
+      "          \"name\": \"unwind\"\n"
+      "        }\n"
+      "      ],\n"
+      "      \"edition\": \"2018\",\n"
+      "      \"cfg\": [\n"
+      "        \"debug_assertions\"\n"
+      "      ]\n"
+      "    },\n"
+      "    {\n"
+      "      \"crate_id\": 5,\n"
+      "      \"root_module\": "
+      "\"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libcollections/"
+      "lib.rs\",\n"
+      "      \"label\": \"collections\",\n"
+      "      \"deps\": [\n"
+      "      ],\n"
+      "      \"edition\": \"2018\",\n"
+      "      \"cfg\": [\n"
+      "        \"debug_assertions\"\n"
+      "      ]\n"
+      "    },\n"
+      "    {\n"
+      "      \"crate_id\": 6,\n"
+      "      \"root_module\": "
+      "\"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/liblibc/lib.rs\",\n"
+      "      \"label\": \"libc\",\n"
+      "      \"deps\": [\n"
+      "      ],\n"
+      "      \"edition\": \"2018\",\n"
+      "      \"cfg\": [\n"
+      "        \"debug_assertions\"\n"
+      "      ]\n"
+      "    },\n"
+      "    {\n"
+      "      \"crate_id\": 7,\n"
+      "      \"root_module\": "
+      "\"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libpanic_unwind/"
+      "lib.rs\",\n"
+      "      \"label\": \"panic_unwind\",\n"
+      "      \"deps\": [\n"
+      "      ],\n"
+      "      \"edition\": \"2018\",\n"
+      "      \"cfg\": [\n"
+      "        \"debug_assertions\"\n"
+      "      ]\n"
+      "    },\n"
+      "    {\n"
+      "      \"crate_id\": 8,\n"
+      "      \"root_module\": "
+      "\"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libproc_macro/"
+      "lib.rs\",\n"
+      "      \"label\": \"proc_macro\",\n"
+      "      \"deps\": [\n"
+      "      ],\n"
+      "      \"edition\": \"2018\",\n"
+      "      \"cfg\": [\n"
+      "        \"debug_assertions\"\n"
+      "      ]\n"
+      "    },\n"
+      "    {\n"
+      "      \"crate_id\": 9,\n"
+      "      \"root_module\": "
+      "\"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/librustc_unicode/"
+      "lib.rs\",\n"
+      "      \"label\": \"rustc_unicode\",\n"
+      "      \"deps\": [\n"
+      "      ],\n"
+      "      \"edition\": \"2018\",\n"
+      "      \"cfg\": [\n"
+      "        \"debug_assertions\"\n"
+      "      ]\n"
+      "    },\n"
+      "    {\n"
+      "      \"crate_id\": 10,\n"
+      "      \"root_module\": "
+      "\"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libstd_unicode/"
+      "lib.rs\",\n"
+      "      \"label\": \"std_unicode\",\n"
+      "      \"deps\": [\n"
+      "      ],\n"
+      "      \"edition\": \"2018\",\n"
+      "      \"cfg\": [\n"
+      "        \"debug_assertions\"\n"
+      "      ]\n"
+      "    },\n"
+      "    {\n"
+      "      \"crate_id\": 11,\n"
+      "      \"root_module\": "
+      "\"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libtest/lib.rs\",\n"
+      "      \"label\": \"test\",\n"
+      "      \"deps\": [\n"
+      "      ],\n"
+      "      \"edition\": \"2018\",\n"
+      "      \"cfg\": [\n"
+      "        \"debug_assertions\"\n"
+      "      ]\n"
+      "    },\n"
+      "    {\n"
+      "      \"crate_id\": 12,\n"
+      "      \"root_module\": "
+      "\"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/liballoc_jemalloc/"
+      "lib.rs\",\n"
+      "      \"label\": \"alloc_jemalloc\",\n"
+      "      \"deps\": [\n"
+      "      ],\n"
+      "      \"edition\": \"2018\",\n"
+      "      \"cfg\": [\n"
+      "        \"debug_assertions\"\n"
+      "      ]\n"
+      "    },\n"
+      "    {\n"
+      "      \"crate_id\": 13,\n"
+      "      \"root_module\": "
+      "\"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/liballoc_system/"
+      "lib.rs\",\n"
+      "      \"label\": \"alloc_system\",\n"
+      "      \"deps\": [\n"
+      "      ],\n"
+      "      \"edition\": \"2018\",\n"
+      "      \"cfg\": [\n"
+      "        \"debug_assertions\"\n"
+      "      ]\n"
+      "    },\n"
+      "    {\n"
+      "      \"crate_id\": 14,\n"
+      "      \"root_module\": "
+      "\"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libcompiler_builtins/"
+      "lib.rs\",\n"
+      "      \"label\": \"compiler_builtins\",\n"
+      "      \"deps\": [\n"
+      "      ],\n"
+      "      \"edition\": \"2018\",\n"
+      "      \"cfg\": [\n"
+      "        \"debug_assertions\"\n"
+      "      ]\n"
+      "    },\n"
+      "    {\n"
+      "      \"crate_id\": 15,\n"
+      "      \"root_module\": "
+      "\"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libgetopts/"
+      "lib.rs\",\n"
+      "      \"label\": \"getopts\",\n"
+      "      \"deps\": [\n"
+      "      ],\n"
+      "      \"edition\": \"2018\",\n"
+      "      \"cfg\": [\n"
+      "        \"debug_assertions\"\n"
+      "      ]\n"
+      "    },\n"
+      "    {\n"
+      "      \"crate_id\": 16,\n"
+      "      \"root_module\": "
+      "\"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libbuild_helper/"
+      "lib.rs\",\n"
+      "      \"label\": \"build_helper\",\n"
+      "      \"deps\": [\n"
+      "      ],\n"
+      "      \"edition\": \"2018\",\n"
+      "      \"cfg\": [\n"
+      "        \"debug_assertions\"\n"
+      "      ]\n"
+      "    },\n"
+      "    {\n"
+      "      \"crate_id\": 17,\n"
+      "      \"root_module\": "
+      "\"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/librustc_asan/"
+      "lib.rs\",\n"
+      "      \"label\": \"rustc_asan\",\n"
+      "      \"deps\": [\n"
+      "      ],\n"
+      "      \"edition\": \"2018\",\n"
+      "      \"cfg\": [\n"
+      "        \"debug_assertions\"\n"
+      "      ]\n"
+      "    },\n"
+      "    {\n"
+      "      \"crate_id\": 18,\n"
+      "      \"root_module\": "
+      "\"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/librustc_lsan/"
+      "lib.rs\",\n"
+      "      \"label\": \"rustc_lsan\",\n"
+      "      \"deps\": [\n"
+      "      ],\n"
+      "      \"edition\": \"2018\",\n"
+      "      \"cfg\": [\n"
+      "        \"debug_assertions\"\n"
+      "      ]\n"
+      "    },\n"
+      "    {\n"
+      "      \"crate_id\": 19,\n"
+      "      \"root_module\": "
+      "\"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/librustc_msan/"
+      "lib.rs\",\n"
+      "      \"label\": \"rustc_msan\",\n"
+      "      \"deps\": [\n"
+      "      ],\n"
+      "      \"edition\": \"2018\",\n"
+      "      \"cfg\": [\n"
+      "        \"debug_assertions\"\n"
+      "      ]\n"
+      "    },\n"
+      "    {\n"
+      "      \"crate_id\": 20,\n"
+      "      \"root_module\": "
+      "\"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/librustc_tsan/"
+      "lib.rs\",\n"
+      "      \"label\": \"rustc_tsan\",\n"
+      "      \"deps\": [\n"
+      "      ],\n"
+      "      \"edition\": \"2018\",\n"
+      "      \"cfg\": [\n"
+      "        \"debug_assertions\"\n"
+      "      ]\n"
+      "    },\n"
+      "    {\n"
+      "      \"crate_id\": 21,\n"
+      "      \"root_module\": "
+      "\"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libsyntax/lib.rs\",\n"
+      "      \"label\": \"syntax\",\n"
+      "      \"deps\": [\n"
+      "      ],\n"
+      "      \"edition\": \"2018\",\n"
+      "      \"cfg\": [\n"
+      "        \"debug_assertions\"\n"
+      "      ]\n"
+      "    }\n"
+      "  ]\n"
+      "}\n";
+
   EXPECT_EQ(expected_json, out);
 }
+
+TEST_F(RustProjectWriterHelper, ExtractCompilerTargetTupleSimple) {
+  TestWithScope setup;
+
+  Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+  target.set_output_type(Target::RUST_LIBRARY);
+  target.visibility().SetPublic();
+  SourceFile lib("//foo/lib.rs");
+  target.sources().push_back(lib);
+  target.source_types_used().Set(SourceFile::SOURCE_RS);
+  target.rust_values().set_crate_root(lib);
+  target.rust_values().crate_name() = "foo";
+  target.config_values().rustflags().push_back("--cfg=feature=\"foo_enabled\"");
+  target.config_values().rustflags().push_back("--target");
+  target.config_values().rustflags().push_back("x86-someos");
+  target.config_values().rustflags().push_back("--edition=2018");
+
+  auto args = ExtractCompilerArgs(&target);
+  std::optional<std::string> result = FindArgValue("--target", args);
+  auto expected = std::optional<std::string>{"x86-someos"};
+  EXPECT_EQ(expected, result);
+}
+
+TEST_F(RustProjectWriterHelper, ExtractCompilerTargetTupleMissing) {
+  TestWithScope setup;
+
+  Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+  target.set_output_type(Target::RUST_LIBRARY);
+  target.visibility().SetPublic();
+  SourceFile lib("//foo/lib.rs");
+  target.sources().push_back(lib);
+  target.source_types_used().Set(SourceFile::SOURCE_RS);
+  target.rust_values().set_crate_root(lib);
+  target.rust_values().crate_name() = "foo";
+  target.config_values().rustflags().push_back(
+      "--cfg=featur4e=\"foo_enabled\"");
+  target.config_values().rustflags().push_back("x86-someos");
+  target.config_values().rustflags().push_back("--edition=2018");
+
+  auto args = ExtractCompilerArgs(&target);
+  std::optional<std::string> result = FindArgValue("--target", args);
+  auto expected = std::nullopt;
+  EXPECT_EQ(expected, result);
+}
+
+TEST_F(RustProjectWriterHelper, ExtractCompilerTargetTupleDontFallOffEnd) {
+  TestWithScope setup;
+
+  Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+  target.set_output_type(Target::RUST_LIBRARY);
+  target.visibility().SetPublic();
+  SourceFile lib("//foo/lib.rs");
+  target.sources().push_back(lib);
+  target.source_types_used().Set(SourceFile::SOURCE_RS);
+  target.rust_values().set_crate_root(lib);
+  target.rust_values().crate_name() = "foo";
+  target.config_values().rustflags().push_back("--cfg=feature=\"foo_enabled\"");
+  target.config_values().rustflags().push_back("--edition=2018");
+  target.config_values().rustflags().push_back("--target");
+
+  auto args = ExtractCompilerArgs(&target);
+  std::optional<std::string> result = FindArgValue("--target", args);
+  auto expected = std::nullopt;
+  EXPECT_EQ(expected, result);
+}
+
+TEST_F(RustProjectWriterHelper, ExtractFirstArgValueWithPrefixMissing) {
+  TestWithScope setup;
+
+  Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+  target.set_output_type(Target::RUST_LIBRARY);
+  target.visibility().SetPublic();
+  SourceFile lib("//foo/lib.rs");
+  target.sources().push_back(lib);
+  target.source_types_used().Set(SourceFile::SOURCE_RS);
+  target.rust_values().set_crate_root(lib);
+  target.rust_values().crate_name() = "foo";
+  target.config_values().rustflags().push_back("--cfg=feature=\"foo_enabled\"");
+  target.config_values().rustflags().push_back("--edition=2018");
+  target.config_values().rustflags().push_back("--target");
+
+  auto args = ExtractCompilerArgs(&target);
+  std::optional<std::string> result =
+      FindArgValueAfterPrefix("--missing", args);
+  auto expected = std::nullopt;
+  EXPECT_EQ(expected, result);
+}
+
+TEST_F(RustProjectWriterHelper, ExtractFirstArgValueWithPrefix) {
+  TestWithScope setup;
+
+  Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+  target.set_output_type(Target::RUST_LIBRARY);
+  target.visibility().SetPublic();
+  SourceFile lib("//foo/lib.rs");
+  target.sources().push_back(lib);
+  target.source_types_used().Set(SourceFile::SOURCE_RS);
+  target.rust_values().set_crate_root(lib);
+  target.rust_values().crate_name() = "foo";
+  target.config_values().rustflags().push_back("--cfg=feature=\"foo_enabled\"");
+  target.config_values().rustflags().push_back("--edition=2018");
+  target.config_values().rustflags().push_back("--target");
+
+  auto args = ExtractCompilerArgs(&target);
+  std::optional<std::string> result =
+      FindArgValueAfterPrefix("--edition=", args);
+  auto expected = std::optional<std::string>{"2018"};
+  EXPECT_EQ(expected, result);
+}
+
+TEST_F(RustProjectWriterHelper, ExtractAllArgValueWithPrefix) {
+  TestWithScope setup;
+
+  Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+  target.set_output_type(Target::RUST_LIBRARY);
+  target.visibility().SetPublic();
+  SourceFile lib("//foo/lib.rs");
+  target.sources().push_back(lib);
+  target.source_types_used().Set(SourceFile::SOURCE_RS);
+  target.rust_values().set_crate_root(lib);
+  target.rust_values().crate_name() = "foo";
+  target.config_values().rustflags().push_back("--cfg=feature=\"foo_enabled\"");
+  target.config_values().rustflags().push_back("--edition=2018");
+  target.config_values().rustflags().push_back("--cfg=feature=\"bar_enabled\"");
+  target.config_values().rustflags().push_back("--target");
+
+  auto args = ExtractCompilerArgs(&target);
+  std::vector<std::string> result = FindAllArgValuesAfterPrefix("--cfg=", args);
+  std::vector<std::string> expected = {"feature=\"foo_enabled\"",
+                                       "feature=\"bar_enabled\""};
+  EXPECT_EQ(expected, result);
+}
\ No newline at end of file
diff --git a/src/gn/rust_project_writer_unittest.cc b/src/gn/rust_project_writer_unittest.cc
index 7891576..9df1ad2 100644
--- a/src/gn/rust_project_writer_unittest.cc
+++ b/src/gn/rust_project_writer_unittest.cc
@@ -50,6 +50,7 @@
       "      \"crate_id\": 0,\n"
       "      \"root_module\": \"path/foo/lib.rs\",\n"
       "      \"label\": \"//foo:bar\",\n"
+      "      \"compiler_args\": [\"--cfg=feature=\\\"foo_enabled\\\"\"],\n"
       "      \"deps\": [\n"
       "      ],\n"
       "      \"edition\": \"2015\",\n"
@@ -102,8 +103,8 @@
   const char expected_json[] =
       "{\n"
       "  \"roots\": [\n"
-      "    \"tortoise/\",\n"
-      "    \"hare/\"\n"
+      "    \"hare/\",\n"
+      "    \"tortoise/\"\n"
       "  ],\n"
       "  \"crates\": [\n"
       "    {\n"
@@ -188,9 +189,9 @@
   const char expected_json[] =
       "{\n"
       "  \"roots\": [\n"
-      "    \"tortoise/\",\n"
       "    \"achilles/\",\n"
-      "    \"hare/\"\n"
+      "    \"hare/\",\n"
+      "    \"tortoise/\"\n"
       "  ],\n"
       "  \"crates\": [\n"
       "    {\n"
@@ -305,9 +306,9 @@
   const char expected_json[] =
       "{\n"
       "  \"roots\": [\n"
+      "    \"hare/\",\n"
       "    \"tortoise/\",\n"
-      "    \"tortoise/macro/\",\n"
-      "    \"hare/\"\n"
+      "    \"tortoise/macro/\"\n"
       "  ],\n"
       "  \"crates\": [\n"
       "    {\n"
@@ -359,3 +360,156 @@
 
   EXPECT_EQ(expected_json, out);
 }
+
+TEST_F(RustProjectJSONWriter, OneRustTargetWithRustcTargetSet) {
+  Err err;
+  TestWithScope setup;
+  setup.build_settings()->SetRootPath(UTF8ToFilePath("path"));
+
+  Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+  target.set_output_type(Target::RUST_LIBRARY);
+  target.visibility().SetPublic();
+  SourceFile lib("//foo/lib.rs");
+  target.sources().push_back(lib);
+  target.source_types_used().Set(SourceFile::SOURCE_RS);
+  target.rust_values().set_crate_root(lib);
+  target.rust_values().crate_name() = "foo";
+  target.config_values().rustflags().push_back("--target");
+  target.config_values().rustflags().push_back("x86-64_unknown");
+  target.SetToolchain(setup.toolchain());
+  ASSERT_TRUE(target.OnResolved(&err));
+
+  std::ostringstream stream;
+  std::vector<const Target*> targets;
+  targets.push_back(&target);
+  RustProjectWriter::RenderJSON(setup.build_settings(), targets, stream);
+  std::string out = stream.str();
+#if defined(OS_WIN)
+  base::ReplaceSubstringsAfterOffset(&out, 0, "\r\n", "\n");
+#endif
+  const char expected_json[] =
+      "{\n"
+      "  \"roots\": [\n"
+      "    \"path/foo/\"\n"
+      "  ],\n"
+      "  \"crates\": [\n"
+      "    {\n"
+      "      \"crate_id\": 0,\n"
+      "      \"root_module\": \"path/foo/lib.rs\",\n"
+      "      \"label\": \"//foo:bar\",\n"
+      "      \"target\": \"x86-64_unknown\",\n"
+      "      \"compiler_args\": [\"--target\", \"x86-64_unknown\"],\n"
+      "      \"deps\": [\n"
+      "      ],\n"
+      "      \"edition\": \"2015\",\n"
+      "      \"cfg\": [\n"
+      "        \"test\",\n"
+      "        \"debug_assertions\"\n"
+      "      ]\n"
+      "    }\n"
+      "  ]\n"
+      "}\n";
+
+  EXPECT_EQ(expected_json, out);
+}
+
+TEST_F(RustProjectJSONWriter, OneRustTargetWithEditionSet) {
+  Err err;
+  TestWithScope setup;
+  setup.build_settings()->SetRootPath(UTF8ToFilePath("path"));
+
+  Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+  target.set_output_type(Target::RUST_LIBRARY);
+  target.visibility().SetPublic();
+  SourceFile lib("//foo/lib.rs");
+  target.sources().push_back(lib);
+  target.source_types_used().Set(SourceFile::SOURCE_RS);
+  target.rust_values().set_crate_root(lib);
+  target.rust_values().crate_name() = "foo";
+  target.config_values().rustflags().push_back("--edition=2018");
+  target.SetToolchain(setup.toolchain());
+  ASSERT_TRUE(target.OnResolved(&err));
+
+  std::ostringstream stream;
+  std::vector<const Target*> targets;
+  targets.push_back(&target);
+  RustProjectWriter::RenderJSON(setup.build_settings(), targets, stream);
+  std::string out = stream.str();
+#if defined(OS_WIN)
+  base::ReplaceSubstringsAfterOffset(&out, 0, "\r\n", "\n");
+#endif
+  const char expected_json[] =
+      "{\n"
+      "  \"roots\": [\n"
+      "    \"path/foo/\"\n"
+      "  ],\n"
+      "  \"crates\": [\n"
+      "    {\n"
+      "      \"crate_id\": 0,\n"
+      "      \"root_module\": \"path/foo/lib.rs\",\n"
+      "      \"label\": \"//foo:bar\",\n"
+      "      \"compiler_args\": [\"--edition=2018\"],\n"
+      "      \"deps\": [\n"
+      "      ],\n"
+      "      \"edition\": \"2018\",\n"
+      "      \"cfg\": [\n"
+      "        \"test\",\n"
+      "        \"debug_assertions\"\n"
+      "      ]\n"
+      "    }\n"
+      "  ]\n"
+      "}\n";
+
+  EXPECT_EQ(expected_json, out);
+}
+
+TEST_F(RustProjectJSONWriter, OneRustTargetWithEditionSetAlternate) {
+  Err err;
+  TestWithScope setup;
+  setup.build_settings()->SetRootPath(UTF8ToFilePath("path"));
+
+  Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+  target.set_output_type(Target::RUST_LIBRARY);
+  target.visibility().SetPublic();
+  SourceFile lib("//foo/lib.rs");
+  target.sources().push_back(lib);
+  target.source_types_used().Set(SourceFile::SOURCE_RS);
+  target.rust_values().set_crate_root(lib);
+  target.rust_values().crate_name() = "foo";
+  target.config_values().rustflags().push_back("--edition");
+  target.config_values().rustflags().push_back("2018");
+  target.SetToolchain(setup.toolchain());
+  ASSERT_TRUE(target.OnResolved(&err));
+
+  std::ostringstream stream;
+  std::vector<const Target*> targets;
+  targets.push_back(&target);
+  RustProjectWriter::RenderJSON(setup.build_settings(), targets, stream);
+  std::string out = stream.str();
+#if defined(OS_WIN)
+  base::ReplaceSubstringsAfterOffset(&out, 0, "\r\n", "\n");
+#endif
+  const char expected_json[] =
+      "{\n"
+      "  \"roots\": [\n"
+      "    \"path/foo/\"\n"
+      "  ],\n"
+      "  \"crates\": [\n"
+      "    {\n"
+      "      \"crate_id\": 0,\n"
+      "      \"root_module\": \"path/foo/lib.rs\",\n"
+      "      \"label\": \"//foo:bar\",\n"
+      "      \"compiler_args\": [\"--edition\", \"2018\"],\n"
+      "      \"deps\": [\n"
+      "      ],\n"
+      "      \"edition\": \"2018\",\n"
+      "      \"cfg\": [\n"
+      "        \"test\",\n"
+      "        \"debug_assertions\"\n"
+      "      ]\n"
+      "    }\n"
+      "  ]\n"
+      "}\n";
+
+  EXPECT_EQ(expected_json, out);
+}
\ No newline at end of file