Factor out the Windows SDK handling into a recipe module

We may be able to share this module with infra/infra, but it's not
possible at the moment so we need cary our own copy of this module.

Change-Id: Ib2ef75062fe514057ec806751be20e1451681cd7
Reviewed-on: https://gn-review.googlesource.com/1780
Reviewed-by: Scott Graham <scottmg@chromium.org>
Commit-Queue: Petr Hosek <phosek@google.com>
diff --git a/infra/README.recipes.md b/infra/README.recipes.md
index 7e43239..9d447de 100644
--- a/infra/README.recipes.md
+++ b/infra/README.recipes.md
@@ -2,17 +2,49 @@
 # Package documentation for [gn]()
 ## Table of Contents
 
+**[Recipe Modules](#Recipe-Modules)**
+  * [windows_sdk](#recipe_modules-windows_sdk)
+
 **[Recipes](#Recipes)**
   * [gn](#recipes-gn) &mdash; Recipe for building GN.
+  * [windows_sdk:examples/full](#recipes-windows_sdk_examples_full)
+## Recipe Modules
+
+### *recipe_modules* / [windows\_sdk](/infra/recipe_modules/windows_sdk)
+
+[DEPS](/infra/recipe_modules/windows_sdk/__init__.py#5): [recipe\_engine/cipd][recipe_engine/recipe_modules/cipd], [recipe\_engine/context][recipe_engine/recipe_modules/context], [recipe\_engine/json][recipe_engine/recipe_modules/json], [recipe\_engine/path][recipe_engine/recipe_modules/path], [recipe\_engine/platform][recipe_engine/recipe_modules/platform], [recipe\_engine/step][recipe_engine/recipe_modules/step]
+
+#### **class [WindowsSDKApi](/infra/recipe_modules/windows_sdk/api.py#10)([RecipeApi][recipe_engine/wkt/RecipeApi]):**
+
+API for using Windows SDK distributed via CIPD.
+
+&emsp; **@contextmanager**<br>&mdash; **def [\_\_call\_\_](/infra/recipe_modules/windows_sdk/api.py#18)(self, path=None, version=None, enabled=True):**
+
+Setups the SDK environment when enabled.
+
+Args:
+  path (path): Path to a directory where to install the SDK
+    (default is '[start_dir]/cipd/windows_sdk')
+  version (str): CIPD instance ID, tag or ref of the SDK
+    (default is set via $infra/windows_sdk.version property)
+  enabled (bool): Whether the SDK should be used or not.
+
+Raises:
+    StepFailure or InfraFailure.
 ## Recipes
 
 ### *recipes* / [gn](/infra/recipes/gn.py)
 
-[DEPS](/infra/recipes/gn.py#6): [recipe\_engine/buildbucket][recipe_engine/recipe_modules/buildbucket], [recipe\_engine/cipd][recipe_engine/recipe_modules/cipd], [recipe\_engine/context][recipe_engine/recipe_modules/context], [recipe\_engine/json][recipe_engine/recipe_modules/json], [recipe\_engine/path][recipe_engine/recipe_modules/path], [recipe\_engine/platform][recipe_engine/recipe_modules/platform], [recipe\_engine/properties][recipe_engine/recipe_modules/properties], [recipe\_engine/python][recipe_engine/recipe_modules/python], [recipe\_engine/step][recipe_engine/recipe_modules/step]
+[DEPS](/infra/recipes/gn.py#6): [windows\_sdk](#recipe_modules-windows_sdk), [recipe\_engine/buildbucket][recipe_engine/recipe_modules/buildbucket], [recipe\_engine/cipd][recipe_engine/recipe_modules/cipd], [recipe\_engine/context][recipe_engine/recipe_modules/context], [recipe\_engine/json][recipe_engine/recipe_modules/json], [recipe\_engine/path][recipe_engine/recipe_modules/path], [recipe\_engine/platform][recipe_engine/recipe_modules/platform], [recipe\_engine/properties][recipe_engine/recipe_modules/properties], [recipe\_engine/python][recipe_engine/recipe_modules/python], [recipe\_engine/step][recipe_engine/recipe_modules/step]
 
 Recipe for building GN.
 
-&mdash; **def [RunSteps](/infra/recipes/gn.py#19)(api):**
+&mdash; **def [RunSteps](/infra/recipes/gn.py#20)(api):**
+### *recipes* / [windows\_sdk:examples/full](/infra/recipe_modules/windows_sdk/examples/full.py)
+
+[DEPS](/infra/recipe_modules/windows_sdk/examples/full.py#5): [windows\_sdk](#recipe_modules-windows_sdk), [recipe\_engine/platform][recipe_engine/recipe_modules/platform], [recipe\_engine/properties][recipe_engine/recipe_modules/properties], [recipe\_engine/step][recipe_engine/recipe_modules/step]
+
+&mdash; **def [RunSteps](/infra/recipe_modules/windows_sdk/examples/full.py#13)(api):**
 
 [recipe_engine/recipe_modules/buildbucket]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/40699af88c76d65ef1b3d364de221611603e2dc6/README.recipes.md#recipe_modules-buildbucket
 [recipe_engine/recipe_modules/cipd]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/40699af88c76d65ef1b3d364de221611603e2dc6/README.recipes.md#recipe_modules-cipd
@@ -23,3 +55,4 @@
 [recipe_engine/recipe_modules/properties]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/40699af88c76d65ef1b3d364de221611603e2dc6/README.recipes.md#recipe_modules-properties
 [recipe_engine/recipe_modules/python]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/40699af88c76d65ef1b3d364de221611603e2dc6/README.recipes.md#recipe_modules-python
 [recipe_engine/recipe_modules/step]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/40699af88c76d65ef1b3d364de221611603e2dc6/README.recipes.md#recipe_modules-step
+[recipe_engine/wkt/RecipeApi]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/40699af88c76d65ef1b3d364de221611603e2dc6/recipe_engine/recipe_api.py#1006
diff --git a/infra/recipe_modules/windows_sdk/__init__.py b/infra/recipe_modules/windows_sdk/__init__.py
new file mode 100644
index 0000000..6183f2b
--- /dev/null
+++ b/infra/recipe_modules/windows_sdk/__init__.py
@@ -0,0 +1,27 @@
+# Copyright 2018 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+DEPS = [
+    'recipe_engine/cipd',
+    'recipe_engine/context',
+    'recipe_engine/json',
+    'recipe_engine/path',
+    'recipe_engine/platform',
+    'recipe_engine/step',
+]
+
+from recipe_engine.recipe_api import Property
+from recipe_engine.config import ConfigGroup, Single
+
+PROPERTIES = {
+    '$gn/windows_sdk':
+        Property(
+            help='Properties specifically for the infra windows_sdk module.',
+            param_name='sdk_properties',
+            kind=ConfigGroup(
+                # CIPD instance ID, tag or ref for the Windows SDK version.
+                version=Single(str),),
+            default={'version': 'uploaded:2018-06-13'},
+        )
+}
diff --git a/infra/recipe_modules/windows_sdk/api.py b/infra/recipe_modules/windows_sdk/api.py
new file mode 100644
index 0000000..0c99d78
--- /dev/null
+++ b/infra/recipe_modules/windows_sdk/api.py
@@ -0,0 +1,112 @@
+# Copyright 2018 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from contextlib import contextmanager
+
+from recipe_engine import recipe_api
+
+
+class WindowsSDKApi(recipe_api.RecipeApi):
+  """API for using Windows SDK distributed via CIPD."""
+
+  def __init__(self, sdk_properties, *args, **kwargs):
+    super(WindowsSDKApi, self).__init__(*args, **kwargs)
+
+    self._sdk_properties = sdk_properties
+
+  @contextmanager
+  def __call__(self, path=None, version=None, enabled=True):
+    """Setups the SDK environment when enabled.
+
+    Args:
+      path (path): Path to a directory where to install the SDK
+        (default is '[start_dir]/cipd/windows_sdk')
+      version (str): CIPD instance ID, tag or ref of the SDK
+        (default is set via $infra/windows_sdk.version property)
+      enabled (bool): Whether the SDK should be used or not.
+
+    Raises:
+        StepFailure or InfraFailure.
+    """
+    if enabled:
+      sdk_dir = self._ensure_sdk(
+          path or self.m.path['start_dir'].join('cipd', 'windows_sdk'),
+          version or self._sdk_properties['version'])
+      try:
+        with self.m.context(**self._sdk_env(sdk_dir)):
+          yield
+      finally:
+        if self.m.platform.is_win:
+          # cl.exe automatically starts background mspdbsrv.exe daemon which
+          # needs to be manually stopped so Swarming can tidy up after itself.
+          self.m.step('taskkill mspdbsrv',
+                      ['taskkill.exe', '/f', '/t', '/im', 'mspdbsrv.exe'])
+    else:
+      yield
+
+  def _ensure_sdk(self, sdk_dir, sdk_version):
+    """Ensures the Windows SDK CIPD package is installed.
+
+    Returns the directory where the SDK package has been installed.
+
+    Args:
+      path (path): Path to a directory.
+      version (str): CIPD instance ID, tag or ref.
+    """
+    with self.m.context(infra_steps=True):
+      sdk_package = 'chrome_internal/third_party/sdk/windows'
+      self.m.cipd.ensure(sdk_dir, {sdk_package: sdk_version})
+      return sdk_dir
+
+  def _sdk_env(self, sdk_dir):
+    """Constructs the environment for the SDK.
+
+    Returns environment and environment prefixes.
+
+    Args:
+      sdk_dir (path): Path to a directory containing the SDK.
+    """
+    env = {}
+    env_prefixes = {}
+
+    # Load .../win_sdk/bin/SetEnv.${arch}.json to extract the required
+    # environment. It contains a dict that looks like this:
+    # {
+    #   "env": {
+    #     "VAR": [["..", "..", "x"], ["..", "..", "y"]],
+    #     ...
+    #   }
+    # }
+    # All these environment variables need to be added to the environment
+    # for the compiler and linker to work.
+    filename = 'SetEnv.%s.json' % {32: 'x86', 64: 'x64'}[self.m.platform.bits]
+    step_result = self.m.json.read(
+        'read %s' % filename,
+        sdk_dir.join('win_sdk', 'bin', filename),
+        step_test_data=lambda: self.m.json.test_api.output({
+            'env': {
+                'PATH': [['..', '..', 'win_sdk', 'bin', 'x64']],
+                'VSINSTALLDIR': [['..', '..\\']],},}))
+    data = step_result.json.output.get('env')
+    for key in data:
+      # recipes' Path() does not like .., ., \, or /, so this is cumbersome.
+      # What we want to do is:
+      #   [sdk_bin_dir.join(*e) for e in env[k]]
+      # Instead do that badly, and rely (but verify) on the fact that the paths
+      # are all specified relative to the root, but specified relative to
+      # win_sdk/bin (i.e. everything starts with "../../".)
+      results = []
+      for value in data[key]:
+        assert value[0] == '..' and (value[1] == '..' or value[1] == '..\\')
+        results.append('%s' % sdk_dir.join(*value[2:]))
+
+      # PATH is special-cased because we don't want to overwrite other things
+      # like C:\Windows\System32. Others are replacements because prepending
+      # doesn't necessarily makes sense, like VSINSTALLDIR.
+      if key.lower() == 'path':
+        env_prefixes[key] = results
+      else:
+        env[key] = ';'.join(results)
+
+    return {'env': env, 'env_prefixes': env_prefixes}
diff --git a/infra/recipe_modules/windows_sdk/examples/full.expected/linux.json b/infra/recipe_modules/windows_sdk/examples/full.expected/linux.json
new file mode 100644
index 0000000..51d9430
--- /dev/null
+++ b/infra/recipe_modules/windows_sdk/examples/full.expected/linux.json
@@ -0,0 +1,23 @@
+[
+  {
+    "cmd": [
+      "gn",
+      "gen",
+      "out/Release"
+    ],
+    "name": "gn"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "out/Release"
+    ],
+    "name": "ninja"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/recipe_modules/windows_sdk/examples/full.expected/mac.json b/infra/recipe_modules/windows_sdk/examples/full.expected/mac.json
new file mode 100644
index 0000000..51d9430
--- /dev/null
+++ b/infra/recipe_modules/windows_sdk/examples/full.expected/mac.json
@@ -0,0 +1,23 @@
+[
+  {
+    "cmd": [
+      "gn",
+      "gen",
+      "out/Release"
+    ],
+    "name": "gn"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "out/Release"
+    ],
+    "name": "ninja"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/recipe_modules/windows_sdk/examples/full.expected/win.json b/infra/recipe_modules/windows_sdk/examples/full.expected/win.json
new file mode 100644
index 0000000..d9a7a65
--- /dev/null
+++ b/infra/recipe_modules/windows_sdk/examples/full.expected/win.json
@@ -0,0 +1,108 @@
+[
+  {
+    "cmd": [
+      "cipd.bat",
+      "ensure",
+      "-root",
+      "[START_DIR]\\cipd\\windows_sdk",
+      "-ensure-file",
+      "chrome_internal/third_party/sdk/windows uploaded:2018-06-13",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "infra_step": true,
+    "name": "ensure_installed",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"result\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"instance_id\": \"resolved-instance_id-of-uploaded:2018-06\", @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"package\": \"chrome_internal/third_party/sdk/windows\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      }@@@",
+      "@@@STEP_LOG_LINE@json.output@    ]@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]\\cipd\\windows_sdk\\win_sdk\\bin\\SetEnv.x64.json",
+      "/path/to/tmp/json"
+    ],
+    "name": "read SetEnv.x64.json",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"env\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"PATH\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      [@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"..\", @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"..\", @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"win_sdk\", @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"bin\", @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"x64\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      ]@@@",
+      "@@@STEP_LOG_LINE@json.output@    ], @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"VSINSTALLDIR\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      [@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"..\", @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"..\\\\\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      ]@@@",
+      "@@@STEP_LOG_LINE@json.output@    ]@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "gn",
+      "gen",
+      "out/Release"
+    ],
+    "env": {
+      "VSINSTALLDIR": "[START_DIR]\\cipd\\windows_sdk"
+    },
+    "env_prefixes": {
+      "PATH": [
+        "[START_DIR]\\cipd\\windows_sdk\\win_sdk\\bin\\x64"
+      ]
+    },
+    "name": "gn"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "out/Release"
+    ],
+    "env": {
+      "VSINSTALLDIR": "[START_DIR]\\cipd\\windows_sdk"
+    },
+    "env_prefixes": {
+      "PATH": [
+        "[START_DIR]\\cipd\\windows_sdk\\win_sdk\\bin\\x64"
+      ]
+    },
+    "name": "ninja"
+  },
+  {
+    "cmd": [
+      "taskkill.exe",
+      "/f",
+      "/t",
+      "/im",
+      "mspdbsrv.exe"
+    ],
+    "name": "taskkill mspdbsrv"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/recipe_modules/windows_sdk/examples/full.py b/infra/recipe_modules/windows_sdk/examples/full.py
new file mode 100644
index 0000000..5e8e503
--- /dev/null
+++ b/infra/recipe_modules/windows_sdk/examples/full.py
@@ -0,0 +1,25 @@
+# Copyright 2018 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+DEPS = [
+    'windows_sdk',
+    'recipe_engine/platform',
+    'recipe_engine/properties',
+    'recipe_engine/step',
+]
+
+
+def RunSteps(api):
+  with api.windows_sdk(enabled=api.platform.is_win):
+    api.step('gn', ['gn', 'gen', 'out/Release'])
+    api.step('ninja', ['ninja', '-C', 'out/Release'])
+
+
+def GenTests(api):
+  for platform in ('linux', 'mac', 'win'):
+    properties = {
+        'buildername': 'test_builder',
+    }
+    yield (api.test(platform) + api.platform.name(platform) +
+           api.properties.generic(**properties))
diff --git a/infra/recipes/gn.expected/ci_win.json b/infra/recipes/gn.expected/ci_win.json
index 9d57f03..75e7300 100644
--- a/infra/recipes/gn.expected/ci_win.json
+++ b/infra/recipes/gn.expected/ci_win.json
@@ -49,7 +49,7 @@
       "-root",
       "[START_DIR]\\cipd",
       "-ensure-file",
-      "chrome_internal/third_party/sdk/windows uploaded:2018-06-13\ninfra/ninja/${platform} version:1.8.2",
+      "infra/ninja/${platform} version:1.8.2",
       "-json-output",
       "/path/to/tmp/json"
     ],
@@ -60,10 +60,6 @@
       "@@@STEP_LOG_LINE@json.output@  \"result\": {@@@",
       "@@@STEP_LOG_LINE@json.output@    \"\": [@@@",
       "@@@STEP_LOG_LINE@json.output@      {@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"instance_id\": \"resolved-instance_id-of-uploaded:2018-06\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"package\": \"chrome_internal/third_party/sdk/windows\"@@@",
-      "@@@STEP_LOG_LINE@json.output@      }, @@@",
-      "@@@STEP_LOG_LINE@json.output@      {@@@",
       "@@@STEP_LOG_LINE@json.output@        \"instance_id\": \"resolved-instance_id-of-version:1.8.2---\", @@@",
       "@@@STEP_LOG_LINE@json.output@        \"package\": \"infra/ninja/${platform}\"@@@",
       "@@@STEP_LOG_LINE@json.output@      }@@@",
@@ -74,76 +70,6 @@
     ]
   },
   {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]\\cipd\\win_sdk\\bin\\SetEnv.x64.json",
-      "/path/to/tmp/json"
-    ],
-    "name": "SetEnv.x64.json",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"env\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"INCLUDE\": [@@@",
-      "@@@STEP_LOG_LINE@json.output@      [@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"..\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"..\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"win_sdk\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"Include\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"10.0.17134.0\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"um\"@@@",
-      "@@@STEP_LOG_LINE@json.output@      ], @@@",
-      "@@@STEP_LOG_LINE@json.output@      [@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"..\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"..\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"win_sdk\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"Include\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"10.0.17134.0\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"shared\"@@@",
-      "@@@STEP_LOG_LINE@json.output@      ], @@@",
-      "@@@STEP_LOG_LINE@json.output@      [@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"..\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"..\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"win_sdk\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"Include\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"10.0.17134.0\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"winrt\"@@@",
-      "@@@STEP_LOG_LINE@json.output@      ]@@@",
-      "@@@STEP_LOG_LINE@json.output@    ], @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"PATH\": [@@@",
-      "@@@STEP_LOG_LINE@json.output@      [@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"..\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"..\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"win_sdk\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"bin\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"10.0.17134.0\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"x64\"@@@",
-      "@@@STEP_LOG_LINE@json.output@      ], @@@",
-      "@@@STEP_LOG_LINE@json.output@      [@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"..\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"..\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"VC\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"Tools\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"MSVC\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"14.14.26428\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"bin\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"HostX64\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"x64\"@@@",
-      "@@@STEP_LOG_LINE@json.output@      ]@@@",
-      "@@@STEP_LOG_LINE@json.output@    ], @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"VSINSTALLDIR\": [@@@",
-      "@@@STEP_LOG_LINE@json.output@      [@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"..\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"..\\\\\"@@@",
-      "@@@STEP_LOG_LINE@json.output@      ]@@@",
-      "@@@STEP_LOG_LINE@json.output@    ]@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@"
-    ]
-  },
-  {
     "cmd": [],
     "name": "debug"
   },
@@ -156,6 +82,69 @@
   },
   {
     "cmd": [
+      "cipd.bat",
+      "ensure",
+      "-root",
+      "[START_DIR]\\cipd\\windows_sdk",
+      "-ensure-file",
+      "chrome_internal/third_party/sdk/windows uploaded:2018-06-13",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "cwd": "[START_DIR]\\gn",
+    "infra_step": true,
+    "name": "debug.build.ensure_installed",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"result\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"instance_id\": \"resolved-instance_id-of-uploaded:2018-06\", @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"package\": \"chrome_internal/third_party/sdk/windows\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      }@@@",
+      "@@@STEP_LOG_LINE@json.output@    ]@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]\\cipd\\windows_sdk\\win_sdk\\bin\\SetEnv.x64.json",
+      "/path/to/tmp/json"
+    ],
+    "cwd": "[START_DIR]\\gn",
+    "name": "debug.build.read SetEnv.x64.json",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"env\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"PATH\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      [@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"..\", @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"..\", @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"win_sdk\", @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"bin\", @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"x64\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      ]@@@",
+      "@@@STEP_LOG_LINE@json.output@    ], @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"VSINSTALLDIR\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      [@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"..\", @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"..\\\\\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      ]@@@",
+      "@@@STEP_LOG_LINE@json.output@    ]@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
       "python",
       "-u",
       "[START_DIR]\\gn\\build\\gen.py",
@@ -163,13 +152,11 @@
     ],
     "cwd": "[START_DIR]\\gn",
     "env": {
-      "INCLUDE": "[START_DIR]\\cipd\\win_sdk\\Include\\10.0.17134.0\\um;[START_DIR]\\cipd\\win_sdk\\Include\\10.0.17134.0\\shared;[START_DIR]\\cipd\\win_sdk\\Include\\10.0.17134.0\\winrt",
-      "VSINSTALLDIR": "[START_DIR]\\cipd"
+      "VSINSTALLDIR": "[START_DIR]\\cipd\\windows_sdk"
     },
     "env_prefixes": {
       "PATH": [
-        "[START_DIR]\\cipd\\win_sdk\\bin\\10.0.17134.0\\x64",
-        "[START_DIR]\\cipd\\VC\\Tools\\MSVC\\14.14.26428\\bin\\HostX64\\x64"
+        "[START_DIR]\\cipd\\windows_sdk\\win_sdk\\bin\\x64"
       ]
     },
     "name": "debug.build.generate",
@@ -185,13 +172,11 @@
     ],
     "cwd": "[START_DIR]\\gn",
     "env": {
-      "INCLUDE": "[START_DIR]\\cipd\\win_sdk\\Include\\10.0.17134.0\\um;[START_DIR]\\cipd\\win_sdk\\Include\\10.0.17134.0\\shared;[START_DIR]\\cipd\\win_sdk\\Include\\10.0.17134.0\\winrt",
-      "VSINSTALLDIR": "[START_DIR]\\cipd"
+      "VSINSTALLDIR": "[START_DIR]\\cipd\\windows_sdk"
     },
     "env_prefixes": {
       "PATH": [
-        "[START_DIR]\\cipd\\win_sdk\\bin\\10.0.17134.0\\x64",
-        "[START_DIR]\\cipd\\VC\\Tools\\MSVC\\14.14.26428\\bin\\HostX64\\x64"
+        "[START_DIR]\\cipd\\windows_sdk\\win_sdk\\bin\\x64"
       ]
     },
     "name": "debug.build.ninja",
@@ -207,6 +192,7 @@
       "/im",
       "mspdbsrv.exe"
     ],
+    "cwd": "[START_DIR]\\gn",
     "name": "debug.build.taskkill mspdbsrv",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@"
@@ -234,19 +220,80 @@
   },
   {
     "cmd": [
+      "cipd.bat",
+      "ensure",
+      "-root",
+      "[START_DIR]\\cipd\\windows_sdk",
+      "-ensure-file",
+      "chrome_internal/third_party/sdk/windows uploaded:2018-06-13",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "cwd": "[START_DIR]\\gn",
+    "infra_step": true,
+    "name": "release.build.ensure_installed",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"result\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"instance_id\": \"resolved-instance_id-of-uploaded:2018-06\", @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"package\": \"chrome_internal/third_party/sdk/windows\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      }@@@",
+      "@@@STEP_LOG_LINE@json.output@    ]@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]\\cipd\\windows_sdk\\win_sdk\\bin\\SetEnv.x64.json",
+      "/path/to/tmp/json"
+    ],
+    "cwd": "[START_DIR]\\gn",
+    "name": "release.build.read SetEnv.x64.json",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"env\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"PATH\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      [@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"..\", @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"..\", @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"win_sdk\", @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"bin\", @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"x64\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      ]@@@",
+      "@@@STEP_LOG_LINE@json.output@    ], @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"VSINSTALLDIR\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      [@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"..\", @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"..\\\\\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      ]@@@",
+      "@@@STEP_LOG_LINE@json.output@    ]@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
       "python",
       "-u",
       "[START_DIR]\\gn\\build\\gen.py"
     ],
     "cwd": "[START_DIR]\\gn",
     "env": {
-      "INCLUDE": "[START_DIR]\\cipd\\win_sdk\\Include\\10.0.17134.0\\um;[START_DIR]\\cipd\\win_sdk\\Include\\10.0.17134.0\\shared;[START_DIR]\\cipd\\win_sdk\\Include\\10.0.17134.0\\winrt",
-      "VSINSTALLDIR": "[START_DIR]\\cipd"
+      "VSINSTALLDIR": "[START_DIR]\\cipd\\windows_sdk"
     },
     "env_prefixes": {
       "PATH": [
-        "[START_DIR]\\cipd\\win_sdk\\bin\\10.0.17134.0\\x64",
-        "[START_DIR]\\cipd\\VC\\Tools\\MSVC\\14.14.26428\\bin\\HostX64\\x64"
+        "[START_DIR]\\cipd\\windows_sdk\\win_sdk\\bin\\x64"
       ]
     },
     "name": "release.build.generate",
@@ -262,13 +309,11 @@
     ],
     "cwd": "[START_DIR]\\gn",
     "env": {
-      "INCLUDE": "[START_DIR]\\cipd\\win_sdk\\Include\\10.0.17134.0\\um;[START_DIR]\\cipd\\win_sdk\\Include\\10.0.17134.0\\shared;[START_DIR]\\cipd\\win_sdk\\Include\\10.0.17134.0\\winrt",
-      "VSINSTALLDIR": "[START_DIR]\\cipd"
+      "VSINSTALLDIR": "[START_DIR]\\cipd\\windows_sdk"
     },
     "env_prefixes": {
       "PATH": [
-        "[START_DIR]\\cipd\\win_sdk\\bin\\10.0.17134.0\\x64",
-        "[START_DIR]\\cipd\\VC\\Tools\\MSVC\\14.14.26428\\bin\\HostX64\\x64"
+        "[START_DIR]\\cipd\\windows_sdk\\win_sdk\\bin\\x64"
       ]
     },
     "name": "release.build.ninja",
@@ -284,6 +329,7 @@
       "/im",
       "mspdbsrv.exe"
     ],
+    "cwd": "[START_DIR]\\gn",
     "name": "release.build.taskkill mspdbsrv",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@"
diff --git a/infra/recipes/gn.expected/cq_win.json b/infra/recipes/gn.expected/cq_win.json
index b09f254..7e56381 100644
--- a/infra/recipes/gn.expected/cq_win.json
+++ b/infra/recipes/gn.expected/cq_win.json
@@ -76,7 +76,7 @@
       "-root",
       "[START_DIR]\\cipd",
       "-ensure-file",
-      "chrome_internal/third_party/sdk/windows uploaded:2018-06-13\ninfra/ninja/${platform} version:1.8.2",
+      "infra/ninja/${platform} version:1.8.2",
       "-json-output",
       "/path/to/tmp/json"
     ],
@@ -87,10 +87,6 @@
       "@@@STEP_LOG_LINE@json.output@  \"result\": {@@@",
       "@@@STEP_LOG_LINE@json.output@    \"\": [@@@",
       "@@@STEP_LOG_LINE@json.output@      {@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"instance_id\": \"resolved-instance_id-of-uploaded:2018-06\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"package\": \"chrome_internal/third_party/sdk/windows\"@@@",
-      "@@@STEP_LOG_LINE@json.output@      }, @@@",
-      "@@@STEP_LOG_LINE@json.output@      {@@@",
       "@@@STEP_LOG_LINE@json.output@        \"instance_id\": \"resolved-instance_id-of-version:1.8.2---\", @@@",
       "@@@STEP_LOG_LINE@json.output@        \"package\": \"infra/ninja/${platform}\"@@@",
       "@@@STEP_LOG_LINE@json.output@      }@@@",
@@ -101,76 +97,6 @@
     ]
   },
   {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]\\cipd\\win_sdk\\bin\\SetEnv.x64.json",
-      "/path/to/tmp/json"
-    ],
-    "name": "SetEnv.x64.json",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"env\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"INCLUDE\": [@@@",
-      "@@@STEP_LOG_LINE@json.output@      [@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"..\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"..\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"win_sdk\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"Include\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"10.0.17134.0\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"um\"@@@",
-      "@@@STEP_LOG_LINE@json.output@      ], @@@",
-      "@@@STEP_LOG_LINE@json.output@      [@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"..\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"..\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"win_sdk\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"Include\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"10.0.17134.0\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"shared\"@@@",
-      "@@@STEP_LOG_LINE@json.output@      ], @@@",
-      "@@@STEP_LOG_LINE@json.output@      [@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"..\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"..\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"win_sdk\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"Include\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"10.0.17134.0\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"winrt\"@@@",
-      "@@@STEP_LOG_LINE@json.output@      ]@@@",
-      "@@@STEP_LOG_LINE@json.output@    ], @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"PATH\": [@@@",
-      "@@@STEP_LOG_LINE@json.output@      [@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"..\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"..\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"win_sdk\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"bin\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"10.0.17134.0\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"x64\"@@@",
-      "@@@STEP_LOG_LINE@json.output@      ], @@@",
-      "@@@STEP_LOG_LINE@json.output@      [@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"..\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"..\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"VC\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"Tools\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"MSVC\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"14.14.26428\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"bin\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"HostX64\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"x64\"@@@",
-      "@@@STEP_LOG_LINE@json.output@      ]@@@",
-      "@@@STEP_LOG_LINE@json.output@    ], @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"VSINSTALLDIR\": [@@@",
-      "@@@STEP_LOG_LINE@json.output@      [@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"..\", @@@",
-      "@@@STEP_LOG_LINE@json.output@        \"..\\\\\"@@@",
-      "@@@STEP_LOG_LINE@json.output@      ]@@@",
-      "@@@STEP_LOG_LINE@json.output@    ]@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@"
-    ]
-  },
-  {
     "cmd": [],
     "name": "debug"
   },
@@ -183,6 +109,69 @@
   },
   {
     "cmd": [
+      "cipd.bat",
+      "ensure",
+      "-root",
+      "[START_DIR]\\cipd\\windows_sdk",
+      "-ensure-file",
+      "chrome_internal/third_party/sdk/windows uploaded:2018-06-13",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "cwd": "[START_DIR]\\gn",
+    "infra_step": true,
+    "name": "debug.build.ensure_installed",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"result\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"instance_id\": \"resolved-instance_id-of-uploaded:2018-06\", @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"package\": \"chrome_internal/third_party/sdk/windows\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      }@@@",
+      "@@@STEP_LOG_LINE@json.output@    ]@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]\\cipd\\windows_sdk\\win_sdk\\bin\\SetEnv.x64.json",
+      "/path/to/tmp/json"
+    ],
+    "cwd": "[START_DIR]\\gn",
+    "name": "debug.build.read SetEnv.x64.json",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"env\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"PATH\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      [@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"..\", @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"..\", @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"win_sdk\", @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"bin\", @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"x64\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      ]@@@",
+      "@@@STEP_LOG_LINE@json.output@    ], @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"VSINSTALLDIR\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      [@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"..\", @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"..\\\\\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      ]@@@",
+      "@@@STEP_LOG_LINE@json.output@    ]@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
       "python",
       "-u",
       "[START_DIR]\\gn\\build\\gen.py",
@@ -190,13 +179,11 @@
     ],
     "cwd": "[START_DIR]\\gn",
     "env": {
-      "INCLUDE": "[START_DIR]\\cipd\\win_sdk\\Include\\10.0.17134.0\\um;[START_DIR]\\cipd\\win_sdk\\Include\\10.0.17134.0\\shared;[START_DIR]\\cipd\\win_sdk\\Include\\10.0.17134.0\\winrt",
-      "VSINSTALLDIR": "[START_DIR]\\cipd"
+      "VSINSTALLDIR": "[START_DIR]\\cipd\\windows_sdk"
     },
     "env_prefixes": {
       "PATH": [
-        "[START_DIR]\\cipd\\win_sdk\\bin\\10.0.17134.0\\x64",
-        "[START_DIR]\\cipd\\VC\\Tools\\MSVC\\14.14.26428\\bin\\HostX64\\x64"
+        "[START_DIR]\\cipd\\windows_sdk\\win_sdk\\bin\\x64"
       ]
     },
     "name": "debug.build.generate",
@@ -212,13 +199,11 @@
     ],
     "cwd": "[START_DIR]\\gn",
     "env": {
-      "INCLUDE": "[START_DIR]\\cipd\\win_sdk\\Include\\10.0.17134.0\\um;[START_DIR]\\cipd\\win_sdk\\Include\\10.0.17134.0\\shared;[START_DIR]\\cipd\\win_sdk\\Include\\10.0.17134.0\\winrt",
-      "VSINSTALLDIR": "[START_DIR]\\cipd"
+      "VSINSTALLDIR": "[START_DIR]\\cipd\\windows_sdk"
     },
     "env_prefixes": {
       "PATH": [
-        "[START_DIR]\\cipd\\win_sdk\\bin\\10.0.17134.0\\x64",
-        "[START_DIR]\\cipd\\VC\\Tools\\MSVC\\14.14.26428\\bin\\HostX64\\x64"
+        "[START_DIR]\\cipd\\windows_sdk\\win_sdk\\bin\\x64"
       ]
     },
     "name": "debug.build.ninja",
@@ -234,6 +219,7 @@
       "/im",
       "mspdbsrv.exe"
     ],
+    "cwd": "[START_DIR]\\gn",
     "name": "debug.build.taskkill mspdbsrv",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@"
@@ -261,19 +247,80 @@
   },
   {
     "cmd": [
+      "cipd.bat",
+      "ensure",
+      "-root",
+      "[START_DIR]\\cipd\\windows_sdk",
+      "-ensure-file",
+      "chrome_internal/third_party/sdk/windows uploaded:2018-06-13",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "cwd": "[START_DIR]\\gn",
+    "infra_step": true,
+    "name": "release.build.ensure_installed",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"result\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"instance_id\": \"resolved-instance_id-of-uploaded:2018-06\", @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"package\": \"chrome_internal/third_party/sdk/windows\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      }@@@",
+      "@@@STEP_LOG_LINE@json.output@    ]@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]\\cipd\\windows_sdk\\win_sdk\\bin\\SetEnv.x64.json",
+      "/path/to/tmp/json"
+    ],
+    "cwd": "[START_DIR]\\gn",
+    "name": "release.build.read SetEnv.x64.json",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"env\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"PATH\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      [@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"..\", @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"..\", @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"win_sdk\", @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"bin\", @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"x64\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      ]@@@",
+      "@@@STEP_LOG_LINE@json.output@    ], @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"VSINSTALLDIR\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      [@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"..\", @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"..\\\\\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      ]@@@",
+      "@@@STEP_LOG_LINE@json.output@    ]@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
       "python",
       "-u",
       "[START_DIR]\\gn\\build\\gen.py"
     ],
     "cwd": "[START_DIR]\\gn",
     "env": {
-      "INCLUDE": "[START_DIR]\\cipd\\win_sdk\\Include\\10.0.17134.0\\um;[START_DIR]\\cipd\\win_sdk\\Include\\10.0.17134.0\\shared;[START_DIR]\\cipd\\win_sdk\\Include\\10.0.17134.0\\winrt",
-      "VSINSTALLDIR": "[START_DIR]\\cipd"
+      "VSINSTALLDIR": "[START_DIR]\\cipd\\windows_sdk"
     },
     "env_prefixes": {
       "PATH": [
-        "[START_DIR]\\cipd\\win_sdk\\bin\\10.0.17134.0\\x64",
-        "[START_DIR]\\cipd\\VC\\Tools\\MSVC\\14.14.26428\\bin\\HostX64\\x64"
+        "[START_DIR]\\cipd\\windows_sdk\\win_sdk\\bin\\x64"
       ]
     },
     "name": "release.build.generate",
@@ -289,13 +336,11 @@
     ],
     "cwd": "[START_DIR]\\gn",
     "env": {
-      "INCLUDE": "[START_DIR]\\cipd\\win_sdk\\Include\\10.0.17134.0\\um;[START_DIR]\\cipd\\win_sdk\\Include\\10.0.17134.0\\shared;[START_DIR]\\cipd\\win_sdk\\Include\\10.0.17134.0\\winrt",
-      "VSINSTALLDIR": "[START_DIR]\\cipd"
+      "VSINSTALLDIR": "[START_DIR]\\cipd\\windows_sdk"
     },
     "env_prefixes": {
       "PATH": [
-        "[START_DIR]\\cipd\\win_sdk\\bin\\10.0.17134.0\\x64",
-        "[START_DIR]\\cipd\\VC\\Tools\\MSVC\\14.14.26428\\bin\\HostX64\\x64"
+        "[START_DIR]\\cipd\\windows_sdk\\win_sdk\\bin\\x64"
       ]
     },
     "name": "release.build.ninja",
@@ -311,6 +356,7 @@
       "/im",
       "mspdbsrv.exe"
     ],
+    "cwd": "[START_DIR]\\gn",
     "name": "release.build.taskkill mspdbsrv",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@"
diff --git a/infra/recipes/gn.py b/infra/recipes/gn.py
index d8ccd79..d0a195c 100644
--- a/infra/recipes/gn.py
+++ b/infra/recipes/gn.py
@@ -13,6 +13,7 @@
     'recipe_engine/properties',
     'recipe_engine/python',
     'recipe_engine/step',
+    'windows_sdk',
 ]
 
 
@@ -49,52 +50,11 @@
             'fuchsia/clang/${platform}': 'goma',
         },
         'mac': {},
-        'win': {
-            'chrome_internal/third_party/sdk/windows': 'uploaded:2018-06-13',
-        },
+        'win': {},
     }[api.platform.name])
     api.cipd.ensure(cipd_dir, packages)
 
-  environ_pre = {}
-
-  win_env = {}
-  if api.platform.name == 'win':
-    # Load .../win_sdk/bin/SetEnv.x64.json to extract the required environment.
-    # It contains a dict that looks like this:
-    # {
-    #   "env": {
-    #     "VAR": [["..", "..", "x"], ["..", "..", "y"]],
-    #     ...
-    #   }
-    # }
-    # All these environment variables need to be added to the environment
-    # for the compiler and linker to work.
-
-    json_file = cipd_dir.join('win_sdk', 'bin', 'SetEnv.x64.json')
-    env = api.json.read('SetEnv.x64.json', json_file).json.output.get('env')
-    for k in env:
-      # recipes' Path() does not like .., ., \, or /, so this is cumbersome.
-      # What we want to do is:
-      #   [sdk_bin_dir.join(*e) for e in env[k]]
-      # Instead do that badly, and rely (but verify) on the fact that the paths
-      # are all specified relative to the root, but specified relative to
-      # win_sdk/bin (i.e. everything starts with "../../".)
-      results = []
-      for data in env[k]:
-        assert data[0] == '..' and (data[1] == '..' or data[1] == '..\\')
-        root_relative = data[2:]
-        results.append('%s' % cipd_dir.join(*root_relative))
-
-      # PATH is special-cased because we don't want to overwrite other things
-      # like C:\Windows\System32. Others are replacements because prepending
-      # doesn't necessarily makes sense, like VSINSTALLDIR.
-      if k.lower() == 'path':
-        # env_prefixes wants a list, not a string like env.
-        environ_pre[k] = results
-      else:
-        win_env[k] = ';'.join(results)
-
-  environ = {
+  env = {
       'linux': {
           'CC': cipd_dir.join('bin', 'clang'),
           'CXX': cipd_dir.join('bin', 'clang++'),
@@ -102,7 +62,7 @@
           'LDFLAGS': '-static-libstdc++ -ldl -lpthread',
       },
       'mac': {},
-      'win': win_env,
+      'win': {},
   }[api.platform.name]
 
   configs = [
@@ -119,55 +79,35 @@
   for config in configs:
     with api.step.nest(config['name']):
       with api.step.nest('build'):
-        with api.context(env=environ, env_prefixes=environ_pre, cwd=src_dir):
-          api.python('generate',
-                     src_dir.join('build', 'gen.py'),
-                     args=config['args'])
+        with api.context(
+            env=env, cwd=src_dir), api.windows_sdk(enabled=api.platform.is_win):
+          api.python(
+              'generate', src_dir.join('build', 'gen.py'), args=config['args'])
 
           # Windows requires the environment modifications when building too.
           api.step('ninja', [cipd_dir.join('ninja'), '-C', src_dir.join('out')])
 
-        if api.platform.name == 'win':
-          # Swarming won't be able to tidy up after the compiler leaves this
-          # daemon running, so we have to manually kill it.
-          api.step('taskkill mspdbsrv',
-                   ['taskkill.exe', '/f', '/t', '/im', 'mspdbsrv.exe'])
-
       api.step('test', [src_dir.join('out', 'gn_unittests')])
 
 
 def GenTests(api):
-  WIN_TOOLCHAIN_DATA = {
-    "env": {
-      "VSINSTALLDIR": [["..", "..\\"]],
-      "INCLUDE": [["..", "..", "win_sdk", "Include", "10.0.17134.0", "um"], ["..", "..", "win_sdk", "Include", "10.0.17134.0", "shared"], ["..", "..", "win_sdk", "Include", "10.0.17134.0", "winrt"]],
-      "PATH": [["..", "..", "win_sdk", "bin", "10.0.17134.0", "x64"], ["..", "..", "VC", "Tools", "MSVC", "14.14.26428", "bin", "HostX64", "x64"]],
-    }
-  }
   for platform in ('linux', 'mac', 'win'):
-    to_yield = (api.test('ci_' + platform) + api.platform.name(platform) +
-                api.properties(buildbucket={
-                  'build': {
-                    'tags': [
-                      'buildset:commit/gitiles/gn.googlesource.com/gn/+/'
-                      'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
-                    ]
-                  }
-                }))
-    if platform == 'win':
-      to_yield += api.override_step_data('SetEnv.x64.json',
-          api.json.output(WIN_TOOLCHAIN_DATA))
-    yield to_yield
+    yield (api.test('ci_' + platform) + api.platform.name(platform) +
+           api.properties(buildbucket={
+               'build': {
+                   'tags': [
+                       'buildset:commit/gitiles/gn.googlesource.com/gn/+/'
+                       'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
+                   ]
+               }
+           }))
 
-    to_yield = (api.test('cq_' + platform) + api.platform.name(platform) +
-                api.properties(buildbucket={
-                  'build': {
-                    'tags': [
-                      'buildset:patch/gerrit/gn-review.googlesource.com/1000/1',
-                    ]
-                  }
-                }))
-    if platform == 'win':
-      to_yield += api.override_step_data('SetEnv.x64.json',
-          api.json.output(WIN_TOOLCHAIN_DATA))
-    yield to_yield
+    yield (
+        api.test('cq_' + platform) + api.platform.name(platform) +
+        api.properties(buildbucket={
+            'build': {
+                'tags': [
+                    'buildset:patch/gerrit/gn-review.googlesource.com/1000/1',
+                ]
+            }
+        }))