Unverified Commit 37f4f1f2 authored by Jenn Magder's avatar Jenn Magder Committed by GitHub

Detect ARM ffi CocoaPods error, suggest gem install (#70801)

parent ef474154
...@@ -135,6 +135,7 @@ Future<T> runInContext<T>( ...@@ -135,6 +135,7 @@ Future<T> runInContext<T>(
platform: globals.platform, platform: globals.platform,
xcodeProjectInterpreter: globals.xcodeProjectInterpreter, xcodeProjectInterpreter: globals.xcodeProjectInterpreter,
artifacts: globals.artifacts, artifacts: globals.artifacts,
usage: globals.flutterUsage,
), ),
CocoaPodsValidator: () => CocoaPodsValidator( CocoaPodsValidator: () => CocoaPodsValidator(
globals.cocoaPods, globals.cocoaPods,
......
...@@ -12,6 +12,7 @@ import '../base/error_handling_io.dart'; ...@@ -12,6 +12,7 @@ import '../base/error_handling_io.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/version.dart'; import '../base/version.dart';
...@@ -19,6 +20,7 @@ import '../build_info.dart'; ...@@ -19,6 +20,7 @@ import '../build_info.dart';
import '../cache.dart'; import '../cache.dart';
import '../ios/xcodeproj.dart'; import '../ios/xcodeproj.dart';
import '../project.dart'; import '../project.dart';
import '../reporting/reporting.dart';
const String noCocoaPodsConsequence = ''' const String noCocoaPodsConsequence = '''
CocoaPods is used to retrieve the iOS and macOS platform side's plugin code that responds to your plugin usage on the Dart side. CocoaPods is used to retrieve the iOS and macOS platform side's plugin code that responds to your plugin usage on the Dart side.
...@@ -82,23 +84,33 @@ class CocoaPods { ...@@ -82,23 +84,33 @@ class CocoaPods {
@required Logger logger, @required Logger logger,
@required Platform platform, @required Platform platform,
@required Artifacts artifacts, @required Artifacts artifacts,
@required Usage usage,
}) : _fileSystem = fileSystem, }) : _fileSystem = fileSystem,
_processManager = processManager, _processManager = processManager,
_xcodeProjectInterpreter = xcodeProjectInterpreter, _xcodeProjectInterpreter = xcodeProjectInterpreter,
_logger = logger, _logger = logger,
_platform = platform, _platform = platform,
_artifacts = artifacts, _artifacts = artifacts,
_usage = usage,
_processUtils = ProcessUtils(processManager: processManager, logger: logger), _processUtils = ProcessUtils(processManager: processManager, logger: logger),
_fileSystemUtils = FileSystemUtils(fileSystem: fileSystem, platform: platform); _fileSystemUtils = FileSystemUtils(fileSystem: fileSystem, platform: platform),
_operatingSystemUtils = OperatingSystemUtils(
fileSystem: fileSystem,
logger: logger,
platform: platform,
processManager: processManager,
);
final FileSystem _fileSystem; final FileSystem _fileSystem;
final ProcessManager _processManager; final ProcessManager _processManager;
final FileSystemUtils _fileSystemUtils; final FileSystemUtils _fileSystemUtils;
final ProcessUtils _processUtils; final ProcessUtils _processUtils;
final OperatingSystemUtils _operatingSystemUtils;
final XcodeProjectInterpreter _xcodeProjectInterpreter; final XcodeProjectInterpreter _xcodeProjectInterpreter;
final Logger _logger; final Logger _logger;
final Platform _platform; final Platform _platform;
final Artifacts _artifacts; final Artifacts _artifacts;
final Usage _usage;
Future<String> _versionText; Future<String> _versionText;
...@@ -370,14 +382,31 @@ class CocoaPods { ...@@ -370,14 +382,31 @@ class CocoaPods {
} }
void _diagnosePodInstallFailure(ProcessResult result) { void _diagnosePodInstallFailure(ProcessResult result) {
final dynamic stdout = result.stdout; if (result.stdout is! String) {
if (stdout is String && stdout.contains('out-of-date source repos')) { return;
}
final String stdout = result.stdout as String;
if (stdout.contains('out-of-date source repos')) {
_logger.printError( _logger.printError(
"Error: CocoaPods's specs repository is too out-of-date to satisfy dependencies.\n" "Error: CocoaPods's specs repository is too out-of-date to satisfy dependencies.\n"
'To update the CocoaPods specs, run:\n' 'To update the CocoaPods specs, run:\n'
' pod repo update\n', ' pod repo update\n',
emphasis: true, emphasis: true,
); );
} else if (stdout.contains('Init_ffi_c') &&
stdout.contains('symbol not found') &&
_operatingSystemUtils.hostPlatform == HostPlatform.darwin_arm) {
// https://github.com/flutter/flutter/issues/70796
UsageEvent(
'pod-install-failure',
'arm-ffi',
flutterUsage: _usage,
).send();
_logger.printError(
'Error: To set up CocoaPods for ARM macOS, run:\n'
' arch -x86_64 sudo gem install ffi\n',
emphasis: true,
);
} }
} }
......
...@@ -16,6 +16,7 @@ import 'package:flutter_tools/src/ios/xcodeproj.dart'; ...@@ -16,6 +16,7 @@ import 'package:flutter_tools/src/ios/xcodeproj.dart';
import 'package:flutter_tools/src/macos/cocoapods.dart'; import 'package:flutter_tools/src/macos/cocoapods.dart';
import 'package:flutter_tools/src/plugins.dart'; import 'package:flutter_tools/src/plugins.dart';
import 'package:flutter_tools/src/project.dart'; import 'package:flutter_tools/src/project.dart';
import 'package:flutter_tools/src/reporting/reporting.dart';
import 'package:mockito/mockito.dart'; import 'package:mockito/mockito.dart';
import 'package:process/process.dart'; import 'package:process/process.dart';
...@@ -32,6 +33,7 @@ void main() { ...@@ -32,6 +33,7 @@ void main() {
CocoaPods cocoaPodsUnderTest; CocoaPods cocoaPodsUnderTest;
InvokeProcess resultOfPodVersion; InvokeProcess resultOfPodVersion;
BufferLogger logger; BufferLogger logger;
Usage usage;
void pretendPodVersionFails() { void pretendPodVersionFails() {
resultOfPodVersion = () async => exitsWithError(); resultOfPodVersion = () async => exitsWithError();
...@@ -68,15 +70,17 @@ void main() { ...@@ -68,15 +70,17 @@ void main() {
projectUnderTest = FlutterProject.fromDirectory(fileSystem.directory('project')); projectUnderTest = FlutterProject.fromDirectory(fileSystem.directory('project'));
projectUnderTest.ios.xcodeProject.createSync(recursive: true); projectUnderTest.ios.xcodeProject.createSync(recursive: true);
projectUnderTest.macos.xcodeProject.createSync(recursive: true); projectUnderTest.macos.xcodeProject.createSync(recursive: true);
usage = Usage.test();
cocoaPodsUnderTest = CocoaPods( cocoaPodsUnderTest = CocoaPods(
fileSystem: fileSystem, fileSystem: fileSystem,
processManager: mockProcessManager, processManager: mockProcessManager,
logger: logger, logger: logger,
platform: FakePlatform(), platform: FakePlatform(operatingSystem: 'macos'),
artifacts: Artifacts.test(), artifacts: Artifacts.test(),
xcodeProjectInterpreter: mockXcodeProjectInterpreter, xcodeProjectInterpreter: mockXcodeProjectInterpreter,
usage: usage,
); );
pretendPodVersionIs('1.8.0'); pretendPodVersionIs('1.9.0');
fileSystem.file(fileSystem.path.join( fileSystem.file(fileSystem.path.join(
Cache.flutterRoot, 'packages', 'flutter_tools', 'templates', 'cocoapods', 'Podfile-ios-objc', Cache.flutterRoot, 'packages', 'flutter_tools', 'templates', 'cocoapods', 'Podfile-ios-objc',
)) ))
...@@ -450,6 +454,79 @@ Note: as of CocoaPods 1.0, `pod repo update` does not happen on `pod install` by ...@@ -450,6 +454,79 @@ Note: as of CocoaPods 1.0, `pod repo update` does not happen on `pod install` by
} }
}); });
testWithoutContext('ffi failure on ARM macOS prompts gem install', () async {
pretendPodIsInstalled();
fileSystem.file(fileSystem.path.join('project', 'ios', 'Podfile'))
..createSync()
..writeAsStringSync('Existing Podfile');
when(mockProcessManager.runSync(<String>['sysctl', 'hw.optional.arm64']))
.thenReturn(ProcessResult(0, 0, 'hw.optional.arm64: 1', ''));
when(mockProcessManager.run(
<String>['pod', 'install', '--verbose'],
workingDirectory: 'project/ios',
environment: <String, String>{
'COCOAPODS_DISABLE_STATS': 'true',
'LANG': 'en_US.UTF-8',
},
)).thenAnswer((_) async => exitsWithError(
'LoadError - dlsym(0x7fbbeb6837d0, Init_ffi_c): symbol not found - /Library/Ruby/Gems/2.6.0/gems/ffi-1.13.1/lib/ffi_c.bundle',
));
// Capture Usage.test() events.
final StringBuffer buffer =
await capturedConsolePrint(() => expectToolExitLater(
cocoaPodsUnderTest.processPods(
xcodeProject: projectUnderTest.ios,
buildMode: BuildMode.debug,
),
equals('Error running pod install'),
));
expect(
logger.errorText,
contains('set up CocoaPods for ARM macOS'),
);
expect(buffer.toString(),
contains('event {category: pod-install-failure, action: arm-ffi'));
});
testWithoutContext('ffi failure on x86 macOS does not prompt gem install', () async {
pretendPodIsInstalled();
fileSystem.file(fileSystem.path.join('project', 'ios', 'Podfile'))
..createSync()
..writeAsStringSync('Existing Podfile');
when(mockProcessManager.runSync(<String>['sysctl', 'hw.optional.arm64']))
.thenReturn(ProcessResult(0, 1, '', ''));
when(mockProcessManager.run(
<String>['pod', 'install', '--verbose'],
workingDirectory: 'project/ios',
environment: <String, String>{
'COCOAPODS_DISABLE_STATS': 'true',
'LANG': 'en_US.UTF-8',
},
)).thenAnswer((_) async => exitsWithError(
'LoadError - dlsym(0x7fbbeb6837d0, Init_ffi_c): symbol not found - /Library/Ruby/Gems/2.6.0/gems/ffi-1.13.1/lib/ffi_c.bundle',
));
// Capture Usage.test() events.
final StringBuffer buffer =
await capturedConsolePrint(() => expectToolExitLater(
cocoaPodsUnderTest.processPods(
xcodeProject: projectUnderTest.ios,
buildMode: BuildMode.debug,
),
equals('Error running pod install'),
));
expect(
logger.errorText,
isNot(contains('ARM macOS')),
);
expect(buffer.isEmpty, true);
});
testWithoutContext('run pod install, if Podfile.lock is missing', () async { testWithoutContext('run pod install, if Podfile.lock is missing', () async {
pretendPodIsInstalled(); pretendPodIsInstalled();
projectUnderTest.ios.podfile projectUnderTest.ios.podfile
......
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