From 202c1b422430af20569af651921a968df6fdcfaa Mon Sep 17 00:00:00 2001 From: Dan Field Date: Wed, 28 Aug 2019 10:27:01 -0700 Subject: [PATCH] Relax requirements around local engine, build hello_world with bitcode (#39357) --- .../ios/Runner.xcodeproj/project.pbxproj | 3 -- packages/flutter_tools/bin/xcode_backend.sh | 10 ++-- .../flutter_tools/lib/src/base/build.dart | 31 +++++------ .../lib/src/commands/build_aot.dart | 28 +++------- .../flutter_tools/lib/src/macos/xcode.dart | 19 ++++--- .../test/general.shard/base/build_test.dart | 15 +----- .../build_system/targets/dart_test.dart | 4 -- .../build_system/targets/macos_test.dart | 3 -- .../commands/build_aot_test.dart | 53 ++----------------- 9 files changed, 41 insertions(+), 125 deletions(-) diff --git a/examples/hello_world/ios/Runner.xcodeproj/project.pbxproj b/examples/hello_world/ios/Runner.xcodeproj/project.pbxproj index df9d159daa3..46c35dd8a1b 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 876dd750f92..e49eb2b7392 100755 --- a/packages/flutter_tools/bin/xcode_backend.sh +++ b/packages/flutter_tools/bin/xcode_backend.sh @@ -114,7 +114,6 @@ BuildApp() { 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 @@ BuildApp() { 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 270c95a6027..09d46d041c3 100644 --- a/packages/flutter_tools/lib/src/base/build.dart +++ b/packages/flutter_tools/lib/src/base/build.dart @@ -179,13 +179,14 @@ class AOTSnapshotter { // 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 @@ class AOTSnapshotter { 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 @@ class AOTSnapshotter { /// source at [assemblyPath]. Future _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 commonBuildOptions = [ '-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([ - ...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 @@ class AOTSnapshotter { '-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) ...[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([ - 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 bbb03f2b1f9..18b7ab5bda1 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 @@ class BuildAotCommand extends BuildSubCommand with TargetPlatformBasedDevelopmen 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 @@ class BuildAotCommand extends BuildSubCommand with TargetPlatformBasedDevelopmen '-create', '-output', fs.path.join(outputPath, 'App.framework', 'App'), ]); - final Iterable dSYMs = iosBuilds.values.map((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([ - '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 exitCodeFuture) async { @@ -202,24 +194,18 @@ class BuildAotCommand extends BuildSubCommand with TargetPlatformBasedDevelopmen } } -Future validateBitcode() async { +Future 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(); - // Check for bitcode in Flutter binary. - final RunResult otoolResult = await xcode.otool([ - '-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(['--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 6fea9ad50f8..8888f8d923a 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 @@ class Xcode { return runCheckedAsync(['xcrun', 'clang', ...args]); } - Future dsymutil(List args) { - return runCheckedAsync(['xcrun', 'dsymutil', ...args]); - } - - Future strip(List args) { - return runCheckedAsync(['xcrun', 'strip', ...args]); - } - - Future otool(List args) { - return runCheckedAsync(['xcrun', 'otool', ...args]); + Future iPhoneSdkLocation() async { + final RunResult runResult = await runCheckedAsync( + ['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 32d78a41d1b..be3556ccc8c 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 @@ void main() { when(mockArtifacts.getArtifactPath(Artifact.snapshotDart, platform: anyNamed('platform'), mode: mode)).thenReturn(kSnapshotDart); } - - when(mockXcode.dsymutil(any)).thenAnswer((_) => Future.value( - RunResult( - ProcessResult(1, 0, '', ''), - ['command name', 'arguments...']), - ), - ); }); final Map contextOverrides = { @@ -210,14 +203,9 @@ void main() { verify(xcode.cc(argThat(contains('-fembed-bitcode')))).called(1); verify(xcode.clang(argThat(contains('-fembed-bitcode')))).called(1); - verify(xcode.dsymutil([ - '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 @@ void main() { ]); 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 7b18e6fb7cc..d30377a5ff7 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 @@ flutter_tools:lib/'''); when(mockXcode.cc(any)).thenAnswer((_) => Future.value(fakeRunResult)); when(mockXcode.clang(any)).thenAnswer((_) => Future.value(fakeRunResult)); - when(mockXcode.dsymutil(any)).thenAnswer((_) => Future.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: { ProcessManager: () => mockProcessManager, Xcode: () => mockXcode, @@ -278,14 +276,12 @@ flutter_tools:lib/'''); when(mockXcode.cc(any)).thenAnswer((_) => Future.value(fakeRunResult)); when(mockXcode.clang(any)).thenAnswer((_) => Future.value(fakeRunResult)); - when(mockXcode.dsymutil(any)).thenAnswer((_) => Future.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: { 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 344bbc1598c..398c0e8d244 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 @@ void main() { when(xcode.clang(any)).thenAnswer((Invocation invocation) { return Future.value(RunResult(FakeProcessResult()..exitCode = 0, ['test'])); }); - when(xcode.dsymutil(any)).thenAnswer((Invocation invocation) { - return Future.value(RunResult(FakeProcessResult()..exitCode = 0, ['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 f12e63bd95b..bcda514f10c 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:file/memory.dart'; 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 @@ void main() { 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: { @@ -49,48 +43,21 @@ void main() { 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 ['foo'], - ); - when(mockXcode.otool(any)).thenAnswer((_) => Future.value(otoolResult)); - await expectToolExitLater( - validateBitcode(), - equals('The Flutter.framework at ios_profile/Flutter.framework does not contain bitcode.'), - ); - }, overrides: { - 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 ['foo'], - ); final RunResult clangResult = RunResult( FakeProcessResult(stdout: 'Apple LLVM version 10.0.0 (clang-4567.1.1.1)\nBlahBlah\n', stderr: ''), const ['foo'], ); - when(mockXcode.otool(any)).thenAnswer((_) => Future.value(otoolResult)); when(mockXcode.clang(any)).thenAnswer((_) => Future.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 @@ void main() { flutterFramework.childFile('Flutter').createSync(); final File infoPlist = flutterFramework.childFile('Info.plist')..createSync(); - final RunResult otoolResult = RunResult( - FakeProcessResult(stdout: '__LLVM', stderr: ''), - const ['foo'], - ); final RunResult clangResult = RunResult( FakeProcessResult(stdout: 'Apple LLVM version 10.0.1 (clang-1234.1.12.1)\nBlahBlah\n', stderr: ''), const ['foo'], ); - when(mockXcode.otool(any)).thenAnswer((_) => Future.value(otoolResult)); when(mockXcode.clang(any)).thenAnswer((_) => Future.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: { @@ -141,19 +103,14 @@ void main() { flutterFramework.childFile('Flutter').createSync(); final File infoPlist = flutterFramework.childFile('Info.plist')..createSync(); - final RunResult otoolResult = RunResult( - FakeProcessResult(stdout: '__LLVM', stderr: ''), - const ['foo'], - ); final RunResult clangResult = RunResult( FakeProcessResult(stdout: 'Apple LLVM version 11.0.1 (clang-1234.1.12.1)\nBlahBlah\n', stderr: ''), const ['foo'], ); - when(mockXcode.otool(any)).thenAnswer((_) => Future.value(otoolResult)); when(mockXcode.clang(any)).thenAnswer((_) => Future.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: {