tools/gn: Implement Windows SDK version command line switch

Windows SDK command line switch enables ability to browse source code
smoothly in Visual Studio using various installed Windows SDK versions.

BUG=634788

Change-Id: I64af058a0ccfd10a189cda61938a9ec7b5f9c60d
Reviewed-on: https://chromium-review.googlesource.com/535639
Reviewed-by: Dirk Pranke <dpranke@chromium.org>
Reviewed-by: Bruce Dawson <brucedawson@chromium.org>
Commit-Queue: Dirk Pranke <dpranke@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#480307}
Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src
Cr-Mirrored-Commit: 8480d86659b860587f9ee3995d167bda743492aa
diff --git a/tools/gn/command_gen.cc b/tools/gn/command_gen.cc
index b2ced8f..a665d2c 100644
--- a/tools/gn/command_gen.cc
+++ b/tools/gn/command_gen.cc
@@ -37,6 +37,7 @@
 const char kSwitchIdeValueVs2013[] = "vs2013";
 const char kSwitchIdeValueVs2015[] = "vs2015";
 const char kSwitchIdeValueVs2017[] = "vs2017";
+const char kSwitchIdeValueWinSdk[] = "winsdk";
 const char kSwitchIdeValueXcode[] = "xcode";
 const char kSwitchIdeValueJson[] = "json";
 const char kSwitchNinjaExtraArgs[] = "ninja-extra-args";
@@ -207,9 +208,13 @@
     std::string filters;
     if (command_line->HasSwitch(kSwitchFilters))
       filters = command_line->GetSwitchValueASCII(kSwitchFilters);
+    std::string win_kit;
+    if (command_line->HasSwitch(kSwitchIdeValueWinSdk))
+      win_kit = command_line->GetSwitchValueASCII(kSwitchIdeValueWinSdk);
     bool no_deps = command_line->HasSwitch(kSwitchNoDeps);
-    bool res = VisualStudioWriter::RunAndWriteFiles(
-        build_settings, builder, version, sln_name, filters, no_deps, err);
+    bool res = VisualStudioWriter::RunAndWriteFiles(build_settings, builder,
+                                                    version, sln_name, filters,
+                                                    win_kit, no_deps, err);
     if (res && !quiet) {
       OutputString("Generating Visual Studio projects took " +
                    base::Int64ToString(timer.Elapsed().InMilliseconds()) +
@@ -321,6 +326,11 @@
       Don't include targets dependencies to the solution. Changes the way how
       --filters option works. Only directly matching targets are included.
 
+  --winsdk=<sdk_version>
+      Use the specified Windows 10 SDK version to generate project files.
+      As an example, "10.0.15063.0" can be specified to use Creators Update SDK
+      instead of the default one.
+
 Xcode Flags
 
   --workspace=<file_name>
diff --git a/tools/gn/visual_studio_writer.cc b/tools/gn/visual_studio_writer.cc
index a498fb9..eede7f4 100644
--- a/tools/gn/visual_studio_writer.cc
+++ b/tools/gn/visual_studio_writer.cc
@@ -74,12 +74,12 @@
 const char kToolsetVersionVs2017[] = "v141";               // Visual Studio 2017
 const char kProjectVersionVs2013[] = "12.0";               // Visual Studio 2013
 const char kProjectVersionVs2015[] = "14.0";               // Visual Studio 2015
-const char kProjectVersionVs2017[] = "15.0";               // Visual Studio 2015
+const char kProjectVersionVs2017[] = "15.0";               // Visual Studio 2017
 const char kVersionStringVs2013[] = "Visual Studio 2013";  // Visual Studio 2013
 const char kVersionStringVs2015[] = "Visual Studio 2015";  // Visual Studio 2015
 const char kVersionStringVs2017[] = "Visual Studio 2017";  // Visual Studio 2017
 const char kWindowsKitsVersion[] = "10";                   // Windows 10 SDK
-const char kWindowsKitsIncludeVersion[] = "10.0.14393.0";  // Windows 10 SDK
+const char kWindowsKitsDefaultVersion[] = "10.0.14393.0";  // Windows 10 SDK
 
 const char kGuidTypeProject[] = "{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}";
 const char kGuidTypeFolder[] = "{2150E333-8FDC-42A3-9474-1A3956D46DE8}";
@@ -89,7 +89,7 @@
 
 const char kConfigurationName[] = "GN";
 
-std::string GetWindowsKitsIncludeDirs() {
+std::string GetWindowsKitsIncludeDirs(const std::string& win_kit) {
   std::string kits_path;
 
 #if defined(OS_WIN)
@@ -115,9 +115,8 @@
                 kWindowsKitsVersion + "\\";
   }
 
-  return kits_path + "Include\\" + kWindowsKitsIncludeVersion + "\\shared;" +
-         kits_path + "Include\\" + kWindowsKitsIncludeVersion + "\\um;" +
-         kits_path + "Include\\" + kWindowsKitsIncludeVersion + "\\winrt;";
+  const std::string kit_prefix = kits_path + "Include\\" + win_kit + "\\";
+  return kit_prefix + "shared;" + kit_prefix + "um;" + kit_prefix + "winrt;";
 }
 
 std::string GetConfigurationType(const Target* target, Err* err) {
@@ -259,12 +258,16 @@
 
 VisualStudioWriter::VisualStudioWriter(const BuildSettings* build_settings,
                                        const char* config_platform,
-                                       Version version)
+                                       Version version,
+                                       const std::string& win_kit)
     : build_settings_(build_settings),
       config_platform_(config_platform),
       ninja_path_output_(build_settings->build_dir(),
                          build_settings->root_path_utf8(),
-                         EscapingMode::ESCAPE_NINJA_COMMAND) {
+                         EscapingMode::ESCAPE_NINJA_COMMAND),
+      windows_sdk_version_(win_kit) {
+  DCHECK(!win_kit.empty());
+
   switch (version) {
     case Version::Vs2013:
       project_version_ = kProjectVersionVs2013;
@@ -285,7 +288,7 @@
       NOTREACHED() << "Not a valid Visual Studio Version: " << version;
   }
 
-  windows_kits_include_dirs_ = GetWindowsKitsIncludeDirs();
+  windows_kits_include_dirs_ = GetWindowsKitsIncludeDirs(win_kit);
 }
 
 VisualStudioWriter::~VisualStudioWriter() {
@@ -297,12 +300,17 @@
                                           Version version,
                                           const std::string& sln_name,
                                           const std::string& filters,
+                                          const std::string& win_sdk,
                                           bool no_deps,
                                           Err* err) {
   std::vector<const Target*> targets;
   if (!FilterTargets(build_settings, builder, filters, no_deps, &targets, err))
     return false;
 
+  std::string win_kit = kWindowsKitsDefaultVersion;
+  if (!win_sdk.empty())
+    win_kit = win_sdk;
+
   const char* config_platform = "Win32";
 
   // Assume the "target_cpu" variable does not change between different
@@ -315,7 +323,7 @@
       config_platform = "x64";
   }
 
-  VisualStudioWriter writer(build_settings, config_platform, version);
+  VisualStudioWriter writer(build_settings, config_platform, version, win_kit);
   writer.projects_.reserve(targets.size());
   writer.folders_.reserve(targets.size());
 
@@ -436,7 +444,7 @@
     globals->SubElement("IgnoreWarnCompileDuplicatedFilename")->Text("true");
     globals->SubElement("PreferredToolArchitecture")->Text("x64");
     globals->SubElement("WindowsTargetPlatformVersion")
-        ->Text(kWindowsKitsIncludeVersion);
+        ->Text(windows_sdk_version_);
   }
 
   project.SubElement(
diff --git a/tools/gn/visual_studio_writer.h b/tools/gn/visual_studio_writer.h
index 1793573..db2f3ea 100644
--- a/tools/gn/visual_studio_writer.h
+++ b/tools/gn/visual_studio_writer.h
@@ -37,12 +37,14 @@
   // semicolon-separated list of label patterns used to limit the set of
   // generated projects. Only matching targets and their dependencies (unless
   // |no_deps| is true) will be included to the solution. On failure will
-  // populate |err| and will return false.
+  // populate |err| and will return false. |win_sdk| is the Windows SDK version
+  // which will be used by Visual Studio IntelliSense.
   static bool RunAndWriteFiles(const BuildSettings* build_settings,
                                const Builder& builder,
                                Version version,
                                const std::string& sln_name,
                                const std::string& filters,
+                               const std::string& win_sdk,
                                bool no_deps,
                                Err* err);
 
@@ -98,7 +100,8 @@
 
   VisualStudioWriter(const BuildSettings* build_settings,
                      const char* config_platform,
-                     Version version);
+                     Version version,
+                     const std::string& win_kit);
   ~VisualStudioWriter();
 
   bool WriteProjectFiles(const Target* target, Err* err);
@@ -150,6 +153,9 @@
   // Path formatter for ninja targets.
   PathOutput ninja_path_output_;
 
+  // Windows 10 SDK version string (e.g. 10.0.14393.0)
+  std::string windows_sdk_version_;
+
   DISALLOW_COPY_AND_ASSIGN(VisualStudioWriter);
 };
 
diff --git a/tools/gn/visual_studio_writer_unittest.cc b/tools/gn/visual_studio_writer_unittest.cc
index f89c1c1..1ad7c61 100644
--- a/tools/gn/visual_studio_writer_unittest.cc
+++ b/tools/gn/visual_studio_writer_unittest.cc
@@ -28,7 +28,8 @@
 
 TEST_F(VisualStudioWriterTest, ResolveSolutionFolders) {
   VisualStudioWriter writer(setup_.build_settings(), "Win32",
-                            VisualStudioWriter::Version::Vs2015);
+                            VisualStudioWriter::Version::Vs2015,
+                            "10.0.14393.0");
 
   std::string path =
       MakeTestPath("/foo/chromium/src/out/Debug/obj/base/base.vcxproj");
@@ -82,7 +83,8 @@
 
 TEST_F(VisualStudioWriterTest, ResolveSolutionFolders_AbsPath) {
   VisualStudioWriter writer(setup_.build_settings(), "Win32",
-                            VisualStudioWriter::Version::Vs2015);
+                            VisualStudioWriter::Version::Vs2015,
+                            "10.0.14393.0");
 
   std::string path =
       MakeTestPath("/foo/chromium/src/out/Debug/obj/base/base.vcxproj");