From 802301e16f23fa05a6446bb498bfc0fed1f619d7 Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Tue, 1 May 2018 15:14:20 -0700 Subject: [PATCH] Extract shared library build from AOT snapshotting (#17166) --- .../flutter_tools/lib/src/base/build.dart | 87 ++++++++++++------- .../flutter_tools/test/base/build_test.dart | 10 +++ 2 files changed, 65 insertions(+), 32 deletions(-) diff --git a/packages/flutter_tools/lib/src/base/build.dart b/packages/flutter_tools/lib/src/base/build.dart index 5818efde31b..975900d70b9 100644 --- a/packages/flutter_tools/lib/src/base/build.dart +++ b/packages/flutter_tools/lib/src/base/build.dart @@ -336,38 +336,13 @@ class AOTSnapshotter { // On iOS, we use Xcode to compile the snapshot into a dynamic library that the // end-developer can link into their app. if (platform == TargetPlatform.ios) { - printStatus('Building App.framework...'); - const List commonBuildOptions = const ['-arch', 'arm64', '-miphoneos-version-min=8.0']; - - final String assemblyO = fs.path.join(outputDir.path, 'snapshot_assembly.o'); - await xcode.cc(commonBuildOptions.toList()..addAll(['-c', assembly, '-o', assemblyO])); - - final String frameworkDir = fs.path.join(outputDir.path, 'App.framework'); - fs.directory(frameworkDir).createSync(recursive: true); - final String appLib = fs.path.join(frameworkDir, 'App'); - final List linkArgs = commonBuildOptions.toList()..addAll([ - '-dynamiclib', - '-Xlinker', '-rpath', '-Xlinker', '@executable_path/Frameworks', - '-Xlinker', '-rpath', '-Xlinker', '@loader_path/Frameworks', - '-install_name', '@rpath/App.framework/App', - '-o', appLib, - assemblyO, - ]); - await xcode.clang(linkArgs); - } else { - if (compileToSharedLibrary) { - // A word of warning: Instead of compiling via two steps, to a .o file and - // then to a .so file we use only one command. When using two commands - // gcc will end up putting a .eh_frame and a .debug_frame into the shared - // library. Without stripping .debug_frame afterwards, unwinding tools - // based upon libunwind use just one and ignore the contents of the other - // (which causes it to not look into the other section and therefore not - // find the correct unwinding information). - final String assemblySo = fs.path.join(outputDir.path, 'app.so'); - await runCheckedAsync([androidSdk.ndkCompiler] - ..addAll(androidSdk.ndkCompilerArgs) - ..addAll([ '-shared', '-nostdlib', '-o', assemblySo, assembly ])); - } + final RunResult result = await _buildIosFramework(assemblyPath: assembly, outputPath: outputDir.path); + if (result.exitCode != 0) + return result.exitCode; + } else if (compileToSharedLibrary) { + final RunResult result = await _buildAndroidSharedLibrary(assemblyPath: assembly, outputPath: outputDir.path); + if (result.exitCode != 0) + return result.exitCode; } // Compute and record build fingerprint. @@ -375,6 +350,54 @@ class AOTSnapshotter { return 0; } + /// Builds an iOS framework at [outputPath]/App.framework from the assembly + /// source at [assemblyPath]. + Future _buildIosFramework({ + @required String assemblyPath, + @required String outputPath, + }) async { + printStatus('Building App.framework...'); + const List commonBuildOptions = const ['-arch', 'arm64', '-miphoneos-version-min=8.0']; + + final String assemblyO = fs.path.join(outputPath, 'snapshot_assembly.o'); + final RunResult compileResult = await xcode.cc(commonBuildOptions.toList()..addAll(['-c', assemblyPath, '-o', assemblyO])); + if (compileResult.exitCode != 0) + return compileResult; + + final String frameworkDir = fs.path.join(outputPath, 'App.framework'); + fs.directory(frameworkDir).createSync(recursive: true); + final String appLib = fs.path.join(frameworkDir, 'App'); + final List linkArgs = commonBuildOptions.toList()..addAll([ + '-dynamiclib', + '-Xlinker', '-rpath', '-Xlinker', '@executable_path/Frameworks', + '-Xlinker', '-rpath', '-Xlinker', '@loader_path/Frameworks', + '-install_name', '@rpath/App.framework/App', + '-o', appLib, + assemblyO, + ]); + final RunResult linkResult = await xcode.clang(linkArgs); + return linkResult; + } + + /// Builds an Android shared library at [outputPath]/app.so from the assembly + /// source at [assemblyPath]. + Future _buildAndroidSharedLibrary({ + @required String assemblyPath, + @required String outputPath, + }) async { + // A word of warning: Instead of compiling via two steps, to a .o file and + // then to a .so file we use only one command. When using two commands + // gcc will end up putting a .eh_frame and a .debug_frame into the shared + // library. Without stripping .debug_frame afterwards, unwinding tools + // based upon libunwind use just one and ignore the contents of the other + // (which causes it to not look into the other section and therefore not + // find the correct unwinding information). + final String assemblySo = fs.path.join(outputPath, 'app.so'); + return await runCheckedAsync([androidSdk.ndkCompiler] + ..addAll(androidSdk.ndkCompilerArgs) + ..addAll([ '-shared', '-nostdlib', '-o', assemblySo, assemblyPath ])); + } + /// Compiles a Dart file to kernel. /// /// Returns the output kernel file path, or null on failure. diff --git a/packages/flutter_tools/test/base/build_test.dart b/packages/flutter_tools/test/base/build_test.dart index c8b654281e5..d34c2330e39 100644 --- a/packages/flutter_tools/test/base/build_test.dart +++ b/packages/flutter_tools/test/base/build_test.dart @@ -12,6 +12,8 @@ import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/base/build.dart'; import 'package:flutter_tools/src/base/context.dart'; import 'package:flutter_tools/src/base/file_system.dart'; +import 'package:flutter_tools/src/base/io.dart'; +import 'package:flutter_tools/src/base/process.dart'; import 'package:flutter_tools/src/ios/mac.dart'; import 'package:flutter_tools/src/version.dart'; import 'package:mockito/mockito.dart'; @@ -646,6 +648,10 @@ void main() { fs.path.join(outputPath, 'snapshot.d'): '', }; + final RunResult successResult = new RunResult(new ProcessResult(1, 0, '', '')); + when(xcode.cc(any)).thenAnswer((_) => new Future.value(successResult)); + when(xcode.clang(any)).thenAnswer((_) => new Future.value(successResult)); + final int genSnapshotExitCode = await snapshotter.build( platform: TargetPlatform.ios, buildMode: BuildMode.profile, @@ -688,6 +694,10 @@ void main() { fs.path.join(outputPath, 'snapshot.d'): '', }; + final RunResult successResult = new RunResult(new ProcessResult(1, 0, '', '')); + when(xcode.cc(any)).thenAnswer((_) => new Future.value(successResult)); + when(xcode.clang(any)).thenAnswer((_) => new Future.value(successResult)); + final int genSnapshotExitCode = await snapshotter.build( platform: TargetPlatform.ios, buildMode: BuildMode.release,