Unverified Commit 2f3cccc4 authored by Jenn Magder's avatar Jenn Magder Committed by GitHub

Run more xcodebuild commands in native arm on Apple Silicon (#69837)

parent 4eabee88
...@@ -10,6 +10,7 @@ import '../base/common.dart'; ...@@ -10,6 +10,7 @@ import '../base/common.dart';
import '../base/file_system.dart'; import '../base/file_system.dart';
import '../base/io.dart'; import '../base/io.dart';
import '../base/logger.dart'; import '../base/logger.dart';
import '../base/os.dart';
import '../base/platform.dart'; import '../base/platform.dart';
import '../base/process.dart'; import '../base/process.dart';
import '../base/terminal.dart'; import '../base/terminal.dart';
...@@ -242,26 +243,32 @@ class XcodeProjectInterpreter { ...@@ -242,26 +243,32 @@ class XcodeProjectInterpreter {
_terminal = terminal, _terminal = terminal,
_logger = logger, _logger = logger,
_processUtils = ProcessUtils(logger: logger, processManager: processManager), _processUtils = ProcessUtils(logger: logger, processManager: processManager),
_operatingSystemUtils = OperatingSystemUtils(
fileSystem: fileSystem,
logger: logger,
platform: platform,
processManager: processManager,
),
_usage = usage; _usage = usage;
final Platform _platform; final Platform _platform;
final FileSystem _fileSystem; final FileSystem _fileSystem;
final ProcessUtils _processUtils; final ProcessUtils _processUtils;
final OperatingSystemUtils _operatingSystemUtils;
final Terminal _terminal; final Terminal _terminal;
final Logger _logger; final Logger _logger;
final Usage _usage; final Usage _usage;
static const String _executable = '/usr/bin/xcodebuild';
static final RegExp _versionRegex = RegExp(r'Xcode ([0-9.]+)'); static final RegExp _versionRegex = RegExp(r'Xcode ([0-9.]+)');
void _updateVersion() { void _updateVersion() {
if (!_platform.isMacOS || !_fileSystem.file(_executable).existsSync()) { if (!_platform.isMacOS || !_fileSystem.file('/usr/bin/xcodebuild').existsSync()) {
return; return;
} }
try { try {
if (_versionText == null) { if (_versionText == null) {
final RunResult result = _processUtils.runSync( final RunResult result = _processUtils.runSync(
<String>[_executable, '-version'], <String>[...xcrunCommand(), 'xcodebuild', '-version'],
); );
if (result.exitCode != 0) { if (result.exitCode != 0) {
return; return;
...@@ -316,6 +323,25 @@ class XcodeProjectInterpreter { ...@@ -316,6 +323,25 @@ class XcodeProjectInterpreter {
return _patchVersion; return _patchVersion;
} }
/// 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;
}
/// Asynchronously retrieve xcode build settings. This one is preferred for /// Asynchronously retrieve xcode build settings. This one is preferred for
/// new call-sites. /// new call-sites.
/// ///
...@@ -331,7 +357,8 @@ class XcodeProjectInterpreter { ...@@ -331,7 +357,8 @@ class XcodeProjectInterpreter {
terminal: _terminal, terminal: _terminal,
); );
final List<String> showBuildSettingsCommand = <String>[ final List<String> showBuildSettingsCommand = <String>[
_executable, ...xcrunCommand(),
'xcodebuild',
'-project', '-project',
_fileSystem.path.absolute(projectPath), _fileSystem.path.absolute(projectPath),
if (scheme != null) if (scheme != null)
...@@ -368,7 +395,8 @@ class XcodeProjectInterpreter { ...@@ -368,7 +395,8 @@ class XcodeProjectInterpreter {
Future<void> cleanWorkspace(String workspacePath, String scheme, { bool verbose = false }) async { Future<void> cleanWorkspace(String workspacePath, String scheme, { bool verbose = false }) async {
await _processUtils.run(<String>[ await _processUtils.run(<String>[
_executable, ...xcrunCommand(),
'xcodebuild',
'-workspace', '-workspace',
workspacePath, workspacePath,
'-scheme', '-scheme',
...@@ -387,7 +415,8 @@ class XcodeProjectInterpreter { ...@@ -387,7 +415,8 @@ class XcodeProjectInterpreter {
const int missingProjectExitCode = 66; const int missingProjectExitCode = 66;
final RunResult result = await _processUtils.run( final RunResult result = await _processUtils.run(
<String>[ <String>[
_executable, ...xcrunCommand(),
'xcodebuild',
'-list', '-list',
if (projectFilename != null) ...<String>['-project', projectFilename], if (projectFilename != null) ...<String>['-project', projectFilename],
], ],
......
...@@ -12,7 +12,6 @@ import '../base/common.dart'; ...@@ -12,7 +12,6 @@ import '../base/common.dart';
import '../base/file_system.dart'; import '../base/file_system.dart';
import '../base/io.dart'; import '../base/io.dart';
import '../base/logger.dart'; import '../base/logger.dart';
import '../base/os.dart';
import '../base/platform.dart'; import '../base/platform.dart';
import '../base/process.dart'; import '../base/process.dart';
import '../build_info.dart'; import '../build_info.dart';
...@@ -66,18 +65,11 @@ class Xcode { ...@@ -66,18 +65,11 @@ class Xcode {
}) : _platform = platform, }) : _platform = platform,
_fileSystem = fileSystem, _fileSystem = fileSystem,
_xcodeProjectInterpreter = xcodeProjectInterpreter, _xcodeProjectInterpreter = xcodeProjectInterpreter,
_operatingSystemUtils = OperatingSystemUtils(
fileSystem: fileSystem,
logger: logger,
platform: platform,
processManager: processManager,
),
_processUtils = _processUtils =
ProcessUtils(logger: logger, processManager: processManager); ProcessUtils(logger: logger, processManager: processManager);
final Platform _platform; final Platform _platform;
final ProcessUtils _processUtils; final ProcessUtils _processUtils;
final OperatingSystemUtils _operatingSystemUtils;
final FileSystem _fileSystem; final FileSystem _fileSystem;
final XcodeProjectInterpreter _xcodeProjectInterpreter; final XcodeProjectInterpreter _xcodeProjectInterpreter;
...@@ -169,24 +161,8 @@ class Xcode { ...@@ -169,24 +161,8 @@ class Xcode {
return false; return false;
} }
/// The `xcrun` Xcode command to run or locate development /// See [XcodeProjectInterpreter.xcrunCommand].
/// tools and properties. List<String> xcrunCommand() => _xcodeProjectInterpreter.xcrunCommand();
///
/// 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) { Future<RunResult> cc(List<String> args) {
return _processUtils.run( return _processUtils.run(
......
...@@ -77,14 +77,6 @@ void main() { ...@@ -77,14 +77,6 @@ void main() {
'xattr', '-r', '-d', 'com.apple.FinderInfo', '/ios' 'xattr', '-r', '-d', 'com.apple.FinderInfo', '/ios'
]); ]);
const FakeCommand armCheckCommand = FakeCommand(
command: <String>[
'sysctl',
'hw.optional.arm64',
],
exitCode: 1,
);
// Creates a FakeCommand for the xcodebuild call to build the app // Creates a FakeCommand for the xcodebuild call to build the app
// in the given configuration. // in the given configuration.
FakeCommand setUpMockXcodeBuildHandler({ bool verbose = false, bool showBuildSettings = false, void Function() onRun }) { FakeCommand setUpMockXcodeBuildHandler({ bool verbose = false, bool showBuildSettings = false, void Function() onRun }) {
...@@ -215,7 +207,6 @@ void main() { ...@@ -215,7 +207,6 @@ void main() {
FileSystem: () => fileSystem, FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.list(<FakeCommand>[ ProcessManager: () => FakeProcessManager.list(<FakeCommand>[
xattrCommand, xattrCommand,
armCheckCommand,
setUpMockXcodeBuildHandler(), setUpMockXcodeBuildHandler(),
setUpMockXcodeBuildHandler(showBuildSettings: true), setUpMockXcodeBuildHandler(showBuildSettings: true),
]), ]),
...@@ -234,7 +225,6 @@ void main() { ...@@ -234,7 +225,6 @@ void main() {
FileSystem: () => fileSystem, FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.list(<FakeCommand>[ ProcessManager: () => FakeProcessManager.list(<FakeCommand>[
xattrCommand, xattrCommand,
armCheckCommand,
setUpMockXcodeBuildHandler(verbose: true), setUpMockXcodeBuildHandler(verbose: true),
setUpMockXcodeBuildHandler(verbose: true, showBuildSettings: true), setUpMockXcodeBuildHandler(verbose: true, showBuildSettings: true),
]), ]),
...@@ -263,7 +253,6 @@ void main() { ...@@ -263,7 +253,6 @@ void main() {
FileSystem: () => fileSystem, FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.list(<FakeCommand>[ ProcessManager: () => FakeProcessManager.list(<FakeCommand>[
xattrCommand, xattrCommand,
armCheckCommand,
setUpMockXcodeBuildHandler(onRun: () { setUpMockXcodeBuildHandler(onRun: () {
fileSystem.file('build/flutter_size_01/snapshot.arm64.json') fileSystem.file('build/flutter_size_01/snapshot.arm64.json')
..createSync(recursive: true) ..createSync(recursive: true)
...@@ -311,7 +300,6 @@ void main() { ...@@ -311,7 +300,6 @@ void main() {
FileSystem: () => fileSystem, FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.list(<FakeCommand>[ ProcessManager: () => FakeProcessManager.list(<FakeCommand>[
xattrCommand, xattrCommand,
armCheckCommand,
setUpMockXcodeBuildHandler(), setUpMockXcodeBuildHandler(),
setUpMockXcodeBuildHandler(showBuildSettings: true), setUpMockXcodeBuildHandler(showBuildSettings: true),
exportArchiveCommand, exportArchiveCommand,
......
...@@ -478,13 +478,6 @@ void main() { ...@@ -478,13 +478,6 @@ void main() {
'--lazy-async-stacks', '--lazy-async-stacks',
'$build/app.dill', '$build/app.dill',
]), ]),
const FakeCommand(
command: <String>[
'sysctl',
'hw.optional.arm64',
],
exitCode: 1,
),
FakeCommand(command: <String>[ FakeCommand(command: <String>[
'xcrun', 'xcrun',
'cc', 'cc',
...@@ -593,13 +586,6 @@ void main() { ...@@ -593,13 +586,6 @@ void main() {
'--lazy-async-stacks', '--lazy-async-stacks',
'$build/app.dill', '$build/app.dill',
]), ]),
const FakeCommand(
command: <String>[
'sysctl',
'hw.optional.arm64',
],
exitCode: 1,
),
FakeCommand(command: <String>[ FakeCommand(command: <String>[
'xcrun', 'xcrun',
'cc', 'cc',
...@@ -677,13 +663,6 @@ void main() { ...@@ -677,13 +663,6 @@ void main() {
'--lazy-async-stacks', '--lazy-async-stacks',
'$build/app.dill', '$build/app.dill',
]), ]),
const FakeCommand(
command: <String>[
'sysctl',
'hw.optional.arm64',
],
exitCode: 1,
),
FakeCommand(command: <String>[ FakeCommand(command: <String>[
'xcrun', 'xcrun',
'cc', 'cc',
......
...@@ -71,14 +71,6 @@ void main() { ...@@ -71,14 +71,6 @@ void main() {
testUsingContext('DebugUniveralFramework creates expected binary with arm64 only arch', () async { testUsingContext('DebugUniveralFramework creates expected binary with arm64 only arch', () async {
environment.defines[kIosArchs] = 'arm64'; environment.defines[kIosArchs] = 'arm64';
processManager.addCommands(<FakeCommand>[ 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']), const FakeCommand(command: <String>['xcrun', '--sdk', 'iphoneos', '--show-sdk-path']),
FakeCommand(command: <String>[ FakeCommand(command: <String>[
'xcrun', 'xcrun',
......
...@@ -51,7 +51,9 @@ void main() { ...@@ -51,7 +51,9 @@ void main() {
// Work around https://github.com/flutter/flutter/issues/56415. // Work around https://github.com/flutter/flutter/issues/56415.
testWithoutContext('xcodebuild versionText returns null when xcodebuild is not installed', () { testWithoutContext('xcodebuild versionText returns null when xcodebuild is not installed', () {
when(processManager.runSync(<String>[xcodebuild, '-version'])) when(processManager.runSync(<String>['sysctl', 'hw.optional.arm64']))
.thenReturn(ProcessResult(0, 1, '', ''));
when(processManager.runSync(<String>['xcrun', 'xcodebuild', '-version']))
.thenThrow(const ProcessException(xcodebuild, <String>['-version'])); .thenThrow(const ProcessException(xcodebuild, <String>['-version']));
expect(xcodeProjectInterpreter.versionText, isNull); expect(xcodeProjectInterpreter.versionText, isNull);
...@@ -65,6 +67,9 @@ void main() { ...@@ -65,6 +67,9 @@ void main() {
); );
platform.environment = const <String, String>{}; platform.environment = const <String, String>{};
when(processManager.runSync(<String>['sysctl', 'hw.optional.arm64']))
.thenReturn(ProcessResult(0, 1, '', ''));
expect(await xcodeProjectInterpreter.getBuildSettings( expect(await xcodeProjectInterpreter.getBuildSettings(
'', scheme: 'Runner', timeout: delay), '', scheme: 'Runner', timeout: delay),
const <String, String>{}); const <String, String>{});
...@@ -75,6 +80,14 @@ void main() { ...@@ -75,6 +80,14 @@ void main() {
}); });
}); });
const FakeCommand kARMCheckCommand = FakeCommand(
command: <String>[
'sysctl',
'hw.optional.arm64',
],
exitCode: 1,
);
FakeProcessManager fakeProcessManager; FakeProcessManager fakeProcessManager;
XcodeProjectInterpreter xcodeProjectInterpreter; XcodeProjectInterpreter xcodeProjectInterpreter;
FakePlatform platform; FakePlatform platform;
...@@ -102,43 +115,55 @@ void main() { ...@@ -102,43 +115,55 @@ void main() {
}); });
testWithoutContext('xcodebuild versionText returns null when xcodebuild is not fully installed', () { testWithoutContext('xcodebuild versionText returns null when xcodebuild is not fully installed', () {
fakeProcessManager.addCommand(const FakeCommand( fakeProcessManager.addCommands(const <FakeCommand>[
command: <String>[xcodebuild, '-version'], kARMCheckCommand,
stdout: "xcode-select: error: tool 'xcodebuild' requires Xcode, " FakeCommand(
"but active developer directory '/Library/Developer/CommandLineTools' " command: <String>['xcrun', 'xcodebuild', '-version'],
'is a command line tools instance', stdout: "xcode-select: error: tool 'xcodebuild' requires Xcode, "
exitCode: 1, "but active developer directory '/Library/Developer/CommandLineTools' "
)); 'is a command line tools instance',
exitCode: 1,
),
]);
expect(xcodeProjectInterpreter.versionText, isNull); expect(xcodeProjectInterpreter.versionText, isNull);
expect(fakeProcessManager.hasRemainingExpectations, isFalse); expect(fakeProcessManager.hasRemainingExpectations, isFalse);
}); });
testWithoutContext('xcodebuild versionText returns formatted version text', () { testWithoutContext('xcodebuild versionText returns formatted version text', () {
fakeProcessManager.addCommand(const FakeCommand( fakeProcessManager.addCommands(const <FakeCommand>[
command: <String>[xcodebuild, '-version'], kARMCheckCommand,
stdout: 'Xcode 8.3.3\nBuild version 8E3004b', FakeCommand(
)); command: <String>['xcrun', 'xcodebuild', '-version'],
stdout: 'Xcode 8.3.3\nBuild version 8E3004b',
),
]);
expect(xcodeProjectInterpreter.versionText, 'Xcode 8.3.3, Build version 8E3004b'); expect(xcodeProjectInterpreter.versionText, 'Xcode 8.3.3, Build version 8E3004b');
expect(fakeProcessManager.hasRemainingExpectations, isFalse); expect(fakeProcessManager.hasRemainingExpectations, isFalse);
}); });
testWithoutContext('xcodebuild versionText handles Xcode version string with unexpected format', () { testWithoutContext('xcodebuild versionText handles Xcode version string with unexpected format', () {
fakeProcessManager.addCommand(const FakeCommand( fakeProcessManager.addCommands(const <FakeCommand>[
command: <String>[xcodebuild, '-version'], kARMCheckCommand,
stdout: 'Xcode Ultra5000\nBuild version 8E3004b', FakeCommand(
)); command: <String>['xcrun', 'xcodebuild', '-version'],
stdout: 'Xcode Ultra5000\nBuild version 8E3004b',
),
]);
expect(xcodeProjectInterpreter.versionText, 'Xcode Ultra5000, Build version 8E3004b'); expect(xcodeProjectInterpreter.versionText, 'Xcode Ultra5000, Build version 8E3004b');
expect(fakeProcessManager.hasRemainingExpectations, isFalse); expect(fakeProcessManager.hasRemainingExpectations, isFalse);
}); });
testWithoutContext('xcodebuild version parts can be parsed', () { testWithoutContext('xcodebuild version parts can be parsed', () {
fakeProcessManager.addCommand(const FakeCommand( fakeProcessManager.addCommands(const <FakeCommand>[
command: <String>[xcodebuild, '-version'], kARMCheckCommand,
stdout: 'Xcode 11.4.1\nBuild version 11N111s', FakeCommand(
)); command: <String>['xcrun', 'xcodebuild', '-version'],
stdout: 'Xcode 11.4.1\nBuild version 11N111s',
),
]);
expect(xcodeProjectInterpreter.majorVersion, 11); expect(xcodeProjectInterpreter.majorVersion, 11);
expect(xcodeProjectInterpreter.minorVersion, 4); expect(xcodeProjectInterpreter.minorVersion, 4);
...@@ -147,10 +172,13 @@ void main() { ...@@ -147,10 +172,13 @@ void main() {
}); });
testWithoutContext('xcodebuild minor and patch version default to 0', () { testWithoutContext('xcodebuild minor and patch version default to 0', () {
fakeProcessManager.addCommand(const FakeCommand( fakeProcessManager.addCommands(const <FakeCommand>[
command: <String>[xcodebuild, '-version'], kARMCheckCommand,
stdout: 'Xcode 11\nBuild version 11N111s', FakeCommand(
)); command: <String>['xcrun', 'xcodebuild', '-version'],
stdout: 'Xcode 11\nBuild version 11N111s',
),
]);
expect(xcodeProjectInterpreter.majorVersion, 11); expect(xcodeProjectInterpreter.majorVersion, 11);
expect(xcodeProjectInterpreter.minorVersion, 0); expect(xcodeProjectInterpreter.minorVersion, 0);
...@@ -159,10 +187,13 @@ void main() { ...@@ -159,10 +187,13 @@ void main() {
}); });
testWithoutContext('xcodebuild version parts is null when version has unexpected format', () { testWithoutContext('xcodebuild version parts is null when version has unexpected format', () {
fakeProcessManager.addCommand(const FakeCommand( fakeProcessManager.addCommands(const <FakeCommand>[
command: <String>[xcodebuild, '-version'], kARMCheckCommand,
stdout: 'Xcode Ultra5000\nBuild version 8E3004b', FakeCommand(
)); command: <String>['xcrun', 'xcodebuild', '-version'],
stdout: 'Xcode Ultra5000\nBuild version 8E3004b',
),
]);
expect(xcodeProjectInterpreter.majorVersion, isNull); expect(xcodeProjectInterpreter.majorVersion, isNull);
expect(xcodeProjectInterpreter.minorVersion, isNull); expect(xcodeProjectInterpreter.minorVersion, isNull);
expect(xcodeProjectInterpreter.patchVersion, isNull); expect(xcodeProjectInterpreter.patchVersion, isNull);
...@@ -192,53 +223,92 @@ void main() { ...@@ -192,53 +223,92 @@ void main() {
expect(fakeProcessManager.hasRemainingExpectations, isFalse); expect(fakeProcessManager.hasRemainingExpectations, isFalse);
}); });
testWithoutContext('xcodebuild isInstalled is false when Xcode is not fully installed', () { testWithoutContext(
fakeProcessManager.addCommand(const FakeCommand( 'xcodebuild isInstalled is false when Xcode is not fully installed', () {
command: <String>[xcodebuild, '-version'], fakeProcessManager.addCommands(const <FakeCommand>[
stdout: "xcode-select: error: tool 'xcodebuild' requires Xcode, " kARMCheckCommand,
"but active developer directory '/Library/Developer/CommandLineTools' " FakeCommand(
'is a command line tools instance', command: <String>['xcrun', 'xcodebuild', '-version'],
exitCode: 1, stdout: "xcode-select: error: tool 'xcodebuild' requires Xcode, "
)); "but active developer directory '/Library/Developer/CommandLineTools' "
'is a command line tools instance',
exitCode: 1,
),
]);
expect(xcodeProjectInterpreter.isInstalled, isFalse); expect(xcodeProjectInterpreter.isInstalled, isFalse);
expect(fakeProcessManager.hasRemainingExpectations, isFalse); expect(fakeProcessManager.hasRemainingExpectations, isFalse);
}); });
testWithoutContext('xcodebuild isInstalled is false when version has unexpected format', () { testWithoutContext('xcodebuild isInstalled is false when version has unexpected format', () {
fakeProcessManager.addCommand(const FakeCommand( fakeProcessManager.addCommands(const <FakeCommand>[
command: <String>[xcodebuild, '-version'], kARMCheckCommand,
stdout: 'Xcode Ultra5000\nBuild version 8E3004b', FakeCommand(
)); command: <String>['xcrun', 'xcodebuild', '-version'],
stdout: 'Xcode Ultra5000\nBuild version 8E3004b',
),
]);
expect(xcodeProjectInterpreter.isInstalled, isFalse); expect(xcodeProjectInterpreter.isInstalled, isFalse);
expect(fakeProcessManager.hasRemainingExpectations, isFalse); expect(fakeProcessManager.hasRemainingExpectations, isFalse);
}); });
testWithoutContext('xcodebuild isInstalled is true when version has expected format', () { testWithoutContext('xcodebuild isInstalled is true when version has expected format', () {
fakeProcessManager.addCommand(const FakeCommand( fakeProcessManager.addCommands(const <FakeCommand>[
command: <String>[xcodebuild, '-version'], kARMCheckCommand,
stdout: 'Xcode 8.3.3\nBuild version 8E3004b', FakeCommand(
)); command: <String>['xcrun', 'xcodebuild', '-version'],
stdout: 'Xcode 8.3.3\nBuild version 8E3004b',
),
]);
expect(xcodeProjectInterpreter.isInstalled, isTrue); expect(xcodeProjectInterpreter.isInstalled, isTrue);
expect(fakeProcessManager.hasRemainingExpectations, 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(xcodeProjectInterpreter.xcrunCommand(), <String>[
'/usr/bin/arch',
'-arm64e',
'xcrun',
]);
expect(fakeProcessManager.hasRemainingExpectations, isFalse);
});
testWithoutContext('xcodebuild build settings is empty when xcodebuild failed to get the build settings', () async { testWithoutContext('xcodebuild build settings is empty when xcodebuild failed to get the build settings', () async {
platform.environment = const <String, String>{}; platform.environment = const <String, String>{};
fakeProcessManager.addCommand(const FakeCommand( fakeProcessManager.addCommands(const <FakeCommand>[
command: <String>[ FakeCommand(
'/usr/bin/xcodebuild', command: <String>[
'-project', 'sysctl',
'/', 'hw.optional.arm64',
'-scheme', ],
'Free', exitCode: 1,
'-showBuildSettings' ),
], FakeCommand(
exitCode: 1, command: <String>[
)); 'xcrun',
'xcodebuild',
'-project',
'/',
'-scheme',
'Free',
'-showBuildSettings'
],
exitCode: 1,
),
]);
expect(await xcodeProjectInterpreter.getBuildSettings('', scheme: 'Free'), const <String, String>{}); expect(await xcodeProjectInterpreter.getBuildSettings('', scheme: 'Free'), const <String, String>{});
expect(fakeProcessManager.hasRemainingExpectations, isFalse); expect(fakeProcessManager.hasRemainingExpectations, isFalse);
...@@ -247,15 +317,19 @@ void main() { ...@@ -247,15 +317,19 @@ void main() {
testWithoutContext('build settings accepts an empty scheme', () async { testWithoutContext('build settings accepts an empty scheme', () async {
platform.environment = const <String, String>{}; platform.environment = const <String, String>{};
fakeProcessManager.addCommand(const FakeCommand( fakeProcessManager.addCommands(const <FakeCommand>[
command: <String>[ kARMCheckCommand,
'/usr/bin/xcodebuild', FakeCommand(
'-project', command: <String>[
'/', 'xcrun',
'-showBuildSettings' 'xcodebuild',
], '-project',
exitCode: 1, '/',
)); '-showBuildSettings'
],
exitCode: 1,
),
]);
expect(await xcodeProjectInterpreter.getBuildSettings(''), const <String, String>{}); expect(await xcodeProjectInterpreter.getBuildSettings(''), const <String, String>{});
expect(fakeProcessManager.hasRemainingExpectations, isFalse); expect(fakeProcessManager.hasRemainingExpectations, isFalse);
...@@ -266,18 +340,22 @@ void main() { ...@@ -266,18 +340,22 @@ void main() {
'FLUTTER_XCODE_CODE_SIGN_STYLE': 'Manual', 'FLUTTER_XCODE_CODE_SIGN_STYLE': 'Manual',
'FLUTTER_XCODE_ARCHS': 'arm64' 'FLUTTER_XCODE_ARCHS': 'arm64'
}; };
fakeProcessManager.addCommand(FakeCommand( fakeProcessManager.addCommands(<FakeCommand>[
command: <String>[ kARMCheckCommand,
xcodebuild, FakeCommand(
'-project', command: <String>[
fileSystem.path.separator, 'xcrun',
'-scheme', 'xcodebuild',
'Free', '-project',
'-showBuildSettings', fileSystem.path.separator,
'CODE_SIGN_STYLE=Manual', '-scheme',
'ARCHS=arm64' 'Free',
], '-showBuildSettings',
)); 'CODE_SIGN_STYLE=Manual',
'ARCHS=arm64'
],
),
]);
expect(await xcodeProjectInterpreter.getBuildSettings('', scheme: 'Free'), const <String, String>{}); expect(await xcodeProjectInterpreter.getBuildSettings('', scheme: 'Free'), const <String, String>{});
expect(fakeProcessManager.hasRemainingExpectations, isFalse); expect(fakeProcessManager.hasRemainingExpectations, isFalse);
}); });
...@@ -288,19 +366,23 @@ void main() { ...@@ -288,19 +366,23 @@ void main() {
'FLUTTER_XCODE_ARCHS': 'arm64' 'FLUTTER_XCODE_ARCHS': 'arm64'
}; };
fakeProcessManager.addCommand(const FakeCommand( fakeProcessManager.addCommands(const <FakeCommand>[
command: <String>[ kARMCheckCommand,
xcodebuild, FakeCommand(
'-workspace', command: <String>[
'workspace_path', 'xcrun',
'-scheme', 'xcodebuild',
'Free', '-workspace',
'-quiet', 'workspace_path',
'clean', '-scheme',
'CODE_SIGN_STYLE=Manual', 'Free',
'ARCHS=arm64' '-quiet',
], 'clean',
)); 'CODE_SIGN_STYLE=Manual',
'ARCHS=arm64'
],
),
]);
await xcodeProjectInterpreter.cleanWorkspace('workspace_path', 'Free'); await xcodeProjectInterpreter.cleanWorkspace('workspace_path', 'Free');
expect(fakeProcessManager.hasRemainingExpectations, isFalse); expect(fakeProcessManager.hasRemainingExpectations, isFalse);
...@@ -308,9 +390,12 @@ void main() { ...@@ -308,9 +390,12 @@ void main() {
testWithoutContext('xcodebuild -list getInfo returns something when xcodebuild -list succeeds', () async { testWithoutContext('xcodebuild -list getInfo returns something when xcodebuild -list succeeds', () async {
const String workingDirectory = '/'; const String workingDirectory = '/';
fakeProcessManager.addCommand(const FakeCommand( fakeProcessManager.addCommands(const <FakeCommand>[
command: <String>[xcodebuild, '-list'], kARMCheckCommand,
)); FakeCommand(
command: <String>['xcrun', 'xcodebuild', '-list'],
),
]);
final XcodeProjectInterpreter xcodeProjectInterpreter = XcodeProjectInterpreter( final XcodeProjectInterpreter xcodeProjectInterpreter = XcodeProjectInterpreter(
logger: logger, logger: logger,
...@@ -329,11 +414,14 @@ void main() { ...@@ -329,11 +414,14 @@ void main() {
const String workingDirectory = '/'; const String workingDirectory = '/';
const String stderr = 'Useful Xcode failure message about missing project.'; const String stderr = 'Useful Xcode failure message about missing project.';
fakeProcessManager.addCommand(const FakeCommand( fakeProcessManager.addCommands(const <FakeCommand>[
command: <String>[xcodebuild, '-list'], kARMCheckCommand,
exitCode: 66, FakeCommand(
stderr: stderr, command: <String>['xcrun', 'xcodebuild', '-list'],
)); exitCode: 66,
stderr: stderr,
),
]);
final XcodeProjectInterpreter xcodeProjectInterpreter = XcodeProjectInterpreter( final XcodeProjectInterpreter xcodeProjectInterpreter = XcodeProjectInterpreter(
logger: logger, logger: logger,
......
...@@ -39,14 +39,16 @@ void main() { ...@@ -39,14 +39,16 @@ void main() {
group('Xcode', () { group('Xcode', () {
Xcode xcode; Xcode xcode;
MockXcodeProjectInterpreter mockXcodeProjectInterpreter;
setUp(() { setUp(() {
mockXcodeProjectInterpreter = MockXcodeProjectInterpreter();
xcode = Xcode( xcode = Xcode(
logger: logger, logger: logger,
platform: FakePlatform(operatingSystem: 'macos'), platform: FakePlatform(operatingSystem: 'macos'),
fileSystem: MemoryFileSystem.test(), fileSystem: MemoryFileSystem.test(),
processManager: processManager, processManager: processManager,
xcodeProjectInterpreter: MockXcodeProjectInterpreter(), xcodeProjectInterpreter: mockXcodeProjectInterpreter,
); );
}); });
...@@ -61,6 +63,8 @@ void main() { ...@@ -61,6 +63,8 @@ void main() {
}); });
testWithoutContext('eulaSigned is false when clang is not installed', () { testWithoutContext('eulaSigned is false when clang is not installed', () {
when(mockXcodeProjectInterpreter.xcrunCommand()).thenReturn(<String>['xcrun']);
when(processManager.runSync(<String>['sysctl', 'hw.optional.arm64'])) when(processManager.runSync(<String>['sysctl', 'hw.optional.arm64']))
.thenReturn(ProcessResult(123, 1, '', '')); .thenReturn(ProcessResult(123, 1, '', ''));
when(processManager.runSync(<String>['xcrun', 'clang'])) when(processManager.runSync(<String>['xcrun', 'clang']))
...@@ -140,6 +144,7 @@ void main() { ...@@ -140,6 +144,7 @@ void main() {
setUp(() { setUp(() {
mockXcodeProjectInterpreter = MockXcodeProjectInterpreter(); mockXcodeProjectInterpreter = MockXcodeProjectInterpreter();
when(mockXcodeProjectInterpreter.xcrunCommand()).thenReturn(<String>['xcrun']);
platform = FakePlatform(operatingSystem: 'macos'); platform = FakePlatform(operatingSystem: 'macos');
xcode = Xcode( xcode = Xcode(
logger: logger, logger: logger,
...@@ -252,25 +257,6 @@ void main() { ...@@ -252,25 +257,6 @@ void main() {
expect(fakeProcessManager.hasRemainingExpectations, 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', () { testWithoutContext('isInstalledAndMeetsVersionCheck is true when macOS and installed and version is satisfied', () {
fakeProcessManager.addCommand(const FakeCommand( fakeProcessManager.addCommand(const FakeCommand(
command: <String>['/usr/bin/xcode-select', '--print-path'], command: <String>['/usr/bin/xcode-select', '--print-path'],
...@@ -287,13 +273,6 @@ void main() { ...@@ -287,13 +273,6 @@ void main() {
testWithoutContext('eulaSigned is false when clang output indicates EULA not yet accepted', () { testWithoutContext('eulaSigned is false when clang output indicates EULA not yet accepted', () {
fakeProcessManager.addCommands(const <FakeCommand>[ fakeProcessManager.addCommands(const <FakeCommand>[
FakeCommand(
command: <String>[
'sysctl',
'hw.optional.arm64',
],
exitCode: 1,
),
FakeCommand( FakeCommand(
command: <String>['xcrun', 'clang'], command: <String>['xcrun', 'clang'],
exitCode: 1, exitCode: 1,
...@@ -309,13 +288,6 @@ void main() { ...@@ -309,13 +288,6 @@ void main() {
testWithoutContext('eulaSigned is true when clang output indicates EULA has been accepted', () { testWithoutContext('eulaSigned is true when clang output indicates EULA has been accepted', () {
fakeProcessManager.addCommands( fakeProcessManager.addCommands(
const <FakeCommand>[ const <FakeCommand>[
FakeCommand(
command: <String>[
'sysctl',
'hw.optional.arm64',
],
exitCode: 1,
),
FakeCommand( FakeCommand(
command: <String>['xcrun', 'clang'], command: <String>['xcrun', 'clang'],
exitCode: 1, exitCode: 1,
...@@ -334,18 +306,6 @@ void main() { ...@@ -334,18 +306,6 @@ void main() {
}); });
group('SDK location', () { 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'; const String sdkroot = 'Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.2.sdk';
testWithoutContext('--show-sdk-path iphoneos', () async { testWithoutContext('--show-sdk-path iphoneos', () async {
......
...@@ -414,6 +414,9 @@ class FakeXcodeProjectInterpreter implements XcodeProjectInterpreter { ...@@ -414,6 +414,9 @@ class FakeXcodeProjectInterpreter implements XcodeProjectInterpreter {
BufferLogger.test(), BufferLogger.test(),
); );
} }
@override
List<String> xcrunCommand() => <String>['xcrun'];
} }
class MockFlutterVersion extends Mock implements FlutterVersion { class MockFlutterVersion extends Mock implements FlutterVersion {
......
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