[rust-project.json] List the Rust sysroot if there is one and only one

List the path to the sysroot using the new 'sysroot' field added by
rust-analyzer if there is one and only one sysroot defined for the
project.

This enables rust-analyzer to find the compiler and tools vendored with
the compiler, such a proc-macro server that matches the compiler's ABI.

Change-Id: I92146a4afbed001e44d7a1b824bad536eb297841
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/14360
Reviewed-by: Tyler Mandry <tmandry@google.com>
Commit-Queue: Tyler Mandry <tmandry@google.com>
Reviewed-by: Brett Wilson <brettw@chromium.org>
diff --git a/src/gn/rust_project_writer.cc b/src/gn/rust_project_writer.cc
index 8c73d57..0eb2a53 100644
--- a/src/gn/rust_project_writer.cc
+++ b/src/gn/rust_project_writer.cc
@@ -29,6 +29,7 @@
 // Current structure of rust-project.json output file
 //
 // {
+//    "sysroot": "path/to/rust/sysroot",  // if there is only one sysroot found
 //    "crates": [
 //        {
 //            "deps": [
@@ -358,8 +359,22 @@
 
 void WriteCrates(const BuildSettings* build_settings,
                  CrateList& crate_list,
+                 SysrootIndexMap& sysroots,
                  std::ostream& rust_project) {
   rust_project << "{" NEWLINE;
+
+  // If there is one, and only one, sysroot found, then that can be used to tell
+  // rust-analyzer where to find the sysroot (and associated tools like the
+  // 'rust-analyzer-proc-macro-srv` proc-macro server that matches the abi used
+  // by 'rustc'
+  if (sysroots.size() == 1) {
+    auto sysroot = sysroots.begin()->first;
+    base::FilePath rebased_out_dir =
+        build_settings->GetFullPath(build_settings->build_dir());
+    auto sysroot_path = FilePathToUTF8(rebased_out_dir) + std::string(sysroot);
+    rust_project << "  \"sysroot\": \"" << sysroot_path << "\"," NEWLINE;
+  }
+
   rust_project << "  \"crates\": [";
   bool first_crate = true;
   for (auto& crate : crate_list) {
@@ -499,5 +514,5 @@
     AddTarget(build_settings, target, lookup, sysroot_lookup, crate_list);
   }
 
-  WriteCrates(build_settings, crate_list, rust_project);
+  WriteCrates(build_settings, crate_list, sysroot_lookup, rust_project);
 }
diff --git a/src/gn/rust_project_writer_helpers.h b/src/gn/rust_project_writer_helpers.h
index 9aa7eba..aba1388 100644
--- a/src/gn/rust_project_writer_helpers.h
+++ b/src/gn/rust_project_writer_helpers.h
@@ -144,6 +144,7 @@
 // on the the given crates list.
 void WriteCrates(const BuildSettings* build_settings,
                  CrateList& crate_list,
+                 SysrootIndexMap& sysroot_lookup,
                  std::ostream& rust_project);
 
 // Assemble the compiler arguments for the given GN Target.
diff --git a/src/gn/rust_project_writer_helpers_unittest.cc b/src/gn/rust_project_writer_helpers_unittest.cc
index ffdfcd8..df07c77 100644
--- a/src/gn/rust_project_writer_helpers_unittest.cc
+++ b/src/gn/rust_project_writer_helpers_unittest.cc
@@ -26,6 +26,8 @@
 TEST_F(RustProjectWriterHelper, WriteCrates) {
   TestWithScope setup;
 
+  SysrootIndexMap sysroot_lookup;
+
   CrateList crates;
   Crate dep = Crate(SourceFile("/root/tortoise/lib.rs"), std::nullopt, 0,
                     "//tortoise:bar", "2015");
@@ -39,7 +41,7 @@
   crates.push_back(target);
 
   std::ostringstream stream;
-  WriteCrates(setup.build_settings(), crates, stream);
+  WriteCrates(setup.build_settings(), crates, sysroot_lookup, stream);
   std::string out = stream.str();
 #if defined(OS_WIN)
   base::ReplaceSubstringsAfterOffset(&out, 0, "\r\n", "\n");
@@ -102,7 +104,7 @@
   AddSysroot(setup.build_settings(), "sysroot", sysroot_lookup, crates);
 
   std::ostringstream stream;
-  WriteCrates(setup.build_settings(), crates, stream);
+  WriteCrates(setup.build_settings(), crates, sysroot_lookup, stream);
   std::string out = stream.str();
 #if defined(OS_WIN)
   base::ReplaceSubstringsAfterOffset(&out, 0, "\r\n", "\n");
@@ -110,6 +112,7 @@
 
   const char expected_json[] =
       "{\n"
+      "  \"sysroot\": \"/root/out/Debug/sysroot\",\n"
       "  \"crates\": [\n"
       "    {\n"
       "      \"crate_id\": 0,\n"