Setting "SubSystem" option on "Link" page for Visual Studio project.

When the debugger session in Visual Studio finished, the console window closed
by default. For console applications, such as unit tests, it is possible not to
close the console window immediately. This is controlled by the property
"SubSystem" on "Link" page in project settings. This patch set property
"SubSystem" in generated *.vcxproj file based on the "ldflags" field.

R=brettw@chromium.org
BUG=

Review-Url: https://chromiumcodereview.appspot.com/2428013002
Cr-Original-Commit-Position: refs/heads/master@{#426977}
Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src
Cr-Mirrored-Commit: 2f8e5b9b27201976adb6f5526dad67b386872fbd
diff --git a/tools/gn/visual_studio_utils.cc b/tools/gn/visual_studio_utils.cc
index 8944722..94a2883 100644
--- a/tools/gn/visual_studio_utils.cc
+++ b/tools/gn/visual_studio_utils.cc
@@ -4,13 +4,20 @@
 
 #include "tools/gn/visual_studio_utils.h"
 
+#include <vector>
+
 #include "base/md5.h"
+#include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 
 CompilerOptions::CompilerOptions() = default;
 
 CompilerOptions::~CompilerOptions() = default;
 
+LinkerOptions::LinkerOptions() = default;
+
+LinkerOptions::~LinkerOptions() = default;
+
 std::string MakeGuid(const std::string& entry_path, const std::string& seed) {
   std::string str = base::ToUpperASCII(base::MD5String(seed + entry_path));
   return '{' + str.substr(0, 8) + '-' + str.substr(8, 4) + '-' +
@@ -115,3 +122,18 @@
   // Put everything else into additional_options.
   options->additional_options += cflag + ' ';
 }
+
+// Parses |ldflags| value and stores it in |options|.
+void ParseLinkerOption(const std::string& ldflag, LinkerOptions* options) {
+  const char kSubsytemPrefix[] ="/SUBSYSTEM:";
+  if (base::StartsWith(ldflag, kSubsytemPrefix,
+                       base::CompareCase::SENSITIVE)) {
+    const std::string subsystem(
+        ldflag.begin() + std::string(kSubsytemPrefix).length(),
+        ldflag.end());
+    const std::vector<std::string> tokens = base::SplitString(
+        subsystem, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+    if (!tokens.empty())
+      options->subsystem = tokens[0];
+  }
+}
diff --git a/tools/gn/visual_studio_utils.h b/tools/gn/visual_studio_utils.h
index b91b218..319428f 100644
--- a/tools/gn/visual_studio_utils.h
+++ b/tools/gn/visual_studio_utils.h
@@ -24,6 +24,16 @@
   std::string warning_level;
 };
 
+// Some linker options which will be written to project file. We don't need to
+// specify all options because generated project file is going to be used only
+// for compilation of single file. For real build ninja files are used.
+struct LinkerOptions {
+  LinkerOptions();
+  ~LinkerOptions();
+
+  std::string subsystem;
+};
+
 // Generates something which looks like a GUID, but depends only on the name and
 // seed. This means the same name / seed will always generate the same GUID, so
 // that projects and solutions which refer to each other can explicitly
@@ -34,4 +44,7 @@
 // Parses |cflag| value and stores it in |options|.
 void ParseCompilerOption(const std::string& cflag, CompilerOptions* options);
 
+// Parses |ldflags| value and stores it in |options|.
+void ParseLinkerOption(const std::string& ldflag, LinkerOptions* options);
+
 #endif  // TOOLS_GN_VISUAL_STUDIO_UTILS_H_
diff --git a/tools/gn/visual_studio_utils_unittest.cc b/tools/gn/visual_studio_utils_unittest.cc
index c4e2530..261c030 100644
--- a/tools/gn/visual_studio_utils_unittest.cc
+++ b/tools/gn/visual_studio_utils_unittest.cc
@@ -92,3 +92,12 @@
   ParseCompilerOption("/Zc:sizedDealloc", &options);
   ASSERT_EQ("/MP /bigobj /Zc:sizedDealloc ", options.additional_options);
 }
+
+TEST(VisualStudioUtils, ParseLinkerOption) {
+  LinkerOptions options;
+  ParseLinkerOption("/SUBSYSTEM:CONSOLE,5.02h", &options);
+  ASSERT_EQ("CONSOLE", options.subsystem);
+
+  ParseLinkerOption("/SUBSYSTEM:WINDOWS", &options);
+  ASSERT_EQ("WINDOWS", options.subsystem);
+}
diff --git a/tools/gn/visual_studio_writer.cc b/tools/gn/visual_studio_writer.cc
index 0c9972d..76c38dc 100644
--- a/tools/gn/visual_studio_writer.cc
+++ b/tools/gn/visual_studio_writer.cc
@@ -153,6 +153,18 @@
   }
 }
 
+void ParseLinkerOptions(const std::vector<std::string>& ldflags,
+                        LinkerOptions* options) {
+  for (const std::string& flag : ldflags)
+    ParseLinkerOption(flag, options);
+}
+
+void ParseLinkerOptions(const Target* target, LinkerOptions* options) {
+  for (ConfigValuesIterator iter(target); !iter.done(); iter.Next()) {
+    ParseLinkerOptions(iter.cur().ldflags(), options);
+  }
+}
+
 // Returns a string piece pointing into the input string identifying the parent
 // directory path, excluding the last slash. Note that the input pointer must
 // outlive the output.
@@ -539,8 +551,17 @@
         cl_compile->SubElement("WarningLevel")->Text(options.warning_level);
     }
 
-    // We don't include resource compilation and link options as ninja files
-    // are used to generate real build.
+    std::unique_ptr<XmlElementWriter> link =
+        item_definitions->SubElement("Link");
+    {
+      LinkerOptions options;
+      ParseLinkerOptions(target, &options);
+      if (!options.subsystem.empty())
+        link->SubElement("SubSystem")->Text(options.subsystem);
+    }
+
+    // We don't include resource compilation and other link options as ninja
+    // files are used to generate real build.
   }
 
   {