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';
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 '../base/terminal.dart';
......@@ -242,26 +243,32 @@ class XcodeProjectInterpreter {
_terminal = terminal,
_logger = logger,
_processUtils = ProcessUtils(logger: logger, processManager: processManager),
_operatingSystemUtils = OperatingSystemUtils(
fileSystem: fileSystem,
logger: logger,
platform: platform,
processManager: processManager,
),
_usage = usage;
final Platform _platform;
final FileSystem _fileSystem;
final ProcessUtils _processUtils;
final OperatingSystemUtils _operatingSystemUtils;
final Terminal _terminal;
final Logger _logger;
final Usage _usage;
static const String _executable = '/usr/bin/xcodebuild';
static final RegExp _versionRegex = RegExp(r'Xcode ([0-9.]+)');
void _updateVersion() {
if (!_platform.isMacOS || !_fileSystem.file(_executable).existsSync()) {
if (!_platform.isMacOS || !_fileSystem.file('/usr/bin/xcodebuild').existsSync()) {
return;
}
try {
if (_versionText == null) {
final RunResult result = _processUtils.runSync(
<String>[_executable, '-version'],
<String>[...xcrunCommand(), 'xcodebuild', '-version'],
);
if (result.exitCode != 0) {
return;
......@@ -316,6 +323,25 @@ class XcodeProjectInterpreter {
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
/// new call-sites.
///
......@@ -331,7 +357,8 @@ class XcodeProjectInterpreter {
terminal: _terminal,
);
final List<String> showBuildSettingsCommand = <String>[
_executable,
...xcrunCommand(),
'xcodebuild',
'-project',
_fileSystem.path.absolute(projectPath),
if (scheme != null)
......@@ -368,7 +395,8 @@ class XcodeProjectInterpreter {
Future<void> cleanWorkspace(String workspacePath, String scheme, { bool verbose = false }) async {
await _processUtils.run(<String>[
_executable,
...xcrunCommand(),
'xcodebuild',
'-workspace',
workspacePath,
'-scheme',
......@@ -387,7 +415,8 @@ class XcodeProjectInterpreter {
const int missingProjectExitCode = 66;
final RunResult result = await _processUtils.run(
<String>[
_executable,
...xcrunCommand(),
'xcodebuild',
'-list',
if (projectFilename != null) ...<String>['-project', projectFilename],
],
......
......@@ -12,7 +12,6 @@ import '../base/common.dart';
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';
......@@ -66,18 +65,11 @@ class Xcode {
}) : _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;
......@@ -169,24 +161,8 @@ class Xcode {
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;
}
/// See [XcodeProjectInterpreter.xcrunCommand].
List<String> xcrunCommand() => _xcodeProjectInterpreter.xcrunCommand();
Future<RunResult> cc(List<String> args) {
return _processUtils.run(
......
......@@ -77,14 +77,6 @@ void main() {
'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
// in the given configuration.
FakeCommand setUpMockXcodeBuildHandler({ bool verbose = false, bool showBuildSettings = false, void Function() onRun }) {
......@@ -215,7 +207,6 @@ void main() {
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.list(<FakeCommand>[
xattrCommand,
armCheckCommand,
setUpMockXcodeBuildHandler(),
setUpMockXcodeBuildHandler(showBuildSettings: true),
]),
......@@ -234,7 +225,6 @@ void main() {
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.list(<FakeCommand>[
xattrCommand,
armCheckCommand,
setUpMockXcodeBuildHandler(verbose: true),
setUpMockXcodeBuildHandler(verbose: true, showBuildSettings: true),
]),
......@@ -263,7 +253,6 @@ void main() {
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.list(<FakeCommand>[
xattrCommand,
armCheckCommand,
setUpMockXcodeBuildHandler(onRun: () {
fileSystem.file('build/flutter_size_01/snapshot.arm64.json')
..createSync(recursive: true)
......@@ -311,7 +300,6 @@ void main() {
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.list(<FakeCommand>[
xattrCommand,
armCheckCommand,
setUpMockXcodeBuildHandler(),
setUpMockXcodeBuildHandler(showBuildSettings: true),
exportArchiveCommand,
......
......@@ -478,13 +478,6 @@ void main() {
'--lazy-async-stacks',
'$build/app.dill',
]),
const FakeCommand(
command: <String>[
'sysctl',
'hw.optional.arm64',
],
exitCode: 1,
),
FakeCommand(command: <String>[
'xcrun',
'cc',
......@@ -593,13 +586,6 @@ void main() {
'--lazy-async-stacks',
'$build/app.dill',
]),
const FakeCommand(
command: <String>[
'sysctl',
'hw.optional.arm64',
],
exitCode: 1,
),
FakeCommand(command: <String>[
'xcrun',
'cc',
......@@ -677,13 +663,6 @@ void main() {
'--lazy-async-stacks',
'$build/app.dill',
]),
const FakeCommand(
command: <String>[
'sysctl',
'hw.optional.arm64',
],
exitCode: 1,
),
FakeCommand(command: <String>[
'xcrun',
'cc',
......
......@@ -71,14 +71,6 @@ void main() {
testUsingContext('DebugUniveralFramework creates expected binary with arm64 only arch', () async {
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',
......
......@@ -39,14 +39,16 @@ void main() {
group('Xcode', () {
Xcode xcode;
MockXcodeProjectInterpreter mockXcodeProjectInterpreter;
setUp(() {
mockXcodeProjectInterpreter = MockXcodeProjectInterpreter();
xcode = Xcode(
logger: logger,
platform: FakePlatform(operatingSystem: 'macos'),
fileSystem: MemoryFileSystem.test(),
processManager: processManager,
xcodeProjectInterpreter: MockXcodeProjectInterpreter(),
xcodeProjectInterpreter: mockXcodeProjectInterpreter,
);
});
......@@ -61,6 +63,8 @@ void main() {
});
testWithoutContext('eulaSigned is false when clang is not installed', () {
when(mockXcodeProjectInterpreter.xcrunCommand()).thenReturn(<String>['xcrun']);
when(processManager.runSync(<String>['sysctl', 'hw.optional.arm64']))
.thenReturn(ProcessResult(123, 1, '', ''));
when(processManager.runSync(<String>['xcrun', 'clang']))
......@@ -140,6 +144,7 @@ void main() {
setUp(() {
mockXcodeProjectInterpreter = MockXcodeProjectInterpreter();
when(mockXcodeProjectInterpreter.xcrunCommand()).thenReturn(<String>['xcrun']);
platform = FakePlatform(operatingSystem: 'macos');
xcode = Xcode(
logger: logger,
......@@ -252,25 +257,6 @@ void main() {
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'],
......@@ -287,13 +273,6 @@ void main() {
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,
......@@ -309,13 +288,6 @@ void main() {
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,
......@@ -334,18 +306,6 @@ void main() {
});
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 {
......
......@@ -414,6 +414,9 @@ class FakeXcodeProjectInterpreter implements XcodeProjectInterpreter {
BufferLogger.test(),
);
}
@override
List<String> xcrunCommand() => <String>['xcrun'];
}
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