Smoke test building IPA and APK on supported platforms (#24601)

* build tests - AOT on all, APK on Linux, IPA on Mac
diff --git a/.cirrus.yml b/.cirrus.yml
index 6f34fde..3305fbe 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -38,6 +38,7 @@
         - analyze
         - tests-linux
         - tool_tests-linux
+        - build_tests-linux
       env:
         SHARD: deploy_gallery
         GOOGLE_DEVELOPER_SERVICE_ACCOUNT_ACTOR_FASTLANE: ENCRYPTED[d9ac1462c3c556fc2f8165c9d5566a16497d8ebc38a50357f7f3abf136b7f83e1d1d76dde36fee356cb0f9ebf7a89346]
@@ -62,9 +63,9 @@
       container:
         cpu: 4
         memory: 12G
-    - name: aot_build_tests-linux
+    - name: build_tests-linux
       env:
-        SHARD: aot_build_tests
+        SHARD: build_tests
       test_script:
         - dart ./dev/bots/test.dart
       container:
@@ -110,6 +111,12 @@
     - name: tool_tests-windows
       env:
         SHARD: tool_tests
+    - name: build_tests-windows
+      env:
+        SHARD: build_tests
+      container:
+        cpu: 4
+        memory: 12G
 
 task:
   use_compute_credits: $CIRRUS_USER_COLLABORATOR == 'true' && $CIRRUS_PR == ''
@@ -121,6 +128,7 @@
     - analyze
     - tests-macos
     - tool_tests-macos
+    - build_tests-macos
   env:
     # Name the SDK directory to include a space so that we constantly
     # test path names with spaces in them.
@@ -153,6 +161,8 @@
     - analyze
   env:
     CIRRUS_WORKING_DIR: "/tmp/flutter sdk"
+  install_cocoapods_script:
+    - sudo gem install cocoapods
   git_fetch_script:
     - git fetch origin
     - git fetch origin master # To set FETCH_HEAD for "git merge-base" to work
@@ -178,6 +188,14 @@
     - name: tool_tests-macos
       env:
         SHARD: tool_tests
+    - name: build_tests-macos
+      env:
+        SHARD: build_tests
+        COCOAPODS_DISABLE_STATS: true
+        FLUTTER_FRAMEWORK_DIR: "/tmp/flutter sdk/bin/cache/artifacts/engine/ios/"
+      container:
+        cpu: 4
+        memory: 12G
 
 
 docker_builder:
@@ -190,6 +208,7 @@
     - analyze
     - tests-linux
     - tool_tests-linux
+    - build_tests-linux
   build_script: "$CIRRUS_WORKING_DIR/dev/ci/docker_linux/docker_build.sh"
   login_script: "$CIRRUS_WORKING_DIR/dev/ci/docker_linux/docker_login.sh"
   push_script: "$CIRRUS_WORKING_DIR/dev/ci/docker_linux/docker_push.sh"
diff --git a/dev/bots/test.dart b/dev/bots/test.dart
index e55847a..289b5de 100644
--- a/dev/bots/test.dart
+++ b/dev/bots/test.dart
@@ -21,7 +21,7 @@
 const Map<String, ShardRunner> _kShards = <String, ShardRunner>{
   'tests': _runTests,
   'tool_tests': _runToolTests,
-  'aot_build_tests': _runAotBuildTests,
+  'build_tests': _runBuildTests,
   'coverage': _runCoverage,
 };
 
@@ -150,25 +150,76 @@
   print('${bold}DONE: All tests successful.$reset');
 }
 
-/// Verifies that AOT builds of some examples apps finish
-/// without crashing. It does not actually launch the AOT-built
-/// apps. That happens later in the devicelab. This is just
-/// a smoke-test.
-Future<void> _runAotBuildTests() async {
-  await _flutterBuildAot(path.join('examples', 'hello_world'));
-  await _flutterBuildAot(path.join('examples', 'flutter_gallery'));
-  await _flutterBuildAot(path.join('examples', 'flutter_view'));
+/// Verifies that AOT, APK, and IPA (if on macOS) builds of some
+/// examples apps finish without crashing. It does not actually
+/// launch the apps. That happens later in the devicelab. This is
+/// just a smoke-test. In particular, this will verify we can build
+/// when there are spaces in the path name for the Flutter SDK and
+/// target app.
+Future<void> _runBuildTests() async {
+  final List<String> paths = <String>[
+    path.join('examples', 'hello_world'),
+    path.join('examples', 'flutter_gallery'),
+    path.join('examples', 'flutter_view'),
+  ];
+  for (String path in paths) {
+    await _flutterBuildAot(path);
+    await _flutterBuildApk(path);
+    await _flutterBuildIpa(path);
+  }
 
-  print('${bold}DONE: All AOT build tests successful.$reset');
+  print('${bold}DONE: All build tests successful.$reset');
 }
 
-Future<void> _flutterBuildAot(String relativePathToApplication) {
-  return runCommand(flutter,
-    <String>['build', 'aot'],
+Future<void> _flutterBuildAot(String relativePathToApplication) async {
+  print('Running AOT build tests...');
+  await runCommand(flutter,
+    <String>['build', 'aot', '-v'],
     workingDirectory: path.join(flutterRoot, relativePathToApplication),
     expectNonZeroExit: false,
     timeout: _kShortTimeout,
   );
+  print('Done.');
+}
+
+Future<void> _flutterBuildApk(String relativePathToApplication) async {
+  // TODO(dnfield): See if we can get Android SDK on all Cirrus platforms.
+  if (Platform.environment['ANDROID_HOME']?.isEmpty ?? true) {
+    return;
+  }
+  print('Running APK build tests...');
+  await runCommand(flutter,
+    <String>['build', 'apk', '--debug', '-v'],
+    workingDirectory: path.join(flutterRoot, relativePathToApplication),
+    expectNonZeroExit: false,
+    timeout: _kShortTimeout,
+  );
+  print('Done.');
+}
+
+Future<void> _flutterBuildIpa(String relativePathToApplication) async {
+  if (!Platform.isMacOS) {
+    return;
+  }
+  print('Running IPA build tests...');
+  // Install Cocoapods.  We don't have these checked in for the examples,
+  // and build ios doesn't take care of it automatically.
+  final File podfile = File(path.join(flutterRoot, relativePathToApplication, 'ios', 'Podfile'));
+  if (podfile.existsSync()) {
+    await runCommand('pod',
+      <String>['install'],
+      workingDirectory: podfile.parent.path,
+      expectNonZeroExit: false,
+      timeout: _kShortTimeout,
+    );
+  }
+  await runCommand(flutter,
+    <String>['build', 'ios', '--no-codesign', '--debug', '-v'],
+    workingDirectory: path.join(flutterRoot, relativePathToApplication),
+    expectNonZeroExit: false,
+    timeout: _kShortTimeout,
+  );
+  print('Done.');
 }
 
 Future<void> _runTests() async {