Add a wrapper module for depot_tools/osx_sdk: flutter_osx_sdk

~It looks like properties passed into `$depot_tools/osx_sdk` are not visible to
flutter_osx_sdk (see the examples/full.py file). So we need to do a s\$depot_tools/osx_sdk\$flutter/flutter_osx_sdk/
in the cfg files?~

Changed from inheritance to composition.
Changed the base class back to RecipeApi.

Change-Id: I1f83ab88fb913cc97f2ad24df57b81220e748e47
Reviewed-on: https://flutter-review.googlesource.com/c/recipes/+/7000
Reviewed-by: Godofredo Contreras <godofredoc@google.com>
Commit-Queue: Weiyu Huang <weiyuhuang@google.com>
diff --git a/recipe_modules/flutter_osx_sdk/__init__.py b/recipe_modules/flutter_osx_sdk/__init__.py
new file mode 100644
index 0000000..c6a08db
--- /dev/null
+++ b/recipe_modules/flutter_osx_sdk/__init__.py
@@ -0,0 +1,23 @@
+from recipe_engine.recipe_api import Property
+from recipe_engine.config import ConfigGroup, Single
+
+DEPS = [
+    'depot_tools/osx_sdk',
+    'recipe_engine/cipd',
+    'recipe_engine/context',
+    'recipe_engine/file',
+    'recipe_engine/path',
+    'recipe_engine/platform',
+    'recipe_engine/step',
+    'recipe_engine/version',
+    ]
+
+PROPERTIES = {
+  '$flutter/flutter_osx_sdk': Property(
+    help='Properties specifically for the flutter osx_sdk module.',
+    param_name='sdk_properties',
+    kind=ConfigGroup(  # pylint: disable=line-too-long
+      iphoneos_sdk=Single(str),
+    ), default={},
+  )
+}
diff --git a/recipe_modules/flutter_osx_sdk/api.py b/recipe_modules/flutter_osx_sdk/api.py
new file mode 100644
index 0000000..bf3a187
--- /dev/null
+++ b/recipe_modules/flutter_osx_sdk/api.py
@@ -0,0 +1,21 @@
+# Copyright 2020 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, nested
+from recipe_engine import recipe_api
+
+
+class FlutterXcodeApi(recipe_api.RecipeApi):
+
+  def __init__(self, sdk_properties, *args, **kwargs):
+    super(FlutterXcodeApi, self).__init__(*args, **kwargs)
+
+  @contextmanager
+  def __call__(self, kind):
+    if self.m.platform.is_mac:
+      with self.m.osx_sdk(kind):
+        yield
+    else:
+      yield
+
diff --git a/recipe_modules/flutter_osx_sdk/examples/full.expected/additional deps.json b/recipe_modules/flutter_osx_sdk/examples/full.expected/additional deps.json
new file mode 100644
index 0000000..b4a1902
--- /dev/null
+++ b/recipe_modules/flutter_osx_sdk/examples/full.expected/additional deps.json
@@ -0,0 +1,67 @@
+[
+  {
+    "cmd": [
+      "cipd",
+      "ensure",
+      "-root",
+      "[CACHE]/osx_sdk",
+      "-ensure-file",
+      "infra/tools/mac_toolchain/${platform} git_revision:9a1adc55bf4a1173784da3ba2f8cb06421606748",
+      "-max-threads",
+      "0",
+      "-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:9a1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"package\": \"infra/tools/mac_toolchain/resolved-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]/osx_sdk/mac_toolchain",
+      "install",
+      "-kind",
+      "mac",
+      "-xcode-version",
+      "deadbeef",
+      "-output-dir",
+      "[CACHE]/osx_sdk/XCode.app"
+    ],
+    "infra_step": true,
+    "name": "install xcode"
+  },
+  {
+    "cmd": [
+      "sudo",
+      "xcode-select",
+      "--switch",
+      "[CACHE]/osx_sdk/XCode.app"
+    ],
+    "infra_step": true,
+    "name": "select XCode"
+  },
+  {
+    "cmd": [
+      "sudo",
+      "xcode-select",
+      "--reset"
+    ],
+    "infra_step": true,
+    "name": "reset XCode"
+  },
+  {
+    "name": "$result"
+  }
+]
\ No newline at end of file
diff --git a/recipe_modules/flutter_osx_sdk/examples/full.expected/basic mac.json b/recipe_modules/flutter_osx_sdk/examples/full.expected/basic mac.json
new file mode 100644
index 0000000..ffd630a
--- /dev/null
+++ b/recipe_modules/flutter_osx_sdk/examples/full.expected/basic mac.json
@@ -0,0 +1,67 @@
+[
+  {
+    "cmd": [
+      "cipd",
+      "ensure",
+      "-root",
+      "[CACHE]/osx_sdk",
+      "-ensure-file",
+      "infra/tools/mac_toolchain/${platform} git_revision:9a1adc55bf4a1173784da3ba2f8cb06421606748",
+      "-max-threads",
+      "0",
+      "-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:9a1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"package\": \"infra/tools/mac_toolchain/resolved-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]/osx_sdk/mac_toolchain",
+      "install",
+      "-kind",
+      "mac",
+      "-xcode-version",
+      "9f2000",
+      "-output-dir",
+      "[CACHE]/osx_sdk/XCode.app"
+    ],
+    "infra_step": true,
+    "name": "install xcode"
+  },
+  {
+    "cmd": [
+      "sudo",
+      "xcode-select",
+      "--switch",
+      "[CACHE]/osx_sdk/XCode.app"
+    ],
+    "infra_step": true,
+    "name": "select XCode"
+  },
+  {
+    "cmd": [
+      "sudo",
+      "xcode-select",
+      "--reset"
+    ],
+    "infra_step": true,
+    "name": "reset XCode"
+  },
+  {
+    "name": "$result"
+  }
+]
\ No newline at end of file
diff --git a/recipe_modules/flutter_osx_sdk/examples/full.expected/basic.json b/recipe_modules/flutter_osx_sdk/examples/full.expected/basic.json
new file mode 100644
index 0000000..b6042b6
--- /dev/null
+++ b/recipe_modules/flutter_osx_sdk/examples/full.expected/basic.json
@@ -0,0 +1,5 @@
+[
+  {
+    "name": "$result"
+  }
+]
\ No newline at end of file
diff --git a/recipe_modules/flutter_osx_sdk/examples/full.expected/xcode_version.json b/recipe_modules/flutter_osx_sdk/examples/full.expected/xcode_version.json
new file mode 100644
index 0000000..b4a1902
--- /dev/null
+++ b/recipe_modules/flutter_osx_sdk/examples/full.expected/xcode_version.json
@@ -0,0 +1,67 @@
+[
+  {
+    "cmd": [
+      "cipd",
+      "ensure",
+      "-root",
+      "[CACHE]/osx_sdk",
+      "-ensure-file",
+      "infra/tools/mac_toolchain/${platform} git_revision:9a1adc55bf4a1173784da3ba2f8cb06421606748",
+      "-max-threads",
+      "0",
+      "-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:9a1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"package\": \"infra/tools/mac_toolchain/resolved-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]/osx_sdk/mac_toolchain",
+      "install",
+      "-kind",
+      "mac",
+      "-xcode-version",
+      "deadbeef",
+      "-output-dir",
+      "[CACHE]/osx_sdk/XCode.app"
+    ],
+    "infra_step": true,
+    "name": "install xcode"
+  },
+  {
+    "cmd": [
+      "sudo",
+      "xcode-select",
+      "--switch",
+      "[CACHE]/osx_sdk/XCode.app"
+    ],
+    "infra_step": true,
+    "name": "select XCode"
+  },
+  {
+    "cmd": [
+      "sudo",
+      "xcode-select",
+      "--reset"
+    ],
+    "infra_step": true,
+    "name": "reset XCode"
+  },
+  {
+    "name": "$result"
+  }
+]
\ No newline at end of file
diff --git a/recipe_modules/flutter_osx_sdk/examples/full.py b/recipe_modules/flutter_osx_sdk/examples/full.py
new file mode 100644
index 0000000..85de994
--- /dev/null
+++ b/recipe_modules/flutter_osx_sdk/examples/full.py
@@ -0,0 +1,44 @@
+# Copyright 2020 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 = [
+    'flutter/flutter_osx_sdk',
+    'recipe_engine/platform',
+    'recipe_engine/properties',
+    'recipe_engine/step',
+]
+
+def RunSteps(api):
+  with api.flutter_osx_sdk('mac'):
+    pass
+
+def GenTests(api):
+  yield api.test('basic')
+  yield api.test(
+    'basic mac',
+    api.platform('mac', 64),
+  )
+  properties_dict = {
+    '$depot_tools/osx_sdk': {
+      'sdk_version':'deadbeef',
+    },
+  }
+  yield api.test(
+    'xcode_version',
+    api.platform('mac', 64),
+    api.properties(**properties_dict)
+  )
+  properties_dict = {
+    '$depot_tools/osx_sdk': {
+      'sdk_version':'deadbeef',
+    },
+    '$flutter/flutter_osx_sdk': {
+      'iphoneos_sdk': '0xDEADBEEF',
+    },
+  }
+  yield api.test(
+    'additional deps',
+    api.platform('mac', 64),
+    api.properties(**properties_dict)
+  )
diff --git a/recipes/engine.py b/recipes/engine.py
index 975cc20..e15faca 100644
--- a/recipes/engine.py
+++ b/recipes/engine.py
@@ -17,10 +17,10 @@
     'depot_tools/gclient',
     'depot_tools/git',
     'depot_tools/gsutil',
-    'depot_tools/osx_sdk',
     'flutter/bucket_util',
     'flutter/json_util',
     'flutter/os_utils',
+    'flutter/flutter_osx_sdk',
     'flutter/repo_util',
     'flutter/zip',
     'fuchsia/display_util',
@@ -988,8 +988,8 @@
 @contextmanager
 def SetupXcode(api):
   # See cr-buildbucket.cfg for how the version is passed in.
-  # https://github.com/flutter/infra/blob/master/config/cr-buildbucket.cfg#L148
-  with api.osx_sdk('ios'):
+  # https://github.com/flutter/infra/blob/35f51ea4bfc91966b41d988f6028e34449aa4279/config/generated/flutter/luci/cr-buildbucket.cfg#L7176-L7203
+  with api.flutter_osx_sdk('ios'):
     yield