[flutter_releases] Flutter 1.22.4 framework cherrypicks (#70327)
* App.framework must support iOS 8 for older Flutter projects
* cherry pick fixes to https://github.com/flutter/flutter/issues/60118
* fix to --observatory-port flag
* update engine version
Co-authored-by: Jenn Magder <magder@google.com>
diff --git a/bin/internal/engine.version b/bin/internal/engine.version
index 45c0882..5aceb02 100644
--- a/bin/internal/engine.version
+++ b/bin/internal/engine.version
@@ -1 +1 @@
-a1440ca392ca23e874a105c5f3248b495bd0e247
+2c956a31c0a3d350827aee6c56bb63337c5b4e6e
diff --git a/examples/flutter_view/ios/Flutter/AppFrameworkInfo.plist b/examples/flutter_view/ios/Flutter/AppFrameworkInfo.plist
index f2872cf..6b4c0f7 100644
--- a/examples/flutter_view/ios/Flutter/AppFrameworkInfo.plist
+++ b/examples/flutter_view/ios/Flutter/AppFrameworkInfo.plist
@@ -21,6 +21,6 @@
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
- <string>9.0</string>
+ <string>8.0</string>
</dict>
</plist>
diff --git a/examples/hello_world/ios/Flutter/AppFrameworkInfo.plist b/examples/hello_world/ios/Flutter/AppFrameworkInfo.plist
index f2872cf..6b4c0f7 100644
--- a/examples/hello_world/ios/Flutter/AppFrameworkInfo.plist
+++ b/examples/hello_world/ios/Flutter/AppFrameworkInfo.plist
@@ -21,6 +21,6 @@
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
- <string>9.0</string>
+ <string>8.0</string>
</dict>
</plist>
diff --git a/examples/image_list/ios/Flutter/AppFrameworkInfo.plist b/examples/image_list/ios/Flutter/AppFrameworkInfo.plist
index f2872cf..6b4c0f7 100644
--- a/examples/image_list/ios/Flutter/AppFrameworkInfo.plist
+++ b/examples/image_list/ios/Flutter/AppFrameworkInfo.plist
@@ -21,6 +21,6 @@
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
- <string>9.0</string>
+ <string>8.0</string>
</dict>
</plist>
diff --git a/examples/layers/ios/Flutter/AppFrameworkInfo.plist b/examples/layers/ios/Flutter/AppFrameworkInfo.plist
index f2872cf..6b4c0f7 100644
--- a/examples/layers/ios/Flutter/AppFrameworkInfo.plist
+++ b/examples/layers/ios/Flutter/AppFrameworkInfo.plist
@@ -21,6 +21,6 @@
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
- <string>9.0</string>
+ <string>8.0</string>
</dict>
</plist>
diff --git a/examples/platform_channel/ios/Flutter/AppFrameworkInfo.plist b/examples/platform_channel/ios/Flutter/AppFrameworkInfo.plist
index f2872cf..6b4c0f7 100644
--- a/examples/platform_channel/ios/Flutter/AppFrameworkInfo.plist
+++ b/examples/platform_channel/ios/Flutter/AppFrameworkInfo.plist
@@ -21,6 +21,6 @@
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
- <string>9.0</string>
+ <string>8.0</string>
</dict>
</plist>
diff --git a/examples/platform_channel_swift/ios/Flutter/AppFrameworkInfo.plist b/examples/platform_channel_swift/ios/Flutter/AppFrameworkInfo.plist
index f2872cf..6b4c0f7 100644
--- a/examples/platform_channel_swift/ios/Flutter/AppFrameworkInfo.plist
+++ b/examples/platform_channel_swift/ios/Flutter/AppFrameworkInfo.plist
@@ -21,6 +21,6 @@
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
- <string>9.0</string>
+ <string>8.0</string>
</dict>
</plist>
diff --git a/examples/platform_view/ios/Flutter/AppFrameworkInfo.plist b/examples/platform_view/ios/Flutter/AppFrameworkInfo.plist
index f2872cf..6b4c0f7 100644
--- a/examples/platform_view/ios/Flutter/AppFrameworkInfo.plist
+++ b/examples/platform_view/ios/Flutter/AppFrameworkInfo.plist
@@ -21,6 +21,6 @@
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
- <string>9.0</string>
+ <string>8.0</string>
</dict>
</plist>
diff --git a/packages/flutter_tools/lib/src/application_package.dart b/packages/flutter_tools/lib/src/application_package.dart
index f84d603..2c1a6ea 100644
--- a/packages/flutter_tools/lib/src/application_package.dart
+++ b/packages/flutter_tools/lib/src/application_package.dart
@@ -329,7 +329,7 @@
}
static Future<IOSApp> fromIosProject(IosProject project, BuildInfo buildInfo) {
- if (getCurrentHostPlatform() != HostPlatform.darwin_x64) {
+ if (!globals.platform.isMacOS) {
return null;
}
if (!project.exists) {
diff --git a/packages/flutter_tools/lib/src/base/build.dart b/packages/flutter_tools/lib/src/base/build.dart
index fadc637..fc097c2 100644
--- a/packages/flutter_tools/lib/src/base/build.dart
+++ b/packages/flutter_tools/lib/src/base/build.dart
@@ -241,7 +241,10 @@
final List<String> commonBuildOptions = <String>[
'-arch', targetArch,
if (isIOS)
- '-miphoneos-version-min=9.0',
+ // When the minimum version is updated, remember to update
+ // template MinimumOSVersion.
+ // https://github.com/flutter/flutter/pull/62902
+ '-miphoneos-version-min=8.0',
];
const String embedBitcodeArg = '-fembed-bitcode';
diff --git a/packages/flutter_tools/lib/src/base/os.dart b/packages/flutter_tools/lib/src/base/os.dart
index e57a8bc..54e7e9a 100644
--- a/packages/flutter_tools/lib/src/base/os.dart
+++ b/packages/flutter_tools/lib/src/base/os.dart
@@ -7,6 +7,7 @@
import 'package:meta/meta.dart';
import 'package:process/process.dart';
+import '../build_info.dart';
import '../globals.dart' as globals;
import 'common.dart';
import 'file_system.dart';
@@ -29,6 +30,13 @@
platform: platform,
processManager: processManager,
);
+ } else if (platform.isMacOS) {
+ return _MacOSUtils(
+ fileSystem: fileSystem,
+ logger: logger,
+ platform: platform,
+ processManager: processManager,
+ );
} else {
return _PosixUtils(
fileSystem: fileSystem,
@@ -114,6 +122,8 @@
return osNames.containsKey(osName) ? osNames[osName] : osName;
}
+ HostPlatform get hostPlatform;
+
List<File> _which(String execName, { bool all = false });
/// Returns the separator between items in the PATH environment variable.
@@ -243,29 +253,63 @@
return _fileSystem.file(path);
}
+ @override
+ String get pathVarSeparator => ':';
+
+ @override
+ HostPlatform hostPlatform = HostPlatform.linux_x64;
+}
+
+class _MacOSUtils extends _PosixUtils {
+ _MacOSUtils({
+ @required FileSystem fileSystem,
+ @required Logger logger,
+ @required Platform platform,
+ @required ProcessManager processManager,
+ }) : super(
+ fileSystem: fileSystem,
+ logger: logger,
+ platform: platform,
+ processManager: processManager,
+ );
+
String _name;
@override
String get name {
if (_name == null) {
- if (_platform.isMacOS) {
- final List<RunResult> results = <RunResult>[
- _processUtils.runSync(<String>['sw_vers', '-productName']),
- _processUtils.runSync(<String>['sw_vers', '-productVersion']),
- _processUtils.runSync(<String>['sw_vers', '-buildVersion']),
- ];
- if (results.every((RunResult result) => result.exitCode == 0)) {
- _name = '${results[0].stdout.trim()} ${results[1].stdout
- .trim()} ${results[2].stdout.trim()}';
- }
+ final List<RunResult> results = <RunResult>[
+ _processUtils.runSync(<String>['sw_vers', '-productName']),
+ _processUtils.runSync(<String>['sw_vers', '-productVersion']),
+ _processUtils.runSync(<String>['sw_vers', '-buildVersion']),
+ ];
+ if (results.every((RunResult result) => result.exitCode == 0)) {
+ _name =
+ '${results[0].stdout.trim()} ${results[1].stdout.trim()} ${results[2].stdout.trim()} ${getNameForHostPlatform(hostPlatform)}';
}
_name ??= super.name;
}
return _name;
}
+ HostPlatform _hostPlatform;
+
+ // On ARM returns arm64, even when this process is running in Rosetta.
@override
- String get pathVarSeparator => ':';
+ HostPlatform get hostPlatform {
+ if (_hostPlatform == null) {
+ final RunResult arm64Check =
+ _processUtils.runSync(<String>['sysctl', 'hw.optional.arm64']);
+ // On arm64 stdout is "sysctl hw.optional.arm64: 1"
+ // On x86 hw.optional.arm64 is unavailable and exits with 1.
+ if (arm64Check.exitCode == 0 && arm64Check.stdout.trim().endsWith('1')) {
+ _hostPlatform = HostPlatform.darwin_arm;
+ } else {
+ _hostPlatform = HostPlatform.darwin_x64;
+ }
+ }
+ return _hostPlatform;
+ }
}
class _WindowsUtils extends OperatingSystemUtils {
@@ -282,6 +326,9 @@
);
@override
+ HostPlatform hostPlatform = HostPlatform.windows_x64;
+
+ @override
void makeExecutable(File file) {}
@override
diff --git a/packages/flutter_tools/lib/src/build_info.dart b/packages/flutter_tools/lib/src/build_info.dart
index 04dadab..4fc8d2f 100644
--- a/packages/flutter_tools/lib/src/build_info.dart
+++ b/packages/flutter_tools/lib/src/build_info.dart
@@ -394,6 +394,7 @@
enum HostPlatform {
darwin_x64,
+ darwin_arm,
linux_x64,
windows_x64,
}
@@ -402,6 +403,8 @@
switch (platform) {
case HostPlatform.darwin_x64:
return 'darwin-x64';
+ case HostPlatform.darwin_arm:
+ return 'darwin-arm';
case HostPlatform.linux_x64:
return 'linux-x64';
case HostPlatform.windows_x64:
@@ -414,6 +417,7 @@
enum TargetPlatform {
android,
ios,
+ // darwin_arm64 not yet supported, macOS desktop targets run in Rosetta as x86.
darwin_x64,
linux_x64,
windows_x64,
diff --git a/packages/flutter_tools/lib/src/build_system/targets/ios.dart b/packages/flutter_tools/lib/src/build_system/targets/ios.dart
index cd218a4..8948142 100644
--- a/packages/flutter_tools/lib/src/build_system/targets/ios.dart
+++ b/packages/flutter_tools/lib/src/build_system/targets/ios.dart
@@ -236,14 +236,15 @@
throw Exception('Failed to create App.framework.');
}
final List<String> lipoCommand = <String>[
- 'xcrun',
+ ...globals.xcode.xcrunCommand(),
'lipo',
'-create',
iphoneFile.path,
simulatorFile.path,
'-output',
- lipoOutputFile.path
+ lipoOutputFile.path,
];
+
final RunResult lipoResult = await processUtils.run(
lipoCommand,
);
diff --git a/packages/flutter_tools/lib/src/commands/build_ios_framework.dart b/packages/flutter_tools/lib/src/commands/build_ios_framework.dart
index eba5be4..9d76323 100644
--- a/packages/flutter_tools/lib/src/commands/build_ios_framework.dart
+++ b/packages/flutter_tools/lib/src/commands/build_ios_framework.dart
@@ -318,7 +318,7 @@
// Remove simulator architecture in profile and release mode.
final List<String> lipoCommand = <String>[
- 'xcrun',
+ ...globals.xcode.xcrunCommand(),
'lipo',
fatFlutterFrameworkBinary.path,
'-remove',
@@ -425,7 +425,7 @@
'bitcode' : 'marker'; // In release, force bitcode embedding without archiving.
List<String> pluginsBuildCommand = <String>[
- 'xcrun',
+ ...globals.xcode.xcrunCommand(),
'xcodebuild',
'-alltargets',
'-sdk',
@@ -451,7 +451,7 @@
if (mode == BuildMode.debug) {
pluginsBuildCommand = <String>[
- 'xcrun',
+ ...globals.xcode.xcrunCommand(),
'xcodebuild',
'-alltargets',
'-sdk',
@@ -503,7 +503,7 @@
modeDirectory.childDirectory(podFrameworkName),
);
final List<String> lipoCommand = <String>[
- 'xcrun',
+ ...globals.xcode.xcrunCommand(),
'lipo',
'-create',
globals.fs.path.join(podProduct.path, binaryName),
@@ -532,7 +532,7 @@
if (boolArg('xcframework')) {
final List<String> xcframeworkCommand = <String>[
- 'xcrun',
+ ...globals.xcode.xcrunCommand(),
'xcodebuild',
'-create-xcframework',
'-framework',
@@ -616,7 +616,7 @@
// Create iOS framework.
List<String> lipoCommand = <String>[
- 'xcrun',
+ ...globals.xcode.xcrunCommand(),
'lipo',
fatFlutterFrameworkBinary.path,
'-remove',
@@ -642,7 +642,7 @@
globals.fsUtils.copyDirectorySync(fatFramework, simulatorFlutterFrameworkDirectory);
lipoCommand = <String>[
- 'xcrun',
+ ...globals.xcode.xcrunCommand(),
'lipo',
fatFlutterFrameworkBinary.path,
'-thin',
@@ -663,7 +663,7 @@
// Create XCFramework from iOS and simulator frameworks.
final List<String> xcframeworkCommand = <String>[
- 'xcrun',
+ ...globals.xcode.xcrunCommand(),
'xcodebuild',
'-create-xcframework',
'-framework', armFlutterFrameworkDirectory.path,
@@ -696,7 +696,7 @@
// Simulator is only supported in Debug mode.
// "Fat" framework here must only contain arm.
final List<String> xcframeworkCommand = <String>[
- 'xcrun',
+ ...globals.xcode.xcrunCommand(),
'xcodebuild',
'-create-xcframework',
'-framework', fatFramework.path,
diff --git a/packages/flutter_tools/lib/src/ios/mac.dart b/packages/flutter_tools/lib/src/ios/mac.dart
index a245b85..823c146 100644
--- a/packages/flutter_tools/lib/src/ios/mac.dart
+++ b/packages/flutter_tools/lib/src/ios/mac.dart
@@ -193,10 +193,10 @@
}
final List<String> buildCommands = <String>[
- '/usr/bin/env',
- 'xcrun',
+ ...globals.xcode.xcrunCommand(),
'xcodebuild',
- '-configuration', configuration,
+ '-configuration',
+ configuration,
];
if (globals.logger.isVerbose) {
diff --git a/packages/flutter_tools/lib/src/ios/simulators.dart b/packages/flutter_tools/lib/src/ios/simulators.dart
index dcba2bb..fc7aa5a 100644
--- a/packages/flutter_tools/lib/src/ios/simulators.dart
+++ b/packages/flutter_tools/lib/src/ios/simulators.dart
@@ -25,7 +25,6 @@
import 'mac.dart';
import 'plist_parser.dart';
-const String _xcrunPath = '/usr/bin/xcrun';
const String iosSimulatorId = 'apple_ios_simulator';
class IOSSimulators extends PollingDeviceDiscovery {
@@ -51,8 +50,12 @@
@required Xcode xcode,
@required Logger logger,
@required ProcessManager processManager,
- }) : _simControl = SimControl(logger: logger, processManager: processManager),
- _xcode = xcode;
+ }) : _simControl = SimControl(
+ logger: logger,
+ processManager: processManager,
+ xcode: xcode,
+ ),
+ _xcode = xcode;
final SimControl _simControl;
final Xcode _xcode;
@@ -80,11 +83,14 @@
SimControl({
@required Logger logger,
@required ProcessManager processManager,
- }) : _logger = logger,
- _processUtils = ProcessUtils(processManager: processManager, logger: logger);
+ @required Xcode xcode,
+ }) : _logger = logger,
+ _xcode = xcode,
+ _processUtils = ProcessUtils(processManager: processManager, logger: logger);
final Logger _logger;
final ProcessUtils _processUtils;
+ final Xcode _xcode;
/// Runs `simctl list --json` and returns the JSON of the corresponding
/// [section].
@@ -106,7 +112,13 @@
// },
// "pairs": { ... },
- final List<String> command = <String>[_xcrunPath, 'simctl', 'list', '--json', section.name];
+ final List<String> command = <String>[
+ ..._xcode.xcrunCommand(),
+ 'simctl',
+ 'list',
+ '--json',
+ section.name,
+ ];
_logger.printTrace(command.join(' '));
final RunResult results = await _processUtils.run(command);
if (results.exitCode != 0) {
@@ -155,7 +167,7 @@
Future<bool> isInstalled(String deviceId, String appId) {
return _processUtils.exitsHappy(<String>[
- _xcrunPath,
+ ..._xcode.xcrunCommand(),
'simctl',
'get_app_container',
deviceId,
@@ -167,7 +179,13 @@
RunResult result;
try {
result = await _processUtils.run(
- <String>[_xcrunPath, 'simctl', 'install', deviceId, appPath],
+ <String>[
+ ..._xcode.xcrunCommand(),
+ 'simctl',
+ 'install',
+ deviceId,
+ appPath,
+ ],
throwOnError: true,
);
} on ProcessException catch (exception) {
@@ -180,7 +198,13 @@
RunResult result;
try {
result = await _processUtils.run(
- <String>[_xcrunPath, 'simctl', 'uninstall', deviceId, appId],
+ <String>[
+ ..._xcode.xcrunCommand(),
+ 'simctl',
+ 'uninstall',
+ deviceId,
+ appId,
+ ],
throwOnError: true,
);
} on ProcessException catch (exception) {
@@ -194,7 +218,7 @@
try {
result = await _processUtils.run(
<String>[
- _xcrunPath,
+ ..._xcode.xcrunCommand(),
'simctl',
'launch',
deviceId,
@@ -212,7 +236,14 @@
Future<void> takeScreenshot(String deviceId, String outputPath) async {
try {
await _processUtils.run(
- <String>[_xcrunPath, 'simctl', 'io', deviceId, 'screenshot', outputPath],
+ <String>[
+ ..._xcode.xcrunCommand(),
+ 'simctl',
+ 'io',
+ deviceId,
+ 'screenshot',
+ outputPath,
+ ],
throwOnError: true,
);
} on ProcessException catch (exception) {
@@ -316,8 +347,6 @@
Map<ApplicationPackage, _IOSSimulatorLogReader> _logReaders;
_IOSSimulatorDevicePortForwarder _portForwarder;
- String get xcrunPath => globals.fs.path.join('/usr', 'bin', 'xcrun');
-
@override
Future<bool> isAppInstalled(
ApplicationPackage app, {
@@ -424,7 +453,7 @@
if (debuggingOptions.skiaDeterministicRendering) '--skia-deterministic-rendering',
if (debuggingOptions.useTestFonts) '--use-test-fonts',
if (debuggingOptions.traceAllowlist != null) '--trace-allowlist="${debuggingOptions.traceAllowlist}"',
- if (dartVmFlags.isNotEmpty) '--dart-flags=$dartVmFlags'
+ if (dartVmFlags.isNotEmpty) '--dart-flags=$dartVmFlags',
'--observatory-port=${debuggingOptions.hostVmServicePort ?? 0}',
],
];
@@ -632,7 +661,16 @@
]);
return processUtils.start(<String>[
- _xcrunPath, 'simctl', 'spawn', device.id, 'log', 'stream', '--style', 'json', '--predicate', predicate,
+ ...globals.xcode.xcrunCommand(),
+ 'simctl',
+ 'spawn',
+ device.id,
+ 'log',
+ 'stream',
+ '--style',
+ 'json',
+ '--predicate',
+ predicate,
]);
}
diff --git a/packages/flutter_tools/lib/src/macos/xcode.dart b/packages/flutter_tools/lib/src/macos/xcode.dart
index fa824af..72bb6db 100644
--- a/packages/flutter_tools/lib/src/macos/xcode.dart
+++ b/packages/flutter_tools/lib/src/macos/xcode.dart
@@ -13,6 +13,7 @@
import '../base/file_system.dart';
import '../base/io.dart';
import '../base/logger.dart';
+import '../base/os.dart';
import '../base/platform.dart';
import '../base/process.dart';
import '../build_info.dart';
@@ -63,13 +64,21 @@
@required Logger logger,
@required FileSystem fileSystem,
@required XcodeProjectInterpreter xcodeProjectInterpreter,
- }) : _platform = platform,
- _fileSystem = fileSystem,
- _xcodeProjectInterpreter = xcodeProjectInterpreter,
- _processUtils = ProcessUtils(logger: logger, processManager: processManager);
+ }) : _platform = platform,
+ _fileSystem = fileSystem,
+ _xcodeProjectInterpreter = xcodeProjectInterpreter,
+ _operatingSystemUtils = OperatingSystemUtils(
+ fileSystem: fileSystem,
+ logger: logger,
+ platform: platform,
+ processManager: processManager,
+ ),
+ _processUtils =
+ ProcessUtils(logger: logger, processManager: processManager);
final Platform _platform;
final ProcessUtils _processUtils;
+ final OperatingSystemUtils _operatingSystemUtils;
final FileSystem _fileSystem;
final XcodeProjectInterpreter _xcodeProjectInterpreter;
@@ -110,7 +119,7 @@
if (_eulaSigned == null) {
try {
final RunResult result = _processUtils.runSync(
- <String>['/usr/bin/xcrun', 'clang'],
+ <String>[...xcrunCommand(), 'clang'],
);
if (result.stdout != null && result.stdout.contains('license')) {
_eulaSigned = false;
@@ -135,7 +144,7 @@
// This command will error if additional components need to be installed in
// xcode 9.2 and above.
final RunResult result = _processUtils.runSync(
- <String>['/usr/bin/xcrun', 'simctl', 'list'],
+ <String>[...xcrunCommand(), 'simctl', 'list'],
);
_isSimctlInstalled = result.stderr == null || result.stderr == '';
} on ProcessException {
@@ -161,16 +170,35 @@
return false;
}
+ /// The `xcrun` Xcode command to run or locate development
+ /// tools and properties.
+ ///
+ /// Returns `xcrun` on x86 macOS.
+ /// Returns `/usr/bin/arch -arm64e xcrun` on ARM macOS to force Xcode commands
+ /// to run outside the x86 Rosetta translation, which may cause crashes.
+ List<String> xcrunCommand() {
+ final List<String> xcrunCommand = <String>[];
+ if (_operatingSystemUtils.hostPlatform == HostPlatform.darwin_arm) {
+ // Force Xcode commands to run outside Rosetta.
+ xcrunCommand.addAll(<String>[
+ '/usr/bin/arch',
+ '-arm64e',
+ ]);
+ }
+ xcrunCommand.add('xcrun');
+ return xcrunCommand;
+ }
+
Future<RunResult> cc(List<String> args) {
return _processUtils.run(
- <String>['xcrun', 'cc', ...args],
+ <String>[...xcrunCommand(), 'cc', ...args],
throwOnError: true,
);
}
Future<RunResult> clang(List<String> args) {
return _processUtils.run(
- <String>['xcrun', 'clang', ...args],
+ <String>[...xcrunCommand(), 'clang', ...args],
throwOnError: true,
);
}
@@ -178,7 +206,7 @@
Future<String> sdkLocation(SdkType sdk) async {
assert(sdk != null);
final RunResult runResult = await _processUtils.run(
- <String>['xcrun', '--sdk', getNameForSdk(sdk), '--show-sdk-path'],
+ <String>[...xcrunCommand(), '--sdk', getNameForSdk(sdk), '--show-sdk-path'],
);
if (runResult.exitCode != 0) {
throwToolExit('Could not find SDK location: ${runResult.stderr}');
@@ -260,28 +288,7 @@
);
}
- bool get isInstalled => _xcode.isInstalledAndMeetsVersionCheck && xcdevicePath != null;
-
- String _xcdevicePath;
- String get xcdevicePath {
- if (_xcdevicePath == null) {
- try {
- _xcdevicePath = _processUtils.runSync(
- <String>[
- 'xcrun',
- '--find',
- 'xcdevice'
- ],
- throwOnError: true,
- ).stdout.trim();
- } on ProcessException catch (exception) {
- _logger.printTrace('Process exception finding xcdevice:\n$exception');
- } on ArgumentError catch (exception) {
- _logger.printTrace('Argument exception finding xcdevice:\n$exception');
- }
- }
- return _xcdevicePath;
- }
+ bool get isInstalled => _xcode.isInstalledAndMeetsVersionCheck;
Future<List<dynamic>> _getAllDevices({
bool useCache = false,
@@ -298,7 +305,7 @@
// USB-tethered devices should be found quickly. 1 second timeout is faster than the default.
final RunResult result = await _processUtils.run(
<String>[
- 'xcrun',
+ ..._xcode.xcrunCommand(),
'xcdevice',
'list',
'--timeout',
@@ -352,7 +359,7 @@
'-t',
'0',
'/dev/null',
- 'xcrun',
+ ..._xcode.xcrunCommand(),
'xcdevice',
'observe',
'--both',
diff --git a/packages/flutter_tools/templates/app/ios.tmpl/Flutter/AppFrameworkInfo.plist b/packages/flutter_tools/templates/app/ios.tmpl/Flutter/AppFrameworkInfo.plist
index f2872cf..6b4c0f7 100644
--- a/packages/flutter_tools/templates/app/ios.tmpl/Flutter/AppFrameworkInfo.plist
+++ b/packages/flutter_tools/templates/app/ios.tmpl/Flutter/AppFrameworkInfo.plist
@@ -21,6 +21,6 @@
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
- <string>9.0</string>
+ <string>8.0</string>
</dict>
</plist>
diff --git a/packages/flutter_tools/templates/module/ios/library/Flutter.tmpl/AppFrameworkInfo.plist b/packages/flutter_tools/templates/module/ios/library/Flutter.tmpl/AppFrameworkInfo.plist
index f2872cf..6b4c0f7 100644
--- a/packages/flutter_tools/templates/module/ios/library/Flutter.tmpl/AppFrameworkInfo.plist
+++ b/packages/flutter_tools/templates/module/ios/library/Flutter.tmpl/AppFrameworkInfo.plist
@@ -21,6 +21,6 @@
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
- <string>9.0</string>
+ <string>8.0</string>
</dict>
</plist>
diff --git a/packages/flutter_tools/test/general.shard/base/build_test.dart b/packages/flutter_tools/test/general.shard/base/build_test.dart
index 8136345..1d7e13c 100644
--- a/packages/flutter_tools/test/general.shard/base/build_test.dart
+++ b/packages/flutter_tools/test/general.shard/base/build_test.dart
@@ -17,6 +17,14 @@
import '../../src/common.dart';
import '../../src/context.dart';
+const FakeCommand kARMCheckCommand = FakeCommand(
+ command: <String>[
+ 'sysctl',
+ 'hw.optional.arm64',
+ ],
+ exitCode: 1,
+);
+
const FakeCommand kSdkPathCommand = FakeCommand(
command: <String>[
'xcrun',
@@ -27,7 +35,7 @@
);
const List<String> kDefaultClang = <String>[
- '-miphoneos-version-min=9.0',
+ '-miphoneos-version-min=8.0',
'-dynamiclib',
'-Xlinker',
'-rpath',
@@ -47,7 +55,7 @@
];
const List<String> kBitcodeClang = <String>[
- '-miphoneos-version-min=9.0',
+ '-miphoneos-version-min=8.0',
'-dynamiclib',
'-Xlinker',
'-rpath',
@@ -265,6 +273,7 @@
'main.dill',
]
));
+ processManager.addCommand(kARMCheckCommand);
processManager.addCommand(kSdkPathCommand);
processManager.addCommand(const FakeCommand(
command: <String>[
@@ -327,6 +336,7 @@
'main.dill',
]
));
+ processManager.addCommand(kARMCheckCommand);
processManager.addCommand(kSdkPathCommand);
processManager.addCommand(const FakeCommand(
command: <String>[
@@ -386,6 +396,7 @@
'main.dill',
]
));
+ processManager.addCommand(kARMCheckCommand);
processManager.addCommand(kSdkPathCommand);
processManager.addCommand(const FakeCommand(
command: <String>[
@@ -444,6 +455,7 @@
'main.dill',
]
));
+ processManager.addCommand(kARMCheckCommand);
processManager.addCommand(kSdkPathCommand);
processManager.addCommand(const FakeCommand(
command: <String>[
@@ -499,6 +511,7 @@
'main.dill',
]
));
+ processManager.addCommand(kARMCheckCommand);
processManager.addCommand(kSdkPathCommand);
processManager.addCommand(const FakeCommand(
command: <String>[
diff --git a/packages/flutter_tools/test/general.shard/base/os_test.dart b/packages/flutter_tools/test/general.shard/base/os_test.dart
index b697736..73e08ea 100644
--- a/packages/flutter_tools/test/general.shard/base/os_test.dart
+++ b/packages/flutter_tools/test/general.shard/base/os_test.dart
@@ -6,10 +6,10 @@
import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/common.dart';
import 'package:flutter_tools/src/base/file_system.dart';
-import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/os.dart';
import 'package:flutter_tools/src/base/platform.dart';
+import 'package:flutter_tools/src/build_info.dart';
import 'package:mockito/mockito.dart';
import 'package:process/process.dart';
@@ -22,9 +22,11 @@
void main() {
MockProcessManager mockProcessManager;
+ FakeProcessManager fakeProcessManager;
setUp(() {
mockProcessManager = MockProcessManager();
+ fakeProcessManager = FakeProcessManager.list(<FakeCommand>[]);
});
OperatingSystemUtils createOSUtils(Platform platform) {
@@ -32,28 +34,50 @@
fileSystem: MemoryFileSystem(),
logger: BufferLogger.test(),
platform: platform,
- processManager: mockProcessManager,
+ processManager: fakeProcessManager,
);
}
group('which on POSIX', () {
testWithoutContext('returns null when executable does not exist', () async {
- when(mockProcessManager.runSync(<String>['which', kExecutable]))
- .thenReturn(ProcessResult(0, 1, null, null));
+ fakeProcessManager.addCommand(
+ const FakeCommand(
+ command: <String>[
+ 'which',
+ kExecutable,
+ ],
+ exitCode: 1,
+ ),
+ );
final OperatingSystemUtils utils = createOSUtils(FakePlatform(operatingSystem: 'linux'));
expect(utils.which(kExecutable), isNull);
});
testWithoutContext('returns exactly one result', () async {
- when(mockProcessManager.runSync(<String>['which', 'foo']))
- .thenReturn(ProcessResult(0, 0, kPath1, null));
+ fakeProcessManager.addCommand(
+ const FakeCommand(
+ command: <String>[
+ 'which',
+ 'foo',
+ ],
+ stdout: kPath1,
+ ),
+ );
final OperatingSystemUtils utils = createOSUtils(FakePlatform(operatingSystem: 'linux'));
expect(utils.which(kExecutable).path, kPath1);
});
testWithoutContext('returns all results for whichAll', () async {
- when(mockProcessManager.runSync(<String>['which', '-a', kExecutable]))
- .thenReturn(ProcessResult(0, 0, '$kPath1\n$kPath2', null));
+ fakeProcessManager.addCommand(
+ const FakeCommand(
+ command: <String>[
+ 'which',
+ '-a',
+ kExecutable,
+ ],
+ stdout: '$kPath1\n$kPath2',
+ ),
+ );
final OperatingSystemUtils utils = createOSUtils(FakePlatform(operatingSystem: 'linux'));
final List<File> result = utils.whichAll(kExecutable);
expect(result, hasLength(2));
@@ -66,27 +90,55 @@
testWithoutContext('throws tool exit if where throws an argument error', () async {
when(mockProcessManager.runSync(<String>['where', kExecutable]))
.thenThrow(ArgumentError('Cannot find executable for where'));
- final OperatingSystemUtils utils = createOSUtils(FakePlatform(operatingSystem: 'windows'));
+ final OperatingSystemUtils utils = OperatingSystemUtils(
+ fileSystem: MemoryFileSystem.test(),
+ logger: BufferLogger.test(),
+ platform: FakePlatform(operatingSystem: 'windows'),
+ processManager: mockProcessManager,
+ );
expect(() => utils.which(kExecutable), throwsA(isA<ToolExit>()));
});
+
testWithoutContext('returns null when executable does not exist', () async {
- when(mockProcessManager.runSync(<String>['where', kExecutable]))
- .thenReturn(ProcessResult(0, 1, null, null));
+ fakeProcessManager.addCommand(
+ const FakeCommand(
+ command: <String>[
+ 'where',
+ kExecutable,
+ ],
+ exitCode: 1,
+ ),
+ );
+
final OperatingSystemUtils utils = createOSUtils(FakePlatform(operatingSystem: 'windows'));
expect(utils.which(kExecutable), isNull);
});
testWithoutContext('returns exactly one result', () async {
- when(mockProcessManager.runSync(<String>['where', 'foo']))
- .thenReturn(ProcessResult(0, 0, '$kPath1\n$kPath2', null));
+ fakeProcessManager.addCommand(
+ const FakeCommand(
+ command: <String>[
+ 'where',
+ 'foo',
+ ],
+ stdout: '$kPath1\n$kPath2',
+ ),
+ );
final OperatingSystemUtils utils = createOSUtils(FakePlatform(operatingSystem: 'windows'));
expect(utils.which(kExecutable).path, kPath1);
});
testWithoutContext('returns all results for whichAll', () async {
- when(mockProcessManager.runSync(<String>['where', kExecutable]))
- .thenReturn(ProcessResult(0, 0, '$kPath1\n$kPath2', null));
+ fakeProcessManager.addCommand(
+ const FakeCommand(
+ command: <String>[
+ 'where',
+ kExecutable,
+ ],
+ stdout: '$kPath1\n$kPath2',
+ ),
+ );
final OperatingSystemUtils utils = createOSUtils(FakePlatform(operatingSystem: 'windows'));
final List<File> result = utils.whichAll(kExecutable);
expect(result, hasLength(2));
@@ -95,11 +147,162 @@
});
});
+ group('host platform', () {
+ testWithoutContext('unknown defaults to Linux', () async {
+ final OperatingSystemUtils utils =
+ createOSUtils(FakePlatform(operatingSystem: 'fuchsia'));
+ expect(utils.hostPlatform, HostPlatform.linux_x64);
+ });
+
+ testWithoutContext('Windows', () async {
+ final OperatingSystemUtils utils =
+ createOSUtils(FakePlatform(operatingSystem: 'windows'));
+ expect(utils.hostPlatform, HostPlatform.windows_x64);
+ });
+
+ testWithoutContext('Linux', () async {
+ final OperatingSystemUtils utils =
+ createOSUtils(FakePlatform(operatingSystem: 'linux'));
+ expect(utils.hostPlatform, HostPlatform.linux_x64);
+ });
+
+ testWithoutContext('macOS ARM', () async {
+ fakeProcessManager.addCommand(
+ const FakeCommand(
+ command: <String>[
+ 'sysctl',
+ 'hw.optional.arm64',
+ ],
+ stdout: 'hw.optional.arm64: 1',
+ ),
+ );
+
+ final OperatingSystemUtils utils =
+ createOSUtils(FakePlatform(operatingSystem: 'macos'));
+ expect(utils.hostPlatform, HostPlatform.darwin_arm);
+ });
+
+ testWithoutContext('macOS 11 x86', () async {
+ fakeProcessManager.addCommand(
+ const FakeCommand(
+ command: <String>[
+ 'sysctl',
+ 'hw.optional.arm64',
+ ],
+ stdout: 'hw.optional.arm64: 0',
+ ),
+ );
+
+ final OperatingSystemUtils utils =
+ createOSUtils(FakePlatform(operatingSystem: 'macos'));
+ expect(utils.hostPlatform, HostPlatform.darwin_x64);
+ });
+
+ testWithoutContext('macOS 10 x86', () async {
+ fakeProcessManager.addCommand(
+ const FakeCommand(
+ command: <String>[
+ 'sysctl',
+ 'hw.optional.arm64',
+ ],
+ exitCode: 1,
+ ),
+ );
+
+ final OperatingSystemUtils utils =
+ createOSUtils(FakePlatform(operatingSystem: 'macos'));
+ expect(utils.hostPlatform, HostPlatform.darwin_x64);
+ });
+
+ testWithoutContext('macOS ARM name', () async {
+ fakeProcessManager.addCommands(<FakeCommand>[
+ const FakeCommand(
+ command: <String>[
+ 'sw_vers',
+ '-productName',
+ ],
+ stdout: 'product',
+ ),
+ const FakeCommand(
+ command: <String>[
+ 'sw_vers',
+ '-productVersion',
+ ],
+ stdout: 'version',
+ ),
+ const FakeCommand(
+ command: <String>[
+ 'sw_vers',
+ '-buildVersion',
+ ],
+ stdout: 'build',
+ ),
+ const FakeCommand(
+ command: <String>[
+ 'sysctl',
+ 'hw.optional.arm64',
+ ],
+ stdout: 'hw.optional.arm64: 1',
+ ),
+ ]);
+
+ final OperatingSystemUtils utils =
+ createOSUtils(FakePlatform(operatingSystem: 'macos'));
+ expect(utils.name, 'product version build darwin-arm');
+ });
+
+ testWithoutContext('macOS x86 name', () async {
+ fakeProcessManager.addCommands(<FakeCommand>[
+ const FakeCommand(
+ command: <String>[
+ 'sw_vers',
+ '-productName',
+ ],
+ stdout: 'product',
+ ),
+ const FakeCommand(
+ command: <String>[
+ 'sw_vers',
+ '-productVersion',
+ ],
+ stdout: 'version',
+ ),
+ const FakeCommand(
+ command: <String>[
+ 'sw_vers',
+ '-buildVersion',
+ ],
+ stdout: 'build',
+ ),
+ const FakeCommand(
+ command: <String>[
+ 'sysctl',
+ 'hw.optional.arm64',
+ ],
+ exitCode: 1,
+ ),
+ ]);
+
+ final OperatingSystemUtils utils =
+ createOSUtils(FakePlatform(operatingSystem: 'macos'));
+ expect(utils.name, 'product version build darwin-x64');
+ });
+ });
+
testWithoutContext('If unzip fails, include stderr in exception text', () {
const String exceptionMessage = 'Something really bad happened.';
- when(mockProcessManager.runSync(
- <String>['unzip', '-o', '-q', null, '-d', null],
- )).thenReturn(ProcessResult(0, 1, '', exceptionMessage));
+
+ fakeProcessManager.addCommand(
+ const FakeCommand(command: <String>[
+ 'unzip',
+ '-o',
+ '-q',
+ null,
+ '-d',
+ null,
+ ], exitCode: 1, stderr: exceptionMessage),
+ );
+
final MockFileSystem fileSystem = MockFileSystem();
final MockFile mockFile = MockFile();
final MockDirectory mockDirectory = MockDirectory();
@@ -111,7 +314,7 @@
fileSystem: fileSystem,
logger: BufferLogger.test(),
platform: FakePlatform(operatingSystem: 'linux'),
- processManager: mockProcessManager,
+ processManager: fakeProcessManager,
);
expect(
diff --git a/packages/flutter_tools/test/general.shard/build_system/targets/dart_test.dart b/packages/flutter_tools/test/general.shard/build_system/targets/dart_test.dart
index 4c36f2f..4019f3d 100644
--- a/packages/flutter_tools/test/general.shard/build_system/targets/dart_test.dart
+++ b/packages/flutter_tools/test/general.shard/build_system/targets/dart_test.dart
@@ -457,6 +457,13 @@
'--lazy-async-stacks',
'$build/app.dill',
]),
+ const FakeCommand(
+ command: <String>[
+ 'sysctl',
+ 'hw.optional.arm64',
+ ],
+ exitCode: 1,
+ ),
const FakeCommand(command: <String>[
'xcrun',
'--sdk',
@@ -498,7 +505,7 @@
'clang',
'-arch',
'armv7',
- '-miphoneos-version-min=9.0',
+ '-miphoneos-version-min=8.0',
'-dynamiclib',
'-Xlinker',
'-rpath',
@@ -521,7 +528,7 @@
'clang',
'-arch',
'arm64',
- '-miphoneos-version-min=9.0',
+ '-miphoneos-version-min=8.0',
'-dynamiclib',
'-Xlinker',
'-rpath',
@@ -575,6 +582,13 @@
'--lazy-async-stacks',
'$build/app.dill',
]),
+ const FakeCommand(
+ command: <String>[
+ 'sysctl',
+ 'hw.optional.arm64',
+ ],
+ exitCode: 1,
+ ),
const FakeCommand(command: <String>[
'xcrun',
'--sdk',
@@ -600,7 +614,7 @@
'clang',
'-arch',
'arm64',
- '-miphoneos-version-min=9.0',
+ '-miphoneos-version-min=8.0',
'-dynamiclib',
'-Xlinker',
'-rpath',
@@ -657,6 +671,13 @@
'--lazy-async-stacks',
'$build/app.dill',
]),
+ const FakeCommand(
+ command: <String>[
+ 'sysctl',
+ 'hw.optional.arm64',
+ ],
+ exitCode: 1,
+ ),
const FakeCommand(command: <String>[
'xcrun',
'--sdk',
@@ -682,7 +703,7 @@
'clang',
'-arch',
'arm64',
- '-miphoneos-version-min=9.0',
+ '-miphoneos-version-min=8.0',
'-dynamiclib',
'-Xlinker',
'-rpath',
diff --git a/packages/flutter_tools/test/general.shard/build_system/targets/ios_test.dart b/packages/flutter_tools/test/general.shard/build_system/targets/ios_test.dart
index 34fbcf4..347b939 100644
--- a/packages/flutter_tools/test/general.shard/build_system/targets/ios_test.dart
+++ b/packages/flutter_tools/test/general.shard/build_system/targets/ios_test.dart
@@ -72,6 +72,13 @@
environment.defines[kIosArchs] = 'arm64';
processManager.addCommands(<FakeCommand>[
// Create iphone stub.
+ const FakeCommand(
+ command: <String>[
+ 'sysctl',
+ 'hw.optional.arm64',
+ ],
+ exitCode: 1,
+ ),
const FakeCommand(command: <String>['xcrun', '--sdk', 'iphoneos', '--show-sdk-path']),
FakeCommand(command: <String>[
'xcrun',
diff --git a/packages/flutter_tools/test/general.shard/ios/ios_device_start_nonprebuilt_test.dart b/packages/flutter_tools/test/general.shard/ios/ios_device_start_nonprebuilt_test.dart
index f46a180..7463934 100644
--- a/packages/flutter_tools/test/general.shard/ios/ios_device_start_nonprebuilt_test.dart
+++ b/packages/flutter_tools/test/general.shard/ios/ios_device_start_nonprebuilt_test.dart
@@ -37,7 +37,6 @@
}
const List<String> kRunReleaseArgs = <String>[
- '/usr/bin/env',
'xcrun',
'xcodebuild',
'-configuration',
@@ -94,6 +93,7 @@
);
mockXcode = MockXcode();
when(mockXcode.isVersionSatisfactory).thenReturn(true);
+ when(mockXcode.xcrunCommand()).thenReturn(<String>['xcrun']);
fileSystem.file('foo/.packages')
..createSync(recursive: true)
..writeAsStringSync('\n');
diff --git a/packages/flutter_tools/test/general.shard/ios/simulators_test.dart b/packages/flutter_tools/test/general.shard/ios/simulators_test.dart
index 72bb8d9..1384825 100644
--- a/packages/flutter_tools/test/general.shard/ios/simulators_test.dart
+++ b/packages/flutter_tools/test/general.shard/ios/simulators_test.dart
@@ -21,6 +21,7 @@
import 'package:flutter_tools/src/ios/simulators.dart';
import 'package:flutter_tools/src/macos/xcode.dart';
import 'package:flutter_tools/src/project.dart';
+import 'package:flutter_tools/src/protocol_discovery.dart';
import 'package:mockito/mockito.dart';
import 'package:process/process.dart';
@@ -87,6 +88,7 @@
Platform: () => osx,
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
+ Xcode: () => mockXcode,
}, testOn: 'posix');
});
@@ -129,6 +131,7 @@
FileSystemUtils: () => fsUtils,
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
+ Xcode: () => mockXcode,
}, testOn: 'posix');
testUsingContext('respects IOS_SIMULATOR_LOG_FILE_PATH', () {
@@ -145,6 +148,7 @@
FileSystemUtils: () => fsUtils,
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
+ Xcode: () => mockXcode,
});
});
@@ -265,6 +269,7 @@
Platform: () => osx,
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
+ Xcode: () => mockXcode,
});
testUsingContext('Apple Watch is unsupported', () {
@@ -278,6 +283,7 @@
Platform: () => osx,
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
+ Xcode: () => mockXcode,
});
testUsingContext('iPad 2 is supported', () {
@@ -291,6 +297,7 @@
Platform: () => osx,
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
+ Xcode: () => mockXcode,
});
testUsingContext('iPad Retina is supported', () {
@@ -304,6 +311,7 @@
Platform: () => osx,
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
+ Xcode: () => mockXcode,
});
testUsingContext('iPhone 5 is supported', () {
@@ -317,6 +325,7 @@
Platform: () => osx,
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
+ Xcode: () => mockXcode,
});
testUsingContext('iPhone 5s is supported', () {
@@ -330,6 +339,7 @@
Platform: () => osx,
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
+ Xcode: () => mockXcode,
});
testUsingContext('iPhone SE is supported', () {
@@ -343,6 +353,7 @@
Platform: () => osx,
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
+ Xcode: () => mockXcode,
});
testUsingContext('iPhone 7 Plus is supported', () {
@@ -356,6 +367,7 @@
Platform: () => osx,
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
+ Xcode: () => mockXcode,
});
testUsingContext('iPhone X is supported', () {
@@ -369,6 +381,7 @@
Platform: () => osx,
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
+ Xcode: () => mockXcode,
});
});
@@ -391,7 +404,11 @@
Future<ProcessResult>.value(ProcessResult(2, 0, '', ''))
);
// Test a real one. Screenshot doesn't require instance states.
- final SimControl simControl = SimControl(processManager: mockProcessManager, logger: mockLogger);
+ final SimControl simControl = SimControl(
+ processManager: mockProcessManager,
+ logger: mockLogger,
+ xcode: mockXcode,
+ );
// Doesn't matter what the device is.
deviceUnderTest = IOSSimulator(
'x',
@@ -399,6 +416,7 @@
simControl: simControl,
xcode: mockXcode,
);
+ when(mockXcode.xcrunCommand()).thenReturn(<String>['xcrun']);
});
testWithoutContext(
@@ -421,7 +439,7 @@
await deviceUnderTest.takeScreenshot(mockFile);
verify(mockProcessManager.run(
<String>[
- '/usr/bin/xcrun',
+ 'xcrun',
'simctl',
'io',
'x',
@@ -446,6 +464,7 @@
.thenAnswer((Invocation invocation) => Future<Process>.value(MockProcess()));
mockSimControl = MockSimControl();
mockXcode = MockXcode();
+ when(mockXcode.xcrunCommand()).thenReturn(<String>['xcrun']);
});
testUsingContext('syslog uses tail', () async {
@@ -469,7 +488,8 @@
FileSystemUtils: () => FileSystemUtils(
fileSystem: fileSystem,
platform: macosPlatform,
- )
+ ),
+ Xcode: () => mockXcode,
});
testUsingContext('unified logging with app name', () async {
@@ -491,7 +511,7 @@
final List<String> command = verify(mockProcessManager.start(captureAny, environment: null, workingDirectory: null)).captured.single as List<String>;
expect(command, <String>[
- '/usr/bin/xcrun',
+ 'xcrun',
'simctl',
'spawn',
'x',
@@ -506,6 +526,7 @@
overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
FileSystem: () => fileSystem,
+ Xcode: () => mockXcode,
});
testUsingContext('unified logging without app name', () async {
@@ -526,7 +547,7 @@
final List<String> command = verify(mockProcessManager.start(captureAny, environment: null, workingDirectory: null)).captured.single as List<String>;
expect(command, <String>[
- '/usr/bin/xcrun',
+ 'xcrun',
'simctl',
'spawn',
'x',
@@ -541,6 +562,7 @@
overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
FileSystem: () => fileSystem,
+ Xcode: () => mockXcode,
});
});
@@ -555,6 +577,7 @@
mockIosProject = MockIosProject();
mockSimControl = MockSimControl();
mockXcode = MockXcode();
+ when(mockXcode.xcrunCommand()).thenReturn(<String>['xcrun']);
});
group('syslog', () {
@@ -594,6 +617,7 @@
ProcessManager: () => fakeProcessManager,
FileSystem: () => fileSystem,
Platform: () => osx,
+ Xcode: () => mockXcode,
});
testUsingContext('simulator can output `)`', () async {
@@ -630,6 +654,7 @@
ProcessManager: () => fakeProcessManager,
FileSystem: () => fileSystem,
Platform: () => osx,
+ Xcode: () => mockXcode,
});
testUsingContext('multiline messages', () async {
@@ -682,6 +707,7 @@
ProcessManager: () => fakeProcessManager,
FileSystem: () => fileSystem,
Platform: () => osx,
+ Xcode: () => mockXcode,
});
});
@@ -694,7 +720,7 @@
'AND NOT(eventMessage CONTAINS " libxpc.dylib ")';
fakeProcessManager.addCommand(const FakeCommand(
command: <String>[
- '/usr/bin/xcrun',
+ 'xcrun',
'simctl',
'spawn',
'123456',
@@ -740,6 +766,7 @@
}, overrides: <Type, Generator>{
ProcessManager: () => fakeProcessManager,
FileSystem: () => fileSystem,
+ Xcode: () => mockXcode,
});
});
});
@@ -791,11 +818,13 @@
return ProcessResult(mockPid, 0, validSimControlOutput, '');
});
+ mockXcode = MockXcode();
+ when(mockXcode.xcrunCommand()).thenReturn(<String>['xcrun']);
simControl = SimControl(
logger: mockLogger,
processManager: mockProcessManager,
+ xcode: mockXcode,
);
- mockXcode = MockXcode();
});
testWithoutContext('getDevices succeeds', () async {
@@ -848,7 +877,7 @@
testWithoutContext('.install() handles exceptions', () async {
when(mockProcessManager.run(
- <String>['/usr/bin/xcrun', 'simctl', 'install', deviceId, appId],
+ <String>['xcrun', 'simctl', 'install', deviceId, appId],
environment: anyNamed('environment'),
workingDirectory: anyNamed('workingDirectory'),
)).thenThrow(const ProcessException('xcrun', <String>[]));
@@ -860,7 +889,7 @@
testWithoutContext('.uninstall() handles exceptions', () async {
when(mockProcessManager.run(
- <String>['/usr/bin/xcrun', 'simctl', 'uninstall', deviceId, appId],
+ <String>['xcrun', 'simctl', 'uninstall', deviceId, appId],
environment: anyNamed('environment'),
workingDirectory: anyNamed('workingDirectory'),
)).thenThrow(const ProcessException('xcrun', <String>[]));
@@ -872,7 +901,7 @@
testWithoutContext('.launch() handles exceptions', () async {
when(mockProcessManager.run(
- <String>['/usr/bin/xcrun', 'simctl', 'launch', deviceId, appId],
+ <String>['xcrun', 'simctl', 'launch', deviceId, appId],
environment: anyNamed('environment'),
workingDirectory: anyNamed('workingDirectory'),
)).thenThrow(const ProcessException('xcrun', <String>[]));
@@ -886,10 +915,13 @@
group('startApp', () {
SimControl simControl;
MockXcode mockXcode;
+ MockPrototcolDiscovery mockPrototcolDiscovery;
setUp(() {
simControl = MockSimControl();
mockXcode = MockXcode();
+ when(mockXcode.xcrunCommand()).thenReturn(<String>['xcrun']);
+ mockPrototcolDiscovery = MockPrototcolDiscovery();
});
testUsingContext("startApp uses compiled app's Info.plist to find CFBundleIdentifier", () async {
@@ -901,19 +933,28 @@
xcode: mockXcode,
);
when(globals.plistParser.getValueFromFile(any, any)).thenReturn('correct');
+ when(mockPrototcolDiscovery.uri)
+ .thenAnswer((_) async => Uri.parse('http://localhost:5678'));
final Directory mockDir = globals.fs.currentDirectory;
final IOSApp package = PrebuiltIOSApp(projectBundleId: 'incorrect', bundleName: 'name', bundleDir: mockDir);
const BuildInfo mockInfo = BuildInfo(BuildMode.debug, 'flavor', treeShakeIcons: false);
- final DebuggingOptions mockOptions = DebuggingOptions.disabled(mockInfo);
+ final DebuggingOptions mockOptions =
+ DebuggingOptions.enabled(mockInfo, hostVmServicePort: 8888);
await device.startApp(package, prebuiltApplication: true, debuggingOptions: mockOptions);
- verify(simControl.launch(any, 'correct', any));
+ verify(simControl.launch(any, 'correct', <String>[
+ '--enable-dart-profiling',
+ '--enable-checked-mode',
+ '--verify-entry-points',
+ '--observatory-port=8888',
+ ]));
}, overrides: <Type, Generator>{
PlistParser: () => MockPlistUtils(),
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
+ Xcode: () => mockXcode,
});
});
@@ -947,6 +988,7 @@
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem(),
ProcessManager: () => FakeProcessManager.any(),
+ Xcode: () => mockXcode,
});
@@ -965,6 +1007,7 @@
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem(),
ProcessManager: () => FakeProcessManager.any(),
+ Xcode: () => mockXcode,
});
testUsingContext('is false with no host app and no module', () async {
@@ -981,8 +1024,10 @@
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem(),
ProcessManager: () => FakeProcessManager.any(),
+ Xcode: () => mockXcode,
});
});
}
+class MockPrototcolDiscovery extends Mock implements ProtocolDiscovery {}
class MockBuildSystem extends Mock implements BuildSystem {}
diff --git a/packages/flutter_tools/test/general.shard/macos/xcode_test.dart b/packages/flutter_tools/test/general.shard/macos/xcode_test.dart
index 0c5f6a2..ea24a99 100644
--- a/packages/flutter_tools/test/general.shard/macos/xcode_test.dart
+++ b/packages/flutter_tools/test/general.shard/macos/xcode_test.dart
@@ -43,7 +43,7 @@
setUp(() {
xcode = Xcode(
logger: logger,
- platform: MockPlatform(),
+ platform: FakePlatform(operatingSystem: 'macos'),
fileSystem: MemoryFileSystem.test(),
processManager: processManager,
xcodeProjectInterpreter: MockXcodeProjectInterpreter(),
@@ -61,8 +61,10 @@
});
testWithoutContext('eulaSigned is false when clang is not installed', () {
- when(processManager.runSync(<String>['/usr/bin/xcrun', 'clang']))
- .thenThrow(const ProcessException('/usr/bin/xcrun', <String>['clang']));
+ when(processManager.runSync(<String>['sysctl', 'hw.optional.arm64']))
+ .thenReturn(ProcessResult(123, 1, '', ''));
+ when(processManager.runSync(<String>['xcrun', 'clang']))
+ .thenThrow(const ProcessException('xcrun', <String>['clang']));
expect(xcode.eulaSigned, isFalse);
});
@@ -83,23 +85,12 @@
cache: MockCache(),
iproxy: IProxy.test(logger: logger, processManager: processManager),
);
- });
-
- testWithoutContext("xcrun can't find xcdevice", () {
- when(mockXcode.isInstalledAndMeetsVersionCheck).thenReturn(true);
-
- when(processManager.runSync(<String>['xcrun', '--find', 'xcdevice']))
- .thenThrow(const ProcessException('xcrun', <String>['--find', 'xcdevice']));
- expect(xcdevice.isInstalled, false);
- verify(processManager.runSync(any)).called(1);
+ when(mockXcode.xcrunCommand()).thenReturn(<String>['xcrun']);
});
testWithoutContext('available devices xcdevice fails', () async {
when(mockXcode.isInstalledAndMeetsVersionCheck).thenReturn(true);
- when(processManager.runSync(<String>['xcrun', '--find', 'xcdevice']))
- .thenReturn(ProcessResult(1, 0, '/path/to/xcdevice', ''));
-
when(processManager.run(<String>['xcrun', 'xcdevice', 'list', '--timeout', '2']))
.thenThrow(const ProcessException('xcrun', <String>['xcdevice', 'list', '--timeout', '2']));
@@ -109,9 +100,6 @@
testWithoutContext('diagnostics xcdevice fails', () async {
when(mockXcode.isInstalledAndMeetsVersionCheck).thenReturn(true);
- when(processManager.runSync(<String>['xcrun', '--find', 'xcdevice']))
- .thenReturn(ProcessResult(1, 0, '/path/to/xcdevice', ''));
-
when(processManager.run(<String>['xcrun', 'xcdevice', 'list', '--timeout', '2']))
.thenThrow(const ProcessException('xcrun', <String>['xcdevice', 'list', '--timeout', '2']));
@@ -128,210 +116,270 @@
});
group('Xcode', () {
- Xcode xcode;
MockXcodeProjectInterpreter mockXcodeProjectInterpreter;
- MockPlatform platform;
setUp(() {
mockXcodeProjectInterpreter = MockXcodeProjectInterpreter();
- platform = MockPlatform();
- xcode = Xcode(
+ });
+
+ testWithoutContext('isInstalledAndMeetsVersionCheck is false when not macOS', () {
+ final Xcode xcode = Xcode(
logger: logger,
- platform: platform,
+ platform: FakePlatform(operatingSystem: 'windows'),
fileSystem: MemoryFileSystem.test(),
processManager: fakeProcessManager,
xcodeProjectInterpreter: mockXcodeProjectInterpreter,
);
- });
-
- testWithoutContext('xcodeSelectPath returns path when xcode-select is installed', () {
- const String xcodePath = '/Applications/Xcode8.0.app/Contents/Developer';
- fakeProcessManager.addCommand(const FakeCommand(
- command: <String>['/usr/bin/xcode-select', '--print-path'],
- stdout: xcodePath,
- ));
-
- expect(xcode.xcodeSelectPath, xcodePath);
- expect(fakeProcessManager.hasRemainingExpectations, isFalse);
- });
-
- testWithoutContext('xcodeVersionSatisfactory is false when version is less than minimum', () {
- when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true);
- when(mockXcodeProjectInterpreter.majorVersion).thenReturn(9);
- when(mockXcodeProjectInterpreter.minorVersion).thenReturn(0);
- when(mockXcodeProjectInterpreter.patchVersion).thenReturn(0);
-
- expect(xcode.isVersionSatisfactory, isFalse);
- });
-
- testWithoutContext('xcodeVersionSatisfactory is false when xcodebuild tools are not installed', () {
- when(mockXcodeProjectInterpreter.isInstalled).thenReturn(false);
-
- expect(xcode.isVersionSatisfactory, isFalse);
- });
-
- testWithoutContext('xcodeVersionSatisfactory is true when version meets minimum', () {
- when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true);
- when(mockXcodeProjectInterpreter.majorVersion).thenReturn(11);
- when(mockXcodeProjectInterpreter.minorVersion).thenReturn(0);
- when(mockXcodeProjectInterpreter.patchVersion).thenReturn(0);
-
- expect(xcode.isVersionSatisfactory, isTrue);
- });
-
- testWithoutContext('xcodeVersionSatisfactory is true when major version exceeds minimum', () {
- when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true);
- when(mockXcodeProjectInterpreter.majorVersion).thenReturn(12);
- when(mockXcodeProjectInterpreter.minorVersion).thenReturn(0);
- when(mockXcodeProjectInterpreter.patchVersion).thenReturn(0);
-
- expect(xcode.isVersionSatisfactory, isTrue);
- });
-
- testWithoutContext('xcodeVersionSatisfactory is true when minor version exceeds minimum', () {
- when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true);
- when(mockXcodeProjectInterpreter.majorVersion).thenReturn(11);
- when(mockXcodeProjectInterpreter.minorVersion).thenReturn(3);
- when(mockXcodeProjectInterpreter.patchVersion).thenReturn(0);
-
- expect(xcode.isVersionSatisfactory, isTrue);
- });
-
- testWithoutContext('xcodeVersionSatisfactory is true when patch version exceeds minimum', () {
- when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true);
- when(mockXcodeProjectInterpreter.majorVersion).thenReturn(11);
- when(mockXcodeProjectInterpreter.minorVersion).thenReturn(0);
- when(mockXcodeProjectInterpreter.patchVersion).thenReturn(1);
-
- expect(xcode.isVersionSatisfactory, isTrue);
- });
-
- testWithoutContext('isInstalledAndMeetsVersionCheck is false when not macOS', () {
- when(platform.isMacOS).thenReturn(false);
expect(xcode.isInstalledAndMeetsVersionCheck, isFalse);
});
- testWithoutContext('isInstalledAndMeetsVersionCheck is false when not installed', () {
- when(platform.isMacOS).thenReturn(true);
- fakeProcessManager.addCommand(const FakeCommand(
- command: <String>['/usr/bin/xcode-select', '--print-path'],
- stdout: '/Applications/Xcode8.0.app/Contents/Developer',
- ));
- when(mockXcodeProjectInterpreter.isInstalled).thenReturn(false);
+ group('macOS', () {
+ Xcode xcode;
+ FakePlatform platform;
- expect(xcode.isInstalledAndMeetsVersionCheck, isFalse);
- expect(fakeProcessManager.hasRemainingExpectations, isFalse);
- });
+ setUp(() {
+ mockXcodeProjectInterpreter = MockXcodeProjectInterpreter();
+ platform = FakePlatform(operatingSystem: 'macos');
+ xcode = Xcode(
+ logger: logger,
+ platform: platform,
+ fileSystem: MemoryFileSystem.test(),
+ processManager: fakeProcessManager,
+ xcodeProjectInterpreter: mockXcodeProjectInterpreter,
+ );
+ });
- testWithoutContext('isInstalledAndMeetsVersionCheck is false when no xcode-select', () {
- when(platform.isMacOS).thenReturn(true);
- fakeProcessManager.addCommand(const FakeCommand(
- command: <String>['/usr/bin/xcode-select', '--print-path'],
- exitCode: 127,
- stderr: 'ERROR',
- ));
- when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true);
- when(mockXcodeProjectInterpreter.majorVersion).thenReturn(11);
- when(mockXcodeProjectInterpreter.minorVersion).thenReturn(0);
- when(mockXcodeProjectInterpreter.patchVersion).thenReturn(0);
-
- expect(xcode.isInstalledAndMeetsVersionCheck, isFalse);
- expect(fakeProcessManager.hasRemainingExpectations, isFalse);
- });
-
- testWithoutContext('isInstalledAndMeetsVersionCheck is false when version not satisfied', () {
- when(platform.isMacOS).thenReturn(true);
- fakeProcessManager.addCommand(const FakeCommand(
- command: <String>['/usr/bin/xcode-select', '--print-path'],
- stdout: '/Applications/Xcode8.0.app/Contents/Developer',
- ));
- when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true);
- when(mockXcodeProjectInterpreter.majorVersion).thenReturn(10);
- when(mockXcodeProjectInterpreter.minorVersion).thenReturn(2);
- when(mockXcodeProjectInterpreter.patchVersion).thenReturn(0);
-
- expect(xcode.isInstalledAndMeetsVersionCheck, isFalse);
- expect(fakeProcessManager.hasRemainingExpectations, isFalse);
- });
-
- testWithoutContext('isInstalledAndMeetsVersionCheck is true when macOS and installed and version is satisfied', () {
- when(platform.isMacOS).thenReturn(true);
- fakeProcessManager.addCommand(const FakeCommand(
- command: <String>['/usr/bin/xcode-select', '--print-path'],
- stdout: '/Applications/Xcode8.0.app/Contents/Developer',
- ));
- when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true);
- when(mockXcodeProjectInterpreter.majorVersion).thenReturn(11);
- when(mockXcodeProjectInterpreter.minorVersion).thenReturn(0);
- when(mockXcodeProjectInterpreter.patchVersion).thenReturn(0);
-
- expect(xcode.isInstalledAndMeetsVersionCheck, isTrue);
- expect(fakeProcessManager.hasRemainingExpectations, isFalse);
- });
-
- testWithoutContext('eulaSigned is false when clang output indicates EULA not yet accepted', () {
- fakeProcessManager.addCommand(const FakeCommand(
- command: <String>['/usr/bin/xcrun', 'clang'],
- exitCode: 1,
- stderr: 'Xcode EULA has not been accepted.\nLaunch Xcode and accept the license.',
- ));
-
- expect(xcode.eulaSigned, isFalse);
- expect(fakeProcessManager.hasRemainingExpectations, isFalse);
- });
-
- testWithoutContext('eulaSigned is true when clang output indicates EULA has been accepted', () {
- fakeProcessManager.addCommand(const FakeCommand(
- command: <String>['/usr/bin/xcrun', 'clang'],
- exitCode: 1,
- stderr: 'clang: error: no input files',
- ));
-
- expect(xcode.eulaSigned, isTrue);
- expect(fakeProcessManager.hasRemainingExpectations, isFalse);
- });
-
- testWithoutContext('SDK name', () {
- expect(getNameForSdk(SdkType.iPhone), 'iphoneos');
- expect(getNameForSdk(SdkType.iPhoneSimulator), 'iphonesimulator');
- expect(getNameForSdk(SdkType.macOS), 'macosx');
- });
-
- group('SDK location', () {
- const String sdkroot = 'Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.2.sdk';
-
- testWithoutContext('--show-sdk-path iphoneos', () async {
+ testWithoutContext('xcodeSelectPath returns path when xcode-select is installed', () {
+ const String xcodePath = '/Applications/Xcode8.0.app/Contents/Developer';
fakeProcessManager.addCommand(const FakeCommand(
- command: <String>['xcrun', '--sdk', 'iphoneos', '--show-sdk-path'],
- stdout: sdkroot,
+ command: <String>['/usr/bin/xcode-select', '--print-path'],
+ stdout: xcodePath,
));
- expect(await xcode.sdkLocation(SdkType.iPhone), sdkroot);
+ expect(xcode.xcodeSelectPath, xcodePath);
expect(fakeProcessManager.hasRemainingExpectations, isFalse);
});
- testWithoutContext('--show-sdk-path macosx', () async {
- fakeProcessManager.addCommand(const FakeCommand(
- command: <String>['xcrun', '--sdk', 'macosx', '--show-sdk-path'],
- stdout: sdkroot,
- ));
+ testWithoutContext('xcodeVersionSatisfactory is false when version is less than minimum', () {
+ when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true);
+ when(mockXcodeProjectInterpreter.majorVersion).thenReturn(9);
+ when(mockXcodeProjectInterpreter.minorVersion).thenReturn(0);
+ when(mockXcodeProjectInterpreter.patchVersion).thenReturn(0);
- expect(await xcode.sdkLocation(SdkType.macOS), sdkroot);
+ expect(xcode.isVersionSatisfactory, isFalse);
+ });
+
+ testWithoutContext('xcodeVersionSatisfactory is false when xcodebuild tools are not installed', () {
+ when(mockXcodeProjectInterpreter.isInstalled).thenReturn(false);
+
+ expect(xcode.isVersionSatisfactory, isFalse);
+ });
+
+ testWithoutContext('xcodeVersionSatisfactory is true when version meets minimum', () {
+ when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true);
+ when(mockXcodeProjectInterpreter.majorVersion).thenReturn(11);
+ when(mockXcodeProjectInterpreter.minorVersion).thenReturn(0);
+ when(mockXcodeProjectInterpreter.patchVersion).thenReturn(0);
+
+ expect(xcode.isVersionSatisfactory, isTrue);
+ });
+
+ testWithoutContext('xcodeVersionSatisfactory is true when major version exceeds minimum', () {
+ when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true);
+ when(mockXcodeProjectInterpreter.majorVersion).thenReturn(12);
+ when(mockXcodeProjectInterpreter.minorVersion).thenReturn(0);
+ when(mockXcodeProjectInterpreter.patchVersion).thenReturn(0);
+
+ expect(xcode.isVersionSatisfactory, isTrue);
+ });
+
+ testWithoutContext('xcodeVersionSatisfactory is true when minor version exceeds minimum', () {
+ when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true);
+ when(mockXcodeProjectInterpreter.majorVersion).thenReturn(11);
+ when(mockXcodeProjectInterpreter.minorVersion).thenReturn(3);
+ when(mockXcodeProjectInterpreter.patchVersion).thenReturn(0);
+
+ expect(xcode.isVersionSatisfactory, isTrue);
+ });
+
+ testWithoutContext('xcodeVersionSatisfactory is true when patch version exceeds minimum', () {
+ when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true);
+ when(mockXcodeProjectInterpreter.majorVersion).thenReturn(11);
+ when(mockXcodeProjectInterpreter.minorVersion).thenReturn(0);
+ when(mockXcodeProjectInterpreter.patchVersion).thenReturn(1);
+
+ expect(xcode.isVersionSatisfactory, isTrue);
+ });
+
+ testWithoutContext('isInstalledAndMeetsVersionCheck is false when not installed', () {
+ fakeProcessManager.addCommand(const FakeCommand(
+ command: <String>['/usr/bin/xcode-select', '--print-path'],
+ stdout: '/Applications/Xcode8.0.app/Contents/Developer',
+ ));
+ when(mockXcodeProjectInterpreter.isInstalled).thenReturn(false);
+
+ expect(xcode.isInstalledAndMeetsVersionCheck, isFalse);
expect(fakeProcessManager.hasRemainingExpectations, isFalse);
});
- testWithoutContext('--show-sdk-path fails', () async {
+ testWithoutContext('isInstalledAndMeetsVersionCheck is false when no xcode-select', () {
fakeProcessManager.addCommand(const FakeCommand(
- command: <String>['xcrun', '--sdk', 'iphoneos', '--show-sdk-path'],
- exitCode: 1,
- stderr: 'xcrun: error:',
+ command: <String>['/usr/bin/xcode-select', '--print-path'],
+ exitCode: 127,
+ stderr: 'ERROR',
));
+ when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true);
+ when(mockXcodeProjectInterpreter.majorVersion).thenReturn(11);
+ when(mockXcodeProjectInterpreter.minorVersion).thenReturn(0);
+ when(mockXcodeProjectInterpreter.patchVersion).thenReturn(0);
- expect(() async => await xcode.sdkLocation(SdkType.iPhone),
- throwsToolExit(message: 'Could not find SDK location'));
+ expect(xcode.isInstalledAndMeetsVersionCheck, isFalse);
expect(fakeProcessManager.hasRemainingExpectations, isFalse);
});
+
+ testWithoutContext('isInstalledAndMeetsVersionCheck is false when version not satisfied', () {
+ fakeProcessManager.addCommand(const FakeCommand(
+ command: <String>['/usr/bin/xcode-select', '--print-path'],
+ stdout: '/Applications/Xcode8.0.app/Contents/Developer',
+ ));
+ when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true);
+ when(mockXcodeProjectInterpreter.majorVersion).thenReturn(10);
+ when(mockXcodeProjectInterpreter.minorVersion).thenReturn(2);
+ when(mockXcodeProjectInterpreter.patchVersion).thenReturn(0);
+
+ expect(xcode.isInstalledAndMeetsVersionCheck, isFalse);
+ expect(fakeProcessManager.hasRemainingExpectations, isFalse);
+ });
+
+ testWithoutContext('xcrun runs natively on arm64', () {
+ fakeProcessManager.addCommands(const <FakeCommand>[
+ FakeCommand(
+ command: <String>[
+ 'sysctl',
+ 'hw.optional.arm64',
+ ],
+ stdout: 'hw.optional.arm64: 1',
+ ),
+ ]);
+
+ expect(xcode.xcrunCommand(), <String>[
+ '/usr/bin/arch',
+ '-arm64e',
+ 'xcrun',
+ ]);
+ expect(fakeProcessManager.hasRemainingExpectations, isFalse);
+ });
+
+ testWithoutContext('isInstalledAndMeetsVersionCheck is true when macOS and installed and version is satisfied', () {
+ fakeProcessManager.addCommand(const FakeCommand(
+ command: <String>['/usr/bin/xcode-select', '--print-path'],
+ stdout: '/Applications/Xcode8.0.app/Contents/Developer',
+ ));
+ when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true);
+ when(mockXcodeProjectInterpreter.majorVersion).thenReturn(11);
+ when(mockXcodeProjectInterpreter.minorVersion).thenReturn(0);
+ when(mockXcodeProjectInterpreter.patchVersion).thenReturn(0);
+
+ expect(xcode.isInstalledAndMeetsVersionCheck, isTrue);
+ expect(fakeProcessManager.hasRemainingExpectations, isFalse);
+ });
+
+ testWithoutContext('eulaSigned is false when clang output indicates EULA not yet accepted', () {
+ fakeProcessManager.addCommands(const <FakeCommand>[
+ FakeCommand(
+ command: <String>[
+ 'sysctl',
+ 'hw.optional.arm64',
+ ],
+ exitCode: 1,
+ ),
+ FakeCommand(
+ command: <String>['xcrun', 'clang'],
+ exitCode: 1,
+ stderr:
+ 'Xcode EULA has not been accepted.\nLaunch Xcode and accept the license.',
+ ),
+ ]);
+
+ expect(xcode.eulaSigned, isFalse);
+ expect(fakeProcessManager.hasRemainingExpectations, isFalse);
+ });
+
+ testWithoutContext('eulaSigned is true when clang output indicates EULA has been accepted', () {
+ fakeProcessManager.addCommands(
+ const <FakeCommand>[
+ FakeCommand(
+ command: <String>[
+ 'sysctl',
+ 'hw.optional.arm64',
+ ],
+ exitCode: 1,
+ ),
+ FakeCommand(
+ command: <String>['xcrun', 'clang'],
+ exitCode: 1,
+ stderr: 'clang: error: no input files',
+ ),
+ ],
+ );
+ expect(xcode.eulaSigned, isTrue);
+ expect(fakeProcessManager.hasRemainingExpectations, isFalse);
+ });
+
+ testWithoutContext('SDK name', () {
+ expect(getNameForSdk(SdkType.iPhone), 'iphoneos');
+ expect(getNameForSdk(SdkType.iPhoneSimulator), 'iphonesimulator');
+ expect(getNameForSdk(SdkType.macOS), 'macosx');
+ });
+
+ group('SDK location', () {
+ setUp(() {
+ fakeProcessManager.addCommand(
+ const FakeCommand(
+ command: <String>[
+ 'sysctl',
+ 'hw.optional.arm64',
+ ],
+ exitCode: 1,
+ ),
+ );
+ });
+
+ const String sdkroot = 'Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.2.sdk';
+
+ testWithoutContext('--show-sdk-path iphoneos', () async {
+ fakeProcessManager.addCommand(const FakeCommand(
+ command: <String>['xcrun', '--sdk', 'iphoneos', '--show-sdk-path'],
+ stdout: sdkroot,
+ ));
+
+ expect(await xcode.sdkLocation(SdkType.iPhone), sdkroot);
+ expect(fakeProcessManager.hasRemainingExpectations, isFalse);
+ });
+
+ testWithoutContext('--show-sdk-path macosx', () async {
+ fakeProcessManager.addCommand(const FakeCommand(
+ command: <String>['xcrun', '--sdk', 'macosx', '--show-sdk-path'],
+ stdout: sdkroot,
+ ));
+
+ expect(await xcode.sdkLocation(SdkType.macOS), sdkroot);
+ expect(fakeProcessManager.hasRemainingExpectations, isFalse);
+ });
+
+ testWithoutContext('--show-sdk-path fails', () async {
+ fakeProcessManager.addCommand(const FakeCommand(
+ command: <String>['xcrun', '--sdk', 'iphoneos', '--show-sdk-path'],
+ exitCode: 1,
+ stderr: 'xcrun: error:',
+ ));
+
+ expect(() async => await xcode.sdkLocation(SdkType.iPhone),
+ throwsToolExit(message: 'Could not find SDK location'));
+ expect(fakeProcessManager.hasRemainingExpectations, isFalse);
+ });
+ });
});
});
@@ -354,6 +402,7 @@
cache: mockCache,
iproxy: IProxy.test(logger: logger, processManager: fakeProcessManager),
);
+ when(mockXcode.xcrunCommand()).thenReturn(<String>['xcrun']);
});
group('installed', () {
@@ -361,17 +410,6 @@
when(mockXcode.isInstalledAndMeetsVersionCheck).thenReturn(false);
expect(xcdevice.isInstalled, false);
});
-
- testWithoutContext('is installed', () {
- when(mockXcode.isInstalledAndMeetsVersionCheck).thenReturn(true);
- fakeProcessManager.addCommand(const FakeCommand(
- command: <String>['xcrun', '--find', 'xcdevice'],
- stdout: '/path/to/xcdevice',
- ));
-
- expect(xcdevice.isInstalled, true);
- expect(fakeProcessManager.hasRemainingExpectations, isFalse);
- });
});
group('observe device events', () {
@@ -384,10 +422,6 @@
testUsingContext('relays events', () async {
when(mockXcode.isInstalledAndMeetsVersionCheck).thenReturn(true);
- fakeProcessManager.addCommand(const FakeCommand(
- command: <String>['xcrun', '--find', 'xcdevice'],
- stdout: '/path/to/xcdevice',
- ));
fakeProcessManager.addCommand(const FakeCommand(
command: <String>[
@@ -446,10 +480,6 @@
testUsingContext('returns devices', () async {
when(mockXcode.isInstalledAndMeetsVersionCheck).thenReturn(true);
- fakeProcessManager.addCommand(const FakeCommand(
- command: <String>['xcrun', '--find', 'xcdevice'],
- stdout: '/path/to/xcdevice',
- ));
const String devicesOutput = '''
[
@@ -570,10 +600,6 @@
testWithoutContext('uses timeout', () async {
when(mockXcode.isInstalledAndMeetsVersionCheck).thenReturn(true);
- fakeProcessManager.addCommand(const FakeCommand(
- command: <String>['xcrun', '--find', 'xcdevice'],
- stdout: '/path/to/xcdevice',
- ));
fakeProcessManager.addCommand(const FakeCommand(
command: <String>['xcrun', 'xcdevice', 'list', '--timeout', '20'],
@@ -585,10 +611,6 @@
testUsingContext('ignores "Preparing debugger support for iPhone" error', () async {
when(mockXcode.isInstalledAndMeetsVersionCheck).thenReturn(true);
- fakeProcessManager.addCommand(const FakeCommand(
- command: <String>['xcrun', '--find', 'xcdevice'],
- stdout: '/path/to/xcdevice',
- ));
const String devicesOutput = '''
[
@@ -629,10 +651,6 @@
testUsingContext('handles unknown architectures', () async {
when(mockXcode.isInstalledAndMeetsVersionCheck).thenReturn(true);
- fakeProcessManager.addCommand(const FakeCommand(
- command: <String>['xcrun', '--find', 'xcdevice'],
- stdout: '/path/to/xcdevice',
- ));
const String devicesOutput = '''
[
@@ -688,10 +706,6 @@
testUsingContext('uses cache', () async {
when(mockXcode.isInstalledAndMeetsVersionCheck).thenReturn(true);
- fakeProcessManager.addCommand(const FakeCommand(
- command: <String>['xcrun', '--find', 'xcdevice'],
- stdout: '/path/to/xcdevice',
- ));
const String devicesOutput = '''
[
@@ -729,10 +743,6 @@
testUsingContext('returns error message', () async {
when(mockXcode.isInstalledAndMeetsVersionCheck).thenReturn(true);
- fakeProcessManager.addCommand(const FakeCommand(
- command: <String>['xcrun', '--find', 'xcdevice'],
- stdout: '/path/to/xcdevice',
- ));
const String devicesOutput = '''
[
@@ -842,6 +852,5 @@
class MockXcode extends Mock implements Xcode {}
class MockProcessManager extends Mock implements ProcessManager {}
class MockXcodeProjectInterpreter extends Mock implements XcodeProjectInterpreter {}
-class MockPlatform extends Mock implements Platform {}
class MockArtifacts extends Mock implements Artifacts {}
class MockCache extends Mock implements Cache {}
diff --git a/packages/flutter_tools/test/src/context.dart b/packages/flutter_tools/test/src/context.dart
index f244796..8a164a1 100644
--- a/packages/flutter_tools/test/src/context.dart
+++ b/packages/flutter_tools/test/src/context.dart
@@ -18,6 +18,7 @@
import 'package:flutter_tools/src/base/terminal.dart';
import 'package:flutter_tools/src/base/time.dart';
import 'package:flutter_tools/src/build_runner/mustache_template.dart';
+import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/context_runner.dart';
import 'package:flutter_tools/src/dart/pub.dart';
@@ -295,6 +296,9 @@
ProcessResult makeExecutable(File file) => null;
@override
+ HostPlatform hostPlatform = HostPlatform.linux_x64;
+
+ @override
void chmod(FileSystemEntity entity, String mode) { }
@override