Relax requirements around local engine, build hello_world with bitcode (#39357)

diff --git a/examples/hello_world/ios/Runner.xcodeproj/project.pbxproj b/examples/hello_world/ios/Runner.xcodeproj/project.pbxproj
index df9d159..46c35dd 100644
--- a/examples/hello_world/ios/Runner.xcodeproj/project.pbxproj
+++ b/examples/hello_world/ios/Runner.xcodeproj/project.pbxproj
@@ -317,7 +317,6 @@
 			baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
-				ENABLE_BITCODE = NO;
 				FRAMEWORK_SEARCH_PATHS = (
 					"$(inherited)",
 					"$(PROJECT_DIR)/Flutter",
@@ -444,7 +443,6 @@
 			baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
-				ENABLE_BITCODE = NO;
 				FRAMEWORK_SEARCH_PATHS = (
 					"$(inherited)",
 					"$(PROJECT_DIR)/Flutter",
@@ -465,7 +463,6 @@
 			baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
-				ENABLE_BITCODE = NO;
 				FRAMEWORK_SEARCH_PATHS = (
 					"$(inherited)",
 					"$(PROJECT_DIR)/Flutter",
diff --git a/packages/flutter_tools/bin/xcode_backend.sh b/packages/flutter_tools/bin/xcode_backend.sh
index 876dd75..e49eb2b 100755
--- a/packages/flutter_tools/bin/xcode_backend.sh
+++ b/packages/flutter_tools/bin/xcode_backend.sh
@@ -114,7 +114,6 @@
     flutter_engine_flag="--local-engine-src-path=${FLUTTER_ENGINE}"
   fi
 
-  local bitcode_flag=""
   if [[ -n "$LOCAL_ENGINE" ]]; then
     if [[ $(echo "$LOCAL_ENGINE" | tr "[:upper:]" "[:lower:]") != *"$build_mode"* ]]; then
       EchoError "========================================================================"
@@ -131,9 +130,12 @@
     local_engine_flag="--local-engine=${LOCAL_ENGINE}"
     flutter_framework="${FLUTTER_ENGINE}/out/${LOCAL_ENGINE}/Flutter.framework"
     flutter_podspec="${FLUTTER_ENGINE}/out/${LOCAL_ENGINE}/Flutter.podspec"
-    if [[ $ENABLE_BITCODE == "YES" ]]; then
-      bitcode_flag="--bitcode"
-    fi
+  fi
+
+  local bitcode_flag=""
+  if [[ $ENABLE_BITCODE == "YES" ]]; then
+    bitcode_flag="--bitcode"
+    echo "Set Bitcode!"
   fi
 
   if [[ -e "${project_path}/.ios" ]]; then
diff --git a/packages/flutter_tools/lib/src/base/build.dart b/packages/flutter_tools/lib/src/base/build.dart
index 270c95a..09d46d0 100644
--- a/packages/flutter_tools/lib/src/base/build.dart
+++ b/packages/flutter_tools/lib/src/base/build.dart
@@ -179,13 +179,14 @@
     // The DWARF section confuses Xcode tooling, so this strips it. Ideally,
     // gen_snapshot would provide an argument to do this automatically.
     if (platform == TargetPlatform.ios && bitcode) {
-      final IOSink sink = fs.file('$assembly.bitcode').openWrite();
+      final IOSink sink = fs.file('$assembly.stripped.S').openWrite();
       for (String line in fs.file(assembly).readAsLinesSync()) {
         if (line.startsWith('.section __DWARF')) {
           break;
         }
         sink.writeln(line);
       }
+      await sink.flush();
       await sink.close();
     }
 
@@ -199,7 +200,8 @@
     if (platform == TargetPlatform.ios || platform == TargetPlatform.darwin_x64) {
       final RunResult result = await _buildFramework(
         appleArch: darwinArch,
-        assemblyPath: bitcode ? '$assembly.bitcode' : assembly,
+        isIOS: platform == TargetPlatform.ios,
+        assemblyPath: bitcode ? '$assembly.stripped.S' : assembly,
         outputPath: outputDir.path,
         bitcode: bitcode,
       );
@@ -213,26 +215,29 @@
   /// source at [assemblyPath].
   Future<RunResult> _buildFramework({
     @required DarwinArch appleArch,
+    @required bool isIOS,
     @required String assemblyPath,
     @required String outputPath,
     @required bool bitcode,
   }) async {
     final String targetArch = getNameForDarwinArch(appleArch);
     printStatus('Building App.framework for $targetArch...');
+
     final List<String> commonBuildOptions = <String>[
       '-arch', targetArch,
-      if (appleArch == DarwinArch.arm64 || appleArch == DarwinArch.armv7)
+      if (isIOS)
         '-miphoneos-version-min=8.0',
     ];
 
+    const String embedBitcodeArg = '-fembed-bitcode';
     final String assemblyO = fs.path.join(outputPath, 'snapshot_assembly.o');
     final RunResult compileResult = await xcode.cc(<String>[
-      ...commonBuildOptions,
+      '-arch', targetArch,
+      if (bitcode) embedBitcodeArg,
       '-c',
       assemblyPath,
       '-o',
       assemblyO,
-      if (bitcode) '-fembed-bitcode',
     ]);
     if (compileResult.exitCode != 0) {
       printError('Failed to compile AOT snapshot. Compiler terminated with exit code ${compileResult.exitCode}');
@@ -248,26 +253,16 @@
       '-Xlinker', '-rpath', '-Xlinker', '@executable_path/Frameworks',
       '-Xlinker', '-rpath', '-Xlinker', '@loader_path/Frameworks',
       '-install_name', '@rpath/App.framework/App',
-      if (bitcode) '-fembed-bitcode',
+      if (bitcode) embedBitcodeArg,
+      if (bitcode && isIOS) ...<String>[embedBitcodeArg, '-isysroot', await xcode.iPhoneSdkLocation()],
       '-o', appLib,
       assemblyO,
     ];
     final RunResult linkResult = await xcode.clang(linkArgs);
     if (linkResult.exitCode != 0) {
       printError('Failed to link AOT snapshot. Linker terminated with exit code ${compileResult.exitCode}');
-      return linkResult;
     }
-    // See https://github.com/flutter/flutter/issues/22560
-    // These have to be placed in a .noindex folder to prevent Xcode from
-    // using Spotlight to find them and potentially attach the wrong ones.
-    final RunResult dsymResult = await xcode.dsymutil(<String>[
-      appLib,
-      '-o', fs.path.join(outputPath, 'App.framework.dSYM.noindex'),
-    ]);
-    if (dsymResult.exitCode != 0) {
-      printError('Failed to extract dSYM out of dynamic lib');
-    }
-    return dsymResult;
+    return linkResult;
   }
 
   /// Compiles a Dart file to kernel.
diff --git a/packages/flutter_tools/lib/src/commands/build_aot.dart b/packages/flutter_tools/lib/src/commands/build_aot.dart
index bbb03f2..18b7ab5 100644
--- a/packages/flutter_tools/lib/src/commands/build_aot.dart
+++ b/packages/flutter_tools/lib/src/commands/build_aot.dart
@@ -83,7 +83,7 @@
       if (platform != TargetPlatform.ios) {
         throwToolExit('Bitcode is only supported on iOS (TargetPlatform is $targetPlatform).');
       }
-      await validateBitcode();
+      await validateBitcode(buildMode, platform);
     }
 
     Status status;
@@ -150,14 +150,6 @@
             '-create',
             '-output', fs.path.join(outputPath, 'App.framework', 'App'),
           ]);
-          final Iterable<String> dSYMs = iosBuilds.values.map<String>((String outputDir) => fs.path.join(outputDir, 'App.framework.dSYM.noindex'));
-          fs.directory(fs.path.join(outputPath, 'App.framework.dSYM.noindex', 'Contents', 'Resources', 'DWARF'))..createSync(recursive: true);
-          await runCheckedAsync(<String>[
-            'lipo',
-            '-create',
-            '-output', fs.path.join(outputPath, 'App.framework.dSYM.noindex', 'Contents', 'Resources', 'DWARF', 'App'),
-            ...dSYMs.map((String path) => fs.path.join(path, 'Contents', 'Resources', 'DWARF', 'App'))
-          ]);
         } else {
           status?.cancel();
           exitCodes.forEach((DarwinArch iosArch, Future<int> exitCodeFuture) async {
@@ -202,24 +194,18 @@
   }
 }
 
-Future<void> validateBitcode() async {
+Future<void> validateBitcode(BuildMode buildMode, TargetPlatform targetPlatform) async {
   final Artifacts artifacts = Artifacts.instance;
-  if (artifacts is! LocalEngineArtifacts) {
-    throwToolExit('Bitcode is only supported with a local engine built with --bitcode.');
-  }
-  final String flutterFrameworkPath = artifacts.getArtifactPath(Artifact.flutterFramework);
+  final String flutterFrameworkPath = artifacts.getArtifactPath(
+    Artifact.flutterFramework,
+    mode: buildMode,
+    platform: targetPlatform,
+  );
   if (!fs.isDirectorySync(flutterFrameworkPath)) {
     throwToolExit('Flutter.framework not found at $flutterFrameworkPath');
   }
   final Xcode xcode = context.get<Xcode>();
 
-  // Check for bitcode in Flutter binary.
-  final RunResult otoolResult = await xcode.otool(<String>[
-    '-l', fs.path.join(flutterFrameworkPath, 'Flutter'),
-  ]);
-  if (!otoolResult.stdout.contains('__LLVM')) {
-    throwToolExit('The Flutter.framework at $flutterFrameworkPath does not contain bitcode.');
-  }
   final RunResult clangResult = await xcode.clang(<String>['--version']);
   final String clangVersion = clangResult.stdout.split('\n').first;
   final String engineClangVersion = PlistParser.instance.getValueFromFile(
diff --git a/packages/flutter_tools/lib/src/macos/xcode.dart b/packages/flutter_tools/lib/src/macos/xcode.dart
index 6fea9ad..8888f8d 100644
--- a/packages/flutter_tools/lib/src/macos/xcode.dart
+++ b/packages/flutter_tools/lib/src/macos/xcode.dart
@@ -4,6 +4,7 @@
 
 import 'dart:async';
 
+import '../base/common.dart';
 import '../base/context.dart';
 import '../base/file_system.dart';
 import '../base/io.dart';
@@ -100,16 +101,14 @@
     return runCheckedAsync(<String>['xcrun', 'clang', ...args]);
   }
 
-  Future<RunResult> dsymutil(List<String> args) {
-    return runCheckedAsync(<String>['xcrun', 'dsymutil', ...args]);
-  }
-
-  Future<RunResult> strip(List<String> args) {
-    return runCheckedAsync(<String>['xcrun', 'strip', ...args]);
-  }
-
-  Future<RunResult> otool(List<String> args) {
-    return runCheckedAsync(<String>['xcrun', 'otool', ...args]);
+  Future<String> iPhoneSdkLocation() async {
+    final RunResult runResult = await runCheckedAsync(
+      <String>['xcrun', '--sdk', 'iphoneos', '--show-sdk-path'],
+    );
+    if (runResult.exitCode != 0) {
+      throwToolExit('Could not find iPhone SDK location: ${runResult.stderr}');
+    }
+    return runResult.stdout.trim();
   }
 
   String getSimulatorPath() {
diff --git a/packages/flutter_tools/test/general.shard/base/build_test.dart b/packages/flutter_tools/test/general.shard/base/build_test.dart
index 32d78a4..be3556c 100644
--- a/packages/flutter_tools/test/general.shard/base/build_test.dart
+++ b/packages/flutter_tools/test/general.shard/base/build_test.dart
@@ -116,13 +116,6 @@
         when(mockArtifacts.getArtifactPath(Artifact.snapshotDart,
             platform: anyNamed('platform'), mode: mode)).thenReturn(kSnapshotDart);
       }
-
-      when(mockXcode.dsymutil(any)).thenAnswer((_) => Future<RunResult>.value(
-        RunResult(
-          ProcessResult(1, 0, '', ''),
-          <String>['command name', 'arguments...']),
-        ),
-      );
     });
 
     final Map<Type, Generator> contextOverrides = <Type, Generator>{
@@ -210,14 +203,9 @@
 
       verify(xcode.cc(argThat(contains('-fembed-bitcode')))).called(1);
       verify(xcode.clang(argThat(contains('-fembed-bitcode')))).called(1);
-      verify(xcode.dsymutil(<String>[
-        'build/foo/App.framework/App',
-        '-o',
-        'build/foo/App.framework.dSYM.noindex',
-      ])).called(1);
 
       final File assemblyFile = fs.file(assembly);
-      final File assemblyBitcodeFile = fs.file('$assembly.bitcode');
+      final File assemblyBitcodeFile = fs.file('$assembly.stripped.S');
       expect(assemblyFile.existsSync(), true);
       expect(assemblyBitcodeFile.existsSync(), true);
       expect(assemblyFile.readAsStringSync().contains('.section __DWARF'), true);
@@ -263,7 +251,6 @@
       ]);
       verifyNever(xcode.cc(argThat(contains('-fembed-bitcode'))));
       verifyNever(xcode.clang(argThat(contains('-fembed-bitcode'))));
-      verify(xcode.dsymutil(any)).called(1);
 
       final File assemblyFile = fs.file(assembly);
       final File assemblyBitcodeFile = fs.file('$assembly.bitcode');
diff --git a/packages/flutter_tools/test/general.shard/build_system/targets/dart_test.dart b/packages/flutter_tools/test/general.shard/build_system/targets/dart_test.dart
index 7b18e6f..d30377a 100644
--- a/packages/flutter_tools/test/general.shard/build_system/targets/dart_test.dart
+++ b/packages/flutter_tools/test/general.shard/build_system/targets/dart_test.dart
@@ -248,14 +248,12 @@
 
     when(mockXcode.cc(any)).thenAnswer((_) => Future<RunResult>.value(fakeRunResult));
     when(mockXcode.clang(any)).thenAnswer((_) => Future<RunResult>.value(fakeRunResult));
-    when(mockXcode.dsymutil(any)).thenAnswer((_) => Future<RunResult>.value(fakeRunResult));
 
     final BuildResult result = await buildSystem.build(const AotAssemblyProfile(), iosEnvironment);
 
     expect(result.success, true);
     verify(mockXcode.cc(argThat(contains('-fembed-bitcode')))).called(1);
     verify(mockXcode.clang(argThat(contains('-fembed-bitcode')))).called(1);
-    verify(mockXcode.dsymutil(any)).called(1);
   }, overrides: <Type, Generator>{
     ProcessManager: () => mockProcessManager,
     Xcode: () => mockXcode,
@@ -278,14 +276,12 @@
 
     when(mockXcode.cc(any)).thenAnswer((_) => Future<RunResult>.value(fakeRunResult));
     when(mockXcode.clang(any)).thenAnswer((_) => Future<RunResult>.value(fakeRunResult));
-    when(mockXcode.dsymutil(any)).thenAnswer((_) => Future<RunResult>.value(fakeRunResult));
 
     final BuildResult result = await buildSystem.build(const AotAssemblyProfile(), iosEnvironment);
 
     expect(result.success, true);
     verify(mockXcode.cc(argThat(contains('-fembed-bitcode')))).called(2);
     verify(mockXcode.clang(argThat(contains('-fembed-bitcode')))).called(2);
-    verify(mockXcode.dsymutil(any)).called(2);
   }, overrides: <Type, Generator>{
     ProcessManager: () => mockProcessManager,
     Xcode: () => mockXcode,
diff --git a/packages/flutter_tools/test/general.shard/build_system/targets/macos_test.dart b/packages/flutter_tools/test/general.shard/build_system/targets/macos_test.dart
index 344bbc1..398c0e8 100644
--- a/packages/flutter_tools/test/general.shard/build_system/targets/macos_test.dart
+++ b/packages/flutter_tools/test/general.shard/build_system/targets/macos_test.dart
@@ -179,9 +179,6 @@
     when(xcode.clang(any)).thenAnswer((Invocation invocation) {
       return Future<RunResult>.value(RunResult(FakeProcessResult()..exitCode = 0, <String>['test']));
     });
-    when(xcode.dsymutil(any)).thenAnswer((Invocation invocation) {
-      return Future<RunResult>.value(RunResult(FakeProcessResult()..exitCode = 0, <String>['test']));
-    });
     environment.buildDir.childFile('app.dill').createSync(recursive: true);
     fs.file('.packages')
       ..createSync()
diff --git a/packages/flutter_tools/test/general.shard/commands/build_aot_test.dart b/packages/flutter_tools/test/general.shard/commands/build_aot_test.dart
index f12e63b..bcda514 100644
--- a/packages/flutter_tools/test/general.shard/commands/build_aot_test.dart
+++ b/packages/flutter_tools/test/general.shard/commands/build_aot_test.dart
@@ -6,6 +6,7 @@
 import 'package:flutter_tools/src/artifacts.dart';
 import 'package:flutter_tools/src/base/logger.dart';
 import 'package:flutter_tools/src/base/process.dart';
+import 'package:flutter_tools/src/build_info.dart';
 import 'package:flutter_tools/src/commands/build_aot.dart';
 import 'package:flutter_tools/src/base/file_system.dart';
 import 'package:flutter_tools/src/ios/plist_parser.dart';
@@ -32,16 +33,9 @@
     mockPlistUtils = MockPlistUtils();
   });
 
-  testUsingContext('build aot validates building with bitcode requires a local engine', () async {
-    await expectToolExitLater(
-      validateBitcode(),
-      equals('Bitcode is only supported with a local engine built with --bitcode.'),
-    );
-  });
-
   testUsingContext('build aot validates existence of Flutter.framework in engine', () async {
     await expectToolExitLater(
-      validateBitcode(),
+      validateBitcode(BuildMode.release, TargetPlatform.ios),
       equals('Flutter.framework not found at ios_profile/Flutter.framework'),
     );
   }, overrides: <Type, Generator>{
@@ -49,48 +43,21 @@
     FileSystem: () => memoryFileSystem,
   });
 
-  testUsingContext('build aot validates Flutter.framework/Flutter contains bitcode', () async {
-    final Directory flutterFramework = memoryFileSystem.directory('ios_profile/Flutter.framework')
-      ..createSync(recursive: true);
-    flutterFramework.childFile('Flutter').createSync();
-    flutterFramework.childFile('Info.plist').createSync();
-
-    final RunResult otoolResult = RunResult(
-      FakeProcessResult(stdout: '', stderr: ''),
-      const <String>['foo'],
-    );
-    when(mockXcode.otool(any)).thenAnswer((_) => Future<RunResult>.value(otoolResult));
-    await expectToolExitLater(
-      validateBitcode(),
-      equals('The Flutter.framework at ios_profile/Flutter.framework does not contain bitcode.'),
-    );
-  }, overrides: <Type, Generator>{
-    Artifacts: () => LocalEngineArtifacts('/engine', 'ios_profile', 'host_profile'),
-    FileSystem: () => memoryFileSystem,
-    ProcessManager: () => mockProcessManager,
-    Xcode: () => mockXcode,
-  });
-
   testUsingContext('build aot validates Flutter.framework/Flutter was built with same toolchain', () async {
     final Directory flutterFramework = memoryFileSystem.directory('ios_profile/Flutter.framework')
       ..createSync(recursive: true);
     flutterFramework.childFile('Flutter').createSync();
     final File infoPlist = flutterFramework.childFile('Info.plist')..createSync();
 
-    final RunResult otoolResult = RunResult(
-      FakeProcessResult(stdout: '__LLVM', stderr: ''),
-      const <String>['foo'],
-    );
     final RunResult clangResult = RunResult(
       FakeProcessResult(stdout: 'Apple LLVM version 10.0.0 (clang-4567.1.1.1)\nBlahBlah\n', stderr: ''),
       const <String>['foo'],
     );
-    when(mockXcode.otool(any)).thenAnswer((_) => Future<RunResult>.value(otoolResult));
     when(mockXcode.clang(any)).thenAnswer((_) => Future<RunResult>.value(clangResult));
     when(mockPlistUtils.getValueFromFile(infoPlist.path, 'ClangVersion')).thenReturn('Apple LLVM version 10.0.1 (clang-1234.1.12.1)');
 
     await expectToolExitLater(
-      validateBitcode(),
+      validateBitcode(BuildMode.release, TargetPlatform.ios),
       equals('The Flutter.framework at ios_profile/Flutter.framework was built with "Apple LLVM version 10.0.1 '
              '(clang-1234.1.12.1)", but the current version of clang is "Apple LLVM version 10.0.0 (clang-4567.1.1.1)". '
              'This will result in failures when trying toarchive an IPA. To resolve this issue, update your version '
@@ -111,19 +78,14 @@
     flutterFramework.childFile('Flutter').createSync();
     final File infoPlist = flutterFramework.childFile('Info.plist')..createSync();
 
-    final RunResult otoolResult = RunResult(
-      FakeProcessResult(stdout: '__LLVM', stderr: ''),
-      const <String>['foo'],
-    );
     final RunResult clangResult = RunResult(
       FakeProcessResult(stdout: 'Apple LLVM version 10.0.1 (clang-1234.1.12.1)\nBlahBlah\n', stderr: ''),
       const <String>['foo'],
     );
-    when(mockXcode.otool(any)).thenAnswer((_) => Future<RunResult>.value(otoolResult));
     when(mockXcode.clang(any)).thenAnswer((_) => Future<RunResult>.value(clangResult));
     when(mockPlistUtils.getValueFromFile(infoPlist.path, 'ClangVersion')).thenReturn('Apple LLVM version 10.0.1 (clang-1234.1.12.1)');
 
-    await validateBitcode();
+    await validateBitcode(BuildMode.release, TargetPlatform.ios);
 
     expect(bufferLogger.statusText, '');
   }, overrides: <Type, Generator>{
@@ -141,19 +103,14 @@
     flutterFramework.childFile('Flutter').createSync();
     final File infoPlist = flutterFramework.childFile('Info.plist')..createSync();
 
-    final RunResult otoolResult = RunResult(
-      FakeProcessResult(stdout: '__LLVM', stderr: ''),
-      const <String>['foo'],
-    );
     final RunResult clangResult = RunResult(
       FakeProcessResult(stdout: 'Apple LLVM version 11.0.1 (clang-1234.1.12.1)\nBlahBlah\n', stderr: ''),
       const <String>['foo'],
     );
-    when(mockXcode.otool(any)).thenAnswer((_) => Future<RunResult>.value(otoolResult));
     when(mockXcode.clang(any)).thenAnswer((_) => Future<RunResult>.value(clangResult));
     when(mockPlistUtils.getValueFromFile(infoPlist.path, 'ClangVersion')).thenReturn('Apple LLVM version 10.0.1 (clang-1234.1.12.1)');
 
-    await validateBitcode();
+    await validateBitcode(BuildMode.release, TargetPlatform.ios);
 
     expect(bufferLogger.statusText, '');
   }, overrides: <Type, Generator>{