Write toochains info to json project info

Toolchains info is needed by IDEs to offer better code completion.

Change-Id: Ifd17fedbebefd5141a2bb41555503af970f781de
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/10940
Reviewed-by: Brett Wilson <brettw@chromium.org>
Commit-Queue: Brett Wilson <brettw@chromium.org>
diff --git a/src/gn/json_project_writer.cc b/src/gn/json_project_writer.cc
index a8f9ac4..a8aad90 100644
--- a/src/gn/json_project_writer.cc
+++ b/src/gn/json_project_writer.cc
@@ -426,6 +426,7 @@
   }
   json_writer.EndDict();  // build_settings
 
+  std::map<Label, const Toolchain*> toolchains;
   json_writer.BeginDict("targets");
   {
     for (const auto* target : sorted_targets) {
@@ -445,10 +446,61 @@
                                          base::JSONWriter::OPTIONS_PRETTY_PRINT,
                                          &json_dict);
       json_writer.AddJSONDict(target_labels[target], json_dict);
+      toolchains[target->toolchain()->label()] = target->toolchain();
     }
   }
   json_writer.EndDict();  // targets
 
+  json_writer.BeginDict("toolchains");
+  {
+    for (const auto& tool_chain_kv : toolchains) {
+      base::Value toolchain{base::Value::Type::DICTIONARY};
+      const auto& tools = tool_chain_kv.second->tools();
+      for (const auto& tool_kv : tools) {
+        base::Value tool_info{base::Value::Type::DICTIONARY};
+        auto setIfNotEmptry = [&](const auto& key, const auto& value) {
+          if (value.size())
+            tool_info.SetKey(key, base::Value{value});
+        };
+        auto setSubstitutionList = [&](const auto& key,
+                                       const SubstitutionList& list) {
+          if (list.list().empty())
+            return;
+          base::Value values{base::Value::Type::LIST};
+          for (const auto& v : list.list())
+            values.GetList().emplace_back(base::Value{v.AsString()});
+          tool_info.SetKey(key, std::move(values));
+        };
+        const auto& tool = tool_kv.second;
+        setIfNotEmptry("command", tool->command().AsString());
+        setIfNotEmptry("command_launcher", tool->command_launcher());
+        setIfNotEmptry("default_output_extension",
+                       tool->default_output_extension());
+        setIfNotEmptry("default_output_dir",
+                       tool->default_output_dir().AsString());
+        setIfNotEmptry("depfile", tool->depfile().AsString());
+        setIfNotEmptry("description", tool->description().AsString());
+        setIfNotEmptry("framework_switch", tool->framework_switch());
+        setIfNotEmptry("weak_framework_switch", tool->weak_framework_switch());
+        setIfNotEmptry("framework_dir_switch", tool->framework_dir_switch());
+        setIfNotEmptry("lib_switch", tool->lib_switch());
+        setIfNotEmptry("lib_dir_switch", tool->lib_dir_switch());
+        setIfNotEmptry("linker_arg", tool->linker_arg());
+        setSubstitutionList("outputs", tool->outputs());
+        setSubstitutionList("partial_outputs", tool->partial_outputs());
+        setSubstitutionList("runtime_outputs", tool->runtime_outputs());
+        setIfNotEmptry("output_prefix", tool->output_prefix());
+
+        toolchain.SetKey(tool_kv.first, std::move(tool_info));
+      }
+      std::string json_dict;
+      base::JSONWriter::WriteWithOptions(
+          toolchain, base::JSONWriter::OPTIONS_PRETTY_PRINT, &json_dict);
+      json_writer.AddJSONDict(tool_chain_kv.first.GetUserVisibleName(false), json_dict);
+    }
+  }
+  json_writer.EndDict();  // toolchains
+
   json_writer.Close();
 
   return out;
diff --git a/src/gn/json_project_writer_unittest.cc b/src/gn/json_project_writer_unittest.cc
index f642ea6..a22511b 100644
--- a/src/gn/json_project_writer_unittest.cc
+++ b/src/gn/json_project_writer_unittest.cc
@@ -58,38 +58,201 @@
   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"
-      "      \"gen_input_files\": [ \"//.gn\", \"//BUILD.gn\", "
-      "\"//build/BUILD.gn\" ],\n"
+      R"_({
+   "build_settings": {
+      "build_dir": "//out/Debug/",
+      "default_toolchain": "//toolchain:default",
+      "gen_input_files": [ "//.gn", "//BUILD.gn", "//build/BUILD.gn" ],
+)_"
 #if defined(OS_WIN)
       "      \"root_path\": \"c:/path/to/src\"\n"
 #else
       "      \"root_path\": \"/path/to/src\"\n"
 #endif
-      "   },\n"
-      "   \"targets\": {\n"
-      "      \"//foo:bar()\": {\n"
-      "         \"args\": [ \"{{response_file_name}}\" ],\n"
-      "         \"deps\": [  ],\n"
-      "         \"inputs\": [ \"//foo/input1.txt\" ],\n"
-      "         \"metadata\": {\n"
-      "\n"
-      "         },\n"
-      "         \"outputs\": [ \"//out/Debug/output1.out\" ],\n"
-      "         \"public\": \"*\",\n"
-      "         \"response_file_contents\": [ \"-j\", \"3\" ],\n"
-      "         \"script\": \"//foo/script.py\",\n"
-      "         \"sources\": [ \"//foo/source1.txt\" ],\n"
-      "         \"testonly\": false,\n"
-      "         \"toolchain\": \"\",\n"
-      "         \"type\": \"action\",\n"
-      "         \"visibility\": [  ]\n"
-      "      }\n"
-      "   }\n"
-      "}\n";
+      R"_(   },
+   "targets": {
+      "//foo:bar()": {
+         "args": [ "{{response_file_name}}" ],
+         "deps": [  ],
+         "inputs": [ "//foo/input1.txt" ],
+         "metadata": {
+
+         },
+         "outputs": [ "//out/Debug/output1.out" ],
+         "public": "*",
+         "response_file_contents": [ "-j", "3" ],
+         "script": "//foo/script.py",
+         "sources": [ "//foo/source1.txt" ],
+         "testonly": false,
+         "toolchain": "",
+         "type": "action",
+         "visibility": [  ]
+      }
+   },
+   "toolchains": {
+      "//toolchain:default": {
+         "alink": {
+            "command": "ar {{output}} {{source}}",
+            "framework_dir_switch": "-F",
+            "framework_switch": "-framework ",
+            "lib_dir_switch": "-L",
+            "lib_switch": "-l",
+            "output_prefix": "lib",
+            "outputs": [ "{{target_out_dir}}/{{target_output_name}}.a" ],
+            "weak_framework_switch": "-weak_framework "
+         },
+         "cc": {
+            "command": "cc {{source}} {{cflags}} {{cflags_c}} {{defines}} {{include_dirs}} -o {{output}}",
+            "framework_dir_switch": "-F",
+            "framework_switch": "-framework ",
+            "lib_dir_switch": "-L",
+            "lib_switch": "-l",
+            "outputs": [ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o" ],
+            "weak_framework_switch": "-weak_framework "
+         },
+         "compile_xcassets": {
+            "command": "touch {{output}}"
+         },
+         "copy": {
+            "command": "cp {{source}} {{output}}"
+         },
+         "copy_bundle_data": {
+            "command": "cp {{source}} {{output}}"
+         },
+         "cxx": {
+            "command": "c++ {{source}} {{cflags}} {{cflags_cc}} {{defines}} {{include_dirs}} -o {{output}}",
+            "command_launcher": "launcher",
+            "framework_dir_switch": "-F",
+            "framework_switch": "-framework ",
+            "lib_dir_switch": "-L",
+            "lib_switch": "-l",
+            "outputs": [ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o" ],
+            "weak_framework_switch": "-weak_framework "
+         },
+         "link": {
+            "command": "ld -o {{target_output_name}} {{source}} {{ldflags}} {{libs}}",
+            "framework_dir_switch": "-F",
+            "framework_switch": "-framework ",
+            "lib_dir_switch": "-L",
+            "lib_switch": "-l",
+            "outputs": [ "{{root_out_dir}}/{{target_output_name}}" ],
+            "weak_framework_switch": "-weak_framework "
+         },
+         "objc": {
+            "command": "objcc {{source}} {{cflags}} {{cflags_objc}} {{defines}} {{include_dirs}} -o {{output}}",
+            "framework_dir_switch": "-F",
+            "framework_switch": "-framework ",
+            "lib_dir_switch": "-L",
+            "lib_switch": "-l",
+            "outputs": [ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o" ],
+            "weak_framework_switch": "-weak_framework "
+         },
+         "objcxx": {
+            "command": "objcxx {{source}} {{cflags}} {{cflags_objcc}} {{defines}} {{include_dirs}} -o {{output}}",
+            "framework_dir_switch": "-F",
+            "framework_switch": "-framework ",
+            "lib_dir_switch": "-L",
+            "lib_switch": "-l",
+            "outputs": [ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o" ],
+            "weak_framework_switch": "-weak_framework "
+         },
+         "rust_bin": {
+            "command": "{{rustenv}} rustc --crate-name {{crate_name}} {{source}} --crate-type {{crate_type}} {{rustflags}} -o {{output}} {{rustdeps}} {{externs}}",
+            "framework_switch": "-lframework=",
+            "lib_dir_switch": "-Lnative=",
+            "lib_switch": "-l",
+            "linker_arg": "-Clink-arg=",
+            "outputs": [ "{{root_out_dir}}/{{crate_name}}{{output_extension}}" ]
+         },
+         "rust_cdylib": {
+            "command": "{{rustenv}} rustc --crate-name {{crate_name}} {{source}} --crate-type {{crate_type}} {{rustflags}} -o {{output}} {{rustdeps}} {{externs}}",
+            "default_output_extension": ".so",
+            "framework_switch": "-lframework=",
+            "lib_dir_switch": "-Lnative=",
+            "lib_switch": "-l",
+            "linker_arg": "-Clink-arg=",
+            "output_prefix": "lib",
+            "outputs": [ "" ]
+         },
+         "rust_dylib": {
+            "command": "{{rustenv}} rustc --crate-name {{crate_name}} {{source}} --crate-type {{crate_type}} {{rustflags}} -o {{output}} {{rustdeps}} {{externs}}",
+            "default_output_extension": ".so",
+            "framework_switch": "-lframework=",
+            "lib_dir_switch": "-Lnative=",
+            "lib_switch": "-l",
+            "linker_arg": "-Clink-arg=",
+            "output_prefix": "lib",
+            "outputs": [ "" ]
+         },
+         "rust_macro": {
+            "command": "{{rustenv}} rustc --crate-name {{crate_name}} {{source}} --crate-type {{crate_type}} {{rustflags}} -o {{output}} {{rustdeps}} {{externs}}",
+            "default_output_extension": ".so",
+            "framework_switch": "-lframework=",
+            "lib_dir_switch": "-Lnative=",
+            "lib_switch": "-l",
+            "linker_arg": "-Clink-arg=",
+            "output_prefix": "lib",
+            "outputs": [ "{{target_out_dir}}/{{target_output_name}}{{output_extension}}" ]
+         },
+         "rust_rlib": {
+            "command": "{{rustenv}} rustc --crate-name {{crate_name}} {{source}} --crate-type {{crate_type}} {{rustflags}} -o {{output}} {{rustdeps}} {{externs}}",
+            "default_output_extension": ".rlib",
+            "framework_switch": "-lframework=",
+            "lib_dir_switch": "-Lnative=",
+            "lib_switch": "-l",
+            "linker_arg": "-Clink-arg=",
+            "output_prefix": "lib",
+            "outputs": [ "{{target_out_dir}}/{{target_output_name}}{{output_extension}}" ]
+         },
+         "rust_staticlib": {
+            "command": "{{rustenv}} rustc --crate-name {{crate_name}} {{source}} --crate-type {{crate_type}} {{rustflags}} -o {{output}} {{rustdeps}} {{externs}}",
+            "default_output_extension": ".a",
+            "framework_switch": "-lframework=",
+            "lib_dir_switch": "-Lnative=",
+            "lib_switch": "-l",
+            "linker_arg": "-Clink-arg=",
+            "output_prefix": "lib",
+            "outputs": [ "{{target_out_dir}}/{{target_output_name}}{{output_extension}}" ]
+         },
+         "solink": {
+            "command": "ld -shared -o {{target_output_name}}.so {{inputs}} {{ldflags}} {{libs}}",
+            "default_output_extension": ".so",
+            "framework_dir_switch": "-F",
+            "framework_switch": "-framework ",
+            "lib_dir_switch": "-L",
+            "lib_switch": "-l",
+            "output_prefix": "lib",
+            "outputs": [ "{{root_out_dir}}/{{target_output_name}}{{output_extension}}" ],
+            "weak_framework_switch": "-weak_framework "
+         },
+         "solink_module": {
+            "command": "ld -bundle -o {{target_output_name}}.so {{inputs}} {{ldflags}} {{libs}}",
+            "default_output_extension": ".so",
+            "framework_dir_switch": "-F",
+            "framework_switch": "-framework ",
+            "lib_dir_switch": "-L",
+            "lib_switch": "-l",
+            "output_prefix": "lib",
+            "outputs": [ "{{root_out_dir}}/{{target_output_name}}{{output_extension}}" ],
+            "weak_framework_switch": "-weak_framework "
+         },
+         "stamp": {
+            "command": "touch {{output}}"
+         },
+         "swift": {
+            "command": "swiftc --module-name {{module_name}} {{module_dirs}} {{inputs}}",
+            "framework_dir_switch": "-F",
+            "framework_switch": "-framework ",
+            "lib_dir_switch": "-L",
+            "lib_switch": "-l",
+            "outputs": [ "{{target_out_dir}}/{{module_name}}.swiftmodule" ],
+            "partial_outputs": [ "{{target_out_dir}}/{{source_name_part}}.o" ],
+            "weak_framework_switch": "-weak_framework "
+         }
+      }
+   }
+}
+)_";
   EXPECT_EQ(expected_json, out) << out;
 }
 
@@ -116,37 +279,200 @@
   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"
-      "      \"gen_input_files\": [  ],\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);
+      R"_({
+   "build_settings": {
+      "build_dir": "//out/Debug/",
+      "default_toolchain": "//toolchain:default",
+      "gen_input_files": [  ],
+      "root_path": ""
+   },
+   "targets": {
+      "//foo:bar()": {
+         "allow_circular_includes_from": [  ],
+         "check_includes": true,
+         "crate_name": "foo",
+         "crate_root": "//foo/lib.rs",
+         "deps": [  ],
+         "externs": {
+
+         },
+         "metadata": {
+
+         },
+         "outputs": [ "//out/Debug/obj/foo/libbar.rlib" ],
+         "public": "*",
+         "sources": [ "//foo/lib.rs" ],
+         "testonly": false,
+         "toolchain": "",
+         "type": "rust_library",
+         "visibility": [ "*" ]
+      }
+   },
+   "toolchains": {
+      "//toolchain:default": {
+         "alink": {
+            "command": "ar {{output}} {{source}}",
+            "framework_dir_switch": "-F",
+            "framework_switch": "-framework ",
+            "lib_dir_switch": "-L",
+            "lib_switch": "-l",
+            "output_prefix": "lib",
+            "outputs": [ "{{target_out_dir}}/{{target_output_name}}.a" ],
+            "weak_framework_switch": "-weak_framework "
+         },
+         "cc": {
+            "command": "cc {{source}} {{cflags}} {{cflags_c}} {{defines}} {{include_dirs}} -o {{output}}",
+            "framework_dir_switch": "-F",
+            "framework_switch": "-framework ",
+            "lib_dir_switch": "-L",
+            "lib_switch": "-l",
+            "outputs": [ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o" ],
+            "weak_framework_switch": "-weak_framework "
+         },
+         "compile_xcassets": {
+            "command": "touch {{output}}"
+         },
+         "copy": {
+            "command": "cp {{source}} {{output}}"
+         },
+         "copy_bundle_data": {
+            "command": "cp {{source}} {{output}}"
+         },
+         "cxx": {
+            "command": "c++ {{source}} {{cflags}} {{cflags_cc}} {{defines}} {{include_dirs}} -o {{output}}",
+            "command_launcher": "launcher",
+            "framework_dir_switch": "-F",
+            "framework_switch": "-framework ",
+            "lib_dir_switch": "-L",
+            "lib_switch": "-l",
+            "outputs": [ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o" ],
+            "weak_framework_switch": "-weak_framework "
+         },
+         "link": {
+            "command": "ld -o {{target_output_name}} {{source}} {{ldflags}} {{libs}}",
+            "framework_dir_switch": "-F",
+            "framework_switch": "-framework ",
+            "lib_dir_switch": "-L",
+            "lib_switch": "-l",
+            "outputs": [ "{{root_out_dir}}/{{target_output_name}}" ],
+            "weak_framework_switch": "-weak_framework "
+         },
+         "objc": {
+            "command": "objcc {{source}} {{cflags}} {{cflags_objc}} {{defines}} {{include_dirs}} -o {{output}}",
+            "framework_dir_switch": "-F",
+            "framework_switch": "-framework ",
+            "lib_dir_switch": "-L",
+            "lib_switch": "-l",
+            "outputs": [ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o" ],
+            "weak_framework_switch": "-weak_framework "
+         },
+         "objcxx": {
+            "command": "objcxx {{source}} {{cflags}} {{cflags_objcc}} {{defines}} {{include_dirs}} -o {{output}}",
+            "framework_dir_switch": "-F",
+            "framework_switch": "-framework ",
+            "lib_dir_switch": "-L",
+            "lib_switch": "-l",
+            "outputs": [ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o" ],
+            "weak_framework_switch": "-weak_framework "
+         },
+         "rust_bin": {
+            "command": "{{rustenv}} rustc --crate-name {{crate_name}} {{source}} --crate-type {{crate_type}} {{rustflags}} -o {{output}} {{rustdeps}} {{externs}}",
+            "framework_switch": "-lframework=",
+            "lib_dir_switch": "-Lnative=",
+            "lib_switch": "-l",
+            "linker_arg": "-Clink-arg=",
+            "outputs": [ "{{root_out_dir}}/{{crate_name}}{{output_extension}}" ]
+         },
+         "rust_cdylib": {
+            "command": "{{rustenv}} rustc --crate-name {{crate_name}} {{source}} --crate-type {{crate_type}} {{rustflags}} -o {{output}} {{rustdeps}} {{externs}}",
+            "default_output_extension": ".so",
+            "framework_switch": "-lframework=",
+            "lib_dir_switch": "-Lnative=",
+            "lib_switch": "-l",
+            "linker_arg": "-Clink-arg=",
+            "output_prefix": "lib",
+            "outputs": [ "" ]
+         },
+         "rust_dylib": {
+            "command": "{{rustenv}} rustc --crate-name {{crate_name}} {{source}} --crate-type {{crate_type}} {{rustflags}} -o {{output}} {{rustdeps}} {{externs}}",
+            "default_output_extension": ".so",
+            "framework_switch": "-lframework=",
+            "lib_dir_switch": "-Lnative=",
+            "lib_switch": "-l",
+            "linker_arg": "-Clink-arg=",
+            "output_prefix": "lib",
+            "outputs": [ "" ]
+         },
+         "rust_macro": {
+            "command": "{{rustenv}} rustc --crate-name {{crate_name}} {{source}} --crate-type {{crate_type}} {{rustflags}} -o {{output}} {{rustdeps}} {{externs}}",
+            "default_output_extension": ".so",
+            "framework_switch": "-lframework=",
+            "lib_dir_switch": "-Lnative=",
+            "lib_switch": "-l",
+            "linker_arg": "-Clink-arg=",
+            "output_prefix": "lib",
+            "outputs": [ "{{target_out_dir}}/{{target_output_name}}{{output_extension}}" ]
+         },
+         "rust_rlib": {
+            "command": "{{rustenv}} rustc --crate-name {{crate_name}} {{source}} --crate-type {{crate_type}} {{rustflags}} -o {{output}} {{rustdeps}} {{externs}}",
+            "default_output_extension": ".rlib",
+            "framework_switch": "-lframework=",
+            "lib_dir_switch": "-Lnative=",
+            "lib_switch": "-l",
+            "linker_arg": "-Clink-arg=",
+            "output_prefix": "lib",
+            "outputs": [ "{{target_out_dir}}/{{target_output_name}}{{output_extension}}" ]
+         },
+         "rust_staticlib": {
+            "command": "{{rustenv}} rustc --crate-name {{crate_name}} {{source}} --crate-type {{crate_type}} {{rustflags}} -o {{output}} {{rustdeps}} {{externs}}",
+            "default_output_extension": ".a",
+            "framework_switch": "-lframework=",
+            "lib_dir_switch": "-Lnative=",
+            "lib_switch": "-l",
+            "linker_arg": "-Clink-arg=",
+            "output_prefix": "lib",
+            "outputs": [ "{{target_out_dir}}/{{target_output_name}}{{output_extension}}" ]
+         },
+         "solink": {
+            "command": "ld -shared -o {{target_output_name}}.so {{inputs}} {{ldflags}} {{libs}}",
+            "default_output_extension": ".so",
+            "framework_dir_switch": "-F",
+            "framework_switch": "-framework ",
+            "lib_dir_switch": "-L",
+            "lib_switch": "-l",
+            "output_prefix": "lib",
+            "outputs": [ "{{root_out_dir}}/{{target_output_name}}{{output_extension}}" ],
+            "weak_framework_switch": "-weak_framework "
+         },
+         "solink_module": {
+            "command": "ld -bundle -o {{target_output_name}}.so {{inputs}} {{ldflags}} {{libs}}",
+            "default_output_extension": ".so",
+            "framework_dir_switch": "-F",
+            "framework_switch": "-framework ",
+            "lib_dir_switch": "-L",
+            "lib_switch": "-l",
+            "output_prefix": "lib",
+            "outputs": [ "{{root_out_dir}}/{{target_output_name}}{{output_extension}}" ],
+            "weak_framework_switch": "-weak_framework "
+         },
+         "stamp": {
+            "command": "touch {{output}}"
+         },
+         "swift": {
+            "command": "swiftc --module-name {{module_name}} {{module_dirs}} {{inputs}}",
+            "framework_dir_switch": "-F",
+            "framework_switch": "-framework ",
+            "lib_dir_switch": "-L",
+            "lib_switch": "-l",
+            "outputs": [ "{{target_out_dir}}/{{module_name}}.swiftmodule" ],
+            "partial_outputs": [ "{{target_out_dir}}/{{source_name_part}}.o" ],
+            "weak_framework_switch": "-weak_framework "
+         }
+      }
+   }
+}
+)_";
+  EXPECT_EQ(expected_json, out) << out;
 }
 
 TEST_F(JSONWriter, ForEachWithResponseFile) {
@@ -191,42 +517,203 @@
   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"
-      "      \"gen_input_files\": [ \"//.gn\", \"//BUILD.gn\" ],\n"
+      R"_({
+   "build_settings": {
+      "build_dir": "//out/Debug/",
+      "default_toolchain": "//toolchain:default",
+      "gen_input_files": [ "//.gn", "//BUILD.gn" ],
+)_"
 #if defined(OS_WIN)
       "      \"root_path\": \"c:/path/to/src\"\n"
 #else
       "      \"root_path\": \"/path/to/src\"\n"
 #endif
-      "   },\n"
-      "   \"targets\": {\n"
-      "      \"//foo:bar()\": {\n"
-      "         \"args\": [ \"{{source}}\", \"{{source_file_part}}\", "
-      "\"{{response_file_name}}\" ],\n"
-      "         \"deps\": [  ],\n"
-      "         \"metadata\": {\n"
-      "\n"
-      "         },\n"
-      "         \"output_patterns\": [ "
-      "\"//out/Debug/{{source_name_part}}.out\" ],\n"
-      "         \"outputs\": [ \"//out/Debug/input1.out\" ],\n"
-      "         \"public\": \"*\",\n"
-      "         \"response_file_contents\": [ \"-j\", \"{{source_name_part}}\" "
-      "],\n"
-      "         \"script\": \"//foo/script.py\",\n"
-      "         \"source_outputs\": {\n"
-      "            \"//foo/input1.txt\": [ \"input1.out\" ]\n"
-      "         },\n"
-      "         \"sources\": [ \"//foo/input1.txt\" ],\n"
-      "         \"testonly\": false,\n"
-      "         \"toolchain\": \"\",\n"
-      "         \"type\": \"action_foreach\",\n"
-      "         \"visibility\": [  ]\n"
-      "      }\n"
-      "   }\n"
-      "}\n";
-  EXPECT_EQ(expected_json, out);
+      R"_(   },
+   "targets": {
+      "//foo:bar()": {
+         "args": [ "{{source}}", "{{source_file_part}}", "{{response_file_name}}" ],
+         "deps": [  ],
+         "metadata": {
+
+         },
+         "output_patterns": [ "//out/Debug/{{source_name_part}}.out" ],
+         "outputs": [ "//out/Debug/input1.out" ],
+         "public": "*",
+         "response_file_contents": [ "-j", "{{source_name_part}}" ],
+         "script": "//foo/script.py",
+         "source_outputs": {
+            "//foo/input1.txt": [ "input1.out" ]
+         },
+         "sources": [ "//foo/input1.txt" ],
+         "testonly": false,
+         "toolchain": "",
+         "type": "action_foreach",
+         "visibility": [  ]
+      }
+   },
+   "toolchains": {
+      "//toolchain:default": {
+         "alink": {
+            "command": "ar {{output}} {{source}}",
+            "framework_dir_switch": "-F",
+            "framework_switch": "-framework ",
+            "lib_dir_switch": "-L",
+            "lib_switch": "-l",
+            "output_prefix": "lib",
+            "outputs": [ "{{target_out_dir}}/{{target_output_name}}.a" ],
+            "weak_framework_switch": "-weak_framework "
+         },
+         "cc": {
+            "command": "cc {{source}} {{cflags}} {{cflags_c}} {{defines}} {{include_dirs}} -o {{output}}",
+            "framework_dir_switch": "-F",
+            "framework_switch": "-framework ",
+            "lib_dir_switch": "-L",
+            "lib_switch": "-l",
+            "outputs": [ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o" ],
+            "weak_framework_switch": "-weak_framework "
+         },
+         "compile_xcassets": {
+            "command": "touch {{output}}"
+         },
+         "copy": {
+            "command": "cp {{source}} {{output}}"
+         },
+         "copy_bundle_data": {
+            "command": "cp {{source}} {{output}}"
+         },
+         "cxx": {
+            "command": "c++ {{source}} {{cflags}} {{cflags_cc}} {{defines}} {{include_dirs}} -o {{output}}",
+            "command_launcher": "launcher",
+            "framework_dir_switch": "-F",
+            "framework_switch": "-framework ",
+            "lib_dir_switch": "-L",
+            "lib_switch": "-l",
+            "outputs": [ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o" ],
+            "weak_framework_switch": "-weak_framework "
+         },
+         "link": {
+            "command": "ld -o {{target_output_name}} {{source}} {{ldflags}} {{libs}}",
+            "framework_dir_switch": "-F",
+            "framework_switch": "-framework ",
+            "lib_dir_switch": "-L",
+            "lib_switch": "-l",
+            "outputs": [ "{{root_out_dir}}/{{target_output_name}}" ],
+            "weak_framework_switch": "-weak_framework "
+         },
+         "objc": {
+            "command": "objcc {{source}} {{cflags}} {{cflags_objc}} {{defines}} {{include_dirs}} -o {{output}}",
+            "framework_dir_switch": "-F",
+            "framework_switch": "-framework ",
+            "lib_dir_switch": "-L",
+            "lib_switch": "-l",
+            "outputs": [ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o" ],
+            "weak_framework_switch": "-weak_framework "
+         },
+         "objcxx": {
+            "command": "objcxx {{source}} {{cflags}} {{cflags_objcc}} {{defines}} {{include_dirs}} -o {{output}}",
+            "framework_dir_switch": "-F",
+            "framework_switch": "-framework ",
+            "lib_dir_switch": "-L",
+            "lib_switch": "-l",
+            "outputs": [ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o" ],
+            "weak_framework_switch": "-weak_framework "
+         },
+         "rust_bin": {
+            "command": "{{rustenv}} rustc --crate-name {{crate_name}} {{source}} --crate-type {{crate_type}} {{rustflags}} -o {{output}} {{rustdeps}} {{externs}}",
+            "framework_switch": "-lframework=",
+            "lib_dir_switch": "-Lnative=",
+            "lib_switch": "-l",
+            "linker_arg": "-Clink-arg=",
+            "outputs": [ "{{root_out_dir}}/{{crate_name}}{{output_extension}}" ]
+         },
+         "rust_cdylib": {
+            "command": "{{rustenv}} rustc --crate-name {{crate_name}} {{source}} --crate-type {{crate_type}} {{rustflags}} -o {{output}} {{rustdeps}} {{externs}}",
+            "default_output_extension": ".so",
+            "framework_switch": "-lframework=",
+            "lib_dir_switch": "-Lnative=",
+            "lib_switch": "-l",
+            "linker_arg": "-Clink-arg=",
+            "output_prefix": "lib",
+            "outputs": [ "" ]
+         },
+         "rust_dylib": {
+            "command": "{{rustenv}} rustc --crate-name {{crate_name}} {{source}} --crate-type {{crate_type}} {{rustflags}} -o {{output}} {{rustdeps}} {{externs}}",
+            "default_output_extension": ".so",
+            "framework_switch": "-lframework=",
+            "lib_dir_switch": "-Lnative=",
+            "lib_switch": "-l",
+            "linker_arg": "-Clink-arg=",
+            "output_prefix": "lib",
+            "outputs": [ "" ]
+         },
+         "rust_macro": {
+            "command": "{{rustenv}} rustc --crate-name {{crate_name}} {{source}} --crate-type {{crate_type}} {{rustflags}} -o {{output}} {{rustdeps}} {{externs}}",
+            "default_output_extension": ".so",
+            "framework_switch": "-lframework=",
+            "lib_dir_switch": "-Lnative=",
+            "lib_switch": "-l",
+            "linker_arg": "-Clink-arg=",
+            "output_prefix": "lib",
+            "outputs": [ "{{target_out_dir}}/{{target_output_name}}{{output_extension}}" ]
+         },
+         "rust_rlib": {
+            "command": "{{rustenv}} rustc --crate-name {{crate_name}} {{source}} --crate-type {{crate_type}} {{rustflags}} -o {{output}} {{rustdeps}} {{externs}}",
+            "default_output_extension": ".rlib",
+            "framework_switch": "-lframework=",
+            "lib_dir_switch": "-Lnative=",
+            "lib_switch": "-l",
+            "linker_arg": "-Clink-arg=",
+            "output_prefix": "lib",
+            "outputs": [ "{{target_out_dir}}/{{target_output_name}}{{output_extension}}" ]
+         },
+         "rust_staticlib": {
+            "command": "{{rustenv}} rustc --crate-name {{crate_name}} {{source}} --crate-type {{crate_type}} {{rustflags}} -o {{output}} {{rustdeps}} {{externs}}",
+            "default_output_extension": ".a",
+            "framework_switch": "-lframework=",
+            "lib_dir_switch": "-Lnative=",
+            "lib_switch": "-l",
+            "linker_arg": "-Clink-arg=",
+            "output_prefix": "lib",
+            "outputs": [ "{{target_out_dir}}/{{target_output_name}}{{output_extension}}" ]
+         },
+         "solink": {
+            "command": "ld -shared -o {{target_output_name}}.so {{inputs}} {{ldflags}} {{libs}}",
+            "default_output_extension": ".so",
+            "framework_dir_switch": "-F",
+            "framework_switch": "-framework ",
+            "lib_dir_switch": "-L",
+            "lib_switch": "-l",
+            "output_prefix": "lib",
+            "outputs": [ "{{root_out_dir}}/{{target_output_name}}{{output_extension}}" ],
+            "weak_framework_switch": "-weak_framework "
+         },
+         "solink_module": {
+            "command": "ld -bundle -o {{target_output_name}}.so {{inputs}} {{ldflags}} {{libs}}",
+            "default_output_extension": ".so",
+            "framework_dir_switch": "-F",
+            "framework_switch": "-framework ",
+            "lib_dir_switch": "-L",
+            "lib_switch": "-l",
+            "output_prefix": "lib",
+            "outputs": [ "{{root_out_dir}}/{{target_output_name}}{{output_extension}}" ],
+            "weak_framework_switch": "-weak_framework "
+         },
+         "stamp": {
+            "command": "touch {{output}}"
+         },
+         "swift": {
+            "command": "swiftc --module-name {{module_name}} {{module_dirs}} {{inputs}}",
+            "framework_dir_switch": "-F",
+            "framework_switch": "-framework ",
+            "lib_dir_switch": "-L",
+            "lib_switch": "-l",
+            "outputs": [ "{{target_out_dir}}/{{module_name}}.swiftmodule" ],
+            "partial_outputs": [ "{{target_out_dir}}/{{source_name_part}}.o" ],
+            "weak_framework_switch": "-weak_framework "
+         }
+      }
+   }
+}
+)_";
+  EXPECT_EQ(expected_json, out) << out;
 }