Adds xcode support to devicelab_drone.

This also adds mac support to adhoc tests.

Change-Id: Ic8c2516aef3f7f5235d7f4e168a2ce3702ad5f89
Bug: https://github.com/flutter/flutter/issues/65223
Reviewed-on: https://flutter-review.googlesource.com/c/recipes/+/6560
Commit-Queue: Godofredo Contreras <godofredoc@google.com>
Reviewed-by: Keyong Han <keyonghan@google.com>
diff --git a/recipe_modules/adhoc_validation/api.py b/recipe_modules/adhoc_validation/api.py
index 723232b..456ae7d 100644
--- a/recipe_modules/adhoc_validation/api.py
+++ b/recipe_modules/adhoc_validation/api.py
@@ -35,7 +35,7 @@
       resource_name = ''
       env = {}
       self.m.kms.decrypt_secrets(env, secrets)
-      if self.m.platform.is_linux:
+      if self.m.platform.is_linux or self.m.platform.is_mac:
         resource_name = self.resource('%s.sh' % validation)
         self.m.step('Set execute permission', ['chmod', '755', resource_name])
       if self.m.platform.is_win:
diff --git a/recipes/devicelab/devicelab_drone.expected/no-task-name.json b/recipes/devicelab/devicelab_drone.expected/no-task-name.json
index dc951bc..33c5743 100644
--- a/recipes/devicelab/devicelab_drone.expected/no-task-name.json
+++ b/recipes/devicelab/devicelab_drone.expected/no-task-name.json
@@ -7,7 +7,7 @@
       "The recipe has crashed at point 'Uncaught exception'!",
       "",
       "Traceback (most recent call last):",
-      "  File \"RECIPE_REPO[flutter]/recipes/devicelab/devicelab_drone.py\", line 23, in RunSteps",
+      "  File \"RECIPE_REPO[flutter]/recipes/devicelab/devicelab_drone.py\", line 24, in RunSteps",
       "    raise ValueError('A task_name property is required')",
       "ValueError: A task_name property is required"
     ]
diff --git a/recipes/devicelab/devicelab_drone.expected/xcode.json b/recipes/devicelab/devicelab_drone.expected/xcode.json
new file mode 100644
index 0000000..b9854b3
--- /dev/null
+++ b/recipes/devicelab/devicelab_drone.expected/xcode.json
@@ -0,0 +1,302 @@
+[
+  {
+    "cmd": [],
+    "name": "Checkout flutter/flutter"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py",
+      "--path",
+      "[START_DIR]/flutter",
+      "--url",
+      "https://chromium.googlesource.com/external/github.com/flutter/flutter"
+    ],
+    "name": "Checkout flutter/flutter.git setup",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "fetch",
+      "origin",
+      "master",
+      "--recurse-submodules",
+      "--progress",
+      "--tags"
+    ],
+    "cwd": "[START_DIR]/flutter",
+    "env": {
+      "PATH": "RECIPE_REPO[depot_tools]:<PATH>"
+    },
+    "infra_step": true,
+    "name": "Checkout flutter/flutter.git fetch",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "checkout",
+      "-f",
+      "FETCH_HEAD"
+    ],
+    "cwd": "[START_DIR]/flutter",
+    "infra_step": true,
+    "name": "Checkout flutter/flutter.git checkout",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "rev-parse",
+      "HEAD"
+    ],
+    "cwd": "[START_DIR]/flutter",
+    "infra_step": true,
+    "name": "Checkout flutter/flutter.read revision",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@",
+      "@@@SET_BUILD_PROPERTY@got_revision@\"deadbeef\"@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "clean",
+      "-f",
+      "-d",
+      "-x"
+    ],
+    "cwd": "[START_DIR]/flutter",
+    "infra_step": true,
+    "name": "Checkout flutter/flutter.git clean",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "sync"
+    ],
+    "cwd": "[START_DIR]/flutter",
+    "infra_step": true,
+    "name": "Checkout flutter/flutter.submodule sync",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "update",
+      "--init",
+      "--recursive"
+    ],
+    "cwd": "[START_DIR]/flutter",
+    "infra_step": true,
+    "name": "Checkout flutter/flutter.submodule update",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "flutter",
+      "doctor"
+    ],
+    "cwd": "[START_DIR]/flutter/dev/devicelab",
+    "env": {
+      "DEPOT_TOOLS": "RECIPE_REPO[depot_tools]",
+      "PUB_CACHE": "[CACHE]/.pub-cache"
+    },
+    "env_prefixes": {
+      "PATH": [
+        "[START_DIR]/flutter/bin",
+        "[START_DIR]/flutter/bin/cache/dart-sdk/bin"
+      ]
+    },
+    "name": "flutter doctor"
+  },
+  {
+    "cmd": [
+      "pub",
+      "get"
+    ],
+    "cwd": "[START_DIR]/flutter/dev/devicelab",
+    "env": {
+      "DEPOT_TOOLS": "RECIPE_REPO[depot_tools]",
+      "PUB_CACHE": "[CACHE]/.pub-cache"
+    },
+    "env_prefixes": {
+      "PATH": [
+        "[START_DIR]/flutter/bin",
+        "[START_DIR]/flutter/bin/cache/dart-sdk/bin"
+      ]
+    },
+    "name": "pub get"
+  },
+  {
+    "cmd": [],
+    "name": "Swift deps"
+  },
+  {
+    "cmd": [
+      "cipd",
+      "ensure",
+      "-root",
+      "[CLEANUP]/swift",
+      "-ensure-file",
+      "flutter_internal/mac/swift/${platform} latest",
+      "-max-threads",
+      "0",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "cwd": "[START_DIR]/flutter/dev/devicelab",
+    "env": {
+      "DEPOT_TOOLS": "RECIPE_REPO[depot_tools]",
+      "PUB_CACHE": "[CACHE]/.pub-cache"
+    },
+    "env_prefixes": {
+      "PATH": [
+        "[START_DIR]/flutter/bin",
+        "[START_DIR]/flutter/bin/cache/dart-sdk/bin"
+      ]
+    },
+    "name": "Swift deps.ensure_installed",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@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-latest----------\", @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"package\": \"flutter_internal/mac/swift/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": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copytree",
+      "[CLEANUP]/swift/swift",
+      "[CACHE]/osx_sdk/XCode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift"
+    ],
+    "cwd": "[START_DIR]/flutter/dev/devicelab",
+    "env": {
+      "DEPOT_TOOLS": "RECIPE_REPO[depot_tools]",
+      "PUB_CACHE": "[CACHE]/.pub-cache"
+    },
+    "env_prefixes": {
+      "PATH": [
+        "[START_DIR]/flutter/bin",
+        "[START_DIR]/flutter/bin/cache/dart-sdk/bin"
+      ]
+    },
+    "infra_step": true,
+    "name": "Swift deps.Copy swift",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copytree",
+      "[CLEANUP]/swift/swift-5.0",
+      "[CACHE]/osx_sdk/XCode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.0"
+    ],
+    "cwd": "[START_DIR]/flutter/dev/devicelab",
+    "env": {
+      "DEPOT_TOOLS": "RECIPE_REPO[depot_tools]",
+      "PUB_CACHE": "[CACHE]/.pub-cache"
+    },
+    "env_prefixes": {
+      "PATH": [
+        "[START_DIR]/flutter/bin",
+        "[START_DIR]/flutter/bin/cache/dart-sdk/bin"
+      ]
+    },
+    "infra_step": true,
+    "name": "Swift deps.Copy swift-5.0",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "listdir",
+      "[CACHE]/osx_sdk/XCode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib"
+    ],
+    "cwd": "[START_DIR]/flutter/dev/devicelab",
+    "env": {
+      "DEPOT_TOOLS": "RECIPE_REPO[depot_tools]",
+      "PUB_CACHE": "[CACHE]/.pub-cache"
+    },
+    "env_prefixes": {
+      "PATH": [
+        "[START_DIR]/flutter/bin",
+        "[START_DIR]/flutter/bin/cache/dart-sdk/bin"
+      ]
+    },
+    "infra_step": true,
+    "name": "Swift deps.List directory",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_END@listdir@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "dart",
+      "bin/run.dart",
+      "-t",
+      "abc"
+    ],
+    "cwd": "[START_DIR]/flutter/dev/devicelab",
+    "env": {
+      "DEPOT_TOOLS": "RECIPE_REPO[depot_tools]",
+      "PUB_CACHE": "[CACHE]/.pub-cache"
+    },
+    "env_prefixes": {
+      "PATH": [
+        "[START_DIR]/flutter/bin",
+        "[START_DIR]/flutter/bin/cache/dart-sdk/bin",
+        "[START_DIR]/flutter/bin",
+        "[START_DIR]/flutter/bin/cache/dart-sdk/bin"
+      ]
+    },
+    "name": "run abc"
+  },
+  {
+    "name": "$result"
+  }
+]
\ No newline at end of file
diff --git a/recipes/devicelab/devicelab_drone.py b/recipes/devicelab/devicelab_drone.py
index 6a9d54a..aedb606 100644
--- a/recipes/devicelab/devicelab_drone.py
+++ b/recipes/devicelab/devicelab_drone.py
@@ -5,6 +5,7 @@
 from recipe_engine.recipe_api import Property
 
 DEPS = [
+    'depot_tools/osx_sdk',
     'flutter/flutter_deps',
     'flutter/repo_util',
     'flutter/os_utils',
@@ -37,8 +38,23 @@
   with api.context(env=env, env_prefixes=env_prefixes, cwd=devicelab_path):
     api.step('flutter doctor', ['flutter', 'doctor'])
     api.step('pub get', ['pub', 'get'])
-    with api.context(env=env, env_prefixes=env_prefixes):
-      api.step('run %s' % task_name, ['dart', 'bin/run.dart', '-t', task_name])
+    dep_list = [d['dependency'] for d in deps]
+    if 'xcode' in dep_list:
+      with api.osx_sdk('ios'):
+        api.flutter_deps.swift()
+        api.flutter_deps.gems(
+            env, env_prefixes, flutter_path.join('dev', 'ci', 'mac')
+        )
+        with api.context(env=env, env_prefixes=env_prefixes):
+          api.step(
+              'run %s' % task_name, ['dart', 'bin/run.dart', '-t', task_name]
+          )
+    else:
+      with api.context(env=env, env_prefixes=env_prefixes):
+        api.step(
+            'run %s' % task_name, ['dart', 'bin/run.dart', '-t', task_name]
+        )
+
   # This is a noop for non windows tasks.
   api.os_utils.kill_win_processes()
 
@@ -52,3 +68,8 @@
       "basic", api.properties(task_name='abc'),
       api.repo_util.flutter_environment_data()
   )
+  yield api.test(
+      "xcode",
+      api.properties(task_name='abc', dependencies=[{'dependency': 'xcode'}]),
+      api.repo_util.flutter_environment_data()
+  )