@@ -205,6 +205,9 @@ Future<Null> _exit(int code) async {
|
||||
printTrace('ensureAnalyticsSent: ${stopwatch.elapsedMilliseconds}ms');
|
||||
}
|
||||
|
||||
// Run shutdown hooks before flushing logs
|
||||
await runShutdownHooks();
|
||||
|
||||
// Write any buffered output.
|
||||
logger.flush();
|
||||
|
||||
|
||||
@@ -8,10 +8,11 @@ import 'package:path/path.dart' as path;
|
||||
import 'package:xml/xml.dart' as xml;
|
||||
|
||||
import 'android/gradle.dart';
|
||||
import 'base/os.dart' show os;
|
||||
import 'base/process.dart';
|
||||
import 'build_info.dart';
|
||||
import 'globals.dart';
|
||||
import 'ios/plist_utils.dart';
|
||||
import 'ios/plist_utils.dart' as plist;
|
||||
import 'ios/xcodeproj.dart';
|
||||
|
||||
abstract class ApplicationPackage {
|
||||
@@ -122,41 +123,82 @@ class AndroidApk extends ApplicationPackage {
|
||||
String get name => path.basename(apkPath);
|
||||
}
|
||||
|
||||
class IOSApp extends ApplicationPackage {
|
||||
static final String kBundleName = 'Runner.app';
|
||||
/// Tests whether a [FileSystemEntity] is an iOS bundle directory
|
||||
bool _isBundleDirectory(FileSystemEntity entity) =>
|
||||
entity is Directory && entity.path.endsWith('.app');
|
||||
|
||||
IOSApp({
|
||||
this.appDirectory,
|
||||
String projectBundleId
|
||||
}) : super(id: projectBundleId);
|
||||
abstract class IOSApp extends ApplicationPackage {
|
||||
IOSApp({String projectBundleId}) : super(id: projectBundleId);
|
||||
|
||||
/// Creates a new IOSApp from an existing IPA.
|
||||
factory IOSApp.fromIpa(String applicationBinary) {
|
||||
Directory bundleDir;
|
||||
try {
|
||||
Directory tempDir = Directory.systemTemp.createTempSync('flutter_app_');
|
||||
addShutdownHook(() async => await tempDir.delete(recursive: true));
|
||||
os.unzip(new File(applicationBinary), tempDir);
|
||||
Directory payloadDir = new Directory(path.join(tempDir.path, 'Payload'));
|
||||
bundleDir = payloadDir.listSync().singleWhere(_isBundleDirectory);
|
||||
} on StateError catch (e, stackTrace) {
|
||||
printError('Invalid prebuilt iOS binary: ${e.toString()}', stackTrace);
|
||||
return null;
|
||||
}
|
||||
|
||||
String plistPath = path.join(bundleDir.path, 'Info.plist');
|
||||
String id = plist.getValueFromFile(plistPath, plist.kCFBundleIdentifierKey);
|
||||
if (id == null)
|
||||
return null;
|
||||
|
||||
return new PrebuiltIOSApp(
|
||||
ipaPath: applicationBinary,
|
||||
bundleDir: bundleDir,
|
||||
bundleName: path.basename(bundleDir.path),
|
||||
projectBundleId: id,
|
||||
);
|
||||
}
|
||||
|
||||
factory IOSApp.fromCurrentDirectory() {
|
||||
if (getCurrentHostPlatform() != HostPlatform.darwin_x64)
|
||||
return null;
|
||||
|
||||
String plistPath = path.join('ios', 'Runner', 'Info.plist');
|
||||
String value = getValueFromFile(plistPath, kCFBundleIdentifierKey);
|
||||
if (value == null)
|
||||
String id = plist.getValueFromFile(plistPath, plist.kCFBundleIdentifierKey);
|
||||
if (id == null)
|
||||
return null;
|
||||
String projectPath = path.join('ios', 'Runner.xcodeproj');
|
||||
value = substituteXcodeVariables(value, projectPath, 'Runner');
|
||||
id = substituteXcodeVariables(id, projectPath, 'Runner');
|
||||
|
||||
return new IOSApp(
|
||||
return new BuildableIOSApp(
|
||||
appDirectory: path.join('ios'),
|
||||
projectBundleId: value
|
||||
projectBundleId: id
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String get displayName => id;
|
||||
|
||||
String get simulatorBundlePath;
|
||||
|
||||
String get deviceBundlePath;
|
||||
}
|
||||
|
||||
class BuildableIOSApp extends IOSApp {
|
||||
static final String kBundleName = 'Runner.app';
|
||||
|
||||
BuildableIOSApp({
|
||||
this.appDirectory,
|
||||
String projectBundleId,
|
||||
}) : super(projectBundleId: projectBundleId);
|
||||
|
||||
final String appDirectory;
|
||||
|
||||
@override
|
||||
String get name => kBundleName;
|
||||
|
||||
@override
|
||||
String get displayName => id;
|
||||
|
||||
final String appDirectory;
|
||||
|
||||
String get simulatorBundlePath => _buildAppPath('iphonesimulator');
|
||||
|
||||
@override
|
||||
String get deviceBundlePath => _buildAppPath('iphoneos');
|
||||
|
||||
String _buildAppPath(String type) {
|
||||
@@ -164,6 +206,30 @@ class IOSApp extends ApplicationPackage {
|
||||
}
|
||||
}
|
||||
|
||||
class PrebuiltIOSApp extends IOSApp {
|
||||
final String ipaPath;
|
||||
final Directory bundleDir;
|
||||
final String bundleName;
|
||||
|
||||
PrebuiltIOSApp({
|
||||
this.ipaPath,
|
||||
this.bundleDir,
|
||||
this.bundleName,
|
||||
String projectBundleId,
|
||||
}) : super(projectBundleId: projectBundleId);
|
||||
|
||||
@override
|
||||
String get name => bundleName;
|
||||
|
||||
@override
|
||||
String get simulatorBundlePath => _bundlePath;
|
||||
|
||||
@override
|
||||
String get deviceBundlePath => _bundlePath;
|
||||
|
||||
String get _bundlePath => bundleDir.path;
|
||||
}
|
||||
|
||||
ApplicationPackage getApplicationPackageForPlatform(TargetPlatform platform, {
|
||||
String applicationBinary
|
||||
}) {
|
||||
@@ -171,11 +237,13 @@ ApplicationPackage getApplicationPackageForPlatform(TargetPlatform platform, {
|
||||
case TargetPlatform.android_arm:
|
||||
case TargetPlatform.android_x64:
|
||||
case TargetPlatform.android_x86:
|
||||
if (applicationBinary != null)
|
||||
return new AndroidApk.fromApk(applicationBinary);
|
||||
return new AndroidApk.fromCurrentDirectory();
|
||||
return applicationBinary == null
|
||||
? new AndroidApk.fromCurrentDirectory()
|
||||
: new AndroidApk.fromApk(applicationBinary);
|
||||
case TargetPlatform.ios:
|
||||
return new IOSApp.fromCurrentDirectory();
|
||||
return applicationBinary == null
|
||||
? new IOSApp.fromCurrentDirectory()
|
||||
: new IOSApp.fromIpa(applicationBinary);
|
||||
case TargetPlatform.darwin_x64:
|
||||
case TargetPlatform.linux_x64:
|
||||
return null;
|
||||
|
||||
@@ -9,9 +9,20 @@ import 'dart:io';
|
||||
import '../globals.dart';
|
||||
|
||||
typedef String StringConverter(String string);
|
||||
typedef Future<dynamic> ShutdownHook();
|
||||
|
||||
// TODO(ianh): We have way too many ways to run subprocesses in this project.
|
||||
|
||||
List<ShutdownHook> _shutdownHooks = <ShutdownHook>[];
|
||||
void addShutdownHook(ShutdownHook shutdownHook) {
|
||||
_shutdownHooks.add(shutdownHook);
|
||||
}
|
||||
|
||||
Future<Null> runShutdownHooks() async {
|
||||
for (ShutdownHook shutdownHook in _shutdownHooks)
|
||||
await shutdownHook();
|
||||
}
|
||||
|
||||
Map<String, String> _environment(bool allowReentrantFlutter, [Map<String, String> environment]) {
|
||||
if (allowReentrantFlutter) {
|
||||
if (environment == null)
|
||||
|
||||
@@ -184,17 +184,19 @@ class IOSDevice extends Device {
|
||||
Map<String, dynamic> platformArgs,
|
||||
bool prebuiltApplication: false
|
||||
}) async {
|
||||
// TODO(chinmaygarde): Use checked, mainPath, route.
|
||||
// TODO(devoncarew): Handle startPaused, debugPort.
|
||||
printTrace('Building ${app.name} for $id');
|
||||
if (!prebuiltApplication) {
|
||||
// TODO(chinmaygarde): Use checked, mainPath, route.
|
||||
// TODO(devoncarew): Handle startPaused, debugPort.
|
||||
printTrace('Building ${app.name} for $id');
|
||||
|
||||
// Step 1: Install the precompiled/DBC application if necessary.
|
||||
XcodeBuildResult buildResult = await buildXcodeProject(app: app, mode: mode, target: mainPath, buildForDevice: true);
|
||||
if (!buildResult.success) {
|
||||
printError('Could not build the precompiled application for the device.');
|
||||
diagnoseXcodeBuildFailure(buildResult);
|
||||
printError('');
|
||||
return new LaunchResult.failed();
|
||||
// Step 1: Build the precompiled/DBC application if necessary.
|
||||
XcodeBuildResult buildResult = await buildXcodeProject(app: app, mode: mode, target: mainPath, buildForDevice: true);
|
||||
if (!buildResult.success) {
|
||||
printError('Could not build the precompiled application for the device.');
|
||||
diagnoseXcodeBuildFailure(buildResult);
|
||||
printError('');
|
||||
return new LaunchResult.failed();
|
||||
}
|
||||
}
|
||||
|
||||
// Step 2: Check that the application exists at the specified path.
|
||||
|
||||
@@ -100,7 +100,7 @@ bool _xcodeVersionCheckValid(int major, int minor) {
|
||||
}
|
||||
|
||||
Future<XcodeBuildResult> buildXcodeProject({
|
||||
IOSApp app,
|
||||
BuildableIOSApp app,
|
||||
BuildMode mode,
|
||||
String target: flx.defaultMainPath,
|
||||
bool buildForDevice,
|
||||
|
||||
@@ -416,10 +416,12 @@ class IOSSimulator extends Device {
|
||||
Map<String, dynamic> platformArgs,
|
||||
bool prebuiltApplication: false
|
||||
}) async {
|
||||
printTrace('Building ${app.name} for $id.');
|
||||
if (!prebuiltApplication) {
|
||||
printTrace('Building ${app.name} for $id.');
|
||||
|
||||
if (!(await _setupUpdatedApplicationBundle(app)))
|
||||
return new LaunchResult.failed();
|
||||
if (!(await _setupUpdatedApplicationBundle(app)))
|
||||
return new LaunchResult.failed();
|
||||
}
|
||||
|
||||
ProtocolDiscovery observatoryDiscovery;
|
||||
|
||||
@@ -427,11 +429,15 @@ class IOSSimulator extends Device {
|
||||
observatoryDiscovery = new ProtocolDiscovery(logReader, ProtocolDiscovery.kObservatoryService);
|
||||
|
||||
// Prepare launch arguments.
|
||||
List<String> args = <String>[
|
||||
"--flx=${path.absolute(path.join(getBuildDirectory(), 'app.flx'))}",
|
||||
"--dart-main=${path.absolute(mainPath)}",
|
||||
"--packages=${path.absolute('.packages')}",
|
||||
];
|
||||
List<String> args = <String>[];
|
||||
|
||||
if (!prebuiltApplication) {
|
||||
args.addAll(<String>[
|
||||
"--flx=${path.absolute(path.join(getBuildDirectory(), 'app.flx'))}",
|
||||
"--dart-main=${path.absolute(mainPath)}",
|
||||
"--packages=${path.absolute('.packages')}",
|
||||
]);
|
||||
}
|
||||
|
||||
if (debuggingOptions.debuggingEnabled) {
|
||||
if (debuggingOptions.buildMode == BuildMode.debug)
|
||||
|
||||
@@ -148,7 +148,7 @@ class RunAndStayResident extends ResidentRunner {
|
||||
}
|
||||
|
||||
// TODO(devoncarew): This fails for ios devices - we haven't built yet.
|
||||
if (device is AndroidDevice) {
|
||||
if (prebuiltMode || device is AndroidDevice) {
|
||||
printTrace('Running install command.');
|
||||
if (!(installApp(device, _package, uninstall: false)))
|
||||
return 1;
|
||||
|
||||
@@ -21,7 +21,7 @@ class MockApplicationPackageStore extends ApplicationPackageStore {
|
||||
apkPath: '/mock/path/to/android/SkyShell.apk',
|
||||
launchActivity: 'io.flutter.android.mock.MockActivity'
|
||||
),
|
||||
iOS: new IOSApp(
|
||||
iOS: new BuildableIOSApp(
|
||||
appDirectory: '/mock/path/to/iOS/SkyShell.app',
|
||||
projectBundleId: 'io.flutter.ios.mock'
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user