diff --git a/packages/flutter_tools/lib/src/base/os.dart b/packages/flutter_tools/lib/src/base/os.dart index 72880d48e12..d5210d9411c 100644 --- a/packages/flutter_tools/lib/src/base/os.dart +++ b/packages/flutter_tools/lib/src/base/os.dart @@ -301,8 +301,26 @@ class _MacOSUtils extends _PosixUtils { @override HostPlatform get hostPlatform { if (_hostPlatform == null) { + String sysctlPath; + if (which('sysctl') == null) { + // Fallback to known install locations. + for (final String path in [ + '/usr/sbin/sysctl', + '/sbin/sysctl', + ]) { + if (_fileSystem.isFileSync(path)) { + sysctlPath = path; + } + } + } else { + sysctlPath = 'sysctl'; + } + + if (sysctlPath == null) { + throwToolExit('sysctl not found. Try adding it to your PATH environment variable.'); + } final RunResult arm64Check = - _processUtils.runSync(['sysctl', 'hw.optional.arm64']); + _processUtils.runSync([sysctlPath, 'hw.optional.arm64']); // On arm64 stdout is "sysctl hw.optional.arm64: 1" // On x86 hw.optional.arm64 is unavailable and exits with 1. if (arm64Check.exitCode == 0 && arm64Check.stdout.trim().endsWith('1')) { diff --git a/packages/flutter_tools/test/general.shard/base/build_test.dart b/packages/flutter_tools/test/general.shard/base/build_test.dart index 98146567892..bce2911bc7c 100644 --- a/packages/flutter_tools/test/general.shard/base/build_test.dart +++ b/packages/flutter_tools/test/general.shard/base/build_test.dart @@ -16,6 +16,13 @@ import 'package:flutter_tools/src/reporting/reporting.dart'; import '../../src/common.dart'; import '../../src/context.dart'; +const FakeCommand kWhichSysctlCommand = FakeCommand( + command: [ + 'which', + 'sysctl', + ], +); + const FakeCommand kARMCheckCommand = FakeCommand( command: [ 'sysctl', @@ -255,9 +262,11 @@ void main() { testWithoutContext('builds iOS with bitcode', () async { final String outputPath = fileSystem.path.join('build', 'foo'); final String assembly = fileSystem.path.join(outputPath, 'snapshot_assembly.S'); - processManager.addCommand(FakeCommand( - command: [ - artifacts.getArtifactPath(Artifact.genSnapshot, platform: TargetPlatform.ios, mode: BuildMode.profile) +'_armv7', + processManager.addCommands([ + FakeCommand(command: [ + artifacts.getArtifactPath(Artifact.genSnapshot, + platform: TargetPlatform.ios, mode: BuildMode.profile) + + '_armv7', '--deterministic', '--snapshot_kind=app-aot-assembly', '--assembly=$assembly', @@ -267,11 +276,10 @@ void main() { '--no-causal-async-stacks', '--lazy-async-stacks', 'main.dill', - ] - )); - processManager.addCommand(kARMCheckCommand); - processManager.addCommand(const FakeCommand( - command: [ + ]), + kWhichSysctlCommand, + kARMCheckCommand, + const FakeCommand(command: [ 'xcrun', 'cc', '-arch', @@ -283,17 +291,15 @@ void main() { 'build/foo/snapshot_assembly.S', '-o', 'build/foo/snapshot_assembly.o', - ] - )); - processManager.addCommand(const FakeCommand( - command: [ + ]), + const FakeCommand(command: [ 'xcrun', 'clang', '-arch', 'armv7', ...kBitcodeClang, - ] - )); + ]), + ]); final int genSnapshotExitCode = await snapshotter.build( platform: TargetPlatform.ios, @@ -315,9 +321,11 @@ void main() { final String outputPath = fileSystem.path.join('build', 'foo'); final String assembly = fileSystem.path.join(outputPath, 'snapshot_assembly.S'); final String debugPath = fileSystem.path.join('foo', 'app.ios-armv7.symbols'); - processManager.addCommand(FakeCommand( - command: [ - artifacts.getArtifactPath(Artifact.genSnapshot, platform: TargetPlatform.ios, mode: BuildMode.profile) +'_armv7', + processManager.addCommands([ + FakeCommand(command: [ + artifacts.getArtifactPath(Artifact.genSnapshot, + platform: TargetPlatform.ios, mode: BuildMode.profile) + + '_armv7', '--deterministic', '--snapshot_kind=app-aot-assembly', '--assembly=$assembly', @@ -329,11 +337,10 @@ void main() { '--dwarf-stack-traces', '--save-debugging-info=$debugPath', 'main.dill', - ] - )); - processManager.addCommand(kARMCheckCommand); - processManager.addCommand(const FakeCommand( - command: [ + ]), + kWhichSysctlCommand, + kARMCheckCommand, + const FakeCommand(command: [ 'xcrun', 'cc', '-arch', @@ -344,17 +351,15 @@ void main() { 'build/foo/snapshot_assembly.S', '-o', 'build/foo/snapshot_assembly.o', - ] - )); - processManager.addCommand(const FakeCommand( - command: [ + ]), + const FakeCommand(command: [ 'xcrun', 'clang', '-arch', 'armv7', ...kDefaultClang, - ] - )); + ]), + ]); final int genSnapshotExitCode = await snapshotter.build( platform: TargetPlatform.ios, @@ -375,9 +380,11 @@ void main() { testWithoutContext('builds iOS armv7 snapshot with obfuscate', () async { final String outputPath = fileSystem.path.join('build', 'foo'); final String assembly = fileSystem.path.join(outputPath, 'snapshot_assembly.S'); - processManager.addCommand(FakeCommand( - command: [ - artifacts.getArtifactPath(Artifact.genSnapshot, platform: TargetPlatform.ios, mode: BuildMode.profile) +'_armv7', + processManager.addCommands([ + FakeCommand(command: [ + artifacts.getArtifactPath(Artifact.genSnapshot, + platform: TargetPlatform.ios, mode: BuildMode.profile) + + '_armv7', '--deterministic', '--snapshot_kind=app-aot-assembly', '--assembly=$assembly', @@ -388,11 +395,10 @@ void main() { '--lazy-async-stacks', '--obfuscate', 'main.dill', - ] - )); - processManager.addCommand(kARMCheckCommand); - processManager.addCommand(const FakeCommand( - command: [ + ]), + kWhichSysctlCommand, + kARMCheckCommand, + const FakeCommand(command: [ 'xcrun', 'cc', '-arch', @@ -403,17 +409,15 @@ void main() { 'build/foo/snapshot_assembly.S', '-o', 'build/foo/snapshot_assembly.o', - ] - )); - processManager.addCommand(const FakeCommand( - command: [ + ]), + const FakeCommand(command: [ 'xcrun', 'clang', '-arch', 'armv7', ...kDefaultClang, - ] - )); + ]), + ]); final int genSnapshotExitCode = await snapshotter.build( platform: TargetPlatform.ios, @@ -434,9 +438,11 @@ void main() { testWithoutContext('builds iOS armv7 snapshot', () async { final String outputPath = fileSystem.path.join('build', 'foo'); - processManager.addCommand(FakeCommand( - command: [ - artifacts.getArtifactPath(Artifact.genSnapshot, platform: TargetPlatform.ios, mode: BuildMode.release) +'_armv7', + processManager.addCommands([ + FakeCommand(command: [ + artifacts.getArtifactPath(Artifact.genSnapshot, + platform: TargetPlatform.ios, mode: BuildMode.release) + + '_armv7', '--deterministic', '--snapshot_kind=app-aot-assembly', '--assembly=${fileSystem.path.join(outputPath, 'snapshot_assembly.S')}', @@ -446,11 +452,10 @@ void main() { '--no-causal-async-stacks', '--lazy-async-stacks', 'main.dill', - ] - )); - processManager.addCommand(kARMCheckCommand); - processManager.addCommand(const FakeCommand( - command: [ + ]), + kWhichSysctlCommand, + kARMCheckCommand, + const FakeCommand(command: [ 'xcrun', 'cc', '-arch', @@ -461,17 +466,15 @@ void main() { 'build/foo/snapshot_assembly.S', '-o', 'build/foo/snapshot_assembly.o', - ] - )); - processManager.addCommand(const FakeCommand( - command: [ + ]), + const FakeCommand(command: [ 'xcrun', 'clang', '-arch', 'armv7', ...kDefaultClang, - ] - )); + ]), + ]); final int genSnapshotExitCode = await snapshotter.build( platform: TargetPlatform.ios, @@ -491,9 +494,11 @@ void main() { testWithoutContext('builds iOS arm64 snapshot', () async { final String outputPath = fileSystem.path.join('build', 'foo'); - processManager.addCommand(FakeCommand( - command: [ - artifacts.getArtifactPath(Artifact.genSnapshot, platform: TargetPlatform.ios, mode: BuildMode.release) +'_arm64', + processManager.addCommands([ + FakeCommand(command: [ + artifacts.getArtifactPath(Artifact.genSnapshot, + platform: TargetPlatform.ios, mode: BuildMode.release) + + '_arm64', '--deterministic', '--snapshot_kind=app-aot-assembly', '--assembly=${fileSystem.path.join(outputPath, 'snapshot_assembly.S')}', @@ -501,11 +506,10 @@ void main() { '--no-causal-async-stacks', '--lazy-async-stacks', 'main.dill', - ] - )); - processManager.addCommand(kARMCheckCommand); - processManager.addCommand(const FakeCommand( - command: [ + ]), + kWhichSysctlCommand, + kARMCheckCommand, + const FakeCommand(command: [ 'xcrun', 'cc', '-arch', @@ -516,17 +520,15 @@ void main() { 'build/foo/snapshot_assembly.S', '-o', 'build/foo/snapshot_assembly.o', - ] - )); - processManager.addCommand(const FakeCommand( - command: [ + ]), + const FakeCommand(command: [ 'xcrun', 'clang', '-arch', 'arm64', ...kDefaultClang, - ] - )); + ]), + ]); final int genSnapshotExitCode = await snapshotter.build( platform: TargetPlatform.ios, diff --git a/packages/flutter_tools/test/general.shard/base/os_test.dart b/packages/flutter_tools/test/general.shard/base/os_test.dart index 4ca073ddb86..db72bc269c8 100644 --- a/packages/flutter_tools/test/general.shard/base/os_test.dart +++ b/packages/flutter_tools/test/general.shard/base/os_test.dart @@ -167,14 +167,22 @@ void main() { }); testWithoutContext('macOS ARM', () async { - fakeProcessManager.addCommand( - const FakeCommand( - command: [ - 'sysctl', - 'hw.optional.arm64', - ], - stdout: 'hw.optional.arm64: 1', - ), + fakeProcessManager.addCommands( + [ + const FakeCommand( + command: [ + 'which', + 'sysctl', + ], + ), + const FakeCommand( + command: [ + 'sysctl', + 'hw.optional.arm64', + ], + stdout: 'hw.optional.arm64: 1', + ), + ], ); final OperatingSystemUtils utils = @@ -183,7 +191,14 @@ void main() { }); testWithoutContext('macOS 11 x86', () async { - fakeProcessManager.addCommand( + fakeProcessManager.addCommands( + [ + const FakeCommand( + command: [ + 'which', + 'sysctl', + ], + ), const FakeCommand( command: [ 'sysctl', @@ -191,15 +206,41 @@ void main() { ], stdout: 'hw.optional.arm64: 0', ), - ); + ], + ); final OperatingSystemUtils utils = createOSUtils(FakePlatform(operatingSystem: 'macos')); expect(utils.hostPlatform, HostPlatform.darwin_x64); }); + testWithoutContext('sysctl not found', () async { + fakeProcessManager.addCommands( + [ + const FakeCommand( + command: [ + 'which', + 'sysctl', + ], + exitCode: 1, + ), + ], + ); + + final OperatingSystemUtils utils = + createOSUtils(FakePlatform(operatingSystem: 'macos')); + expect(() => utils.hostPlatform, throwsToolExit(message: 'sysctl')); + }); + testWithoutContext('macOS 10 x86', () async { - fakeProcessManager.addCommand( + fakeProcessManager.addCommands( + [ + const FakeCommand( + command: [ + 'which', + 'sysctl', + ], + ), const FakeCommand( command: [ 'sysctl', @@ -207,7 +248,8 @@ void main() { ], exitCode: 1, ), - ); + ], + ); final OperatingSystemUtils utils = createOSUtils(FakePlatform(operatingSystem: 'macos')); @@ -237,6 +279,12 @@ void main() { ], stdout: 'build', ), + const FakeCommand( + command: [ + 'which', + 'sysctl', + ], + ), const FakeCommand( command: [ 'sysctl', @@ -274,6 +322,12 @@ void main() { ], stdout: 'build', ), + const FakeCommand( + command: [ + 'which', + 'sysctl', + ], + ), const FakeCommand( command: [ 'sysctl', diff --git a/packages/flutter_tools/test/general.shard/ios/xcodeproj_test.dart b/packages/flutter_tools/test/general.shard/ios/xcodeproj_test.dart index cd702de36f1..75b1bae8f29 100644 --- a/packages/flutter_tools/test/general.shard/ios/xcodeproj_test.dart +++ b/packages/flutter_tools/test/general.shard/ios/xcodeproj_test.dart @@ -50,6 +50,8 @@ void main() { // Work around https://github.com/flutter/flutter/issues/56415. testWithoutContext('xcodebuild versionText returns null when xcodebuild is not installed', () { + when(processManager.runSync(['which', 'sysctl'])) + .thenReturn(ProcessResult(0, 0, '', '')); when(processManager.runSync(['sysctl', 'hw.optional.arm64'])) .thenReturn(ProcessResult(0, 1, '', '')); when(processManager.runSync(['xcrun', 'xcodebuild', '-version'])) @@ -66,6 +68,8 @@ void main() { ); platform.environment = const {}; + when(processManager.runSync(['which', 'sysctl'])) + .thenReturn(ProcessResult(0, 0, '', '')); when(processManager.runSync(['sysctl', 'hw.optional.arm64'])) .thenReturn(ProcessResult(0, 1, '', '')); @@ -79,6 +83,13 @@ void main() { }); }); + const FakeCommand kWhichSysctlCommand = FakeCommand( + command: [ + 'which', + 'sysctl', + ], + ); + const FakeCommand kARMCheckCommand = FakeCommand( command: [ 'sysctl', @@ -115,6 +126,7 @@ void main() { testWithoutContext('xcodebuild versionText returns null when xcodebuild is not fully installed', () { fakeProcessManager.addCommands(const [ + kWhichSysctlCommand, kARMCheckCommand, FakeCommand( command: ['xcrun', 'xcodebuild', '-version'], @@ -131,6 +143,7 @@ void main() { testWithoutContext('xcodebuild versionText returns formatted version text', () { fakeProcessManager.addCommands(const [ + kWhichSysctlCommand, kARMCheckCommand, FakeCommand( command: ['xcrun', 'xcodebuild', '-version'], @@ -144,6 +157,7 @@ void main() { testWithoutContext('xcodebuild versionText handles Xcode version string with unexpected format', () { fakeProcessManager.addCommands(const [ + kWhichSysctlCommand, kARMCheckCommand, FakeCommand( command: ['xcrun', 'xcodebuild', '-version'], @@ -157,6 +171,7 @@ void main() { testWithoutContext('xcodebuild version parts can be parsed', () { fakeProcessManager.addCommands(const [ + kWhichSysctlCommand, kARMCheckCommand, FakeCommand( command: ['xcrun', 'xcodebuild', '-version'], @@ -172,6 +187,7 @@ void main() { testWithoutContext('xcodebuild minor and patch version default to 0', () { fakeProcessManager.addCommands(const [ + kWhichSysctlCommand, kARMCheckCommand, FakeCommand( command: ['xcrun', 'xcodebuild', '-version'], @@ -187,6 +203,7 @@ void main() { testWithoutContext('xcodebuild version parts is null when version has unexpected format', () { fakeProcessManager.addCommands(const [ + kWhichSysctlCommand, kARMCheckCommand, FakeCommand( command: ['xcrun', 'xcodebuild', '-version'], @@ -225,6 +242,7 @@ void main() { testWithoutContext( 'xcodebuild isInstalled is false when Xcode is not fully installed', () { fakeProcessManager.addCommands(const [ + kWhichSysctlCommand, kARMCheckCommand, FakeCommand( command: ['xcrun', 'xcodebuild', '-version'], @@ -241,6 +259,7 @@ void main() { testWithoutContext('xcodebuild isInstalled is false when version has unexpected format', () { fakeProcessManager.addCommands(const [ + kWhichSysctlCommand, kARMCheckCommand, FakeCommand( command: ['xcrun', 'xcodebuild', '-version'], @@ -254,6 +273,7 @@ void main() { testWithoutContext('xcodebuild isInstalled is true when version has expected format', () { fakeProcessManager.addCommands(const [ + kWhichSysctlCommand, kARMCheckCommand, FakeCommand( command: ['xcrun', 'xcodebuild', '-version'], @@ -267,6 +287,7 @@ void main() { testWithoutContext('xcrun runs natively on arm64', () { fakeProcessManager.addCommands(const [ + kWhichSysctlCommand, FakeCommand( command: [ 'sysctl', @@ -288,6 +309,7 @@ void main() { platform.environment = const {}; fakeProcessManager.addCommands(const [ + kWhichSysctlCommand, FakeCommand( command: [ 'sysctl', @@ -317,6 +339,7 @@ void main() { platform.environment = const {}; fakeProcessManager.addCommands(const [ + kWhichSysctlCommand, kARMCheckCommand, FakeCommand( command: [ @@ -340,6 +363,7 @@ void main() { 'FLUTTER_XCODE_ARCHS': 'arm64' }; fakeProcessManager.addCommands([ + kWhichSysctlCommand, kARMCheckCommand, FakeCommand( command: [ @@ -366,6 +390,7 @@ void main() { }; fakeProcessManager.addCommands(const [ + kWhichSysctlCommand, kARMCheckCommand, FakeCommand( command: [ @@ -390,6 +415,7 @@ void main() { testWithoutContext('xcodebuild -list getInfo returns something when xcodebuild -list succeeds', () async { const String workingDirectory = '/'; fakeProcessManager.addCommands(const [ + kWhichSysctlCommand, kARMCheckCommand, FakeCommand( command: ['xcrun', 'xcodebuild', '-list'], @@ -414,6 +440,7 @@ void main() { const String stderr = 'Useful Xcode failure message about missing project.'; fakeProcessManager.addCommands(const [ + kWhichSysctlCommand, kARMCheckCommand, FakeCommand( command: ['xcrun', 'xcodebuild', '-list'], diff --git a/packages/flutter_tools/test/general.shard/macos/cocoapods_test.dart b/packages/flutter_tools/test/general.shard/macos/cocoapods_test.dart index 95325916491..ea88b0f06c0 100644 --- a/packages/flutter_tools/test/general.shard/macos/cocoapods_test.dart +++ b/packages/flutter_tools/test/general.shard/macos/cocoapods_test.dart @@ -460,6 +460,8 @@ Note: as of CocoaPods 1.0, `pod repo update` does not happen on `pod install` by ..createSync() ..writeAsStringSync('Existing Podfile'); + when(mockProcessManager.runSync(['which', 'sysctl'])) + .thenReturn(ProcessResult(0, 0, '', '')); when(mockProcessManager.runSync(['sysctl', 'hw.optional.arm64'])) .thenReturn(ProcessResult(0, 0, 'hw.optional.arm64: 1', '')); @@ -497,6 +499,8 @@ Note: as of CocoaPods 1.0, `pod repo update` does not happen on `pod install` by ..createSync() ..writeAsStringSync('Existing Podfile'); + when(mockProcessManager.runSync(['which', 'sysctl'])) + .thenReturn(ProcessResult(0, 0, '', '')); when(mockProcessManager.runSync(['sysctl', 'hw.optional.arm64'])) .thenReturn(ProcessResult(0, 1, '', '')); diff --git a/packages/flutter_tools/test/general.shard/macos/xcode_test.dart b/packages/flutter_tools/test/general.shard/macos/xcode_test.dart index 0b6d716658b..90d6d10b7a5 100644 --- a/packages/flutter_tools/test/general.shard/macos/xcode_test.dart +++ b/packages/flutter_tools/test/general.shard/macos/xcode_test.dart @@ -65,6 +65,8 @@ void main() { testWithoutContext('eulaSigned is false when clang is not installed', () { when(mockXcodeProjectInterpreter.xcrunCommand()).thenReturn(['xcrun']); + when(processManager.runSync(['which', 'sysctl'])) + .thenReturn(ProcessResult(1, 0, '', '')); when(processManager.runSync(['sysctl', 'hw.optional.arm64'])) .thenReturn(ProcessResult(123, 1, '', '')); when(processManager.runSync(['xcrun', 'clang']))