Ensure that Xcode SDK is installed on macOS

We need to ensure that Xcode SDK is installed on macOS the same way
we ensure that Windows SDK is installed on Windows. This change also
aligns the recipe modules for the two SDKs.

Change-Id: I0a427de6397a723c8fba3255edd9fe9bf78e5948
Reviewed-on: https://gn-review.googlesource.com/c/2960
Reviewed-by: Scott Graham <scottmg@chromium.org>
diff --git a/infra/README.recipes.md b/infra/README.recipes.md
index 46036ee..d8005aa 100644
--- a/infra/README.recipes.md
+++ b/infra/README.recipes.md
@@ -3,13 +3,61 @@
 ## Table of Contents
 
 **[Recipe Modules](#Recipe-Modules)**
+  * [macos_sdk](#recipe_modules-macos_sdk) &mdash; The `macos_sdk` module provides safe functions to access a semi-hermetic XCode installation.
   * [windows_sdk](#recipe_modules-windows_sdk)
 
 **[Recipes](#Recipes)**
   * [gn](#recipes-gn) &mdash; Recipe for building GN.
+  * [macos_sdk:examples/full](#recipes-macos_sdk_examples_full)
   * [windows_sdk:examples/full](#recipes-windows_sdk_examples_full)
 ## Recipe Modules
 
+### *recipe_modules* / [macos\_sdk](/infra/recipe_modules/macos_sdk)
+
+[DEPS](/infra/recipe_modules/macos_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]
+
+The `macos_sdk` module provides safe functions to access a semi-hermetic
+XCode installation.
+
+Available only to Google-run bots.
+
+#### **class [MacOSSDKApi](/infra/recipe_modules/macos_sdk/api.py#14)([RecipeApi][recipe_engine/wkt/RecipeApi]):**
+
+API for using OS X SDK distributed via CIPD.
+
+&emsp; **@contextmanager**<br>&mdash; **def [\_\_call\_\_](/infra/recipe_modules/macos_sdk/api.py#24)(self):**
+
+Sets up the XCode SDK environment.
+
+This call is a no-op on non-Mac platforms.
+
+This will deploy the helper tool and the XCode.app bundle at
+`[START_DIR]/cache/macos_sdk`.
+
+To avoid machines rebuilding these on every run, set up a named cache in
+your cr-buildbucket.cfg file like:
+
+    caches: {
+      # Cache for mac_toolchain tool and XCode.app
+      name: "macos_sdk"
+      path: "macos_sdk"
+    }
+
+If you have builders which e.g. use a non-current SDK, you can give them
+a uniqely named cache:
+
+    caches: {
+      # Cache for N-1 version mac_toolchain tool and XCode.app
+      name: "macos_sdk_old"
+      path: "macos_sdk"
+    }
+
+Usage:
+  with api.macos_sdk():
+    # sdk with mac build bits
+
+Raises:
+    StepFailure or InfraFailure.
 ### *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]
@@ -18,16 +66,11 @@
 
 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):**
+&emsp; **@contextmanager**<br>&mdash; **def [\_\_call\_\_](/infra/recipe_modules/windows_sdk/api.py#19)(self):**
 
-Setups the SDK environment when enabled.
+Setups the Windows SDK environment.
 
-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.
+This call is a no-op on non-Windows platforms.
 
 Raises:
     StepFailure or InfraFailure.
@@ -35,11 +78,16 @@
 
 ### *recipes* / [gn](/infra/recipes/gn.py)
 
-[DEPS](/infra/recipes/gn.py#8): [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/file][recipe_engine/recipe_modules/file], [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/raw\_io][recipe_engine/recipe_modules/raw_io], [recipe\_engine/step][recipe_engine/recipe_modules/step]
+[DEPS](/infra/recipes/gn.py#8): [macos\_sdk](#recipe_modules-macos_sdk), [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/file][recipe_engine/recipe_modules/file], [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/raw\_io][recipe_engine/recipe_modules/raw_io], [recipe\_engine/step][recipe_engine/recipe_modules/step]
 
 Recipe for building GN.
 
-&mdash; **def [RunSteps](/infra/recipes/gn.py#28)(api, repository):**
+&mdash; **def [RunSteps](/infra/recipes/gn.py#29)(api, repository):**
+### *recipes* / [macos\_sdk:examples/full](/infra/recipe_modules/macos_sdk/examples/full.py)
+
+[DEPS](/infra/recipe_modules/macos_sdk/examples/full.py#5): [macos\_sdk](#recipe_modules-macos_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/macos_sdk/examples/full.py#13)(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]
diff --git a/infra/recipe_modules/macos_sdk/__init__.py b/infra/recipe_modules/macos_sdk/__init__.py
new file mode 100644
index 0000000..2fe9d51
--- /dev/null
+++ b/infra/recipe_modules/macos_sdk/__init__.py
@@ -0,0 +1,44 @@
+# 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/macos_sdk':
+        Property(
+            help='Properties specifically for the macos_sdk module.',
+            param_name='sdk_properties',
+            kind=ConfigGroup(  # pylint: disable=line-too-long
+                # XCode build version number. Internally maps to an XCode build id like
+                # '9c40b'. See
+                #
+                #   https://chrome-infra-packages.appspot.com/p/infra_internal/ios/xcode/mac/+/
+                #
+                # For an up to date list of the latest SDK builds.
+                sdk_version=Single(str),
+
+                # The CIPD toolchain tool package and version.
+                tool_pkg=Single(str),
+                tool_ver=Single(str),
+            ),
+            default={
+                'sdk_version':
+                    '9c40b',
+                'tool_package':
+                    'infra/tools/mac_toolchain/${platform}',
+                'tool_version':
+                    'git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a',
+            },
+        )
+}
diff --git a/infra/recipe_modules/macos_sdk/api.py b/infra/recipe_modules/macos_sdk/api.py
new file mode 100644
index 0000000..23d0066
--- /dev/null
+++ b/infra/recipe_modules/macos_sdk/api.py
@@ -0,0 +1,92 @@
+# 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.
+"""The `macos_sdk` module provides safe functions to access a semi-hermetic
+XCode installation.
+
+Available only to Google-run bots."""
+
+from contextlib import contextmanager
+
+from recipe_engine import recipe_api
+
+
+class MacOSSDKApi(recipe_api.RecipeApi):
+  """API for using OS X SDK distributed via CIPD."""
+
+  def __init__(self, sdk_properties, *args, **kwargs):
+    super(MacOSSDKApi, self).__init__(*args, **kwargs)
+
+    self._sdk_version = sdk_properties['sdk_version'].lower()
+    self._tool_package = sdk_properties['tool_package']
+    self._tool_version = sdk_properties['tool_version']
+
+  @contextmanager
+  def __call__(self):
+    """Sets up the XCode SDK environment.
+
+    This call is a no-op on non-Mac platforms.
+
+    This will deploy the helper tool and the XCode.app bundle at
+    `[START_DIR]/cache/macos_sdk`.
+
+    To avoid machines rebuilding these on every run, set up a named cache in
+    your cr-buildbucket.cfg file like:
+
+        caches: {
+          # Cache for mac_toolchain tool and XCode.app
+          name: "macos_sdk"
+          path: "macos_sdk"
+        }
+
+    If you have builders which e.g. use a non-current SDK, you can give them
+    a uniqely named cache:
+
+        caches: {
+          # Cache for N-1 version mac_toolchain tool and XCode.app
+          name: "macos_sdk_old"
+          path: "macos_sdk"
+        }
+
+    Usage:
+      with api.macos_sdk():
+        # sdk with mac build bits
+
+    Raises:
+        StepFailure or InfraFailure.
+    """
+    if not self.m.platform.is_mac:
+      yield
+      return
+
+    try:
+      with self.m.context(infra_steps=True):
+        sdk_dir = self._ensure_sdk()
+        self.m.step('select XCode',
+                    ['sudo', 'xcode-select', '--switch', sdk_dir])
+      yield
+    finally:
+      with self.m.context(infra_steps=True):
+        self.m.step('reset XCode', ['sudo', 'xcode-select', '--reset'])
+
+  def _ensure_sdk(self):
+    """Ensures the mac_toolchain tool and MacOS SDK packages are installed.
+
+    Returns Path to the installed sdk app bundle."""
+    cache_dir = self.m.path['cache'].join('macos_sdk')
+    pkgs = self.m.cipd.EnsureFile()
+    pkgs.add_package(self._tool_package, self._tool_version)
+    self.m.cipd.ensure(cache_dir, pkgs)
+
+    sdk_dir = cache_dir.join('XCode.app')
+    self.m.step('install xcode', [
+        cache_dir.join('mac_toolchain'),
+        'install',
+        '-kind',
+        'mac',
+        '-xcode-version',
+        self._sdk_version,
+        '-output-dir',
+        sdk_dir,
+    ])
+    return sdk_dir
diff --git a/infra/recipe_modules/macos_sdk/examples/full.expected/linux.json b/infra/recipe_modules/macos_sdk/examples/full.expected/linux.json
new file mode 100644
index 0000000..51d9430
--- /dev/null
+++ b/infra/recipe_modules/macos_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/macos_sdk/examples/full.expected/mac.json b/infra/recipe_modules/macos_sdk/examples/full.expected/mac.json
new file mode 100644
index 0000000..4c42f24
--- /dev/null
+++ b/infra/recipe_modules/macos_sdk/examples/full.expected/mac.json
@@ -0,0 +1,83 @@
+[
+  {
+    "cmd": [
+      "cipd",
+      "ensure",
+      "-root",
+      "[CACHE]/macos_sdk",
+      "-ensure-file",
+      "infra/tools/mac_toolchain/${platform} git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a",
+      "-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-git_revision:796\", @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"package\": \"infra/tools/mac_toolchain/${platform}\"@@@",
+      "@@@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": [
+      "[CACHE]/macos_sdk/mac_toolchain",
+      "install",
+      "-kind",
+      "mac",
+      "-xcode-version",
+      "9c40b",
+      "-output-dir",
+      "[CACHE]/macos_sdk/XCode.app"
+    ],
+    "infra_step": true,
+    "name": "install xcode"
+  },
+  {
+    "cmd": [
+      "sudo",
+      "xcode-select",
+      "--switch",
+      "[CACHE]/macos_sdk/XCode.app"
+    ],
+    "infra_step": true,
+    "name": "select XCode"
+  },
+  {
+    "cmd": [
+      "gn",
+      "gen",
+      "out/Release"
+    ],
+    "name": "gn"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "out/Release"
+    ],
+    "name": "ninja"
+  },
+  {
+    "cmd": [
+      "sudo",
+      "xcode-select",
+      "--reset"
+    ],
+    "infra_step": true,
+    "name": "reset XCode"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/recipe_modules/macos_sdk/examples/full.expected/win.json b/infra/recipe_modules/macos_sdk/examples/full.expected/win.json
new file mode 100644
index 0000000..51d9430
--- /dev/null
+++ b/infra/recipe_modules/macos_sdk/examples/full.expected/win.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/macos_sdk/examples/full.py b/infra/recipe_modules/macos_sdk/examples/full.py
new file mode 100644
index 0000000..5546ad5
--- /dev/null
+++ b/infra/recipe_modules/macos_sdk/examples/full.py
@@ -0,0 +1,22 @@
+# 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 = [
+    'macos_sdk',
+    'recipe_engine/platform',
+    'recipe_engine/properties',
+    'recipe_engine/step',
+]
+
+
+def RunSteps(api):
+  with api.macos_sdk():
+    api.step('gn', ['gn', 'gen', 'out/Release'])
+    api.step('ninja', ['ninja', '-C', 'out/Release'])
+
+
+def GenTests(api):
+  for platform in ('linux', 'mac', 'win'):
+    yield (api.test(platform) + api.platform.name(platform) +
+           api.properties.generic(buildername='test_builder'))
diff --git a/infra/recipe_modules/windows_sdk/__init__.py b/infra/recipe_modules/windows_sdk/__init__.py
index 6183f2b..3c790de 100644
--- a/infra/recipe_modules/windows_sdk/__init__.py
+++ b/infra/recipe_modules/windows_sdk/__init__.py
@@ -17,11 +17,15 @@
 PROPERTIES = {
     '$gn/windows_sdk':
         Property(
-            help='Properties specifically for the infra windows_sdk module.',
+            help='Properties specifically for the 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'},
+                # The CIPD package and version.
+                sdk_package=Single(str),
+                sdk_version=Single(str)),
+            default={
+                'sdk_package': 'chrome_internal/third_party/sdk/windows',
+                'sdk_version': 'uploaded:2018-06-13'
+            },
         )
 }
diff --git a/infra/recipe_modules/windows_sdk/api.py b/infra/recipe_modules/windows_sdk/api.py
index 7c4067d..d47f984 100644
--- a/infra/recipe_modules/windows_sdk/api.py
+++ b/infra/recipe_modules/windows_sdk/api.py
@@ -13,39 +13,34 @@
   def __init__(self, sdk_properties, *args, **kwargs):
     super(WindowsSDKApi, self).__init__(*args, **kwargs)
 
-    self._sdk_properties = sdk_properties
+    self._sdk_package = sdk_properties['sdk_package']
+    self._sdk_version = sdk_properties['sdk_version']
 
   @contextmanager
-  def __call__(self, path=None, version=None, enabled=True):
-    """Setups the SDK environment when enabled.
+  def __call__(self):
+    """Setups the Windows SDK environment.
 
-    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.
+    This call is a no-op on non-Windows platforms.
 
     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:
+    if not self.m.platform.is_win:
       yield
+      return
 
-  def _ensure_sdk(self, sdk_dir, sdk_version):
+    try:
+      with self.m.context(infra_steps=True):
+        sdk_dir = self._ensure_sdk()
+      with self.m.context(**self._sdk_env(sdk_dir)):
+        yield
+    finally:
+      # 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'])
+
+  def _ensure_sdk(self):
     """Ensures the Windows SDK CIPD package is installed.
 
     Returns the directory where the SDK package has been installed.
@@ -54,11 +49,11 @@
       path (path): Path to a directory.
       version (str): CIPD instance ID, tag or ref.
     """
-    with self.m.context(infra_steps=True):
-      pkgs = self.m.cipd.EnsureFile()
-      pkgs.add_package('chrome_internal/third_party/sdk/windows', sdk_version)
-      self.m.cipd.ensure(sdk_dir, pkgs)
-      return sdk_dir
+    sdk_dir = self.m.path['cache'].join('windows_sdk')
+    pkgs = self.m.cipd.EnsureFile()
+    pkgs.add_package(self._sdk_package, self._sdk_version)
+    self.m.cipd.ensure(sdk_dir, pkgs)
+    return sdk_dir
 
   def _sdk_env(self, sdk_dir):
     """Constructs the environment for the SDK.
diff --git a/infra/recipe_modules/windows_sdk/examples/full.expected/win.json b/infra/recipe_modules/windows_sdk/examples/full.expected/win.json
index d9a7a65..092354d 100644
--- a/infra/recipe_modules/windows_sdk/examples/full.expected/win.json
+++ b/infra/recipe_modules/windows_sdk/examples/full.expected/win.json
@@ -4,7 +4,7 @@
       "cipd.bat",
       "ensure",
       "-root",
-      "[START_DIR]\\cipd\\windows_sdk",
+      "[CACHE]\\windows_sdk",
       "-ensure-file",
       "chrome_internal/third_party/sdk/windows uploaded:2018-06-13",
       "-json-output",
@@ -31,7 +31,7 @@
       "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",
+      "[CACHE]\\windows_sdk\\win_sdk\\bin\\SetEnv.x64.json",
       "/path/to/tmp/json"
     ],
     "name": "read SetEnv.x64.json",
@@ -65,11 +65,11 @@
       "out/Release"
     ],
     "env": {
-      "VSINSTALLDIR": "[START_DIR]\\cipd\\windows_sdk"
+      "VSINSTALLDIR": "[CACHE]\\windows_sdk"
     },
     "env_prefixes": {
       "PATH": [
-        "[START_DIR]\\cipd\\windows_sdk\\win_sdk\\bin\\x64"
+        "[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
       ]
     },
     "name": "gn"
@@ -81,11 +81,11 @@
       "out/Release"
     ],
     "env": {
-      "VSINSTALLDIR": "[START_DIR]\\cipd\\windows_sdk"
+      "VSINSTALLDIR": "[CACHE]\\windows_sdk"
     },
     "env_prefixes": {
       "PATH": [
-        "[START_DIR]\\cipd\\windows_sdk\\win_sdk\\bin\\x64"
+        "[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
       ]
     },
     "name": "ninja"
diff --git a/infra/recipe_modules/windows_sdk/examples/full.py b/infra/recipe_modules/windows_sdk/examples/full.py
index 5e8e503..902c491 100644
--- a/infra/recipe_modules/windows_sdk/examples/full.py
+++ b/infra/recipe_modules/windows_sdk/examples/full.py
@@ -11,7 +11,7 @@
 
 
 def RunSteps(api):
-  with api.windows_sdk(enabled=api.platform.is_win):
+  with api.windows_sdk():
     api.step('gn', ['gn', 'gen', 'out/Release'])
     api.step('ninja', ['ninja', '-C', 'out/Release'])
 
diff --git a/infra/recipes/gn.expected/ci_linux.json b/infra/recipes/gn.expected/ci_linux.json
new file mode 100644
index 0000000..45e521b
--- /dev/null
+++ b/infra/recipes/gn.expected/ci_linux.json
@@ -0,0 +1,217 @@
+[
+  {
+    "cmd": [],
+    "name": "git"
+  },
+  {
+    "cmd": [
+      "git",
+      "init",
+      "[START_DIR]/gn"
+    ],
+    "infra_step": true,
+    "name": "git.init",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "fetch",
+      "--tags",
+      "https://gn.googlesource.com/gn",
+      "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+    ],
+    "cwd": "[START_DIR]/gn",
+    "infra_step": true,
+    "name": "git.fetch",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "checkout",
+      "FETCH_HEAD"
+    ],
+    "cwd": "[START_DIR]/gn",
+    "infra_step": true,
+    "name": "git.checkout",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "cipd",
+      "ensure",
+      "-root",
+      "[START_DIR]/cipd",
+      "-ensure-file",
+      "infra/ninja/${platform} version:1.8.2\nfuchsia/clang/${platform} goma",
+      "-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-goma------------\", @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"package\": \"fuchsia/clang/${platform}\"@@@",
+      "@@@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@      }@@@",
+      "@@@STEP_LOG_LINE@json.output@    ]@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "debug"
+  },
+  {
+    "cmd": [],
+    "name": "debug.build",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/gn/build/gen.py",
+      "-d"
+    ],
+    "cwd": "[START_DIR]/gn",
+    "env": {
+      "AR": "[START_DIR]/cipd/bin/llvm-ar",
+      "CC": "[START_DIR]/cipd/bin/clang",
+      "CXX": "[START_DIR]/cipd/bin/clang++",
+      "LDFLAGS": "-static-libstdc++ -ldl -lpthread"
+    },
+    "name": "debug.build.generate",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/cipd/ninja",
+      "-C",
+      "[START_DIR]/gn/out"
+    ],
+    "cwd": "[START_DIR]/gn",
+    "env": {
+      "AR": "[START_DIR]/cipd/bin/llvm-ar",
+      "CC": "[START_DIR]/cipd/bin/clang",
+      "CXX": "[START_DIR]/cipd/bin/clang++",
+      "LDFLAGS": "-static-libstdc++ -ldl -lpthread"
+    },
+    "name": "debug.build.ninja",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/gn/out/gn_unittests"
+    ],
+    "name": "debug.test",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "release"
+  },
+  {
+    "cmd": [],
+    "name": "release.build",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/gn/build/gen.py"
+    ],
+    "cwd": "[START_DIR]/gn",
+    "env": {
+      "AR": "[START_DIR]/cipd/bin/llvm-ar",
+      "CC": "[START_DIR]/cipd/bin/clang",
+      "CXX": "[START_DIR]/cipd/bin/clang++",
+      "LDFLAGS": "-static-libstdc++ -ldl -lpthread"
+    },
+    "name": "release.build.generate",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/cipd/ninja",
+      "-C",
+      "[START_DIR]/gn/out"
+    ],
+    "cwd": "[START_DIR]/gn",
+    "env": {
+      "AR": "[START_DIR]/cipd/bin/llvm-ar",
+      "CC": "[START_DIR]/cipd/bin/clang",
+      "CXX": "[START_DIR]/cipd/bin/clang++",
+      "LDFLAGS": "-static-libstdc++ -ldl -lpthread"
+    },
+    "name": "release.build.ninja",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/gn/out/gn_unittests"
+    ],
+    "name": "release.test",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "cipd",
+      "pkg-build",
+      "-pkg-def",
+      "{\"data\": [{\"file\": \"gn\"}, {\"version_file\": \".versions/gn.cipd_version\"}], \"install_mode\": \"copy\", \"package\": \"gn/gn/linux-amd64\", \"root\": \"[START_DIR]/gn/out\"}",
+      "-out",
+      "[CLEANUP]/gn.cipd",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "name": "build gn/gn/linux-amd64",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"result\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"instance_id\": \"40-chars-fake-of-the-package-instance_id\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"package\": \"gn/gn/linux-amd64\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/recipes/gn.expected/ci_mac.json b/infra/recipes/gn.expected/ci_mac.json
new file mode 100644
index 0000000..fbd3119
--- /dev/null
+++ b/infra/recipes/gn.expected/ci_mac.json
@@ -0,0 +1,337 @@
+[
+  {
+    "cmd": [],
+    "name": "git"
+  },
+  {
+    "cmd": [
+      "git",
+      "init",
+      "[START_DIR]/gn"
+    ],
+    "infra_step": true,
+    "name": "git.init",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "fetch",
+      "--tags",
+      "https://gn.googlesource.com/gn",
+      "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+    ],
+    "cwd": "[START_DIR]/gn",
+    "infra_step": true,
+    "name": "git.fetch",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "checkout",
+      "FETCH_HEAD"
+    ],
+    "cwd": "[START_DIR]/gn",
+    "infra_step": true,
+    "name": "git.checkout",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "cipd",
+      "ensure",
+      "-root",
+      "[START_DIR]/cipd",
+      "-ensure-file",
+      "infra/ninja/${platform} version:1.8.2",
+      "-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-version:1.8.2---\", @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"package\": \"infra/ninja/${platform}\"@@@",
+      "@@@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"
+  },
+  {
+    "cmd": [],
+    "name": "debug.build",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "cipd",
+      "ensure",
+      "-root",
+      "[CACHE]/macos_sdk",
+      "-ensure-file",
+      "infra/tools/mac_toolchain/${platform} git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a",
+      "-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-git_revision:796\", @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"package\": \"infra/tools/mac_toolchain/${platform}\"@@@",
+      "@@@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": [
+      "[CACHE]/macos_sdk/mac_toolchain",
+      "install",
+      "-kind",
+      "mac",
+      "-xcode-version",
+      "9c40b",
+      "-output-dir",
+      "[CACHE]/macos_sdk/XCode.app"
+    ],
+    "cwd": "[START_DIR]/gn",
+    "infra_step": true,
+    "name": "debug.build.install xcode",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "sudo",
+      "xcode-select",
+      "--switch",
+      "[CACHE]/macos_sdk/XCode.app"
+    ],
+    "cwd": "[START_DIR]/gn",
+    "infra_step": true,
+    "name": "debug.build.select XCode",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/gn/build/gen.py",
+      "-d"
+    ],
+    "cwd": "[START_DIR]/gn",
+    "name": "debug.build.generate",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/cipd/ninja",
+      "-C",
+      "[START_DIR]/gn/out"
+    ],
+    "cwd": "[START_DIR]/gn",
+    "name": "debug.build.ninja",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "sudo",
+      "xcode-select",
+      "--reset"
+    ],
+    "cwd": "[START_DIR]/gn",
+    "infra_step": true,
+    "name": "debug.build.reset XCode",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/gn/out/gn_unittests"
+    ],
+    "name": "debug.test",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "release"
+  },
+  {
+    "cmd": [],
+    "name": "release.build",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "cipd",
+      "ensure",
+      "-root",
+      "[CACHE]/macos_sdk",
+      "-ensure-file",
+      "infra/tools/mac_toolchain/${platform} git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a",
+      "-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-git_revision:796\", @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"package\": \"infra/tools/mac_toolchain/${platform}\"@@@",
+      "@@@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": [
+      "[CACHE]/macos_sdk/mac_toolchain",
+      "install",
+      "-kind",
+      "mac",
+      "-xcode-version",
+      "9c40b",
+      "-output-dir",
+      "[CACHE]/macos_sdk/XCode.app"
+    ],
+    "cwd": "[START_DIR]/gn",
+    "infra_step": true,
+    "name": "release.build.install xcode",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "sudo",
+      "xcode-select",
+      "--switch",
+      "[CACHE]/macos_sdk/XCode.app"
+    ],
+    "cwd": "[START_DIR]/gn",
+    "infra_step": true,
+    "name": "release.build.select XCode",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/gn/build/gen.py"
+    ],
+    "cwd": "[START_DIR]/gn",
+    "name": "release.build.generate",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/cipd/ninja",
+      "-C",
+      "[START_DIR]/gn/out"
+    ],
+    "cwd": "[START_DIR]/gn",
+    "name": "release.build.ninja",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "sudo",
+      "xcode-select",
+      "--reset"
+    ],
+    "cwd": "[START_DIR]/gn",
+    "infra_step": true,
+    "name": "release.build.reset XCode",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/gn/out/gn_unittests"
+    ],
+    "name": "release.test",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "cipd",
+      "pkg-build",
+      "-pkg-def",
+      "{\"data\": [{\"file\": \"gn\"}, {\"version_file\": \".versions/gn.cipd_version\"}], \"install_mode\": \"copy\", \"package\": \"gn/gn/mac-amd64\", \"root\": \"[START_DIR]/gn/out\"}",
+      "-out",
+      "[CLEANUP]/gn.cipd",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "name": "build gn/gn/mac-amd64",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"result\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"instance_id\": \"40-chars-fake-of-the-package-instance_id\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"package\": \"gn/gn/mac-amd64\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/recipes/gn.expected/ci.json b/infra/recipes/gn.expected/ci_win.json
similarity index 93%
rename from infra/recipes/gn.expected/ci.json
rename to infra/recipes/gn.expected/ci_win.json
index e2e8933..dacb8eb 100644
--- a/infra/recipes/gn.expected/ci.json
+++ b/infra/recipes/gn.expected/ci_win.json
@@ -86,7 +86,7 @@
       "cipd.bat",
       "ensure",
       "-root",
-      "[START_DIR]\\cipd\\windows_sdk",
+      "[CACHE]\\windows_sdk",
       "-ensure-file",
       "chrome_internal/third_party/sdk/windows uploaded:2018-06-13",
       "-json-output",
@@ -115,7 +115,7 @@
       "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",
+      "[CACHE]\\windows_sdk\\win_sdk\\bin\\SetEnv.x64.json",
       "/path/to/tmp/json"
     ],
     "cwd": "[START_DIR]\\gn",
@@ -153,11 +153,11 @@
     ],
     "cwd": "[START_DIR]\\gn",
     "env": {
-      "VSINSTALLDIR": "[START_DIR]\\cipd\\windows_sdk"
+      "VSINSTALLDIR": "[CACHE]\\windows_sdk"
     },
     "env_prefixes": {
       "PATH": [
-        "[START_DIR]\\cipd\\windows_sdk\\win_sdk\\bin\\x64"
+        "[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
       ]
     },
     "name": "debug.build.generate",
@@ -173,11 +173,11 @@
     ],
     "cwd": "[START_DIR]\\gn",
     "env": {
-      "VSINSTALLDIR": "[START_DIR]\\cipd\\windows_sdk"
+      "VSINSTALLDIR": "[CACHE]\\windows_sdk"
     },
     "env_prefixes": {
       "PATH": [
-        "[START_DIR]\\cipd\\windows_sdk\\win_sdk\\bin\\x64"
+        "[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
       ]
     },
     "name": "debug.build.ninja",
@@ -224,7 +224,7 @@
       "cipd.bat",
       "ensure",
       "-root",
-      "[START_DIR]\\cipd\\windows_sdk",
+      "[CACHE]\\windows_sdk",
       "-ensure-file",
       "chrome_internal/third_party/sdk/windows uploaded:2018-06-13",
       "-json-output",
@@ -253,7 +253,7 @@
       "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",
+      "[CACHE]\\windows_sdk\\win_sdk\\bin\\SetEnv.x64.json",
       "/path/to/tmp/json"
     ],
     "cwd": "[START_DIR]\\gn",
@@ -290,11 +290,11 @@
     ],
     "cwd": "[START_DIR]\\gn",
     "env": {
-      "VSINSTALLDIR": "[START_DIR]\\cipd\\windows_sdk"
+      "VSINSTALLDIR": "[CACHE]\\windows_sdk"
     },
     "env_prefixes": {
       "PATH": [
-        "[START_DIR]\\cipd\\windows_sdk\\win_sdk\\bin\\x64"
+        "[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
       ]
     },
     "name": "release.build.generate",
@@ -310,11 +310,11 @@
     ],
     "cwd": "[START_DIR]\\gn",
     "env": {
-      "VSINSTALLDIR": "[START_DIR]\\cipd\\windows_sdk"
+      "VSINSTALLDIR": "[CACHE]\\windows_sdk"
     },
     "env_prefixes": {
       "PATH": [
-        "[START_DIR]\\cipd\\windows_sdk\\win_sdk\\bin\\x64"
+        "[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
       ]
     },
     "name": "release.build.ninja",
diff --git a/infra/recipes/gn.expected/cq_linux.json b/infra/recipes/gn.expected/cq_linux.json
new file mode 100644
index 0000000..f7309c0
--- /dev/null
+++ b/infra/recipes/gn.expected/cq_linux.json
@@ -0,0 +1,222 @@
+[
+  {
+    "cmd": [],
+    "name": "git"
+  },
+  {
+    "cmd": [
+      "git",
+      "init",
+      "[START_DIR]/gn"
+    ],
+    "infra_step": true,
+    "name": "git.init",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "fetch",
+      "--tags",
+      "https://gn.googlesource.com/gn",
+      "refs/heads/master"
+    ],
+    "cwd": "[START_DIR]/gn",
+    "infra_step": true,
+    "name": "git.fetch",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "checkout",
+      "FETCH_HEAD"
+    ],
+    "cwd": "[START_DIR]/gn",
+    "infra_step": true,
+    "name": "git.checkout",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "fetch",
+      "https://gn.googlesource.com/gn",
+      "refs/changes/00/1000/1"
+    ],
+    "cwd": "[START_DIR]/gn",
+    "infra_step": true,
+    "name": "git.fetch 1000/1",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "cherry-pick",
+      "FETCH_HEAD"
+    ],
+    "cwd": "[START_DIR]/gn",
+    "infra_step": true,
+    "name": "git.cherry-pick 1000/1",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "cipd",
+      "ensure",
+      "-root",
+      "[START_DIR]/cipd",
+      "-ensure-file",
+      "infra/ninja/${platform} version:1.8.2\nfuchsia/clang/${platform} goma",
+      "-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-goma------------\", @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"package\": \"fuchsia/clang/${platform}\"@@@",
+      "@@@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@      }@@@",
+      "@@@STEP_LOG_LINE@json.output@    ]@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "debug"
+  },
+  {
+    "cmd": [],
+    "name": "debug.build",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/gn/build/gen.py",
+      "-d"
+    ],
+    "cwd": "[START_DIR]/gn",
+    "env": {
+      "AR": "[START_DIR]/cipd/bin/llvm-ar",
+      "CC": "[START_DIR]/cipd/bin/clang",
+      "CXX": "[START_DIR]/cipd/bin/clang++",
+      "LDFLAGS": "-static-libstdc++ -ldl -lpthread"
+    },
+    "name": "debug.build.generate",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/cipd/ninja",
+      "-C",
+      "[START_DIR]/gn/out"
+    ],
+    "cwd": "[START_DIR]/gn",
+    "env": {
+      "AR": "[START_DIR]/cipd/bin/llvm-ar",
+      "CC": "[START_DIR]/cipd/bin/clang",
+      "CXX": "[START_DIR]/cipd/bin/clang++",
+      "LDFLAGS": "-static-libstdc++ -ldl -lpthread"
+    },
+    "name": "debug.build.ninja",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/gn/out/gn_unittests"
+    ],
+    "name": "debug.test",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "release"
+  },
+  {
+    "cmd": [],
+    "name": "release.build",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/gn/build/gen.py"
+    ],
+    "cwd": "[START_DIR]/gn",
+    "env": {
+      "AR": "[START_DIR]/cipd/bin/llvm-ar",
+      "CC": "[START_DIR]/cipd/bin/clang",
+      "CXX": "[START_DIR]/cipd/bin/clang++",
+      "LDFLAGS": "-static-libstdc++ -ldl -lpthread"
+    },
+    "name": "release.build.generate",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/cipd/ninja",
+      "-C",
+      "[START_DIR]/gn/out"
+    ],
+    "cwd": "[START_DIR]/gn",
+    "env": {
+      "AR": "[START_DIR]/cipd/bin/llvm-ar",
+      "CC": "[START_DIR]/cipd/bin/clang",
+      "CXX": "[START_DIR]/cipd/bin/clang++",
+      "LDFLAGS": "-static-libstdc++ -ldl -lpthread"
+    },
+    "name": "release.build.ninja",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/gn/out/gn_unittests"
+    ],
+    "name": "release.test",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/recipes/gn.expected/cq_mac.json b/infra/recipes/gn.expected/cq_mac.json
new file mode 100644
index 0000000..1b0e09c
--- /dev/null
+++ b/infra/recipes/gn.expected/cq_mac.json
@@ -0,0 +1,342 @@
+[
+  {
+    "cmd": [],
+    "name": "git"
+  },
+  {
+    "cmd": [
+      "git",
+      "init",
+      "[START_DIR]/gn"
+    ],
+    "infra_step": true,
+    "name": "git.init",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "fetch",
+      "--tags",
+      "https://gn.googlesource.com/gn",
+      "refs/heads/master"
+    ],
+    "cwd": "[START_DIR]/gn",
+    "infra_step": true,
+    "name": "git.fetch",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "checkout",
+      "FETCH_HEAD"
+    ],
+    "cwd": "[START_DIR]/gn",
+    "infra_step": true,
+    "name": "git.checkout",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "fetch",
+      "https://gn.googlesource.com/gn",
+      "refs/changes/00/1000/1"
+    ],
+    "cwd": "[START_DIR]/gn",
+    "infra_step": true,
+    "name": "git.fetch 1000/1",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "cherry-pick",
+      "FETCH_HEAD"
+    ],
+    "cwd": "[START_DIR]/gn",
+    "infra_step": true,
+    "name": "git.cherry-pick 1000/1",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "cipd",
+      "ensure",
+      "-root",
+      "[START_DIR]/cipd",
+      "-ensure-file",
+      "infra/ninja/${platform} version:1.8.2",
+      "-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-version:1.8.2---\", @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"package\": \"infra/ninja/${platform}\"@@@",
+      "@@@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"
+  },
+  {
+    "cmd": [],
+    "name": "debug.build",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "cipd",
+      "ensure",
+      "-root",
+      "[CACHE]/macos_sdk",
+      "-ensure-file",
+      "infra/tools/mac_toolchain/${platform} git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a",
+      "-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-git_revision:796\", @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"package\": \"infra/tools/mac_toolchain/${platform}\"@@@",
+      "@@@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": [
+      "[CACHE]/macos_sdk/mac_toolchain",
+      "install",
+      "-kind",
+      "mac",
+      "-xcode-version",
+      "9c40b",
+      "-output-dir",
+      "[CACHE]/macos_sdk/XCode.app"
+    ],
+    "cwd": "[START_DIR]/gn",
+    "infra_step": true,
+    "name": "debug.build.install xcode",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "sudo",
+      "xcode-select",
+      "--switch",
+      "[CACHE]/macos_sdk/XCode.app"
+    ],
+    "cwd": "[START_DIR]/gn",
+    "infra_step": true,
+    "name": "debug.build.select XCode",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/gn/build/gen.py",
+      "-d"
+    ],
+    "cwd": "[START_DIR]/gn",
+    "name": "debug.build.generate",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/cipd/ninja",
+      "-C",
+      "[START_DIR]/gn/out"
+    ],
+    "cwd": "[START_DIR]/gn",
+    "name": "debug.build.ninja",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "sudo",
+      "xcode-select",
+      "--reset"
+    ],
+    "cwd": "[START_DIR]/gn",
+    "infra_step": true,
+    "name": "debug.build.reset XCode",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/gn/out/gn_unittests"
+    ],
+    "name": "debug.test",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "release"
+  },
+  {
+    "cmd": [],
+    "name": "release.build",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "cipd",
+      "ensure",
+      "-root",
+      "[CACHE]/macos_sdk",
+      "-ensure-file",
+      "infra/tools/mac_toolchain/${platform} git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a",
+      "-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-git_revision:796\", @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"package\": \"infra/tools/mac_toolchain/${platform}\"@@@",
+      "@@@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": [
+      "[CACHE]/macos_sdk/mac_toolchain",
+      "install",
+      "-kind",
+      "mac",
+      "-xcode-version",
+      "9c40b",
+      "-output-dir",
+      "[CACHE]/macos_sdk/XCode.app"
+    ],
+    "cwd": "[START_DIR]/gn",
+    "infra_step": true,
+    "name": "release.build.install xcode",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "sudo",
+      "xcode-select",
+      "--switch",
+      "[CACHE]/macos_sdk/XCode.app"
+    ],
+    "cwd": "[START_DIR]/gn",
+    "infra_step": true,
+    "name": "release.build.select XCode",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/gn/build/gen.py"
+    ],
+    "cwd": "[START_DIR]/gn",
+    "name": "release.build.generate",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/cipd/ninja",
+      "-C",
+      "[START_DIR]/gn/out"
+    ],
+    "cwd": "[START_DIR]/gn",
+    "name": "release.build.ninja",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "sudo",
+      "xcode-select",
+      "--reset"
+    ],
+    "cwd": "[START_DIR]/gn",
+    "infra_step": true,
+    "name": "release.build.reset XCode",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/gn/out/gn_unittests"
+    ],
+    "name": "release.test",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/recipes/gn.expected/cq.json b/infra/recipes/gn.expected/cq_win.json
similarity index 93%
rename from infra/recipes/gn.expected/cq.json
rename to infra/recipes/gn.expected/cq_win.json
index e9b8b25..57dea98 100644
--- a/infra/recipes/gn.expected/cq.json
+++ b/infra/recipes/gn.expected/cq_win.json
@@ -113,7 +113,7 @@
       "cipd.bat",
       "ensure",
       "-root",
-      "[START_DIR]\\cipd\\windows_sdk",
+      "[CACHE]\\windows_sdk",
       "-ensure-file",
       "chrome_internal/third_party/sdk/windows uploaded:2018-06-13",
       "-json-output",
@@ -142,7 +142,7 @@
       "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",
+      "[CACHE]\\windows_sdk\\win_sdk\\bin\\SetEnv.x64.json",
       "/path/to/tmp/json"
     ],
     "cwd": "[START_DIR]\\gn",
@@ -180,11 +180,11 @@
     ],
     "cwd": "[START_DIR]\\gn",
     "env": {
-      "VSINSTALLDIR": "[START_DIR]\\cipd\\windows_sdk"
+      "VSINSTALLDIR": "[CACHE]\\windows_sdk"
     },
     "env_prefixes": {
       "PATH": [
-        "[START_DIR]\\cipd\\windows_sdk\\win_sdk\\bin\\x64"
+        "[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
       ]
     },
     "name": "debug.build.generate",
@@ -200,11 +200,11 @@
     ],
     "cwd": "[START_DIR]\\gn",
     "env": {
-      "VSINSTALLDIR": "[START_DIR]\\cipd\\windows_sdk"
+      "VSINSTALLDIR": "[CACHE]\\windows_sdk"
     },
     "env_prefixes": {
       "PATH": [
-        "[START_DIR]\\cipd\\windows_sdk\\win_sdk\\bin\\x64"
+        "[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
       ]
     },
     "name": "debug.build.ninja",
@@ -251,7 +251,7 @@
       "cipd.bat",
       "ensure",
       "-root",
-      "[START_DIR]\\cipd\\windows_sdk",
+      "[CACHE]\\windows_sdk",
       "-ensure-file",
       "chrome_internal/third_party/sdk/windows uploaded:2018-06-13",
       "-json-output",
@@ -280,7 +280,7 @@
       "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",
+      "[CACHE]\\windows_sdk\\win_sdk\\bin\\SetEnv.x64.json",
       "/path/to/tmp/json"
     ],
     "cwd": "[START_DIR]\\gn",
@@ -317,11 +317,11 @@
     ],
     "cwd": "[START_DIR]\\gn",
     "env": {
-      "VSINSTALLDIR": "[START_DIR]\\cipd\\windows_sdk"
+      "VSINSTALLDIR": "[CACHE]\\windows_sdk"
     },
     "env_prefixes": {
       "PATH": [
-        "[START_DIR]\\cipd\\windows_sdk\\win_sdk\\bin\\x64"
+        "[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
       ]
     },
     "name": "release.build.generate",
@@ -337,11 +337,11 @@
     ],
     "cwd": "[START_DIR]\\gn",
     "env": {
-      "VSINSTALLDIR": "[START_DIR]\\cipd\\windows_sdk"
+      "VSINSTALLDIR": "[CACHE]\\windows_sdk"
     },
     "env_prefixes": {
       "PATH": [
-        "[START_DIR]\\cipd\\windows_sdk\\win_sdk\\bin\\x64"
+        "[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
       ]
     },
     "name": "release.build.ninja",
diff --git a/infra/recipes/gn.py b/infra/recipes/gn.py
index 0a88862..86eae2d 100644
--- a/infra/recipes/gn.py
+++ b/infra/recipes/gn.py
@@ -17,6 +17,7 @@
     'recipe_engine/python',
     'recipe_engine/raw_io',
     'recipe_engine/step',
+    'macos_sdk',
     'windows_sdk',
 ]
 
@@ -83,7 +84,7 @@
     with api.step.nest(config['name']):
       with api.step.nest('build'):
         with api.context(
-            env=env, cwd=src_dir), api.windows_sdk(enabled=api.platform.is_win):
+            env=env, cwd=src_dir), api.macos_sdk(), api.windows_sdk():
           api.python(
               'generate', src_dir.join('build', 'gen.py'), args=config['args'])
 
@@ -149,16 +150,19 @@
 def GenTests(api):
   REVISION = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
 
-  yield (api.test('ci') + api.platform.name('win') + api.buildbucket.ci_build(
-      git_repo='gn.googlesource.com/gn',
-      revision=REVISION,
-  ))
+  for platform in ('linux', 'mac', 'win'):
+    yield (api.test('ci_' + platform) + api.platform.name(platform) +
+           api.buildbucket.ci_build(
+               git_repo='gn.googlesource.com/gn',
+               revision=REVISION,
+           ))
 
-  yield (api.test('cq') + api.platform.name('win') + api.buildbucket.try_build(
-      gerrit_host='gn-review.googlesource.com',
-      change_number=1000,
-      patch_set=1,
-  ))
+    yield (api.test('cq_' + platform) + api.platform.name(platform) +
+           api.buildbucket.try_build(
+               gerrit_host='gn-review.googlesource.com',
+               change_number=1000,
+               patch_set=1,
+           ))
 
   yield (api.test('cipd_exists') + api.buildbucket.ci_build(
       project='infra-internal',