Export Rust information in JSON IDE format

Adds test for rust library JSON output

Change-Id: I6ea3612260bac7b14d53fa660803fcbae5bcb7b6
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/7380
Commit-Queue: Petr Hosek <phosek@google.com>
Reviewed-by: Petr Hosek <phosek@google.com>
diff --git a/src/gn/desc_builder.cc b/src/gn/desc_builder.cc
index 4e8f690..f2a57e4 100644
--- a/src/gn/desc_builder.cc
+++ b/src/gn/desc_builder.cc
@@ -53,6 +53,8 @@
 //   "metadata" : [ dictionary of target metadata values ]
 //   "data_keys" : [ list of target data keys ]
 //   "walk_keys" : [ list of target walk keys ]
+//   "crate_root" : "root file of a Rust target"
+//   "crate_name" : "name of a Rust target"
 //   "rebase" : true or false
 //   "output_conversion" : "string for output conversion"
 //   "response_file_contents": [ list of response file contents entries ]
@@ -317,6 +319,13 @@
               target_->label().GetToolchainLabel().GetUserVisibleName(false)));
     }
 
+    if (target_->source_types_used().RustSourceUsed()) {
+      res->SetWithoutPathExpansion(
+          "crate_root", RenderValue(target_->rust_values().crate_root()));
+      res->SetKey("crate_name",
+                  base::Value(target_->rust_values().crate_name()));
+    }
+
     // General target meta variables.
 
     if (what(variables::kMetadata)) {
diff --git a/src/gn/json_project_writer.h b/src/gn/json_project_writer.h
index 72780ee..ee2e7b9 100644
--- a/src/gn/json_project_writer.h
+++ b/src/gn/json_project_writer.h
@@ -25,6 +25,7 @@
  private:
   FRIEND_TEST_ALL_PREFIXES(JSONProjectWriter, ActionWithResponseFile);
   FRIEND_TEST_ALL_PREFIXES(JSONProjectWriter, ForEachWithResponseFile);
+  FRIEND_TEST_ALL_PREFIXES(JSONProjectWriter, RustTarget);
 
   static std::string RenderJSON(const BuildSettings* build_settings,
                                 std::vector<const Target*>& all_targets);
diff --git a/src/gn/json_project_writer_unittest.cc b/src/gn/json_project_writer_unittest.cc
index f82bc0d..709d7c1 100644
--- a/src/gn/json_project_writer_unittest.cc
+++ b/src/gn/json_project_writer_unittest.cc
@@ -72,6 +72,61 @@
   EXPECT_EQ(expected_json, out);
 }
 
+TEST(JSONProjectWriter, RustTarget) {
+  Err err;
+  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.SetToolchain(setup.toolchain());
+  ASSERT_TRUE(target.OnResolved(&err));
+
+  std::vector<const Target*> targets;
+  targets.push_back(&target);
+  std::string out =
+      JSONProjectWriter::RenderJSON(setup.build_settings(), targets);
+#if defined(OS_WIN)
+  base::ReplaceSubstringsAfterOffset(&out, 0, "\r\n", "\n");
+#endif
+  const char expected_json[] =
+      "{\n"
+      "   \"build_settings\": {\n"
+      "      \"build_dir\": \"//out/Debug/\",\n"
+      "      \"default_toolchain\": \"//toolchain:default\",\n"
+      "      \"root_path\": \"\"\n"
+      "   },\n"
+      "   \"targets\": {\n"
+      "      \"//foo:bar()\": {\n"
+      "         \"allow_circular_includes_from\": [  ],\n"
+      "         \"check_includes\": true,\n"
+      "         \"crate_name\": \"foo\",\n"
+      "         \"crate_root\": \"//foo/lib.rs\",\n"
+      "         \"deps\": [  ],\n"
+      "         \"externs\": {\n"
+      "\n"
+      "         },\n"
+      "         \"metadata\": {\n"
+      "\n"
+      "         },\n"
+      "         \"outputs\": [ \"//out/Debug/obj/foo/libbar.rlib\" ],\n"
+      "         \"public\": \"*\",\n"
+      "         \"sources\": [ \"//foo/lib.rs\" ],\n"
+      "         \"testonly\": false,\n"
+      "         \"toolchain\": \"\",\n"
+      "         \"type\": \"rust_library\",\n"
+      "         \"visibility\": [ \"*\" ]\n"
+      "      }\n"
+      "   }\n"
+      "}\n";
+  EXPECT_EQ(expected_json, out);
+}
+
 TEST(JSONProjectWriter, ForEachWithResponseFile) {
   Err err;
   TestWithScope setup;
diff --git a/src/gn/substitution_writer.cc b/src/gn/substitution_writer.cc
index 6674c9b..199faa3 100644
--- a/src/gn/substitution_writer.cc
+++ b/src/gn/substitution_writer.cc
@@ -381,6 +381,8 @@
     NOTREACHED() << "Cannot use substitution " << type->name
                  << " without target";
     return std::string();
+  } else if (IsValidRustSubstitution(type)) {
+    to_rebase = source.value();
   } else {
     NOTREACHED() << "Unsupported substitution for this function: "
                  << type->name;
diff --git a/src/gn/target.cc b/src/gn/target.cc
index 8721f1a..6df5ede 100644
--- a/src/gn/target.cc
+++ b/src/gn/target.cc
@@ -501,6 +501,11 @@
     return true;
   }
 
+  // Rust generates on a module level, not source.
+  if (file_type == SourceFile::SOURCE_RS) {
+    return false;
+  }
+
   *computed_tool_type = Tool::GetToolTypeForSourceType(file_type);
   if (*computed_tool_type == Tool::kToolNone)
     return false;  // No tool for this file (it's a header file or something).