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',
......
...@@ -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