GN support for generating VS2013 project files

Optional Visual Studio version can now be specified for "gn gen".
The default remains Visual Studio 2015.

BUG=

Review URL: https://codereview.chromium.org/1713363002

Cr-Original-Commit-Position: refs/heads/master@{#376849}
Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src
Cr-Mirrored-Commit: d0bc0fa329d753da4fb333c572361f575411b8a7
diff --git a/tools/gn/command_gen.cc b/tools/gn/command_gen.cc
index 7ba7789..b5ffc14 100644
--- a/tools/gn/command_gen.cc
+++ b/tools/gn/command_gen.cc
@@ -29,6 +29,8 @@
 const char kSwitchIde[] = "ide";
 const char kSwitchIdeValueEclipse[] = "eclipse";
 const char kSwitchIdeValueVs[] = "vs";
+const char kSwitchIdeValueVs2013[] = "vs2013";
+const char kSwitchIdeValueVs2015[] = "vs2015";
 
 // Called on worker thread to write the ninja file.
 void BackgroundDoWrite(const Target* target) {
@@ -156,15 +158,20 @@
   base::ElapsedTimer timer;
   if (ide == kSwitchIdeValueEclipse) {
     bool res = EclipseWriter::RunAndWriteFile(build_settings, builder, err);
-    if (res) {
+    if (res &&
+        !base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kQuiet)) {
       OutputString("Generating Eclipse settings took " +
                    base::Int64ToString(timer.Elapsed().InMilliseconds()) +
                    "ms\n");
     }
     return res;
-  } else if (ide == kSwitchIdeValueVs) {
-    bool res =
-        VisualStudioWriter::RunAndWriteFiles(build_settings, builder, err);
+  } else if (ide == kSwitchIdeValueVs || ide == kSwitchIdeValueVs2013 ||
+             ide == kSwitchIdeValueVs2015) {
+    VisualStudioWriter::Version version =
+        ide == kSwitchIdeValueVs2013 ? VisualStudioWriter::Version::Vs2013
+                                     : VisualStudioWriter::Version::Vs2015;
+    bool res = VisualStudioWriter::RunAndWriteFiles(build_settings, builder,
+                                                    version, err);
     if (res &&
         !base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kQuiet)) {
       OutputString("Generating Visual Studio projects took " +
@@ -198,8 +205,11 @@
     "\n"
     "  --ide=<ide_name>\n"
     "    Also generate files for an IDE. Currently supported values:\n"
-    "      'vs' - Visual Studio project/solution files.\n"
-    "      'eclipse' - Eclipse CDT settings file.\n"
+    "      \"eclipse\" - Eclipse CDT settings file.\n"
+    "      \"vs\" - Visual Studio project/solution files.\n"
+    "             (default Visual Studio version: 2015)\n"
+    "      \"vs2013\" - Visual Studio 2013 project/solution files.\n"
+    "      \"vs2015\" - Visual Studio 2015 project/solution files.\n"
     "\n"
     "  See \"gn help switches\" for the common command-line switches.\n"
     "\n"
diff --git a/tools/gn/visual_studio_writer.cc b/tools/gn/visual_studio_writer.cc
index dec0837..b0392b4 100644
--- a/tools/gn/visual_studio_writer.cc
+++ b/tools/gn/visual_studio_writer.cc
@@ -64,8 +64,12 @@
   const SourceFile& source_file_;
 };
 
-const char kToolsetVersion[] = "v140";                     // Visual Studio 2015
-const char kVisualStudioVersion[] = "14.0";                // Visual Studio 2015
+const char kToolsetVersionVs2013[] = "v120";               // Visual Studio 2013
+const char kToolsetVersionVs2015[] = "v140";               // Visual Studio 2015
+const char kProjectVersionVs2013[] = "12.0";               // Visual Studio 2013
+const char kProjectVersionVs2015[] = "14.0";               // Visual Studio 2015
+const char kVersionStringVs2013[] = "Visual Studio 2013";  // Visual Studio 2013
+const char kVersionStringVs2015[] = "Visual Studio 2015";  // Visual Studio 2015
 const char kWindowsKitsVersion[] = "10";                   // Windows 10 SDK
 const char kWindowsKitsIncludeVersion[] = "10.0.10240.0";  // Windows 10 SDK
 
@@ -173,7 +177,8 @@
 
 VisualStudioWriter::SolutionProject::~SolutionProject() = default;
 
-VisualStudioWriter::VisualStudioWriter(const BuildSettings* build_settings)
+VisualStudioWriter::VisualStudioWriter(const BuildSettings* build_settings,
+                                       Version version)
     : build_settings_(build_settings) {
   const Value* value = build_settings->build_args().GetArgOverride("is_debug");
   is_debug_config_ = value == nullptr || value->boolean_value();
@@ -182,6 +187,21 @@
   if (value != nullptr && value->string_value() == "x64")
     config_platform_ = "x64";
 
+  switch (version) {
+    case Version::Vs2013:
+      project_version_ = kProjectVersionVs2013;
+      toolset_version_ = kToolsetVersionVs2013;
+      version_string_ = kVersionStringVs2013;
+      break;
+    case Version::Vs2015:
+      project_version_ = kProjectVersionVs2015;
+      toolset_version_ = kToolsetVersionVs2015;
+      version_string_ = kVersionStringVs2015;
+      break;
+    default:
+      NOTREACHED() << "Not a valid Visual Studio Version: " << version;
+  }
+
   windows_kits_include_dirs_ = GetWindowsKitsIncludeDirs();
 }
 
@@ -193,10 +213,11 @@
 // static
 bool VisualStudioWriter::RunAndWriteFiles(const BuildSettings* build_settings,
                                           Builder* builder,
+                                          Version version,
                                           Err* err) {
   std::vector<const Target*> targets = builder->GetAllResolvedTargets();
 
-  VisualStudioWriter writer(build_settings);
+  VisualStudioWriter writer(build_settings, version);
   writer.projects_.reserve(targets.size());
   writer.folders_.reserve(targets.size());
 
@@ -290,7 +311,7 @@
   XmlElementWriter project(
       out, "Project",
       XmlAttributes("DefaultTargets", "Build")
-          .add("ToolsVersion", kVisualStudioVersion)
+          .add("ToolsVersion", project_version_)
           .add("xmlns", "http://schemas.microsoft.com/developer/msbuild/2003"));
 
   {
@@ -333,7 +354,7 @@
   {
     scoped_ptr<XmlElementWriter> locals =
         project.SubElement("PropertyGroup", XmlAttributes("Label", "Locals"));
-    locals->SubElement("PlatformToolset")->Text(kToolsetVersion);
+    locals->SubElement("PlatformToolset")->Text(toolset_version_);
   }
 
   project.SubElement(
@@ -580,7 +601,7 @@
     const base::FilePath& solution_dir_path) {
   out << "Microsoft Visual Studio Solution File, Format Version 12.00"
       << std::endl;
-  out << "# Visual Studio 2015" << std::endl;
+  out << "# " << version_string_ << std::endl;
 
   SourceDir solution_dir(FilePathToUTF8(solution_dir_path));
   for (const SolutionEntry* folder : folders_) {
diff --git a/tools/gn/visual_studio_writer.h b/tools/gn/visual_studio_writer.h
index bd94f92..0409220 100644
--- a/tools/gn/visual_studio_writer.h
+++ b/tools/gn/visual_studio_writer.h
@@ -23,9 +23,15 @@
 
 class VisualStudioWriter {
  public:
+  enum Version {
+    Vs2013 = 1,  // Visual Studio 2013
+    Vs2015       // Visual Studio 2015
+  };
+
   // On failure will populate |err| and will return false.
   static bool RunAndWriteFiles(const BuildSettings* build_settings,
                                Builder* builder,
+                               Version version,
                                Err* err);
 
  private:
@@ -67,7 +73,8 @@
   using SolutionProjects = std::vector<SolutionProject*>;
   using SolutionFolders = std::vector<SolutionEntry*>;
 
-  explicit VisualStudioWriter(const BuildSettings* build_settings);
+  explicit VisualStudioWriter(const BuildSettings* build_settings,
+                              Version version);
   ~VisualStudioWriter();
 
   bool WriteProjectFiles(const Target* target, Err* err);
@@ -86,6 +93,15 @@
 
   const BuildSettings* build_settings_;
 
+  // Toolset version.
+  const char* toolset_version_;
+
+  // Project version.
+  const char* project_version_;
+
+  // Visual Studio version string.
+  const char* version_string_;
+
   // Indicates if project files are generated for Debug mode configuration.
   bool is_debug_config_;
 
diff --git a/tools/gn/visual_studio_writer_unittest.cc b/tools/gn/visual_studio_writer_unittest.cc
index 91c7089..1675e4f 100644
--- a/tools/gn/visual_studio_writer_unittest.cc
+++ b/tools/gn/visual_studio_writer_unittest.cc
@@ -26,7 +26,8 @@
 }  // namespace
 
 TEST_F(VisualStudioWriterTest, ResolveSolutionFolders) {
-  VisualStudioWriter writer(setup_.build_settings());
+  VisualStudioWriter writer(setup_.build_settings(),
+                            VisualStudioWriter::Version::Vs2015);
 
   std::string path =
       MakeTestPath("/foo/chromium/src/out/Debug/obj/base/base.vcxproj");
@@ -79,7 +80,8 @@
 }
 
 TEST_F(VisualStudioWriterTest, ResolveSolutionFolders_AbsPath) {
-  VisualStudioWriter writer(setup_.build_settings());
+  VisualStudioWriter writer(setup_.build_settings(),
+                            VisualStudioWriter::Version::Vs2015);
 
   std::string path =
       MakeTestPath("/foo/chromium/src/out/Debug/obj/base/base.vcxproj");