Unverified Commit b21e780a authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[flutter_tools] refactor AotBuilder, removing globals and duplicated kernel compilation (#52896)

Reland of #52091
parent 11efa00f
...@@ -6,15 +6,18 @@ import 'dart:async'; ...@@ -6,15 +6,18 @@ import 'dart:async';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import 'base/build.dart';
import 'base/common.dart'; import 'base/common.dart';
import 'base/io.dart';
import 'base/logger.dart'; import 'base/logger.dart';
import 'base/process.dart';
import 'build_info.dart'; import 'build_info.dart';
import 'dart/package_map.dart'; import 'build_system/build_system.dart';
import 'build_system/targets/dart.dart';
import 'build_system/targets/icon_tree_shaker.dart';
import 'build_system/targets/ios.dart';
import 'cache.dart';
import 'convert.dart';
import 'globals.dart' as globals; import 'globals.dart' as globals;
import 'ios/bitcode.dart'; import 'ios/bitcode.dart';
import 'project.dart';
/// Builds AOT snapshots given a platform, build mode and a path to a Dart /// Builds AOT snapshots given a platform, build mode and a path to a Dart
/// library. /// library.
...@@ -26,18 +29,41 @@ class AotBuilder { ...@@ -26,18 +29,41 @@ class AotBuilder {
@required String mainDartFile, @required String mainDartFile,
bool bitcode = kBitcodeEnabledDefault, bool bitcode = kBitcodeEnabledDefault,
bool quiet = true, bool quiet = true,
bool reportTimings = false,
Iterable<DarwinArch> iosBuildArchs = defaultIOSArchs, Iterable<DarwinArch> iosBuildArchs = defaultIOSArchs,
}) async { }) async {
if (platform == null) { if (platform == null) {
throwToolExit('No AOT build platform specified'); throwToolExit('No AOT build platform specified');
} }
Target target;
if (bitcode) { bool expectSo = false;
if (platform != TargetPlatform.ios) { switch (platform) {
throwToolExit('Bitcode is only supported on iOS (TargetPlatform is $platform).'); case TargetPlatform.android:
} case TargetPlatform.darwin_x64:
await validateBitcode(buildInfo.mode, platform); case TargetPlatform.linux_x64:
case TargetPlatform.windows_x64:
case TargetPlatform.fuchsia_arm64:
case TargetPlatform.tester:
case TargetPlatform.web_javascript:
case TargetPlatform.android_x86:
throwToolExit('$platform is not supported in AOT.');
break;
case TargetPlatform.fuchsia_x64:
throwToolExit(
"To build release for fuchsia, use 'flutter build fuchsia --release'"
);
break;
case TargetPlatform.ios:
target = buildInfo.isRelease
? const AotAssemblyRelease()
: const AotAssemblyProfile();
break;
case TargetPlatform.android_arm:
case TargetPlatform.android_arm64:
case TargetPlatform.android_x64:
expectSo = true;
target = buildInfo.isRelease
? const AotElfRelease()
: const AotElfProfile();
} }
Status status; Status status;
...@@ -48,102 +74,46 @@ class AotBuilder { ...@@ -48,102 +74,46 @@ class AotBuilder {
timeout: timeoutConfiguration.slowOperation, timeout: timeoutConfiguration.slowOperation,
); );
} }
try {
final AOTSnapshotter snapshotter = AOTSnapshotter(reportTimings: reportTimings);
// Compile to kernel. final Environment environment = Environment(
final String kernelOut = await snapshotter.compileKernel( projectDir: globals.fs.currentDirectory,
platform: platform, outputDir: globals.fs.directory(outputPath),
buildMode: buildInfo.mode, buildDir: FlutterProject.current().dartTool.childDirectory('flutter_build'),
mainPath: mainDartFile, cacheDir: null,
packagesPath: PackageMap.globalPackagesPath, flutterRootDir: globals.fs.directory(Cache.flutterRoot),
trackWidgetCreation: buildInfo.trackWidgetCreation, defines: <String, String>{
outputPath: outputPath, kTargetFile: mainDartFile ?? globals.fs.path.join('lib', 'main.dart'),
extraFrontEndOptions: buildInfo.extraFrontEndOptions, kBuildMode: getNameForBuildMode(buildInfo.mode),
dartDefines: buildInfo.dartDefines kTargetPlatform: getNameForTargetPlatform(platform),
); kIconTreeShakerFlag: buildInfo.treeShakeIcons.toString(),
if (kernelOut == null) { kDartDefines: jsonEncode(buildInfo.dartDefines),
throwToolExit('Compiler terminated unexpectedly.'); kBitcodeFlag: bitcode.toString(),
return; if (buildInfo?.extraGenSnapshotOptions?.isNotEmpty ?? false)
kExtraGenSnapshotOptions: buildInfo.extraGenSnapshotOptions.join(','),
if (buildInfo?.extraFrontEndOptions?.isNotEmpty ?? false)
kExtraFrontEndOptions: buildInfo.extraFrontEndOptions.join(','),
if (platform == TargetPlatform.ios)
kIosArchs: iosBuildArchs.map(getNameForDarwinArch).join(' ')
} }
);
final BuildResult result = await buildSystem.build(target, environment);
status?.stop();
// Build AOT snapshot. if (!result.success) {
if (platform == TargetPlatform.ios) { for (final ExceptionMeasurement measurement in result.exceptions.values) {
// Determine which iOS architectures to build for. globals.printError(measurement.exception.toString());
final Map<DarwinArch, String> iosBuilds = <DarwinArch, String>{};
for (final DarwinArch arch in iosBuildArchs) {
iosBuilds[arch] = globals.fs.path.join(outputPath, getNameForDarwinArch(arch));
}
// Generate AOT snapshot and compile to arch-specific App.framework.
final Map<DarwinArch, Future<int>> exitCodes = <DarwinArch, Future<int>>{};
iosBuilds.forEach((DarwinArch iosArch, String outputPath) {
exitCodes[iosArch] = snapshotter.build(
platform: platform,
darwinArch: iosArch,
buildMode: buildInfo.mode,
mainPath: kernelOut,
packagesPath: PackageMap.globalPackagesPath,
outputPath: outputPath,
extraGenSnapshotOptions: buildInfo.extraGenSnapshotOptions,
bitcode: bitcode,
quiet: quiet,
splitDebugInfo: null,
dartObfuscation: false,
).then<int>((int buildExitCode) {
return buildExitCode;
});
});
// Merge arch-specific App.frameworks into a multi-arch App.framework.
if ((await Future.wait<int>(exitCodes.values)).every((int buildExitCode) => buildExitCode == 0)) {
final Iterable<String> dylibs = iosBuilds.values.map<String>(
(String outputDir) => globals.fs.path.join(outputDir, 'App.framework', 'App'));
globals.fs.directory(globals.fs.path.join(outputPath, 'App.framework')).createSync();
await processUtils.run(
<String>[
'lipo',
...dylibs,
'-create',
'-output', globals.fs.path.join(outputPath, 'App.framework', 'App'),
],
throwOnError: true,
);
} else {
status?.cancel();
exitCodes.forEach((DarwinArch iosArch, Future<int> exitCodeFuture) async {
final int buildExitCode = await exitCodeFuture;
globals.printError('Snapshotting ($iosArch) exited with non-zero exit code: $buildExitCode');
});
}
} else {
// Android AOT snapshot.
final int snapshotExitCode = await snapshotter.build(
platform: platform,
buildMode: buildInfo.mode,
mainPath: kernelOut,
packagesPath: PackageMap.globalPackagesPath,
outputPath: outputPath,
extraGenSnapshotOptions: buildInfo.extraGenSnapshotOptions,
bitcode: false,
splitDebugInfo: null,
dartObfuscation: false,
);
if (snapshotExitCode != 0) {
status?.cancel();
throwToolExit('Snapshotting exited with non-zero exit code: $snapshotExitCode');
}
} }
} on ProcessException catch (error) { throwToolExit('The aot build failed.');
// Catch the String exceptions thrown from the `runSync` methods below.
status?.cancel();
globals.printError(error.toString());
return;
} }
status?.stop();
if (outputPath == null) { if (expectSo) {
throwToolExit(null); environment.buildDir.childFile('app.so')
.copySync(globals.fs.path.join(outputPath, 'app.so'));
} else {
globals.fs.directory(globals.fs.path.join(outputPath, 'App.framework'))
.createSync(recursive: true);
environment.buildDir.childDirectory('App.framework').childFile('App')
.copySync(globals.fs.path.join(outputPath, 'App.framework', 'App'));
} }
final String builtMessage = 'Built to $outputPath${globals.fs.path.separator}.'; final String builtMessage = 'Built to $outputPath${globals.fs.path.separator}.';
......
...@@ -3,23 +3,17 @@ ...@@ -3,23 +3,17 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:async'; import 'dart:async';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import 'package:process/process.dart';
import '../artifacts.dart'; import '../artifacts.dart';
import '../build_info.dart'; import '../build_info.dart';
import '../bundle.dart';
import '../compile.dart';
import '../globals.dart' as globals;
import '../macos/xcode.dart'; import '../macos/xcode.dart';
import '../project.dart';
import 'context.dart';
import 'file_system.dart'; import 'file_system.dart';
import 'logger.dart';
import 'process.dart'; import 'process.dart';
GenSnapshot get genSnapshot => context.get<GenSnapshot>();
/// A snapshot build configuration. /// A snapshot build configuration.
class SnapshotType { class SnapshotType {
SnapshotType(this.platform, this.mode) SnapshotType(this.platform, this.mode)
...@@ -34,10 +28,18 @@ class SnapshotType { ...@@ -34,10 +28,18 @@ class SnapshotType {
/// Interface to the gen_snapshot command-line tool. /// Interface to the gen_snapshot command-line tool.
class GenSnapshot { class GenSnapshot {
const GenSnapshot(); GenSnapshot({
@required Artifacts artifacts,
static String getSnapshotterPath(SnapshotType snapshotType) { @required ProcessManager processManager,
return globals.artifacts.getArtifactPath( @required Logger logger,
}) : _artifacts = artifacts,
_processUtils = ProcessUtils(logger: logger, processManager: processManager);
final Artifacts _artifacts;
final ProcessUtils _processUtils;
String getSnapshotterPath(SnapshotType snapshotType) {
return _artifacts.getArtifactPath(
Artifact.genSnapshot, platform: snapshotType.platform, mode: snapshotType.mode); Artifact.genSnapshot, platform: snapshotType.platform, mode: snapshotType.mode);
} }
...@@ -51,6 +53,7 @@ class GenSnapshot { ...@@ -51,6 +53,7 @@ class GenSnapshot {
'Warning: This VM has been configured to obfuscate symbol information which violates the Dart standard.', 'Warning: This VM has been configured to obfuscate symbol information which violates the Dart standard.',
' See dartbug.com/30524 for more information.', ' See dartbug.com/30524 for more information.',
}; };
Future<int> run({ Future<int> run({
@required SnapshotType snapshotType, @required SnapshotType snapshotType,
DarwinArch darwinArch, DarwinArch darwinArch,
...@@ -68,7 +71,7 @@ class GenSnapshot { ...@@ -68,7 +71,7 @@ class GenSnapshot {
snapshotterPath += '_' + getNameForDarwinArch(darwinArch); snapshotterPath += '_' + getNameForDarwinArch(darwinArch);
} }
return processUtils.stream( return _processUtils.stream(
<String>[snapshotterPath, ...args], <String>[snapshotterPath, ...args],
mapFunction: (String line) => kIgnoredWarnings.contains(line) ? null : line, mapFunction: (String line) => kIgnoredWarnings.contains(line) ? null : line,
); );
...@@ -76,7 +79,26 @@ class GenSnapshot { ...@@ -76,7 +79,26 @@ class GenSnapshot {
} }
class AOTSnapshotter { class AOTSnapshotter {
AOTSnapshotter({this.reportTimings = false}); AOTSnapshotter({
this.reportTimings = false,
@required Logger logger,
@required FileSystem fileSystem,
@required Xcode xcode,
@required ProcessManager processManager,
@required Artifacts artifacts,
}) : _logger = logger,
_fileSystem = fileSystem,
_xcode = xcode,
_genSnapshot = GenSnapshot(
artifacts: artifacts,
processManager: processManager,
logger: logger,
);
final Logger _logger;
final FileSystem _fileSystem;
final Xcode _xcode;
final GenSnapshot _genSnapshot;
/// If true then AOTSnapshotter would report timings for individual building /// If true then AOTSnapshotter would report timings for individual building
/// steps (Dart front-end parsing and snapshot generation) in a stable /// steps (Dart front-end parsing and snapshot generation) in a stable
...@@ -97,40 +119,43 @@ class AOTSnapshotter { ...@@ -97,40 +119,43 @@ class AOTSnapshotter {
@required bool dartObfuscation, @required bool dartObfuscation,
bool quiet = false, bool quiet = false,
}) async { }) async {
// TODO(cbracken): replace IOSArch with TargetPlatform.ios_{armv7,arm64}.
assert(platform != TargetPlatform.ios || darwinArch != null);
if (bitcode && platform != TargetPlatform.ios) { if (bitcode && platform != TargetPlatform.ios) {
globals.printError('Bitcode is only supported for iOS.'); _logger.printError('Bitcode is only supported for iOS.');
return 1; return 1;
} }
if (!_isValidAotPlatform(platform, buildMode)) { if (!_isValidAotPlatform(platform, buildMode)) {
globals.printError('${getNameForTargetPlatform(platform)} does not support AOT compilation.'); _logger.printError('${getNameForTargetPlatform(platform)} does not support AOT compilation.');
return 1; return 1;
} }
// TODO(cbracken): replace IOSArch with TargetPlatform.ios_{armv7,arm64}.
assert(platform != TargetPlatform.ios || darwinArch != null);
final Directory outputDir = globals.fs.directory(outputPath); final Directory outputDir = _fileSystem.directory(outputPath);
outputDir.createSync(recursive: true); outputDir.createSync(recursive: true);
final List<String> genSnapshotArgs = <String>[ final List<String> genSnapshotArgs = <String>[
'--deterministic', '--deterministic',
]; ];
if (extraGenSnapshotOptions != null && extraGenSnapshotOptions.isNotEmpty) { if (extraGenSnapshotOptions != null && extraGenSnapshotOptions.isNotEmpty) {
globals.printTrace('Extra gen_snapshot options: $extraGenSnapshotOptions'); _logger.printTrace('Extra gen_snapshot options: $extraGenSnapshotOptions');
genSnapshotArgs.addAll(extraGenSnapshotOptions); genSnapshotArgs.addAll(extraGenSnapshotOptions);
} }
final String assembly = globals.fs.path.join(outputDir.path, 'snapshot_assembly.S'); final String assembly = _fileSystem.path.join(outputDir.path, 'snapshot_assembly.S');
if (platform == TargetPlatform.ios || platform == TargetPlatform.darwin_x64) { if (platform == TargetPlatform.ios || platform == TargetPlatform.darwin_x64) {
// Assembly AOT snapshot. genSnapshotArgs.addAll(<String>[
genSnapshotArgs.add('--snapshot_kind=app-aot-assembly'); '--snapshot_kind=app-aot-assembly',
genSnapshotArgs.add('--assembly=$assembly'); '--assembly=$assembly',
genSnapshotArgs.add('--strip'); '--strip'
]);
} else { } else {
final String aotSharedLibrary = globals.fs.path.join(outputDir.path, 'app.so'); final String aotSharedLibrary = _fileSystem.path.join(outputDir.path, 'app.so');
genSnapshotArgs.add('--snapshot_kind=app-aot-elf'); genSnapshotArgs.addAll(<String>[
genSnapshotArgs.add('--elf=$aotSharedLibrary'); '--snapshot_kind=app-aot-elf',
genSnapshotArgs.add('--strip'); '--elf=$aotSharedLibrary',
'--strip'
]);
} }
if (platform == TargetPlatform.android_arm || darwinArch == DarwinArch.armv7) { if (platform == TargetPlatform.android_arm || darwinArch == DarwinArch.armv7) {
...@@ -150,7 +175,7 @@ class AOTSnapshotter { ...@@ -150,7 +175,7 @@ class AOTSnapshotter {
final String debugFilename = 'app.$archName.symbols'; final String debugFilename = 'app.$archName.symbols';
final bool shouldSplitDebugInfo = splitDebugInfo?.isNotEmpty ?? false; final bool shouldSplitDebugInfo = splitDebugInfo?.isNotEmpty ?? false;
if (shouldSplitDebugInfo) { if (shouldSplitDebugInfo) {
globals.fs.directory(splitDebugInfo) _fileSystem.directory(splitDebugInfo)
.createSync(recursive: true); .createSync(recursive: true);
} }
...@@ -161,7 +186,7 @@ class AOTSnapshotter { ...@@ -161,7 +186,7 @@ class AOTSnapshotter {
'--lazy-async-stacks', '--lazy-async-stacks',
if (shouldSplitDebugInfo) ...<String>[ if (shouldSplitDebugInfo) ...<String>[
'--dwarf-stack-traces', '--dwarf-stack-traces',
'--save-debugging-info=${globals.fs.path.join(splitDebugInfo, debugFilename)}' '--save-debugging-info=${_fileSystem.path.join(splitDebugInfo, debugFilename)}'
], ],
if (dartObfuscation) if (dartObfuscation)
'--obfuscate', '--obfuscate',
...@@ -170,15 +195,13 @@ class AOTSnapshotter { ...@@ -170,15 +195,13 @@ class AOTSnapshotter {
genSnapshotArgs.add(mainPath); genSnapshotArgs.add(mainPath);
final SnapshotType snapshotType = SnapshotType(platform, buildMode); final SnapshotType snapshotType = SnapshotType(platform, buildMode);
final int genSnapshotExitCode = final int genSnapshotExitCode = await _genSnapshot.run(
await _timedStep('snapshot(CompileTime)', 'aot-snapshot',
() => genSnapshot.run(
snapshotType: snapshotType, snapshotType: snapshotType,
additionalArgs: genSnapshotArgs, additionalArgs: genSnapshotArgs,
darwinArch: darwinArch, darwinArch: darwinArch,
)); );
if (genSnapshotExitCode != 0) { if (genSnapshotExitCode != 0) {
globals.printError('Dart snapshot generator failed with exit code $genSnapshotExitCode'); _logger.printError('Dart snapshot generator failed with exit code $genSnapshotExitCode');
return genSnapshotExitCode; return genSnapshotExitCode;
} }
...@@ -212,7 +235,7 @@ class AOTSnapshotter { ...@@ -212,7 +235,7 @@ class AOTSnapshotter {
}) async { }) async {
final String targetArch = getNameForDarwinArch(appleArch); final String targetArch = getNameForDarwinArch(appleArch);
if (!quiet) { if (!quiet) {
globals.printStatus('Building App.framework for $targetArch...'); _logger.printStatus('Building App.framework for $targetArch...');
} }
final List<String> commonBuildOptions = <String>[ final List<String> commonBuildOptions = <String>[
...@@ -222,15 +245,15 @@ class AOTSnapshotter { ...@@ -222,15 +245,15 @@ class AOTSnapshotter {
]; ];
const String embedBitcodeArg = '-fembed-bitcode'; const String embedBitcodeArg = '-fembed-bitcode';
final String assemblyO = globals.fs.path.join(outputPath, 'snapshot_assembly.o'); final String assemblyO = _fileSystem.path.join(outputPath, 'snapshot_assembly.o');
List<String> isysrootArgs; List<String> isysrootArgs;
if (isIOS) { if (isIOS) {
final String iPhoneSDKLocation = await globals.xcode.sdkLocation(SdkType.iPhone); final String iPhoneSDKLocation = await _xcode.sdkLocation(SdkType.iPhone);
if (iPhoneSDKLocation != null) { if (iPhoneSDKLocation != null) {
isysrootArgs = <String>['-isysroot', iPhoneSDKLocation]; isysrootArgs = <String>['-isysroot', iPhoneSDKLocation];
} }
} }
final RunResult compileResult = await globals.xcode.cc(<String>[ final RunResult compileResult = await _xcode.cc(<String>[
'-arch', targetArch, '-arch', targetArch,
if (isysrootArgs != null) ...isysrootArgs, if (isysrootArgs != null) ...isysrootArgs,
if (bitcode) embedBitcodeArg, if (bitcode) embedBitcodeArg,
...@@ -240,13 +263,13 @@ class AOTSnapshotter { ...@@ -240,13 +263,13 @@ class AOTSnapshotter {
assemblyO, assemblyO,
]); ]);
if (compileResult.exitCode != 0) { if (compileResult.exitCode != 0) {
globals.printError('Failed to compile AOT snapshot. Compiler terminated with exit code ${compileResult.exitCode}'); _logger.printError('Failed to compile AOT snapshot. Compiler terminated with exit code ${compileResult.exitCode}');
return compileResult; return compileResult;
} }
final String frameworkDir = globals.fs.path.join(outputPath, 'App.framework'); final String frameworkDir = _fileSystem.path.join(outputPath, 'App.framework');
globals.fs.directory(frameworkDir).createSync(recursive: true); _fileSystem.directory(frameworkDir).createSync(recursive: true);
final String appLib = globals.fs.path.join(frameworkDir, 'App'); final String appLib = _fileSystem.path.join(frameworkDir, 'App');
final List<String> linkArgs = <String>[ final List<String> linkArgs = <String>[
...commonBuildOptions, ...commonBuildOptions,
'-dynamiclib', '-dynamiclib',
...@@ -258,64 +281,13 @@ class AOTSnapshotter { ...@@ -258,64 +281,13 @@ class AOTSnapshotter {
'-o', appLib, '-o', appLib,
assemblyO, assemblyO,
]; ];
final RunResult linkResult = await globals.xcode.clang(linkArgs); final RunResult linkResult = await _xcode.clang(linkArgs);
if (linkResult.exitCode != 0) { if (linkResult.exitCode != 0) {
globals.printError('Failed to link AOT snapshot. Linker terminated with exit code ${compileResult.exitCode}'); _logger.printError('Failed to link AOT snapshot. Linker terminated with exit code ${compileResult.exitCode}');
} }
return linkResult; return linkResult;
} }
/// Compiles a Dart file to kernel.
///
/// Returns the output kernel file path, or null on failure.
Future<String> compileKernel({
@required TargetPlatform platform,
@required BuildMode buildMode,
@required String mainPath,
@required String packagesPath,
@required String outputPath,
@required bool trackWidgetCreation,
@required List<String> dartDefines,
List<String> extraFrontEndOptions = const <String>[],
}) async {
final FlutterProject flutterProject = FlutterProject.current();
final Directory outputDir = globals.fs.directory(outputPath);
outputDir.createSync(recursive: true);
globals.printTrace('Compiling Dart to kernel: $mainPath');
if ((extraFrontEndOptions != null) && extraFrontEndOptions.isNotEmpty) {
globals.printTrace('Extra front-end options: $extraFrontEndOptions');
}
final String depfilePath = globals.fs.path.join(outputPath, 'kernel_compile.d');
final KernelCompiler kernelCompiler = await kernelCompilerFactory.create(flutterProject);
final CompilerOutput compilerOutput =
await _timedStep('frontend(CompileTime)', 'aot-kernel',
() => kernelCompiler.compile(
sdkRoot: globals.artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath, mode: buildMode),
mainPath: mainPath,
packagesPath: packagesPath,
outputFilePath: getKernelPathForTransformerOptions(
globals.fs.path.join(outputPath, 'app.dill'),
trackWidgetCreation: trackWidgetCreation,
),
depFilePath: depfilePath,
extraFrontEndOptions: extraFrontEndOptions,
linkPlatformKernelIn: true,
aot: true,
buildMode: buildMode,
trackWidgetCreation: trackWidgetCreation,
dartDefines: dartDefines,
));
// Write path to frontend_server, since things need to be re-generated when that changes.
final String frontendPath = globals.artifacts.getArtifactPath(Artifact.frontendServerSnapshotForEngineDartSdk);
globals.fs.directory(outputPath).childFile('frontend_server.d').writeAsStringSync('frontend_server.d: $frontendPath\n');
return compilerOutput?.outputFilename;
}
bool _isValidAotPlatform(TargetPlatform platform, BuildMode buildMode) { bool _isValidAotPlatform(TargetPlatform platform, BuildMode buildMode) {
if (buildMode == BuildMode.debug) { if (buildMode == BuildMode.debug) {
return false; return false;
...@@ -328,19 +300,4 @@ class AOTSnapshotter { ...@@ -328,19 +300,4 @@ class AOTSnapshotter {
TargetPlatform.darwin_x64, TargetPlatform.darwin_x64,
].contains(platform); ].contains(platform);
} }
/// This method is used to measure duration of an action and emit it into
/// verbose output from flutter_tool for other tools (e.g. benchmark runner)
/// to find.
/// Important: external performance tracking tools expect format of this
/// output to be stable.
Future<T> _timedStep<T>(String marker, String analyticsVar, FutureOr<T> Function() action) async {
final Stopwatch sw = Stopwatch()..start();
final T value = await action();
if (reportTimings) {
globals.printStatus('$marker: ${sw.elapsedMilliseconds} ms.');
}
globals.flutterUsage.sendTiming('build', analyticsVar, Duration(milliseconds: sw.elapsedMilliseconds));
return value;
}
} }
...@@ -697,7 +697,7 @@ abstract class Status { ...@@ -697,7 +697,7 @@ abstract class Status {
@required Duration timeout, @required Duration timeout,
@required TimeoutConfiguration timeoutConfiguration, @required TimeoutConfiguration timeoutConfiguration,
@required Stopwatch stopwatch, @required Stopwatch stopwatch,
@required AnsiTerminal terminal, @required Terminal terminal,
VoidCallback onFinish, VoidCallback onFinish,
SlowWarningCallback slowWarningCallback, SlowWarningCallback slowWarningCallback,
}) { }) {
...@@ -877,7 +877,7 @@ class AnsiSpinner extends Status { ...@@ -877,7 +877,7 @@ class AnsiSpinner extends Status {
@required Duration timeout, @required Duration timeout,
@required TimeoutConfiguration timeoutConfiguration, @required TimeoutConfiguration timeoutConfiguration,
@required Stopwatch stopwatch, @required Stopwatch stopwatch,
@required AnsiTerminal terminal, @required Terminal terminal,
VoidCallback onFinish, VoidCallback onFinish,
this.slowWarningCallback, this.slowWarningCallback,
Stdio stdio, Stdio stdio,
...@@ -893,7 +893,7 @@ class AnsiSpinner extends Status { ...@@ -893,7 +893,7 @@ class AnsiSpinner extends Status {
final String _backspaceChar = '\b'; final String _backspaceChar = '\b';
final String _clearChar = ' '; final String _clearChar = ' ';
final Stdio _stdio; final Stdio _stdio;
final AnsiTerminal _terminal; final Terminal _terminal;
bool timedOut = false; bool timedOut = false;
......
...@@ -210,7 +210,14 @@ class AndroidAot extends AotElfBase { ...@@ -210,7 +210,14 @@ class AndroidAot extends AotElfBase {
@override @override
Future<void> build(Environment environment) async { Future<void> build(Environment environment) async {
final AOTSnapshotter snapshotter = AOTSnapshotter(reportTimings: false); final AOTSnapshotter snapshotter = AOTSnapshotter(
reportTimings: false,
fileSystem: globals.fs,
logger: globals.logger,
xcode: globals.xcode,
processManager: globals.processManager,
artifacts: globals.artifacts,
);
final Directory output = environment.buildDir.childDirectory(_androidAbiName); final Directory output = environment.buildDir.childDirectory(_androidAbiName);
final String splitDebugInfo = environment.defines[kSplitDebugInfo]; final String splitDebugInfo = environment.defines[kSplitDebugInfo];
if (environment.defines[kBuildMode] == null) { if (environment.defines[kBuildMode] == null) {
......
...@@ -204,9 +204,7 @@ class KernelSnapshot extends Target { ...@@ -204,9 +204,7 @@ class KernelSnapshot extends Target {
final TargetPlatform targetPlatform = getTargetPlatformForName(environment.defines[kTargetPlatform]); final TargetPlatform targetPlatform = getTargetPlatformForName(environment.defines[kTargetPlatform]);
// This configuration is all optional. // This configuration is all optional.
final List<String> extraFrontEndOptions = <String>[ final List<String> extraFrontEndOptions = environment.defines[kExtraFrontEndOptions]?.split(',');
...?environment.defines[kExtraFrontEndOptions]?.split(',')
];
final List<String> fileSystemRoots = environment.defines[kFileSystemRoots]?.split(','); final List<String> fileSystemRoots = environment.defines[kFileSystemRoots]?.split(',');
final String fileSystemScheme = environment.defines[kFileSystemScheme]; final String fileSystemScheme = environment.defines[kFileSystemScheme];
...@@ -261,7 +259,14 @@ abstract class AotElfBase extends Target { ...@@ -261,7 +259,14 @@ abstract class AotElfBase extends Target {
@override @override
Future<void> build(Environment environment) async { Future<void> build(Environment environment) async {
final AOTSnapshotter snapshotter = AOTSnapshotter(reportTimings: false); final AOTSnapshotter snapshotter = AOTSnapshotter(
reportTimings: false,
fileSystem: globals.fs,
logger: globals.logger,
xcode: globals.xcode,
processManager: globals.processManager,
artifacts: globals.artifacts,
);
final String outputPath = environment.buildDir.path; final String outputPath = environment.buildDir.path;
if (environment.defines[kBuildMode] == null) { if (environment.defines[kBuildMode] == null) {
throw MissingDefineException(kBuildMode, 'aot_elf'); throw MissingDefineException(kBuildMode, 'aot_elf');
......
...@@ -28,7 +28,14 @@ abstract class AotAssemblyBase extends Target { ...@@ -28,7 +28,14 @@ abstract class AotAssemblyBase extends Target {
@override @override
Future<void> build(Environment environment) async { Future<void> build(Environment environment) async {
final AOTSnapshotter snapshotter = AOTSnapshotter(reportTimings: false); final AOTSnapshotter snapshotter = AOTSnapshotter(
reportTimings: false,
fileSystem: globals.fs,
logger: globals.logger,
xcode: globals.xcode,
artifacts: globals.artifacts,
processManager: globals.processManager,
);
final String buildOutputPath = environment.buildDir.path; final String buildOutputPath = environment.buildDir.path;
if (environment.defines[kBuildMode] == null) { if (environment.defines[kBuildMode] == null) {
throw MissingDefineException(kBuildMode, 'aot_assembly'); throw MissingDefineException(kBuildMode, 'aot_assembly');
...@@ -36,6 +43,8 @@ abstract class AotAssemblyBase extends Target { ...@@ -36,6 +43,8 @@ abstract class AotAssemblyBase extends Target {
if (environment.defines[kTargetPlatform] == null) { if (environment.defines[kTargetPlatform] == null) {
throw MissingDefineException(kTargetPlatform, 'aot_assembly'); throw MissingDefineException(kTargetPlatform, 'aot_assembly');
} }
final List<String> extraGenSnapshotOptions = environment
.defines[kExtraGenSnapshotOptions]?.split(',') ?? const <String>[];
final bool bitcode = environment.defines[kBitcodeFlag] == 'true'; final bool bitcode = environment.defines[kBitcodeFlag] == 'true';
final BuildMode buildMode = getBuildModeForName(environment.defines[kBuildMode]); final BuildMode buildMode = getBuildModeForName(environment.defines[kBuildMode]);
final TargetPlatform targetPlatform = getTargetPlatformForName(environment.defines[kTargetPlatform]); final TargetPlatform targetPlatform = getTargetPlatformForName(environment.defines[kTargetPlatform]);
...@@ -65,6 +74,7 @@ abstract class AotAssemblyBase extends Target { ...@@ -65,6 +74,7 @@ abstract class AotAssemblyBase extends Target {
quiet: true, quiet: true,
splitDebugInfo: splitDebugInfo, splitDebugInfo: splitDebugInfo,
dartObfuscation: dartObfuscation, dartObfuscation: dartObfuscation,
extraGenSnapshotOptions: extraGenSnapshotOptions,
)); ));
} }
final List<int> results = await Future.wait(pending); final List<int> results = await Future.wait(pending);
......
...@@ -202,7 +202,15 @@ class CompileMacOSFramework extends Target { ...@@ -202,7 +202,15 @@ class CompileMacOSFramework extends Target {
} }
final String splitDebugInfo = environment.defines[kSplitDebugInfo]; final String splitDebugInfo = environment.defines[kSplitDebugInfo];
final bool dartObfuscation = environment.defines[kDartObfuscation] == 'true'; final bool dartObfuscation = environment.defines[kDartObfuscation] == 'true';
final int result = await AOTSnapshotter(reportTimings: false).build( final AOTSnapshotter snapshotter = AOTSnapshotter(
reportTimings: false,
fileSystem: globals.fs,
logger: globals.logger,
xcode: globals.xcode,
artifacts: globals.artifacts,
processManager: globals.processManager
);
final int result = await snapshotter.build(
bitcode: false, bitcode: false,
buildMode: buildMode, buildMode: buildMode,
mainPath: environment.buildDir.childFile('app.dill').path, mainPath: environment.buildDir.childFile('app.dill').path,
......
...@@ -27,11 +27,6 @@ class BuildAotCommand extends BuildSubCommand with TargetPlatformBasedDevelopmen ...@@ -27,11 +27,6 @@ class BuildAotCommand extends BuildSubCommand with TargetPlatformBasedDevelopmen
allowed: <String>['android-arm', 'android-arm64', 'ios', 'android-x64'], allowed: <String>['android-arm', 'android-arm64', 'ios', 'android-x64'],
) )
..addFlag('quiet', defaultsTo: false) ..addFlag('quiet', defaultsTo: false)
..addFlag('report-timings',
negatable: false,
defaultsTo: false,
help: 'Report timing information about build steps in machine readable form,',
)
..addMultiOption('ios-arch', ..addMultiOption('ios-arch',
splitCommas: true, splitCommas: true,
defaultsTo: defaultIOSArchs.map<String>(getNameForDarwinArch), defaultsTo: defaultIOSArchs.map<String>(getNameForDarwinArch),
...@@ -80,7 +75,6 @@ class BuildAotCommand extends BuildSubCommand with TargetPlatformBasedDevelopmen ...@@ -80,7 +75,6 @@ class BuildAotCommand extends BuildSubCommand with TargetPlatformBasedDevelopmen
mainDartFile: findMainDartFile(targetFile), mainDartFile: findMainDartFile(targetFile),
bitcode: boolArg('bitcode'), bitcode: boolArg('bitcode'),
quiet: boolArg('quiet'), quiet: boolArg('quiet'),
reportTimings: boolArg('report-timings'),
iosBuildArchs: stringsArg('ios-arch').map<DarwinArch>(getIOSArchForName), iosBuildArchs: stringsArg('ios-arch').map<DarwinArch>(getIOSArchForName),
); );
return FlutterCommandResult.success(); return FlutterCommandResult.success();
......
...@@ -436,7 +436,6 @@ end ...@@ -436,7 +436,6 @@ end
mainDartFile: globals.fs.path.absolute(targetFile), mainDartFile: globals.fs.path.absolute(targetFile),
quiet: true, quiet: true,
bitcode: true, bitcode: true,
reportTimings: false,
iosBuildArchs: <DarwinArch>[DarwinArch.armv7, DarwinArch.arm64], iosBuildArchs: <DarwinArch>[DarwinArch.armv7, DarwinArch.arm64],
); );
} finally { } finally {
......
...@@ -11,7 +11,6 @@ import 'android/gradle_utils.dart'; ...@@ -11,7 +11,6 @@ import 'android/gradle_utils.dart';
import 'application_package.dart'; import 'application_package.dart';
import 'artifacts.dart'; import 'artifacts.dart';
import 'asset.dart'; import 'asset.dart';
import 'base/build.dart';
import 'base/config.dart'; import 'base/config.dart';
import 'base/context.dart'; import 'base/context.dart';
import 'base/io.dart'; import 'base/io.dart';
...@@ -125,7 +124,6 @@ Future<T> runInContext<T>( ...@@ -125,7 +124,6 @@ Future<T> runInContext<T>(
FuchsiaDeviceTools: () => FuchsiaDeviceTools(), FuchsiaDeviceTools: () => FuchsiaDeviceTools(),
FuchsiaSdk: () => FuchsiaSdk(), FuchsiaSdk: () => FuchsiaSdk(),
FuchsiaWorkflow: () => FuchsiaWorkflow(), FuchsiaWorkflow: () => FuchsiaWorkflow(),
GenSnapshot: () => const GenSnapshot(),
GradleUtils: () => GradleUtils(), GradleUtils: () => GradleUtils(),
HotRunnerConfig: () => HotRunnerConfig(), HotRunnerConfig: () => HotRunnerConfig(),
IMobileDevice: () => IMobileDevice(), IMobileDevice: () => IMobileDevice(),
......
...@@ -249,7 +249,7 @@ class XcodeProjectInterpreter { ...@@ -249,7 +249,7 @@ class XcodeProjectInterpreter {
@required ProcessManager processManager, @required ProcessManager processManager,
@required Logger logger, @required Logger logger,
@required FileSystem fileSystem, @required FileSystem fileSystem,
@required AnsiTerminal terminal, @required Terminal terminal,
}) : _platform = platform, }) : _platform = platform,
_fileSystem = fileSystem, _fileSystem = fileSystem,
_terminal = terminal, _terminal = terminal,
...@@ -259,7 +259,7 @@ class XcodeProjectInterpreter { ...@@ -259,7 +259,7 @@ class XcodeProjectInterpreter {
final Platform _platform; final Platform _platform;
final FileSystem _fileSystem; final FileSystem _fileSystem;
final ProcessUtils _processUtils; final ProcessUtils _processUtils;
final AnsiTerminal _terminal; final Terminal _terminal;
final Logger _logger; final Logger _logger;
static const String _executable = '/usr/bin/xcodebuild'; static const String _executable = '/usr/bin/xcodebuild';
......
...@@ -2,75 +2,70 @@ ...@@ -2,75 +2,70 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:async';
import 'dart:convert';
import 'package:file/memory.dart'; import 'package:file/memory.dart';
import 'package:flutter_tools/src/android/android_sdk.dart'; import 'package:mockito/mockito.dart';
import 'package:platform/platform.dart';
import 'package:flutter_tools/src/artifacts.dart'; import 'package:flutter_tools/src/artifacts.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/terminal.dart';
import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/base/build.dart'; import 'package:flutter_tools/src/base/build.dart';
import 'package:flutter_tools/src/base/context.dart'; import 'package:flutter_tools/src/ios/xcodeproj.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/macos/xcode.dart'; import 'package:flutter_tools/src/macos/xcode.dart';
import 'package:flutter_tools/src/version.dart';
import 'package:flutter_tools/src/globals.dart' as globals;
import 'package:mockito/mockito.dart';
import 'package:process/process.dart';
import '../../src/common.dart'; import '../../src/common.dart';
import '../../src/context.dart'; import '../../src/context.dart';
class MockFlutterVersion extends Mock implements FlutterVersion {} const FakeCommand kSdkPathCommand = FakeCommand(
class MockAndroidSdk extends Mock implements AndroidSdk {} command: <String>[
class MockArtifacts extends Mock implements Artifacts {} 'xcrun',
class MockXcode extends Mock implements Xcode {} '--sdk',
class MockProcessManager extends Mock implements ProcessManager {} 'iphoneos',
class MockProcess extends Mock implements Process {} '--show-sdk-path'
]
class _FakeGenSnapshot implements GenSnapshot { );
_FakeGenSnapshot({
this.succeed = true, const List<String> kDefaultClang = <String>[
}); '-miphoneos-version-min=8.0',
'-dynamiclib',
final bool succeed; '-Xlinker',
Map<String, String> outputs = <String, String>{}; '-rpath',
int _callCount = 0; '-Xlinker',
SnapshotType _snapshotType; '@executable_path/Frameworks',
String _depfilePath; '-Xlinker',
List<String> _additionalArgs; '-rpath',
'-Xlinker',
int get callCount => _callCount; '@loader_path/Frameworks',
'-install_name',
SnapshotType get snapshotType => _snapshotType; '@rpath/App.framework/App',
'-isysroot',
String get depfilePath => _depfilePath; '',
'-o',
List<String> get additionalArgs => _additionalArgs; 'build/foo/App.framework/App',
'build/foo/snapshot_assembly.o',
@override ];
Future<int> run({
SnapshotType snapshotType, const List<String> kBitcodeClang = <String>[
String depfilePath, '-miphoneos-version-min=8.0',
DarwinArch darwinArch, '-dynamiclib',
Iterable<String> additionalArgs = const <String>[], '-Xlinker',
}) async { '-rpath',
_callCount += 1; '-Xlinker',
_snapshotType = snapshotType; '@executable_path/Frameworks',
_depfilePath = depfilePath; '-Xlinker',
_additionalArgs = additionalArgs.toList(); '-rpath',
'-Xlinker',
if (!succeed) { '@loader_path/Frameworks',
return 1; '-install_name',
} '@rpath/App.framework/App',
outputs.forEach((String filePath, String fileContent) { '-fembed-bitcode',
globals.fs.file(filePath).writeAsString(fileContent); '-isysroot',
}); '',
return 0; '-o',
} 'build/foo/App.framework/App',
} 'build/foo/snapshot_assembly.o',
];
void main() { void main() {
group('SnapshotType', () { group('SnapshotType', () {
...@@ -81,181 +76,137 @@ void main() { ...@@ -81,181 +76,137 @@ void main() {
); );
}); });
test('does not throw, if target platform is null', () { test('does not throw, if target platform is null', () {
expect(SnapshotType(null, BuildMode.release), isNotNull); expect(() => SnapshotType(null, BuildMode.release), returnsNormally);
}); });
}); });
group('GenSnapshot', () { group('GenSnapshot', () {
GenSnapshot genSnapshot; GenSnapshot genSnapshot;
MockArtifacts mockArtifacts; MockArtifacts mockArtifacts;
MockProcessManager mockProcessManager; FakeProcessManager processManager;
MockProcess mockProc; BufferLogger logger;
setUp(() async { setUp(() async {
genSnapshot = const GenSnapshot();
mockArtifacts = MockArtifacts(); mockArtifacts = MockArtifacts();
mockProcessManager = MockProcessManager(); logger = BufferLogger.test();
mockProc = MockProcess(); processManager = FakeProcessManager.list(< FakeCommand>[]);
genSnapshot = GenSnapshot(
artifacts: mockArtifacts,
logger: logger,
processManager: processManager,
);
when(mockArtifacts.getArtifactPath(
any,
platform: anyNamed('platform'),
mode: anyNamed('mode'),
)).thenReturn('gen_snapshot');
}); });
final Map<Type, Generator> contextOverrides = <Type, Generator>{ testWithoutContext('android_x64', () async {
Artifacts: () => mockArtifacts, processManager.addCommand(const FakeCommand(
ProcessManager: () => mockProcessManager, command: <String>['gen_snapshot', '--additional_arg']
}; ));
testUsingContext('android_x64', () async { final int result = await genSnapshot.run(
when(mockArtifacts.getArtifactPath(Artifact.genSnapshot, snapshotType: SnapshotType(TargetPlatform.android_x64, BuildMode.release),
platform: TargetPlatform.android_x64, mode: BuildMode.release)) darwinArch: null,
.thenReturn('gen_snapshot'); additionalArgs: <String>['--additional_arg'],
when(mockProcessManager.start(any, );
workingDirectory: anyNamed('workingDirectory'), expect(result, 0);
environment: anyNamed('environment'))) });
.thenAnswer((_) => Future<Process>.value(mockProc));
when(mockProc.stdout).thenAnswer((_) => const Stream<List<int>>.empty()); testWithoutContext('iOS armv7', () async {
when(mockProc.stderr).thenAnswer((_) => const Stream<List<int>>.empty()); processManager.addCommand(const FakeCommand(
await genSnapshot.run( command: <String>['gen_snapshot_armv7', '--additional_arg']
snapshotType: ));
SnapshotType(TargetPlatform.android_x64, BuildMode.release),
darwinArch: null, final int result = await genSnapshot.run(
additionalArgs: <String>['--additional_arg']); snapshotType: SnapshotType(TargetPlatform.ios, BuildMode.release),
verify(mockProcessManager.start( darwinArch: DarwinArch.armv7,
<String>[ additionalArgs: <String>['--additional_arg'],
'gen_snapshot', );
'--additional_arg', expect(result, 0);
], });
workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment'), testWithoutContext('iOS arm64', () async {
)).called(1); processManager.addCommand(const FakeCommand(
}, overrides: contextOverrides); command: <String>['gen_snapshot_arm64', '--additional_arg']
));
testUsingContext('iOS armv7', () async {
when(mockArtifacts.getArtifactPath(Artifact.genSnapshot, final int result = await genSnapshot.run(
platform: TargetPlatform.ios, mode: BuildMode.release)) snapshotType: SnapshotType(TargetPlatform.ios, BuildMode.release),
.thenReturn('gen_snapshot'); darwinArch: DarwinArch.arm64,
when(mockProcessManager.start(any, additionalArgs: <String>['--additional_arg'],
workingDirectory: anyNamed('workingDirectory'), );
environment: anyNamed('environment'))) expect(result, 0);
.thenAnswer((_) => Future<Process>.value(mockProc)); });
when(mockProc.stdout).thenAnswer((_) => const Stream<List<int>>.empty());
when(mockProc.stderr).thenAnswer((_) => const Stream<List<int>>.empty()); testWithoutContext('--strip filters error output from gen_snapshot', () async {
await genSnapshot.run( processManager.addCommand(FakeCommand(
snapshotType: SnapshotType(TargetPlatform.ios, BuildMode.release), command: const <String>['gen_snapshot', '--strip'],
darwinArch: DarwinArch.armv7, stderr: 'ABC\n${GenSnapshot.kIgnoredWarnings.join('\n')}\nXYZ\n'
additionalArgs: <String>['--additional_arg']); ));
verify(mockProcessManager.start(
<String>[ final int result = await genSnapshot.run(
'gen_snapshot_armv7', snapshotType: SnapshotType(TargetPlatform.android_x64, BuildMode.release),
'--additional_arg', darwinArch: null,
], additionalArgs: <String>['--strip'],
workingDirectory: anyNamed('workingDirectory'), );
environment: anyNamed('environment')),
).called(1);
}, overrides: contextOverrides);
testUsingContext('iOS arm64', () async {
when(mockArtifacts.getArtifactPath(Artifact.genSnapshot,
platform: TargetPlatform.ios, mode: BuildMode.release))
.thenReturn('gen_snapshot');
when(mockProcessManager.start(any,
workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment')))
.thenAnswer((_) => Future<Process>.value(mockProc));
when(mockProc.stdout).thenAnswer((_) => const Stream<List<int>>.empty());
when(mockProc.stderr).thenAnswer((_) => const Stream<List<int>>.empty());
await genSnapshot.run(
snapshotType: SnapshotType(TargetPlatform.ios, BuildMode.release),
darwinArch: DarwinArch.arm64,
additionalArgs: <String>['--additional_arg']);
verify(mockProcessManager.start(
<String>[
'gen_snapshot_arm64',
'--additional_arg',
],
workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment'),
)).called(1);
}, overrides: contextOverrides);
testUsingContext('--strip filters outputs', () async {
when(mockArtifacts.getArtifactPath(Artifact.genSnapshot,
platform: TargetPlatform.android_x64, mode: BuildMode.release))
.thenReturn('gen_snapshot');
when(mockProcessManager.start(
<String>['gen_snapshot', '--strip'],
workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment')))
.thenAnswer((_) => Future<Process>.value(mockProc));
when(mockProc.stdout).thenAnswer((_) => const Stream<List<int>>.empty());
when(mockProc.stderr)
.thenAnswer((_) => Stream<String>.fromIterable(<String>[
'--ABC\n',
'Warning: Generating ELF library without DWARF debugging information.\n',
'--XYZ\n',
])
.transform<List<int>>(utf8.encoder));
await genSnapshot.run(
snapshotType:
SnapshotType(TargetPlatform.android_x64, BuildMode.release),
darwinArch: null,
additionalArgs: <String>['--strip']);
verify(mockProcessManager.start(
<String>['gen_snapshot', '--strip'],
workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment')))
.called(1);
expect(testLogger.errorText, contains('ABC'));
expect(testLogger.errorText, isNot(contains('ELF library')));
expect(testLogger.errorText, contains('XYZ'));
}, overrides: contextOverrides);
});
group('Snapshotter - AOT', () { expect(result, 0);
const String kSnapshotDart = 'snapshot.dart'; expect(logger.errorText, contains('ABC'));
const String kSDKPath = '/path/to/sdk'; for (final String ignoredWarning in GenSnapshot.kIgnoredWarnings) {
String skyEnginePath; expect(logger.errorText, isNot(contains(ignoredWarning)));
}
expect(logger.errorText, contains('XYZ'));
});
});
_FakeGenSnapshot genSnapshot; group('AOTSnapshotter', () {
MemoryFileSystem fs; MemoryFileSystem fileSystem;
AOTSnapshotter snapshotter; AOTSnapshotter snapshotter;
AOTSnapshotter snapshotterWithTimings;
MockAndroidSdk mockAndroidSdk;
MockArtifacts mockArtifacts; MockArtifacts mockArtifacts;
MockXcode mockXcode; FakeProcessManager processManager;
Logger logger;
setUp(() async { setUp(() async {
fs = MemoryFileSystem(); final Platform platform = FakePlatform(operatingSystem: 'macos');
fs.file(kSnapshotDart).createSync(); logger = BufferLogger.test();
fs.file('.packages').writeAsStringSync('sky_engine:file:///flutter/bin/cache/pkg/sky_engine/lib/'); fileSystem = MemoryFileSystem.test();
skyEnginePath = fs.path.fromUri(Uri.file('/flutter/bin/cache/pkg/sky_engine'));
fs.directory(fs.path.join(skyEnginePath, 'lib', 'ui')).createSync(recursive: true);
fs.directory(fs.path.join(skyEnginePath, 'sdk_ext')).createSync(recursive: true);
fs.file(fs.path.join(skyEnginePath, '.packages')).createSync();
fs.file(fs.path.join(skyEnginePath, 'lib', 'ui', 'ui.dart')).createSync();
fs.file(fs.path.join(skyEnginePath, 'sdk_ext', 'vmservice_io.dart')).createSync();
genSnapshot = _FakeGenSnapshot();
snapshotter = AOTSnapshotter();
snapshotterWithTimings = AOTSnapshotter(reportTimings: true);
mockAndroidSdk = MockAndroidSdk();
mockArtifacts = MockArtifacts(); mockArtifacts = MockArtifacts();
mockXcode = MockXcode(); processManager = FakeProcessManager.list(<FakeCommand>[]);
when(mockXcode.sdkLocation(any)).thenAnswer((_) => Future<String>.value(kSDKPath)); snapshotter = AOTSnapshotter(
fileSystem: fileSystem,
logger: logger,
xcode: Xcode(
fileSystem: fileSystem,
logger: logger,
platform: FakePlatform(operatingSystem: 'macos'),
processManager: processManager,
xcodeProjectInterpreter: XcodeProjectInterpreter(
platform: platform,
processManager: processManager,
logger: logger,
fileSystem: fileSystem,
terminal: Terminal.test(),
),
),
artifacts: mockArtifacts,
processManager: processManager,
);
when(mockArtifacts.getArtifactPath(
Artifact.genSnapshot,
platform: anyNamed('platform'),
mode: anyNamed('mode')),
).thenReturn('gen_snapshot');
}); });
final Map<Type, Generator> contextOverrides = <Type, Generator>{ testWithoutContext('does not build iOS with debug build mode', () async {
AndroidSdk: () => mockAndroidSdk, final String outputPath = fileSystem.path.join('build', 'foo');
Artifacts: () => mockArtifacts,
FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(),
GenSnapshot: () => genSnapshot,
Xcode: () => mockXcode,
};
testUsingContext('iOS debug AOT snapshot is invalid', () async {
final String outputPath = globals.fs.path.join('build', 'foo');
expect(await snapshotter.build( expect(await snapshotter.build(
platform: TargetPlatform.ios, platform: TargetPlatform.ios,
darwinArch: DarwinArch.arm64,
buildMode: BuildMode.debug, buildMode: BuildMode.debug,
mainPath: 'main.dill', mainPath: 'main.dill',
packagesPath: '.packages', packagesPath: '.packages',
...@@ -264,10 +215,11 @@ void main() { ...@@ -264,10 +215,11 @@ void main() {
splitDebugInfo: null, splitDebugInfo: null,
dartObfuscation: false, dartObfuscation: false,
), isNot(equals(0))); ), isNot(equals(0)));
}, overrides: contextOverrides); });
testWithoutContext('does not build android-arm with debug build mode', () async {
final String outputPath = fileSystem.path.join('build', 'foo');
testUsingContext('Android arm debug AOT snapshot is invalid', () async {
final String outputPath = globals.fs.path.join('build', 'foo');
expect(await snapshotter.build( expect(await snapshotter.build(
platform: TargetPlatform.android_arm, platform: TargetPlatform.android_arm,
buildMode: BuildMode.debug, buildMode: BuildMode.debug,
...@@ -278,10 +230,11 @@ void main() { ...@@ -278,10 +230,11 @@ void main() {
splitDebugInfo: null, splitDebugInfo: null,
dartObfuscation: false, dartObfuscation: false,
), isNot(0)); ), isNot(0));
}, overrides: contextOverrides); });
testWithoutContext('does not build android-arm64 with debug build mode', () async {
final String outputPath = fileSystem.path.join('build', 'foo');
testUsingContext('Android arm64 debug AOT snapshot is invalid', () async {
final String outputPath = globals.fs.path.join('build', 'foo');
expect(await snapshotter.build( expect(await snapshotter.build(
platform: TargetPlatform.android_arm64, platform: TargetPlatform.android_arm64,
buildMode: BuildMode.debug, buildMode: BuildMode.debug,
...@@ -292,22 +245,50 @@ void main() { ...@@ -292,22 +245,50 @@ void main() {
splitDebugInfo: null, splitDebugInfo: null,
dartObfuscation: false, dartObfuscation: false,
), isNot(0)); ), isNot(0));
}, overrides: contextOverrides); });
testUsingContext('iOS profile AOT with bitcode uses right flags', () async {
globals.fs.file('main.dill').writeAsStringSync('binary magic');
final String outputPath = globals.fs.path.join('build', 'foo');
globals.fs.directory(outputPath).createSync(recursive: true);
final String assembly = globals.fs.path.join(outputPath, 'snapshot_assembly.S');
genSnapshot.outputs = <String, String>{
assembly: 'blah blah\n.section __DWARF\nblah blah\n',
};
final RunResult successResult = RunResult(ProcessResult(1, 0, '', ''), <String>['command name', 'arguments...']); testWithoutContext('builds iOS with bitcode', () async {
when(mockXcode.cc(any)).thenAnswer((_) => Future<RunResult>.value(successResult)); final String outputPath = fileSystem.path.join('build', 'foo');
when(mockXcode.clang(any)).thenAnswer((_) => Future<RunResult>.value(successResult)); final String assembly = fileSystem.path.join(outputPath, 'snapshot_assembly.S');
processManager.addCommand(FakeCommand(
command: <String>[
'gen_snapshot_armv7',
'--deterministic',
'--snapshot_kind=app-aot-assembly',
'--assembly=$assembly',
'--strip',
'--no-sim-use-hardfp',
'--no-use-integer-division',
'--no-causal-async-stacks',
'--lazy-async-stacks',
'main.dill',
]
));
processManager.addCommand(kSdkPathCommand);
processManager.addCommand(const FakeCommand(
command: <String>[
'xcrun',
'cc',
'-arch',
'armv7',
'-isysroot',
'',
'-fembed-bitcode',
'-c',
'build/foo/snapshot_assembly.S',
'-o',
'build/foo/snapshot_assembly.o',
]
));
processManager.addCommand(const FakeCommand(
command: <String>[
'xcrun',
'clang',
'-arch',
'armv7',
...kBitcodeClang,
]
));
final int genSnapshotExitCode = await snapshotter.build( final int genSnapshotExitCode = await snapshotter.build(
platform: TargetPlatform.ios, platform: TargetPlatform.ios,
...@@ -322,169 +303,53 @@ void main() { ...@@ -322,169 +303,53 @@ void main() {
); );
expect(genSnapshotExitCode, 0); expect(genSnapshotExitCode, 0);
expect(genSnapshot.callCount, 1); expect(processManager.hasRemainingExpectations, false);
expect(genSnapshot.snapshotType.platform, TargetPlatform.ios); });
expect(genSnapshot.snapshotType.mode, BuildMode.profile);
expect(genSnapshot.additionalArgs, <String>[
'--deterministic',
'--snapshot_kind=app-aot-assembly',
'--assembly=$assembly',
'--strip',
'--no-sim-use-hardfp',
'--no-use-integer-division',
'--no-causal-async-stacks',
'--lazy-async-stacks',
'main.dill',
]);
final VerificationResult toVerifyCC = verify(mockXcode.cc(captureAny));
expect(toVerifyCC.callCount, 1);
final dynamic ccArgs = toVerifyCC.captured.first;
expect(ccArgs, contains('-fembed-bitcode'));
expect(ccArgs, contains('-isysroot'));
expect(ccArgs, contains(kSDKPath));
final VerificationResult toVerifyClang = verify(mockXcode.clang(captureAny));
expect(toVerifyClang.callCount, 1);
final dynamic clangArgs = toVerifyClang.captured.first;
expect(clangArgs, contains('-fembed-bitcode'));
expect(clangArgs, contains('-isysroot'));
expect(clangArgs, contains(kSDKPath));
final File assemblyFile = globals.fs.file(assembly);
expect(assemblyFile.existsSync(), true);
expect(assemblyFile.readAsStringSync().contains('.section __DWARF'), true);
}, overrides: contextOverrides);
testUsingContext('iOS release AOT with bitcode uses right flags', () async {
globals.fs.file('main.dill').writeAsStringSync('binary magic');
final String outputPath = globals.fs.path.join('build', 'foo');
globals.fs.directory(outputPath).createSync(recursive: true);
final String assembly = globals.fs.path.join(outputPath, 'snapshot_assembly.S');
genSnapshot.outputs = <String, String>{
assembly: 'blah blah\n',
};
final RunResult successResult = RunResult(ProcessResult(1, 0, '', ''), <String>['command name', 'arguments...']);
when(mockXcode.cc(any)).thenAnswer((_) => Future<RunResult>.value(successResult));
when(mockXcode.clang(any)).thenAnswer((_) => Future<RunResult>.value(successResult));
final int genSnapshotExitCode = await snapshotter.build(
platform: TargetPlatform.ios,
buildMode: BuildMode.release,
mainPath: 'main.dill',
packagesPath: '.packages',
outputPath: outputPath,
darwinArch: DarwinArch.armv7,
bitcode: true,
splitDebugInfo: null,
dartObfuscation: false,
);
expect(genSnapshotExitCode, 0);
expect(genSnapshot.callCount, 1);
expect(genSnapshot.snapshotType.platform, TargetPlatform.ios);
expect(genSnapshot.snapshotType.mode, BuildMode.release);
expect(genSnapshot.additionalArgs, <String>[
'--deterministic',
'--snapshot_kind=app-aot-assembly',
'--assembly=$assembly',
'--strip',
'--no-sim-use-hardfp',
'--no-use-integer-division',
'--no-causal-async-stacks',
'--lazy-async-stacks',
'main.dill',
]);
final VerificationResult toVerifyCC = verify(mockXcode.cc(captureAny));
expect(toVerifyCC.callCount, 1);
final dynamic ccArgs = toVerifyCC.captured.first;
expect(ccArgs, contains('-fembed-bitcode'));
expect(ccArgs, contains('-isysroot'));
expect(ccArgs, contains(kSDKPath));
final VerificationResult toVerifyClang = verify(mockXcode.clang(captureAny));
expect(toVerifyClang.callCount, 1);
final dynamic clangArgs = toVerifyClang.captured.first;
expect(clangArgs, contains('-fembed-bitcode'));
expect(clangArgs, contains('-isysroot'));
expect(clangArgs, contains(kSDKPath));
final File assemblyFile = globals.fs.file(assembly);
expect(assemblyFile.existsSync(), true);
}, overrides: contextOverrides);
testUsingContext('builds iOS armv7 profile AOT snapshot', () async {
globals.fs.file('main.dill').writeAsStringSync('binary magic');
final String outputPath = globals.fs.path.join('build', 'foo');
globals.fs.directory(outputPath).createSync(recursive: true);
final String assembly = globals.fs.path.join(outputPath, 'snapshot_assembly.S');
genSnapshot.outputs = <String, String>{
assembly: 'blah blah\n.section __DWARF\nblah blah\n',
};
final RunResult successResult = RunResult(ProcessResult(1, 0, '', ''), <String>['command name', 'arguments...']);
when(mockXcode.cc(any)).thenAnswer((_) => Future<RunResult>.value(successResult));
when(mockXcode.clang(any)).thenAnswer((_) => Future<RunResult>.value(successResult));
final int genSnapshotExitCode = await snapshotter.build(
platform: TargetPlatform.ios,
buildMode: BuildMode.profile,
mainPath: 'main.dill',
packagesPath: '.packages',
outputPath: outputPath,
darwinArch: DarwinArch.armv7,
bitcode: false,
splitDebugInfo: null,
dartObfuscation: false,
);
expect(genSnapshotExitCode, 0); testWithoutContext('builds iOS armv7 snapshot with dwarStackTraces', () async {
expect(genSnapshot.callCount, 1); final String outputPath = fileSystem.path.join('build', 'foo');
expect(genSnapshot.snapshotType.platform, TargetPlatform.ios); final String assembly = fileSystem.path.join(outputPath, 'snapshot_assembly.S');
expect(genSnapshot.snapshotType.mode, BuildMode.profile); final String debugPath = fileSystem.path.join('foo', 'app.ios-armv7.symbols');
expect(genSnapshot.additionalArgs, <String>[ processManager.addCommand(FakeCommand(
'--deterministic', command: <String>[
'--snapshot_kind=app-aot-assembly', 'gen_snapshot_armv7',
'--assembly=$assembly', '--deterministic',
'--strip', '--snapshot_kind=app-aot-assembly',
'--no-sim-use-hardfp', '--assembly=$assembly',
'--no-use-integer-division', '--strip',
'--no-causal-async-stacks', '--no-sim-use-hardfp',
'--lazy-async-stacks', '--no-use-integer-division',
'main.dill', '--no-causal-async-stacks',
]); '--lazy-async-stacks',
verifyNever(mockXcode.cc(argThat(contains('-fembed-bitcode')))); '--dwarf-stack-traces',
verifyNever(mockXcode.clang(argThat(contains('-fembed-bitcode')))); '--save-debugging-info=$debugPath',
'main.dill',
verify(mockXcode.cc(argThat(contains('-isysroot')))).called(1); ]
verify(mockXcode.clang(argThat(contains('-isysroot')))).called(1); ));
processManager.addCommand(kSdkPathCommand);
final File assemblyFile = globals.fs.file(assembly); processManager.addCommand(const FakeCommand(
expect(assemblyFile.existsSync(), true); command: <String>[
expect(assemblyFile.readAsStringSync().contains('.section __DWARF'), true); 'xcrun',
}, overrides: contextOverrides); 'cc',
'-arch',
testUsingContext('builds iOS armv7 profile AOT snapshot with dwarStackTraces', () async { 'armv7',
globals.fs.file('main.dill').writeAsStringSync('binary magic'); '-isysroot',
'',
final String outputPath = globals.fs.path.join('build', 'foo'); '-c',
globals.fs.directory(outputPath).createSync(recursive: true); 'build/foo/snapshot_assembly.S',
'-o',
final String assembly = globals.fs.path.join(outputPath, 'snapshot_assembly.S'); 'build/foo/snapshot_assembly.o',
genSnapshot.outputs = <String, String>{ ]
assembly: 'blah blah\n.section __DWARF\nblah blah\n', ));
}; processManager.addCommand(const FakeCommand(
final String debugPath = globals.fs.path.join('foo', 'app.ios-armv7.symbols'); command: <String>[
'xcrun',
final RunResult successResult = RunResult(ProcessResult(1, 0, '', ''), <String>['command name', 'arguments...']); 'clang',
when(mockXcode.cc(any)).thenAnswer((_) => Future<RunResult>.value(successResult)); '-arch',
when(mockXcode.clang(any)).thenAnswer((_) => Future<RunResult>.value(successResult)); 'armv7',
...kDefaultClang,
]
));
final int genSnapshotExitCode = await snapshotter.build( final int genSnapshotExitCode = await snapshotter.build(
platform: TargetPlatform.ios, platform: TargetPlatform.ios,
...@@ -499,47 +364,51 @@ void main() { ...@@ -499,47 +364,51 @@ void main() {
); );
expect(genSnapshotExitCode, 0); expect(genSnapshotExitCode, 0);
expect(genSnapshot.callCount, 1); expect(processManager.hasRemainingExpectations, false);
expect(genSnapshot.snapshotType.platform, TargetPlatform.ios); });
expect(genSnapshot.snapshotType.mode, BuildMode.profile);
expect(genSnapshot.additionalArgs, <String>[ testWithoutContext('builds iOS armv7 snapshot with obfuscate', () async {
'--deterministic', final String outputPath = fileSystem.path.join('build', 'foo');
'--snapshot_kind=app-aot-assembly', final String assembly = fileSystem.path.join(outputPath, 'snapshot_assembly.S');
'--assembly=$assembly', processManager.addCommand(FakeCommand(
'--strip', command: <String>[
'--no-sim-use-hardfp', 'gen_snapshot_armv7',
'--no-use-integer-division', '--deterministic',
'--no-causal-async-stacks', '--snapshot_kind=app-aot-assembly',
'--lazy-async-stacks', '--assembly=$assembly',
'--dwarf-stack-traces', '--strip',
'--save-debugging-info=$debugPath', '--no-sim-use-hardfp',
'main.dill', '--no-use-integer-division',
]); '--no-causal-async-stacks',
verifyNever(mockXcode.cc(argThat(contains('-fembed-bitcode')))); '--lazy-async-stacks',
verifyNever(mockXcode.clang(argThat(contains('-fembed-bitcode')))); '--obfuscate',
'main.dill',
verify(mockXcode.cc(argThat(contains('-isysroot')))).called(1); ]
verify(mockXcode.clang(argThat(contains('-isysroot')))).called(1); ));
processManager.addCommand(kSdkPathCommand);
final File assemblyFile = globals.fs.file(assembly); processManager.addCommand(const FakeCommand(
expect(assemblyFile.existsSync(), true); command: <String>[
expect(assemblyFile.readAsStringSync().contains('.section __DWARF'), true); 'xcrun',
}, overrides: contextOverrides); 'cc',
'-arch',
testUsingContext('builds iOS armv7 profile AOT snapshot with obfuscate', () async { 'armv7',
globals.fs.file('main.dill').writeAsStringSync('binary magic'); '-isysroot',
'',
final String outputPath = globals.fs.path.join('build', 'foo'); '-c',
globals.fs.directory(outputPath).createSync(recursive: true); 'build/foo/snapshot_assembly.S',
'-o',
final String assembly = globals.fs.path.join(outputPath, 'snapshot_assembly.S'); 'build/foo/snapshot_assembly.o',
genSnapshot.outputs = <String, String>{ ]
assembly: 'blah blah\n.section __DWARF\nblah blah\n', ));
}; processManager.addCommand(const FakeCommand(
command: <String>[
final RunResult successResult = RunResult(ProcessResult(1, 0, '', ''), <String>['command name', 'arguments...']); 'xcrun',
when(mockXcode.cc(any)).thenAnswer((_) => Future<RunResult>.value(successResult)); 'clang',
when(mockXcode.clang(any)).thenAnswer((_) => Future<RunResult>.value(successResult)); '-arch',
'armv7',
...kDefaultClang,
]
));
final int genSnapshotExitCode = await snapshotter.build( final int genSnapshotExitCode = await snapshotter.build(
platform: TargetPlatform.ios, platform: TargetPlatform.ios,
...@@ -554,86 +423,50 @@ void main() { ...@@ -554,86 +423,50 @@ void main() {
); );
expect(genSnapshotExitCode, 0); expect(genSnapshotExitCode, 0);
expect(genSnapshot.callCount, 1); expect(processManager.hasRemainingExpectations, false);
expect(genSnapshot.snapshotType.platform, TargetPlatform.ios); });
expect(genSnapshot.snapshotType.mode, BuildMode.profile);
expect(genSnapshot.additionalArgs, <String>[
'--deterministic',
'--snapshot_kind=app-aot-assembly',
'--assembly=$assembly',
'--strip',
'--no-sim-use-hardfp',
'--no-use-integer-division',
'--no-causal-async-stacks',
'--lazy-async-stacks',
'--obfuscate',
'main.dill',
]);
verifyNever(mockXcode.cc(argThat(contains('-fembed-bitcode'))));
verifyNever(mockXcode.clang(argThat(contains('-fembed-bitcode'))));
verify(mockXcode.cc(argThat(contains('-isysroot')))).called(1);
verify(mockXcode.clang(argThat(contains('-isysroot')))).called(1);
final File assemblyFile = globals.fs.file(assembly);
expect(assemblyFile.existsSync(), true);
expect(assemblyFile.readAsStringSync().contains('.section __DWARF'), true);
}, overrides: contextOverrides);
testUsingContext('builds iOS arm64 profile AOT snapshot', () async {
globals.fs.file('main.dill').writeAsStringSync('binary magic');
final String outputPath = globals.fs.path.join('build', 'foo');
globals.fs.directory(outputPath).createSync(recursive: true);
genSnapshot.outputs = <String, String>{
globals.fs.path.join(outputPath, 'snapshot_assembly.S'): '',
};
final RunResult successResult = RunResult(ProcessResult(1, 0, '', ''), <String>['command name', 'arguments...']);
when(mockXcode.cc(any)).thenAnswer((_) => Future<RunResult>.value(successResult));
when(mockXcode.clang(any)).thenAnswer((_) => Future<RunResult>.value(successResult));
final int genSnapshotExitCode = await snapshotter.build(
platform: TargetPlatform.ios,
buildMode: BuildMode.profile,
mainPath: 'main.dill',
packagesPath: '.packages',
outputPath: outputPath,
darwinArch: DarwinArch.arm64,
bitcode: false,
splitDebugInfo: null,
dartObfuscation: false,
);
expect(genSnapshotExitCode, 0); testWithoutContext('builds iOS armv7 snapshot', () async {
expect(genSnapshot.callCount, 1); final String outputPath = fileSystem.path.join('build', 'foo');
expect(genSnapshot.snapshotType.platform, TargetPlatform.ios); processManager.addCommand(FakeCommand(
expect(genSnapshot.snapshotType.mode, BuildMode.profile); command: <String>[
expect(genSnapshot.additionalArgs, <String>[ 'gen_snapshot_armv7',
'--deterministic', '--deterministic',
'--snapshot_kind=app-aot-assembly', '--snapshot_kind=app-aot-assembly',
'--assembly=${globals.fs.path.join(outputPath, 'snapshot_assembly.S')}', '--assembly=${fileSystem.path.join(outputPath, 'snapshot_assembly.S')}',
'--strip', '--strip',
'--no-causal-async-stacks', '--no-sim-use-hardfp',
'--lazy-async-stacks', '--no-use-integer-division',
'main.dill', '--no-causal-async-stacks',
]); '--lazy-async-stacks',
}, overrides: contextOverrides); 'main.dill',
]
testUsingContext('builds iOS release armv7 AOT snapshot', () async { ));
globals.fs.file('main.dill').writeAsStringSync('binary magic'); processManager.addCommand(kSdkPathCommand);
processManager.addCommand(const FakeCommand(
final String outputPath = globals.fs.path.join('build', 'foo'); command: <String>[
globals.fs.directory(outputPath).createSync(recursive: true); 'xcrun',
'cc',
genSnapshot.outputs = <String, String>{ '-arch',
globals.fs.path.join(outputPath, 'snapshot_assembly.S'): '', 'armv7',
}; '-isysroot',
'',
final RunResult successResult = RunResult(ProcessResult(1, 0, '', ''), <String>['command name', 'arguments...']); '-c',
when(mockXcode.cc(any)).thenAnswer((_) => Future<RunResult>.value(successResult)); 'build/foo/snapshot_assembly.S',
when(mockXcode.clang(any)).thenAnswer((_) => Future<RunResult>.value(successResult)); '-o',
'build/foo/snapshot_assembly.o',
]
));
processManager.addCommand(const FakeCommand(
command: <String>[
'xcrun',
'clang',
'-arch',
'armv7',
...kDefaultClang,
]
));
final int genSnapshotExitCode = await snapshotter.build( final int genSnapshotExitCode = await snapshotter.build(
platform: TargetPlatform.ios, platform: TargetPlatform.ios,
...@@ -648,35 +481,47 @@ void main() { ...@@ -648,35 +481,47 @@ void main() {
); );
expect(genSnapshotExitCode, 0); expect(genSnapshotExitCode, 0);
expect(genSnapshot.callCount, 1); expect(processManager.hasRemainingExpectations, false);
expect(genSnapshot.snapshotType.platform, TargetPlatform.ios); });
expect(genSnapshot.snapshotType.mode, BuildMode.release);
expect(genSnapshot.additionalArgs, <String>[ testWithoutContext('builds iOS arm64 snapshot', () async {
'--deterministic', final String outputPath = fileSystem.path.join('build', 'foo');
'--snapshot_kind=app-aot-assembly', processManager.addCommand(FakeCommand(
'--assembly=${globals.fs.path.join(outputPath, 'snapshot_assembly.S')}', command: <String>[
'--strip', 'gen_snapshot_arm64',
'--no-sim-use-hardfp', '--deterministic',
'--no-use-integer-division', '--snapshot_kind=app-aot-assembly',
'--no-causal-async-stacks', '--assembly=${fileSystem.path.join(outputPath, 'snapshot_assembly.S')}',
'--lazy-async-stacks', '--strip',
'main.dill', '--no-causal-async-stacks',
]); '--lazy-async-stacks',
}, overrides: contextOverrides); 'main.dill',
]
testUsingContext('builds iOS release arm64 AOT snapshot', () async { ));
globals.fs.file('main.dill').writeAsStringSync('binary magic'); processManager.addCommand(kSdkPathCommand);
processManager.addCommand(const FakeCommand(
final String outputPath = globals.fs.path.join('build', 'foo'); command: <String>[
globals.fs.directory(outputPath).createSync(recursive: true); 'xcrun',
'cc',
genSnapshot.outputs = <String, String>{ '-arch',
globals.fs.path.join(outputPath, 'snapshot_assembly.S'): '', 'arm64',
}; '-isysroot',
'',
final RunResult successResult = RunResult(ProcessResult(1, 0, '', ''), <String>['command name', 'arguments...']); '-c',
when(mockXcode.cc(any)).thenAnswer((_) => Future<RunResult>.value(successResult)); 'build/foo/snapshot_assembly.S',
when(mockXcode.clang(any)).thenAnswer((_) => Future<RunResult>.value(successResult)); '-o',
'build/foo/snapshot_assembly.o',
]
));
processManager.addCommand(const FakeCommand(
command: <String>[
'xcrun',
'clang',
'-arch',
'arm64',
...kDefaultClang,
]
));
final int genSnapshotExitCode = await snapshotter.build( final int genSnapshotExitCode = await snapshotter.build(
platform: TargetPlatform.ios, platform: TargetPlatform.ios,
...@@ -691,25 +536,25 @@ void main() { ...@@ -691,25 +536,25 @@ void main() {
); );
expect(genSnapshotExitCode, 0); expect(genSnapshotExitCode, 0);
expect(genSnapshot.callCount, 1); expect(processManager.hasRemainingExpectations, false);
expect(genSnapshot.snapshotType.platform, TargetPlatform.ios); });
expect(genSnapshot.snapshotType.mode, BuildMode.release);
expect(genSnapshot.additionalArgs, <String>[ testWithoutContext('builds shared library for android-arm (32bit)', () async {
'--deterministic', final String outputPath = fileSystem.path.join('build', 'foo');
'--snapshot_kind=app-aot-assembly', processManager.addCommand(const FakeCommand(
'--assembly=${globals.fs.path.join(outputPath, 'snapshot_assembly.S')}', command: <String>[
'--strip', 'gen_snapshot',
'--no-causal-async-stacks', '--deterministic',
'--lazy-async-stacks', '--snapshot_kind=app-aot-elf',
'main.dill', '--elf=build/foo/app.so',
]); '--strip',
}, overrides: contextOverrides); '--no-sim-use-hardfp',
'--no-use-integer-division',
testUsingContext('builds shared library for android-arm', () async { '--no-causal-async-stacks',
globals.fs.file('main.dill').writeAsStringSync('binary magic'); '--lazy-async-stacks',
'main.dill',
final String outputPath = globals.fs.path.join('build', 'foo'); ]
globals.fs.directory(outputPath).createSync(recursive: true); ));
final int genSnapshotExitCode = await snapshotter.build( final int genSnapshotExitCode = await snapshotter.build(
platform: TargetPlatform.android_arm, platform: TargetPlatform.android_arm,
...@@ -723,28 +568,28 @@ void main() { ...@@ -723,28 +568,28 @@ void main() {
); );
expect(genSnapshotExitCode, 0); expect(genSnapshotExitCode, 0);
expect(genSnapshot.callCount, 1); expect(processManager.hasRemainingExpectations, false);
expect(genSnapshot.snapshotType.platform, TargetPlatform.android_arm); });
expect(genSnapshot.snapshotType.mode, BuildMode.release);
expect(genSnapshot.additionalArgs, <String>[ testWithoutContext('builds shared library for android-arm with dwarf stack traces', () async {
'--deterministic', final String outputPath = fileSystem.path.join('build', 'foo');
'--snapshot_kind=app-aot-elf', final String debugPath = fileSystem.path.join('foo', 'app.android-arm.symbols');
'--elf=build/foo/app.so', processManager.addCommand(FakeCommand(
'--strip', command: <String>[
'--no-sim-use-hardfp', 'gen_snapshot',
'--no-use-integer-division', '--deterministic',
'--no-causal-async-stacks', '--snapshot_kind=app-aot-elf',
'--lazy-async-stacks', '--elf=build/foo/app.so',
'main.dill', '--strip',
]); '--no-sim-use-hardfp',
}, overrides: contextOverrides); '--no-use-integer-division',
'--no-causal-async-stacks',
testUsingContext('builds shared library for android-arm with dwarf stack traces', () async { '--lazy-async-stacks',
globals.fs.file('main.dill').writeAsStringSync('binary magic'); '--dwarf-stack-traces',
'--save-debugging-info=$debugPath',
final String outputPath = globals.fs.path.join('build', 'foo'); 'main.dill',
final String debugPath = globals.fs.path.join('foo', 'app.android-arm.symbols'); ]
globals.fs.directory(outputPath).createSync(recursive: true); ));
final int genSnapshotExitCode = await snapshotter.build( final int genSnapshotExitCode = await snapshotter.build(
platform: TargetPlatform.android_arm, platform: TargetPlatform.android_arm,
...@@ -758,29 +603,26 @@ void main() { ...@@ -758,29 +603,26 @@ void main() {
); );
expect(genSnapshotExitCode, 0); expect(genSnapshotExitCode, 0);
expect(genSnapshot.callCount, 1); expect(processManager.hasRemainingExpectations, false);
expect(genSnapshot.snapshotType.platform, TargetPlatform.android_arm); });
expect(genSnapshot.snapshotType.mode, BuildMode.release);
expect(genSnapshot.additionalArgs, <String>[ testWithoutContext('builds shared library for android-arm with obfuscate', () async {
'--deterministic', final String outputPath = fileSystem.path.join('build', 'foo');
'--snapshot_kind=app-aot-elf', processManager.addCommand(const FakeCommand(
'--elf=build/foo/app.so', command: <String>[
'--strip', 'gen_snapshot',
'--no-sim-use-hardfp', '--deterministic',
'--no-use-integer-division', '--snapshot_kind=app-aot-elf',
'--no-causal-async-stacks', '--elf=build/foo/app.so',
'--lazy-async-stacks', '--strip',
'--dwarf-stack-traces', '--no-sim-use-hardfp',
'--save-debugging-info=$debugPath', '--no-use-integer-division',
'main.dill', '--no-causal-async-stacks',
]); '--lazy-async-stacks',
}, overrides: contextOverrides); '--obfuscate',
'main.dill',
testUsingContext('builds shared library for android-arm with obfuscate', () async { ]
globals.fs.file('main.dill').writeAsStringSync('binary magic'); ));
final String outputPath = globals.fs.path.join('build', 'foo');
globals.fs.directory(outputPath).createSync(recursive: true);
final int genSnapshotExitCode = await snapshotter.build( final int genSnapshotExitCode = await snapshotter.build(
platform: TargetPlatform.android_arm, platform: TargetPlatform.android_arm,
...@@ -794,28 +636,25 @@ void main() { ...@@ -794,28 +636,25 @@ void main() {
); );
expect(genSnapshotExitCode, 0); expect(genSnapshotExitCode, 0);
expect(genSnapshot.callCount, 1); expect(processManager.hasRemainingExpectations, false);
expect(genSnapshot.snapshotType.platform, TargetPlatform.android_arm); });
expect(genSnapshot.snapshotType.mode, BuildMode.release);
expect(genSnapshot.additionalArgs, <String>[ testWithoutContext('builds shared library for android-arm without dwarf stack traces due to empty string', () async {
'--deterministic', final String outputPath = fileSystem.path.join('build', 'foo');
'--snapshot_kind=app-aot-elf', processManager.addCommand(const FakeCommand(
'--elf=build/foo/app.so', command: <String>[
'--strip', 'gen_snapshot',
'--no-sim-use-hardfp', '--deterministic',
'--no-use-integer-division', '--snapshot_kind=app-aot-elf',
'--no-causal-async-stacks', '--elf=build/foo/app.so',
'--lazy-async-stacks', '--strip',
'--obfuscate', '--no-sim-use-hardfp',
'main.dill', '--no-use-integer-division',
]); '--no-causal-async-stacks',
}, overrides: contextOverrides); '--lazy-async-stacks',
'main.dill',
testUsingContext('builds shared library for android-arm without dwarf stack traces due to empty string', () async { ]
globals.fs.file('main.dill').writeAsStringSync('binary magic'); ));
final String outputPath = globals.fs.path.join('build', 'foo');
globals.fs.directory(outputPath).createSync(recursive: true);
final int genSnapshotExitCode = await snapshotter.build( final int genSnapshotExitCode = await snapshotter.build(
platform: TargetPlatform.android_arm, platform: TargetPlatform.android_arm,
...@@ -829,27 +668,23 @@ void main() { ...@@ -829,27 +668,23 @@ void main() {
); );
expect(genSnapshotExitCode, 0); expect(genSnapshotExitCode, 0);
expect(genSnapshot.callCount, 1); expect(processManager.hasRemainingExpectations, false);
expect(genSnapshot.snapshotType.platform, TargetPlatform.android_arm); });
expect(genSnapshot.snapshotType.mode, BuildMode.release);
expect(genSnapshot.additionalArgs, <String>[ testWithoutContext('builds shared library for android-arm64', () async {
'--deterministic', final String outputPath = fileSystem.path.join('build', 'foo');
'--snapshot_kind=app-aot-elf', processManager.addCommand(const FakeCommand(
'--elf=build/foo/app.so', command: <String>[
'--strip', 'gen_snapshot',
'--no-sim-use-hardfp', '--deterministic',
'--no-use-integer-division', '--snapshot_kind=app-aot-elf',
'--no-causal-async-stacks', '--elf=build/foo/app.so',
'--lazy-async-stacks', '--strip',
'main.dill', '--no-causal-async-stacks',
]); '--lazy-async-stacks',
}, overrides: contextOverrides); 'main.dill',
]
testUsingContext('builds shared library for android-arm64', () async { ));
globals.fs.file('main.dill').writeAsStringSync('binary magic');
final String outputPath = globals.fs.path.join('build', 'foo');
globals.fs.directory(outputPath).createSync(recursive: true);
final int genSnapshotExitCode = await snapshotter.build( final int genSnapshotExitCode = await snapshotter.build(
platform: TargetPlatform.android_arm64, platform: TargetPlatform.android_arm64,
...@@ -863,48 +698,9 @@ void main() { ...@@ -863,48 +698,9 @@ void main() {
); );
expect(genSnapshotExitCode, 0); expect(genSnapshotExitCode, 0);
expect(genSnapshot.callCount, 1); expect(processManager.hasRemainingExpectations, false);
expect(genSnapshot.snapshotType.platform, TargetPlatform.android_arm64); });
expect(genSnapshot.snapshotType.mode, BuildMode.release);
expect(genSnapshot.additionalArgs, <String>[
'--deterministic',
'--snapshot_kind=app-aot-elf',
'--elf=build/foo/app.so',
'--strip',
'--no-causal-async-stacks',
'--lazy-async-stacks',
'main.dill',
]);
}, overrides: contextOverrides);
testUsingContext('reports timing', () async {
globals.fs.file('main.dill').writeAsStringSync('binary magic');
final String outputPath = globals.fs.path.join('build', 'foo');
globals.fs.directory(outputPath).createSync(recursive: true);
genSnapshot.outputs = <String, String>{
globals.fs.path.join(outputPath, 'app.so'): '',
};
final RunResult successResult = RunResult(ProcessResult(1, 0, '', ''), <String>['command name', 'arguments...']);
when(mockXcode.cc(any)).thenAnswer((_) => Future<RunResult>.value(successResult));
when(mockXcode.clang(any)).thenAnswer((_) => Future<RunResult>.value(successResult));
final int genSnapshotExitCode = await snapshotterWithTimings.build(
platform: TargetPlatform.android_arm,
buildMode: BuildMode.release,
mainPath: 'main.dill',
packagesPath: '.packages',
outputPath: outputPath,
bitcode: false,
splitDebugInfo: null,
dartObfuscation: false,
);
expect(genSnapshotExitCode, 0);
expect(genSnapshot.callCount, 1);
expect(testLogger.statusText, matches(RegExp(r'snapshot\(CompileTime\): \d+ ms.')));
}, overrides: contextOverrides);
}); });
} }
class MockArtifacts extends Mock implements Artifacts {}
...@@ -5,7 +5,6 @@ ...@@ -5,7 +5,6 @@
import 'package:flutter_tools/src/base/build.dart'; import 'package:flutter_tools/src/base/build.dart';
import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/io.dart'; import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/process.dart';
import 'package:flutter_tools/src/build_system/build_system.dart'; import 'package:flutter_tools/src/build_system/build_system.dart';
import 'package:flutter_tools/src/build_system/targets/dart.dart'; import 'package:flutter_tools/src/build_system/targets/dart.dart';
import 'package:flutter_tools/src/build_system/targets/macos.dart'; import 'package:flutter_tools/src/build_system/targets/macos.dart';
...@@ -49,7 +48,6 @@ void main() { ...@@ -49,7 +48,6 @@ void main() {
Testbed testbed; Testbed testbed;
Environment environment; Environment environment;
MockPlatform mockPlatform; MockPlatform mockPlatform;
MockXcode mockXcode;
setUpAll(() { setUpAll(() {
Cache.disableLocking(); Cache.disableLocking();
...@@ -57,7 +55,6 @@ void main() { ...@@ -57,7 +55,6 @@ void main() {
}); });
setUp(() { setUp(() {
mockXcode = MockXcode();
mockPlatform = MockPlatform(); mockPlatform = MockPlatform();
when(mockPlatform.isWindows).thenReturn(false); when(mockPlatform.isWindows).thenReturn(false);
when(mockPlatform.isMacOS).thenReturn(true); when(mockPlatform.isMacOS).thenReturn(true);
...@@ -193,35 +190,6 @@ void main() { ...@@ -193,35 +190,6 @@ void main() {
expect(outputFramework.readAsStringSync(), 'DEF'); expect(outputFramework.readAsStringSync(), 'DEF');
})); }));
test('release/profile macOS compilation uses correct gen_snapshot', () => testbed.run(() async {
when(genSnapshot.run(
snapshotType: anyNamed('snapshotType'),
additionalArgs: anyNamed('additionalArgs'),
darwinArch: anyNamed('darwinArch'),
)).thenAnswer((Invocation invocation) {
environment.buildDir.childFile('snapshot_assembly.o').createSync();
environment.buildDir.childFile('snapshot_assembly.S').createSync();
return Future<int>.value(0);
});
when(mockXcode.cc(any)).thenAnswer((Invocation invocation) {
return Future<RunResult>.value(RunResult(FakeProcessResult()..exitCode = 0, <String>['test']));
});
when(mockXcode.clang(any)).thenAnswer((Invocation invocation) {
return Future<RunResult>.value(RunResult(FakeProcessResult()..exitCode = 0, <String>['test']));
});
environment.buildDir.childFile('app.dill').createSync(recursive: true);
globals.fs.file('.packages')
..createSync()
..writeAsStringSync('''
# Generated
sky_engine:file:///bin/cache/pkg/sky_engine/lib/
flutter_tools:lib/''');
await const CompileMacOSFramework().build(environment..defines[kBuildMode] = 'release');
}, overrides: <Type, Generator>{
GenSnapshot: () => MockGenSnapshot(),
Xcode: () => mockXcode,
}));
} }
class MockPlatform extends Mock implements Platform {} class MockPlatform extends Mock implements Platform {}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment