Skip to content

Commit 49f5980

Browse files
authored
Suggest Rosetta when x64 binary cannot be run (#114558)
* Suggest Rosetta when x64 binary cannot be run * validator * Adjust error message
1 parent 458f129 commit 49f5980

File tree

5 files changed

+95
-4
lines changed

5 files changed

+95
-4
lines changed

packages/flutter_tools/lib/src/base/error_handling_io.dart

+20-2
Original file line numberDiff line numberDiff line change
@@ -581,8 +581,10 @@ Future<T> _run<T>(Future<T> Function() op, {
581581
} on io.ProcessException catch (e) {
582582
if (platform.isWindows) {
583583
_handleWindowsException(e, failureMessage, e.errorCode);
584-
} else if (platform.isLinux || platform.isMacOS) {
584+
} else if (platform.isLinux) {
585585
_handlePosixException(e, failureMessage, e.errorCode, posixPermissionSuggestion);
586+
} if (platform.isMacOS) {
587+
_handleMacOSException(e, failureMessage, e.errorCode, posixPermissionSuggestion);
586588
}
587589
rethrow;
588590
}
@@ -611,8 +613,10 @@ T _runSync<T>(T Function() op, {
611613
} on io.ProcessException catch (e) {
612614
if (platform.isWindows) {
613615
_handleWindowsException(e, failureMessage, e.errorCode);
614-
} else if (platform.isLinux || platform.isMacOS) {
616+
} else if (platform.isLinux) {
615617
_handlePosixException(e, failureMessage, e.errorCode, posixPermissionSuggestion);
618+
} if (platform.isMacOS) {
619+
_handleMacOSException(e, failureMessage, e.errorCode, posixPermissionSuggestion);
616620
}
617621
rethrow;
618622
}
@@ -762,6 +766,20 @@ void _handlePosixException(Exception e, String? message, int errorCode, String?
762766
_throwFileSystemException(errorMessage);
763767
}
764768

769+
void _handleMacOSException(Exception e, String? message, int errorCode, String? posixPermissionSuggestion) {
770+
// https://github.com/apple/darwin-xnu/blob/master/bsd/dev/dtrace/scripts/errno.d
771+
const int ebadarch = 86;
772+
if (errorCode == ebadarch) {
773+
final StringBuffer errorBuffer = StringBuffer();
774+
errorBuffer.writeln(message);
775+
errorBuffer.writeln('This binary was built with the incorrect architecture to run on this machine.');
776+
errorBuffer.writeln('Flutter requires the Rosetta translation environment. If you are on an ARM Mac, try running:');
777+
errorBuffer.writeln(' sudo softwareupdate --install-rosetta --agree-to-license');
778+
_throwFileSystemException(errorBuffer.toString());
779+
}
780+
_handlePosixException(e, message, errorCode, posixPermissionSuggestion);
781+
}
782+
765783
void _handleWindowsException(Exception e, String? message, int errorCode) {
766784
// From:
767785
// https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes

packages/flutter_tools/lib/src/base/user_messages.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ class UserMessages {
4242
String flutterMirrorURL(String url) => 'Flutter download mirror $url';
4343
String get flutterBinariesDoNotRun =>
4444
'Downloaded executables cannot execute on host.\n'
45-
'See https://github.com/flutter/flutter/issues/6207 for more information';
45+
'See https://github.com/flutter/flutter/issues/6207 for more information.';
4646
String get flutterBinariesLinuxRepairCommands =>
4747
'On Debian/Ubuntu/Mint: sudo apt-get install lib32stdc++6\n'
4848
'On Fedora: dnf install libstdc++.i686\n'

packages/flutter_tools/lib/src/doctor.dart

+3
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,9 @@ class FlutterValidator extends DoctorValidator {
549549
buffer.writeln(_userMessages.flutterBinariesDoNotRun);
550550
if (_platform.isLinux) {
551551
buffer.writeln(_userMessages.flutterBinariesLinuxRepairCommands);
552+
} else if (_platform.isMacOS && _operatingSystemUtils.hostPlatform == HostPlatform.darwin_arm64) {
553+
buffer.writeln('Flutter requires the Rosetta translation environment on ARM Macs. Try running:');
554+
buffer.writeln(' sudo softwareupdate --install-rosetta --agree-to-license');
552555
}
553556
messages.add(ValidationMessage.error(buffer.toString()));
554557
}

packages/flutter_tools/test/general.shard/base/error_handling_io_test.dart

+23
Original file line numberDiff line numberDiff line change
@@ -1051,6 +1051,7 @@ void main() {
10511051
group('ProcessManager on macOS throws tool exit', () {
10521052
const int enospc = 28;
10531053
const int eacces = 13;
1054+
const int ebadarch = 86;
10541055

10551056
testWithoutContext('when writing to a full device', () {
10561057
final FakeProcessManager fakeProcessManager = FakeProcessManager.list(<FakeCommand>[
@@ -1109,6 +1110,28 @@ void main() {
11091110

11101111
expect(() async => processManager.canRun('/path/to/dart'), throwsToolExit(message: expectedMessage));
11111112
});
1113+
1114+
testWithoutContext('when bad CPU type', () async {
1115+
final FakeProcessManager fakeProcessManager = FakeProcessManager.list(<FakeCommand>[
1116+
const FakeCommand(command: <String>['foo'], exception: ProcessException('', <String>[], '', ebadarch)),
1117+
const FakeCommand(command: <String>['foo'], exception: ProcessException('', <String>[], '', ebadarch)),
1118+
const FakeCommand(command: <String>['foo'], exception: ProcessException('', <String>[], '', ebadarch)),
1119+
]);
1120+
1121+
final ProcessManager processManager = ErrorHandlingProcessManager(
1122+
delegate: fakeProcessManager,
1123+
platform: macOSPlatform,
1124+
);
1125+
1126+
const String expectedMessage = 'Flutter requires the Rosetta translation environment';
1127+
1128+
expect(() async => processManager.start(<String>['foo']),
1129+
throwsToolExit(message: expectedMessage));
1130+
expect(() async => processManager.run(<String>['foo']),
1131+
throwsToolExit(message: expectedMessage));
1132+
expect(() => processManager.runSync(<String>['foo']),
1133+
throwsToolExit(message: expectedMessage));
1134+
});
11121135
});
11131136

11141137
testWithoutContext('ErrorHandlingProcessManager delegates killPid correctly', () async {

packages/flutter_tools/test/general.shard/flutter_validator_test.dart

+48-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ void main() {
6666
messages: containsAll(const <ValidationMessage>[
6767
ValidationMessage.error(
6868
'Downloaded executables cannot execute on host.\n'
69-
'See https://github.com/flutter/flutter/issues/6207 for more information\n'
69+
'See https://github.com/flutter/flutter/issues/6207 for more information.\n'
7070
'On Debian/Ubuntu/Mint: sudo apt-get install lib32stdc++6\n'
7171
'On Fedora: dnf install libstdc++.i686\n'
7272
'On Arch: pacman -S lib32-gcc-libs\n',
@@ -75,6 +75,49 @@ void main() {
7575
);
7676
});
7777

78+
testWithoutContext('FlutterValidator shows an error message if Rosetta is needed', () async {
79+
final FakeFlutterVersion flutterVersion = FakeFlutterVersion(
80+
frameworkVersion: '1.0.0',
81+
channel: 'beta',
82+
);
83+
final MemoryFileSystem fileSystem = MemoryFileSystem.test();
84+
final Artifacts artifacts = Artifacts.test();
85+
final FlutterValidator flutterValidator = FlutterValidator(
86+
platform: FakePlatform(
87+
operatingSystem: 'macos',
88+
localeName: 'en_US.UTF-8',
89+
environment: <String, String>{},
90+
),
91+
flutterVersion: () => flutterVersion,
92+
devToolsVersion: () => '2.8.0',
93+
userMessages: UserMessages(),
94+
artifacts: artifacts,
95+
fileSystem: fileSystem,
96+
flutterRoot: () => 'sdk/flutter',
97+
operatingSystemUtils: FakeOperatingSystemUtils(name: 'macOS', hostPlatform: HostPlatform.darwin_arm64),
98+
processManager: FakeProcessManager.list(<FakeCommand>[
99+
const FakeCommand(
100+
command: <String>['Artifact.genSnapshot'],
101+
exitCode: 1,
102+
),
103+
])
104+
);
105+
fileSystem.file(artifacts.getArtifactPath(Artifact.genSnapshot)).createSync(recursive: true);
106+
107+
expect(await flutterValidator.validate(), _matchDoctorValidation(
108+
validationType: ValidationType.partial,
109+
statusInfo: 'Channel beta, 1.0.0, on macOS, locale en_US.UTF-8',
110+
messages: containsAll(const <ValidationMessage>[
111+
ValidationMessage.error(
112+
'Downloaded executables cannot execute on host.\n'
113+
'See https://github.com/flutter/flutter/issues/6207 for more information.\n'
114+
'Flutter requires the Rosetta translation environment on ARM Macs. Try running:\n'
115+
' sudo softwareupdate --install-rosetta --agree-to-license\n'
116+
),
117+
])),
118+
);
119+
});
120+
78121
testWithoutContext('FlutterValidator does not run gen_snapshot binary check if it is not already downloaded', () async {
79122
final FakeFlutterVersion flutterVersion = FakeFlutterVersion(
80123
frameworkVersion: '1.0.0',
@@ -569,6 +612,7 @@ void main() {
569612
class FakeOperatingSystemUtils extends Fake implements OperatingSystemUtils {
570613
FakeOperatingSystemUtils({
571614
required this.name,
615+
this.hostPlatform = HostPlatform.linux_x64,
572616
this.whichLookup,
573617
FileSystem? fs,
574618
}) {
@@ -587,6 +631,9 @@ class FakeOperatingSystemUtils extends Fake implements OperatingSystemUtils {
587631

588632
@override
589633
final String name;
634+
635+
@override
636+
final HostPlatform hostPlatform;
590637
}
591638

592639
class FakeThrowingFlutterVersion extends FakeFlutterVersion {

0 commit comments

Comments
 (0)