mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Detect ARM macOS arch with sysctl hw.optional.arm64 (#67970)
This commit is contained in:
parent
0343555af1
commit
4dffc851bc
@ -328,7 +328,7 @@ abstract class IOSApp extends ApplicationPackage {
|
||||
}
|
||||
|
||||
static Future<IOSApp> fromIosProject(IosProject project, BuildInfo buildInfo) {
|
||||
if (getCurrentHostPlatform() != HostPlatform.darwin_x64) {
|
||||
if (!globals.platform.isMacOS) {
|
||||
return null;
|
||||
}
|
||||
if (!project.exists) {
|
||||
|
||||
@ -7,6 +7,7 @@ import 'package:file/file.dart';
|
||||
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 @@ abstract class OperatingSystemUtils {
|
||||
platform: platform,
|
||||
processManager: processManager,
|
||||
);
|
||||
} else if (platform.isMacOS) {
|
||||
return _MacOSUtils(
|
||||
fileSystem: fileSystem,
|
||||
logger: logger,
|
||||
platform: platform,
|
||||
processManager: processManager,
|
||||
);
|
||||
} else {
|
||||
return _PosixUtils(
|
||||
fileSystem: fileSystem,
|
||||
@ -112,6 +120,8 @@ abstract class OperatingSystemUtils {
|
||||
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.
|
||||
@ -246,30 +256,63 @@ class _PosixUtils extends OperatingSystemUtils {
|
||||
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']),
|
||||
_processUtils.runSync(<String>['uname', '-m']),
|
||||
];
|
||||
if (results.every((RunResult result) => result.exitCode == 0)) {
|
||||
_name = '${results[0].stdout.trim()} ${results[1].stdout
|
||||
.trim()} ${results[2].stdout.trim()} ${results[3].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']);
|
||||
// hw.optional.arm64 is unavailable on < macOS 11 and exits with 1, assume x86 on failure.
|
||||
// On arm64 stdout is "sysctl hw.optional.arm64: 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 {
|
||||
@ -285,6 +328,9 @@ class _WindowsUtils extends OperatingSystemUtils {
|
||||
processManager: processManager,
|
||||
);
|
||||
|
||||
@override
|
||||
HostPlatform hostPlatform = HostPlatform.windows_x64;
|
||||
|
||||
@override
|
||||
void makeExecutable(File file) {}
|
||||
|
||||
|
||||
@ -398,6 +398,7 @@ bool isEmulatorBuildMode(BuildMode mode) {
|
||||
|
||||
enum HostPlatform {
|
||||
darwin_x64,
|
||||
darwin_arm,
|
||||
linux_x64,
|
||||
windows_x64,
|
||||
}
|
||||
@ -406,6 +407,8 @@ String getNameForHostPlatform(HostPlatform platform) {
|
||||
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:
|
||||
@ -418,6 +421,7 @@ String getNameForHostPlatform(HostPlatform platform) {
|
||||
enum TargetPlatform {
|
||||
android,
|
||||
ios,
|
||||
// darwin_arm64 not yet supported, macOS desktop targets run in Rosetta as x86.
|
||||
darwin_x64,
|
||||
linux_x64,
|
||||
windows_x64,
|
||||
|
||||
@ -6,10 +6,10 @@ import 'package:file/file.dart';
|
||||
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 @@ const String kPath2 = '/another/bin/$kExecutable';
|
||||
|
||||
void main() {
|
||||
MockProcessManager mockProcessManager;
|
||||
FakeProcessManager fakeProcessManager;
|
||||
|
||||
setUp(() {
|
||||
mockProcessManager = MockProcessManager();
|
||||
fakeProcessManager = FakeProcessManager.list(<FakeCommand>[]);
|
||||
});
|
||||
|
||||
OperatingSystemUtils createOSUtils(Platform platform) {
|
||||
@ -32,28 +34,50 @@ void main() {
|
||||
fileSystem: MemoryFileSystem.test(),
|
||||
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 @@ void main() {
|
||||
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,34 +147,162 @@ void main() {
|
||||
});
|
||||
});
|
||||
|
||||
testWithoutContext('macos name', () async {
|
||||
when(mockProcessManager.runSync(
|
||||
<String>['sw_vers', '-productName'],
|
||||
)).thenReturn(ProcessResult(0, 0, 'product', ''));
|
||||
when(mockProcessManager.runSync(
|
||||
<String>['sw_vers', '-productVersion'],
|
||||
)).thenReturn(ProcessResult(0, 0, 'version', ''));
|
||||
when(mockProcessManager.runSync(
|
||||
<String>['sw_vers', '-buildVersion'],
|
||||
)).thenReturn(ProcessResult(0, 0, 'build', ''));
|
||||
when(mockProcessManager.runSync(
|
||||
<String>['uname', '-m'],
|
||||
)).thenReturn(ProcessResult(0, 0, 'arch', ''));
|
||||
final MockFileSystem fileSystem = MockFileSystem();
|
||||
final OperatingSystemUtils utils = OperatingSystemUtils(
|
||||
fileSystem: fileSystem,
|
||||
logger: BufferLogger.test(),
|
||||
platform: FakePlatform(operatingSystem: 'macos'),
|
||||
processManager: mockProcessManager,
|
||||
);
|
||||
expect(utils.name, 'product version build arch');
|
||||
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();
|
||||
@ -134,7 +314,7 @@ void main() {
|
||||
fileSystem: fileSystem,
|
||||
logger: BufferLogger.test(),
|
||||
platform: FakePlatform(operatingSystem: 'linux'),
|
||||
processManager: mockProcessManager,
|
||||
processManager: fakeProcessManager,
|
||||
);
|
||||
|
||||
expect(
|
||||
|
||||
@ -17,6 +17,7 @@ import 'package:flutter_tools/src/base/signals.dart';
|
||||
import 'package:flutter_tools/src/base/template.dart';
|
||||
import 'package:flutter_tools/src/base/terminal.dart';
|
||||
import 'package:flutter_tools/src/base/time.dart';
|
||||
import 'package:flutter_tools/src/build_info.dart';
|
||||
import 'package:flutter_tools/src/isolated/mustache_template.dart';
|
||||
import 'package:flutter_tools/src/cache.dart';
|
||||
import 'package:flutter_tools/src/context_runner.dart';
|
||||
@ -294,6 +295,9 @@ class FakeOperatingSystemUtils implements OperatingSystemUtils {
|
||||
@override
|
||||
ProcessResult makeExecutable(File file) => null;
|
||||
|
||||
@override
|
||||
HostPlatform hostPlatform = HostPlatform.linux_x64;
|
||||
|
||||
@override
|
||||
void chmod(FileSystemEntity entity, String mode) { }
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user