From 8a55c941cf858e868826f0048d4464bf71bb68e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Sharma?= <737941+loic-sharma@users.noreply.github.com> Date: Wed, 13 Nov 2024 13:45:07 -0800 Subject: [PATCH] [SwiftPM] Move the logic for SwiftPM enablement to the platform project (#158213) In the future, it will be possible for Swift Package Manager to be enabled on one but not all platforms (see https://github.com/flutter/flutter/issues/151567#issuecomment-2455941279). This moves the `usesSwiftPackageManager` property from the platform-agnostic `Project` to the platform-specific `IosProject` and `MacOSProject`. This will allow the `IosProject` and `MacOSProject` to return different values for `usesSwiftPackageManager` in the future. For now, both of these projects will always return the same value. Part of https://github.com/flutter/flutter/issues/151567 --- .../lib/src/flutter_plugins.dart | 16 +- packages/flutter_tools/lib/src/ios/mac.dart | 8 +- .../lib/src/macos/build_macos.dart | 4 +- .../lib/src/macos/cocoapod_utils.dart | 4 +- .../lib/src/macos/cocoapods.dart | 2 +- .../macos/darwin_dependency_management.dart | 8 +- packages/flutter_tools/lib/src/project.dart | 19 -- .../flutter_tools/lib/src/xcode_project.dart | 37 ++++ .../test/general.shard/ios/mac_test.dart | 15 +- .../macos/cocoapod_utils_test.dart | 25 +-- .../darwin_dependency_management_test.dart | 31 ++-- .../test/general.shard/plugins_test.dart | 71 +++++++- .../test/general.shard/project_test.dart | 98 ---------- .../general.shard/xcode_project_test.dart | 169 +++++++++++++++++- 14 files changed, 341 insertions(+), 166 deletions(-) diff --git a/packages/flutter_tools/lib/src/flutter_plugins.dart b/packages/flutter_tools/lib/src/flutter_plugins.dart index 471dba42f9..fcd45e26d8 100644 --- a/packages/flutter_tools/lib/src/flutter_plugins.dart +++ b/packages/flutter_tools/lib/src/flutter_plugins.dart @@ -1071,12 +1071,22 @@ Future refreshPluginsList( // Write the legacy plugin files to avoid breaking existing apps. final bool legacyChanged = useImplicitPubspecResolution && _writeFlutterPluginsListLegacy(project, plugins); - final bool swiftPackageManagerEnabled = !forceCocoaPodsOnly && project.usesSwiftPackageManager; + bool swiftPackageManagerEnabledIos = false; + bool swiftPackageManagerEnabledMacos = false; + if (!forceCocoaPodsOnly) { + if (iosPlatform) { + swiftPackageManagerEnabledIos = project.ios.usesSwiftPackageManager; + } + if (macOSPlatform) { + swiftPackageManagerEnabledMacos = project.macos.usesSwiftPackageManager; + } + } + final bool changed = _writeFlutterPluginsList( project, plugins, - swiftPackageManagerEnabledIos: swiftPackageManagerEnabled, - swiftPackageManagerEnabledMacos: swiftPackageManagerEnabled, + swiftPackageManagerEnabledIos: swiftPackageManagerEnabledIos, + swiftPackageManagerEnabledMacos: swiftPackageManagerEnabledMacos, ); if (changed || legacyChanged || forceCocoaPodsOnly) { createPluginSymlinks(project, force: true); diff --git a/packages/flutter_tools/lib/src/ios/mac.dart b/packages/flutter_tools/lib/src/ios/mac.dart index 523555533f..06be3d76ff 100644 --- a/packages/flutter_tools/lib/src/ios/mac.dart +++ b/packages/flutter_tools/lib/src/ios/mac.dart @@ -166,7 +166,7 @@ Future buildXcodeProject({ RemoveBitcodeMigration(app.project, globals.logger), XcodeThinBinaryBuildPhaseInputPathsMigration(app.project, globals.logger), UIApplicationMainDeprecationMigration(app.project, globals.logger), - if (project.usesSwiftPackageManager && app.project.flutterPluginSwiftPackageManifest.existsSync()) + if (app.project.usesSwiftPackageManager && app.project.flutterPluginSwiftPackageManifest.existsSync()) SwiftPackageManagerIntegrationMigration( app.project, SupportedPlatform.ios, @@ -267,7 +267,7 @@ Future buildXcodeProject({ targetOverride: targetOverride, buildInfo: buildInfo, ); - if (project.usesSwiftPackageManager) { + if (app.project.usesSwiftPackageManager) { final String? iosDeploymentTarget = buildSettings['IPHONEOS_DEPLOYMENT_TARGET']; if (iosDeploymentTarget != null) { SwiftPackageManager.updateMinimumDeployment( @@ -888,7 +888,7 @@ Future _handleIssues( logger.printError(missingPlatformInstructions(missingPlatform), emphasis: true); } else if (duplicateModules.isNotEmpty) { final bool usesCocoapods = xcodeProject.podfile.existsSync(); - final bool usesSwiftPackageManager = project.usesSwiftPackageManager; + final bool usesSwiftPackageManager = xcodeProject.usesSwiftPackageManager; if (usesCocoapods && usesSwiftPackageManager) { logger.printError( 'Your project uses both CocoaPods and Swift Package Manager, which can ' @@ -906,7 +906,7 @@ Future _handleIssues( } } else if (missingModules.isNotEmpty) { final bool usesCocoapods = xcodeProject.podfile.existsSync(); - final bool usesSwiftPackageManager = project.usesSwiftPackageManager; + final bool usesSwiftPackageManager = xcodeProject.usesSwiftPackageManager; if (usesCocoapods && !usesSwiftPackageManager) { final List swiftPackageOnlyPlugins = []; for (final String module in missingModules) { diff --git a/packages/flutter_tools/lib/src/macos/build_macos.dart b/packages/flutter_tools/lib/src/macos/build_macos.dart index 5b8b186a23..f268a928cc 100644 --- a/packages/flutter_tools/lib/src/macos/build_macos.dart +++ b/packages/flutter_tools/lib/src/macos/build_macos.dart @@ -91,7 +91,7 @@ Future buildMacOS({ FlutterApplicationMigration(flutterProject.macos, globals.logger), NSApplicationMainDeprecationMigration(flutterProject.macos, globals.logger), SecureRestorableStateMigration(flutterProject.macos, globals.logger), - if (flutterProject.usesSwiftPackageManager && flutterProject.macos.flutterPluginSwiftPackageManifest.existsSync()) + if (flutterProject.macos.usesSwiftPackageManager && flutterProject.macos.flutterPluginSwiftPackageManifest.existsSync()) SwiftPackageManagerIntegrationMigration( flutterProject.macos, SupportedPlatform.macos, @@ -145,7 +145,7 @@ Future buildMacOS({ useMacOSConfig: true, ); - if (flutterProject.usesSwiftPackageManager) { + if (flutterProject.macos.usesSwiftPackageManager) { final String? macOSDeploymentTarget = buildSettings['MACOSX_DEPLOYMENT_TARGET']; if (macOSDeploymentTarget != null) { SwiftPackageManager.updateMinimumDeployment( diff --git a/packages/flutter_tools/lib/src/macos/cocoapod_utils.dart b/packages/flutter_tools/lib/src/macos/cocoapod_utils.dart index fc78aee991..007d6206c8 100644 --- a/packages/flutter_tools/lib/src/macos/cocoapod_utils.dart +++ b/packages/flutter_tools/lib/src/macos/cocoapod_utils.dart @@ -22,7 +22,7 @@ Future processPodsIfNeeded( // When using Swift Package Manager, the Podfile may not exist so if there // isn't a Podfile, skip processing pods. - if (project.usesSwiftPackageManager && + if (xcodeProject.usesSwiftPackageManager && !xcodeProject.podfile.existsSync() && !forceCocoaPodsOnly) { return; @@ -50,7 +50,7 @@ Future processPodsIfNeeded( // If forcing the use of only CocoaPods, but the project is using Swift // Package Manager, print a warning that CocoaPods will be used. - if (forceCocoaPodsOnly && project.usesSwiftPackageManager) { + if (forceCocoaPodsOnly && xcodeProject.usesSwiftPackageManager) { globals.logger.printWarning( 'Swift Package Manager does not yet support this command. ' 'CocoaPods will be used instead.'); diff --git a/packages/flutter_tools/lib/src/macos/cocoapods.dart b/packages/flutter_tools/lib/src/macos/cocoapods.dart index 1205983bcf..8bfd56bbac 100644 --- a/packages/flutter_tools/lib/src/macos/cocoapods.dart +++ b/packages/flutter_tools/lib/src/macos/cocoapods.dart @@ -167,7 +167,7 @@ class CocoaPods { }) async { if (!xcodeProject.podfile.existsSync()) { // Swift Package Manager doesn't need Podfile, so don't error. - if (xcodeProject.parent.usesSwiftPackageManager) { + if (xcodeProject.usesSwiftPackageManager) { return false; } throwToolExit('Podfile missing'); diff --git a/packages/flutter_tools/lib/src/macos/darwin_dependency_management.dart b/packages/flutter_tools/lib/src/macos/darwin_dependency_management.dart index c681308743..a615726aa1 100644 --- a/packages/flutter_tools/lib/src/macos/darwin_dependency_management.dart +++ b/packages/flutter_tools/lib/src/macos/darwin_dependency_management.dart @@ -60,7 +60,7 @@ class DarwinDependencyManagement { final XcodeBasedProject xcodeProject = platform == SupportedPlatform.ios ? _project.ios : _project.macos; - if (_project.usesSwiftPackageManager) { + if (xcodeProject.usesSwiftPackageManager) { await _swiftPackageManager.generatePluginsSwiftPackage( _plugins, platform, @@ -89,7 +89,7 @@ class DarwinDependencyManagement { ); final bool useCocoapods; - if (_project.usesSwiftPackageManager) { + if (xcodeProject.usesSwiftPackageManager) { useCocoapods = swiftPackageCount < totalCount; } else { // When Swift Package Manager is not enabled, set up Podfile if plugins @@ -161,7 +161,7 @@ class DarwinDependencyManagement { // If not using Swift Package Manager and plugin does not have podspec // but does have a Package.swift, throw an error. Otherwise, it'll error // when it builds. - if (!_project.usesSwiftPackageManager && + if (!xcodeProject.usesSwiftPackageManager && !cocoaPodsCompatible && swiftPackageManagerCompatible) { throwToolExit( @@ -175,7 +175,7 @@ class DarwinDependencyManagement { // Only show warnings to remove CocoaPods if the project is using Swift // Package Manager, has already been migrated to have SPM integration, and // all plugins are Swift Packages. - if (_project.usesSwiftPackageManager && + if (xcodeProject.usesSwiftPackageManager && xcodeProject.flutterPluginSwiftPackageInProjectSettings && pluginCount == swiftPackageCount && swiftPackageCount != 0) { diff --git a/packages/flutter_tools/lib/src/project.dart b/packages/flutter_tools/lib/src/project.dart index d1462587a3..12e1076083 100644 --- a/packages/flutter_tools/lib/src/project.dart +++ b/packages/flutter_tools/lib/src/project.dart @@ -21,7 +21,6 @@ import 'features.dart'; import 'flutter_manifest.dart'; import 'flutter_plugins.dart'; import 'globals.dart' as globals; -import 'macos/xcode.dart'; import 'platform_plugins.dart'; import 'project_validator_result.dart'; import 'template.dart'; @@ -262,24 +261,6 @@ class FlutterProject { /// True if this project has an example application. bool get hasExampleApp => _exampleDirectory(directory).existsSync(); - /// True if this project doesn't have Swift Package Manager disabled in the - /// pubspec, has either an iOS or macOS platform implementation, is not a - /// module project, Xcode is 15 or greater, and the Swift Package Manager - /// feature is enabled. - bool get usesSwiftPackageManager { - if (!manifest.disabledSwiftPackageManager && - (ios.existsSync() || macos.existsSync()) && - !isModule) { - final Xcode? xcode = globals.xcode; - final Version? xcodeVersion = xcode?.currentVersion; - if (xcodeVersion == null || xcodeVersion.major < 15) { - return false; - } - return featureFlags.isSwiftPackageManagerEnabled; - } - return false; - } - /// Returns a list of platform names that are supported by the project. List getSupportedPlatforms({bool includeRoot = false}) { return [ diff --git a/packages/flutter_tools/lib/src/xcode_project.dart b/packages/flutter_tools/lib/src/xcode_project.dart index eaa3237031..f411a01524 100644 --- a/packages/flutter_tools/lib/src/xcode_project.dart +++ b/packages/flutter_tools/lib/src/xcode_project.dart @@ -5,15 +5,18 @@ import 'base/error_handling_io.dart'; import 'base/file_system.dart'; import 'base/utils.dart'; +import 'base/version.dart'; import 'build_info.dart'; import 'bundle.dart' as bundle; import 'convert.dart'; +import 'features.dart'; import 'flutter_plugins.dart'; import 'globals.dart' as globals; import 'ios/code_signing.dart'; import 'ios/plist_parser.dart'; import 'ios/xcode_build_settings.dart' as xcode; import 'ios/xcodeproj.dart'; +import 'macos/xcode.dart'; import 'platform_plugins.dart'; import 'project.dart'; import 'template.dart'; @@ -147,6 +150,40 @@ abstract class XcodeBasedProject extends FlutterProjectPlatform { .contains('FlutterGeneratedPluginSwiftPackage'); } + /// True if this project doesn't have Swift Package Manager disabled in the + /// pubspec, has either an iOS or macOS platform implementation, is not a + /// module project, Xcode is 15 or greater, and the Swift Package Manager + /// feature is enabled. + bool get usesSwiftPackageManager { + if (!featureFlags.isSwiftPackageManagerEnabled) { + return false; + } + + // The project can disable Swift Package Manager in its pubspec.yaml. + if (parent.manifest.disabledSwiftPackageManager) { + return false; + } + + // TODO(loic-sharma): Support Swift Package Manager in add-to-app modules. + // https://github.com/flutter/flutter/issues/146957 + if (parent.isModule) { + return false; + } + + if (!existsSync()) { + return false; + } + + // Swift Package Manager requires Xcode 15 or greater. + final Xcode? xcode = globals.xcode; + final Version? xcodeVersion = xcode?.currentVersion; + if (xcodeVersion == null || xcodeVersion.major < 15) { + return false; + } + + return true; + } + Future projectInfo() async { final XcodeProjectInterpreter? xcodeProjectInterpreter = globals.xcodeProjectInterpreter; if (!xcodeProject.existsSync() || xcodeProjectInterpreter == null || !xcodeProjectInterpreter.isInstalled) { diff --git a/packages/flutter_tools/test/general.shard/ios/mac_test.dart b/packages/flutter_tools/test/general.shard/ios/mac_test.dart index 0bb3476092..a6cef39e78 100644 --- a/packages/flutter_tools/test/general.shard/ios/mac_test.dart +++ b/packages/flutter_tools/test/general.shard/ios/mac_test.dart @@ -816,6 +816,7 @@ void createFakePlugins( class FakeIosProject extends Fake implements IosProject { FakeIosProject({ required MemoryFileSystem fileSystem, + this.usesSwiftPackageManager = false, }) : hostAppRoot = fileSystem.directory('app_name').childDirectory('ios'); @override @@ -832,6 +833,9 @@ class FakeIosProject extends Fake implements IosProject { @override File get podfile => hostAppRoot.childFile('Podfile'); + + @override + final bool usesSwiftPackageManager; } class FakeFlutterProject extends Fake implements FlutterProject { @@ -841,7 +845,8 @@ class FakeFlutterProject extends Fake implements FlutterProject { this.isModule = false, }); - MemoryFileSystem fileSystem; + final MemoryFileSystem fileSystem; + final bool usesSwiftPackageManager; @override late final Directory directory = fileSystem.directory('app_name'); @@ -856,10 +861,10 @@ class FakeFlutterProject extends Fake implements FlutterProject { File get flutterPluginsDependenciesFile => directory.childFile('.flutter-plugins-dependencies'); @override - late final IosProject ios = FakeIosProject(fileSystem: fileSystem); - - @override - final bool usesSwiftPackageManager; + late final IosProject ios = FakeIosProject( + fileSystem: fileSystem, + usesSwiftPackageManager: usesSwiftPackageManager, + ); @override final bool isModule; diff --git a/packages/flutter_tools/test/general.shard/macos/cocoapod_utils_test.dart b/packages/flutter_tools/test/general.shard/macos/cocoapod_utils_test.dart index fde5c90e4a..aa07bd3b1c 100644 --- a/packages/flutter_tools/test/general.shard/macos/cocoapod_utils_test.dart +++ b/packages/flutter_tools/test/general.shard/macos/cocoapod_utils_test.dart @@ -177,7 +177,7 @@ void main() { 'plugin_one', 'plugin_two' ]); - flutterProject.usesSwiftPackageManager = true; + flutterProject.ios.usesSwiftPackageManager = true; flutterProject.ios.podfile.createSync(recursive: true); await processPodsIfNeeded( @@ -200,7 +200,7 @@ void main() { 'plugin_one', 'plugin_two' ]); - flutterProject.usesSwiftPackageManager = true; + flutterProject.ios.usesSwiftPackageManager = true; await processPodsIfNeeded( flutterProject.ios, @@ -221,7 +221,7 @@ void main() { 'plugin_one', 'plugin_two' ]); - flutterProject.usesSwiftPackageManager = true; + flutterProject.ios.usesSwiftPackageManager = true; flutterProject.ios.flutterPluginSwiftPackageManifest.createSync(recursive: true); await processPodsIfNeeded( @@ -336,7 +336,7 @@ void main() { 'plugin_one', 'plugin_two' ]); - flutterProject.usesSwiftPackageManager = true; + flutterProject.macos.usesSwiftPackageManager = true; flutterProject.macos.podfile.createSync(recursive: true); await processPodsIfNeeded( @@ -359,7 +359,7 @@ void main() { 'plugin_one', 'plugin_two' ]); - flutterProject.usesSwiftPackageManager = true; + flutterProject.macos.usesSwiftPackageManager = true; await processPodsIfNeeded( flutterProject.macos, @@ -380,7 +380,7 @@ void main() { 'plugin_one', 'plugin_two' ]); - flutterProject.usesSwiftPackageManager = true; + flutterProject.macos.usesSwiftPackageManager = true; flutterProject.macos.flutterPluginSwiftPackageManifest.createSync(recursive: true); await processPodsIfNeeded( @@ -420,9 +420,6 @@ class FakeFlutterProject extends Fake implements FlutterProject { @override bool isModule = false; - @override - bool usesSwiftPackageManager = false; - @override late FlutterManifest manifest; @@ -436,10 +433,10 @@ class FakeFlutterProject extends Fake implements FlutterProject { late File flutterPluginsDependenciesFile; @override - late IosProject ios; + late FakeIosProject ios; @override - late MacOSProject macos; + late FakeMacOSProject macos; @override late AndroidProject android; @@ -489,6 +486,9 @@ class FakeMacOSProject extends Fake implements MacOSProject { .childDirectory('Packages') .childDirectory('FlutterGeneratedPluginSwiftPackage') .childFile('Package.swift'); + + @override + bool usesSwiftPackageManager = false; } class FakeIosProject extends Fake implements IosProject { @@ -527,6 +527,9 @@ class FakeIosProject extends Fake implements IosProject { .childDirectory('Packages') .childDirectory('FlutterGeneratedPluginSwiftPackage') .childFile('Package.swift'); + + @override + bool usesSwiftPackageManager = false; } class FakeAndroidProject extends Fake implements AndroidProject { diff --git a/packages/flutter_tools/test/general.shard/macos/darwin_dependency_management_test.dart b/packages/flutter_tools/test/general.shard/macos/darwin_dependency_management_test.dart index ba539c3e1e..c4acf59948 100644 --- a/packages/flutter_tools/test/general.shard/macos/darwin_dependency_management_test.dart +++ b/packages/flutter_tools/test/general.shard/macos/darwin_dependency_management_test.dart @@ -505,6 +505,7 @@ void main() { class FakeIosProject extends Fake implements IosProject { FakeIosProject({ required MemoryFileSystem fileSystem, + required this.usesSwiftPackageManager, }) : hostAppRoot = fileSystem.directory('app_name').childDirectory('ios'); @override @@ -530,6 +531,9 @@ class FakeIosProject extends Fake implements IosProject { .contains('FlutterGeneratedPluginSwiftPackage'); } + @override + bool usesSwiftPackageManager; + @override Directory get managedDirectory => hostAppRoot.childDirectory('Flutter'); @@ -540,6 +544,7 @@ class FakeIosProject extends Fake implements IosProject { class FakeMacOSProject extends Fake implements MacOSProject { FakeMacOSProject({ required MemoryFileSystem fileSystem, + required this.usesSwiftPackageManager, }) : hostAppRoot = fileSystem.directory('app_name').childDirectory('macos'); @override @@ -565,6 +570,9 @@ class FakeMacOSProject extends Fake implements MacOSProject { .contains('FlutterGeneratedPluginSwiftPackage'); } + @override + bool usesSwiftPackageManager; + @override Directory get managedDirectory => hostAppRoot.childDirectory('Flutter'); @@ -579,18 +587,21 @@ class FakeFlutterProject extends Fake implements FlutterProject { this.isModule = false, }); - MemoryFileSystem fileSystem; - - - @override - late final IosProject ios = FakeIosProject(fileSystem: fileSystem); - - @override - late final MacOSProject macos = FakeMacOSProject(fileSystem: fileSystem); - - @override + final MemoryFileSystem fileSystem; final bool usesSwiftPackageManager; + @override + late final FakeIosProject ios = FakeIosProject( + fileSystem: fileSystem, + usesSwiftPackageManager: usesSwiftPackageManager, + ); + + @override + late final FakeMacOSProject macos = FakeMacOSProject( + fileSystem: fileSystem, + usesSwiftPackageManager: usesSwiftPackageManager, + ); + @override final bool isModule; } diff --git a/packages/flutter_tools/test/general.shard/plugins_test.dart b/packages/flutter_tools/test/general.shard/plugins_test.dart index 08c07c4fa7..8c38156dba 100644 --- a/packages/flutter_tools/test/general.shard/plugins_test.dart +++ b/packages/flutter_tools/test/general.shard/plugins_test.dart @@ -667,9 +667,15 @@ dependencies: final DateTime dateCreated = DateTime(1970); systemClock.currentTime = dateCreated; - flutterProject.usesSwiftPackageManager = true; + iosProject.usesSwiftPackageManager = true; + macosProject.usesSwiftPackageManager = true; - await refreshPluginsList(flutterProject, useImplicitPubspecResolution: true); + await refreshPluginsList( + flutterProject, + iosPlatform: true, + macOSPlatform: true, + useImplicitPubspecResolution: true, + ); expect(flutterProject.flutterPluginsDependenciesFile.existsSync(), true); final String pluginsString = flutterProject.flutterPluginsDependenciesFile @@ -711,7 +717,8 @@ dependencies: final DateTime dateCreated = DateTime(1970); systemClock.currentTime = dateCreated; - flutterProject.usesSwiftPackageManager = true; + iosProject.usesSwiftPackageManager = true; + macosProject.usesSwiftPackageManager = true; await refreshPluginsList(flutterProject, forceCocoaPodsOnly: true, useImplicitPubspecResolution: true); @@ -735,6 +742,55 @@ dependencies: FlutterVersion: () => flutterVersion, }); + testUsingContext( + '.flutter-plugins-dependencies can have different swift_package_manager_enabled values for iOS and macoS', () async { + createPlugin( + name: 'plugin-a', + platforms: const { + // Native-only; should include native build. + 'android': _PluginPlatformInfo(pluginClass: 'Foo', androidPackage: 'bar.foo'), + // Hybrid native and Dart; should include native build. + 'ios': _PluginPlatformInfo(pluginClass: 'Foo', dartPluginClass: 'Bar', sharedDarwinSource: true), + // Web; should not have the native build key at all since it doesn't apply. + 'web': _PluginPlatformInfo(pluginClass: 'Foo', fileName: 'lib/foo.dart'), + // Dart-only; should not include native build. + 'windows': _PluginPlatformInfo(dartPluginClass: 'Foo'), + }); + iosProject.testExists = true; + + final DateTime dateCreated = DateTime(1970); + systemClock.currentTime = dateCreated; + + iosProject.usesSwiftPackageManager = true; + macosProject.usesSwiftPackageManager = false; + + await refreshPluginsList( + flutterProject, + iosPlatform: true, + macOSPlatform: true, + useImplicitPubspecResolution: true, + ); + + expect(flutterProject.flutterPluginsDependenciesFile.existsSync(), true); + final String pluginsString = flutterProject.flutterPluginsDependenciesFile + .readAsStringSync(); + final Map jsonContent = json.decode(pluginsString) as Map; + + final Map expectedSwiftPackageManagerEnabled = { + 'ios': true, + 'macos': false, + }; + expect( + jsonContent['swift_package_manager_enabled'], + expectedSwiftPackageManagerEnabled, + ); + }, overrides: { + FileSystem: () => fs, + ProcessManager: () => FakeProcessManager.any(), + SystemClock: () => systemClock, + FlutterVersion: () => flutterVersion, + }); + testUsingContext('Changes to the plugin list invalidates the Cocoapod lockfiles', () async { simulatePodInstallRun(iosProject); simulatePodInstallRun(macosProject); @@ -2108,9 +2164,6 @@ class FakeFlutterProject extends Fake implements FlutterProject { @override bool isModule = false; - @override - bool usesSwiftPackageManager = false; - @override late FlutterManifest manifest; @@ -2154,6 +2207,9 @@ class FakeMacOSProject extends Fake implements MacOSProject { @override late File podManifestLock; + @override + bool usesSwiftPackageManager = false; + @override late Directory managedDirectory; @@ -2187,6 +2243,9 @@ class FakeIosProject extends Fake implements IosProject { @override late File podManifestLock; + + @override + bool usesSwiftPackageManager = false; } class FakeAndroidProject extends Fake implements AndroidProject { diff --git a/packages/flutter_tools/test/general.shard/project_test.dart b/packages/flutter_tools/test/general.shard/project_test.dart index 7203f9d847..6c1e8f15ae 100644 --- a/packages/flutter_tools/test/general.shard/project_test.dart +++ b/packages/flutter_tools/test/general.shard/project_test.dart @@ -375,91 +375,6 @@ void main() { }); }); - group('usesSwiftPackageManager', () { - testUsingContext('is true when iOS project exists', () async { - final MemoryFileSystem fs = MemoryFileSystem.test(); - final Directory projectDirectory = fs.directory('path'); - projectDirectory.childDirectory('ios').createSync(recursive: true); - final FlutterManifest manifest = FakeFlutterManifest(); - final FlutterProject project = FlutterProject(projectDirectory, manifest, manifest); - expect(project.usesSwiftPackageManager, isTrue); - }, overrides: { - FeatureFlags: () => TestFeatureFlags(isSwiftPackageManagerEnabled: true), - XcodeProjectInterpreter: () => FakeXcodeProjectInterpreter(version: Version(15, 0, 0)), - }); - - testUsingContext('is true when macOS project exists', () async { - final MemoryFileSystem fs = MemoryFileSystem.test(); - final Directory projectDirectory = fs.directory('path'); - projectDirectory.childDirectory('macos').createSync(recursive: true); - final FlutterManifest manifest = FakeFlutterManifest(); - final FlutterProject project = FlutterProject(projectDirectory, manifest, manifest); - expect(project.usesSwiftPackageManager, isTrue); - }, overrides: { - FeatureFlags: () => TestFeatureFlags(isSwiftPackageManagerEnabled: true), - XcodeProjectInterpreter: () => FakeXcodeProjectInterpreter(version: Version(15, 0, 0)), - }); - - testUsingContext('is false when disabled via manifest', () async { - final MemoryFileSystem fs = MemoryFileSystem.test(); - final Directory projectDirectory = fs.directory('path'); - projectDirectory.childDirectory('ios').createSync(recursive: true); - final FlutterManifest manifest = FakeFlutterManifest(disabledSwiftPackageManager: true); - final FlutterProject project = FlutterProject(projectDirectory, manifest, manifest); - expect(project.usesSwiftPackageManager, isFalse); - }, overrides: { - FeatureFlags: () => TestFeatureFlags(isSwiftPackageManagerEnabled: true), - XcodeProjectInterpreter: () => FakeXcodeProjectInterpreter(version: Version(15, 0, 0)), - }); - - testUsingContext("is false when iOS and macOS project don't exist", () async { - final MemoryFileSystem fs = MemoryFileSystem.test(); - final Directory projectDirectory = fs.directory('path'); - final FlutterManifest manifest = FakeFlutterManifest(); - final FlutterProject project = FlutterProject(projectDirectory, manifest, manifest); - expect(project.usesSwiftPackageManager, isFalse); - }, overrides: { - FeatureFlags: () => TestFeatureFlags(isSwiftPackageManagerEnabled: true), - XcodeProjectInterpreter: () => FakeXcodeProjectInterpreter(version: Version(15, 0, 0)), - }); - - testUsingContext('is false when Xcode is less than 15', () async { - final MemoryFileSystem fs = MemoryFileSystem.test(); - final Directory projectDirectory = fs.directory('path'); - projectDirectory.childDirectory('ios').createSync(recursive: true); - final FlutterManifest manifest = FakeFlutterManifest(); - final FlutterProject project = FlutterProject(projectDirectory, manifest, manifest); - expect(project.usesSwiftPackageManager, isFalse); - }, overrides: { - FeatureFlags: () => TestFeatureFlags(isSwiftPackageManagerEnabled: true), - XcodeProjectInterpreter: () => FakeXcodeProjectInterpreter(version: Version(14, 0, 0)), - }); - - testUsingContext('is false when Swift Package Manager feature is not enabled', () async { - final MemoryFileSystem fs = MemoryFileSystem.test(); - final Directory projectDirectory = fs.directory('path'); - projectDirectory.childDirectory('ios').createSync(recursive: true); - final FlutterManifest manifest = FakeFlutterManifest(); - final FlutterProject project = FlutterProject(projectDirectory, manifest, manifest); - expect(project.usesSwiftPackageManager, isFalse); - }, overrides: { - FeatureFlags: () => TestFeatureFlags(), - XcodeProjectInterpreter: () => FakeXcodeProjectInterpreter(version: Version(15, 0, 0)), - }); - - testUsingContext('is false when project is a module', () async { - final MemoryFileSystem fs = MemoryFileSystem.test(); - final Directory projectDirectory = fs.directory('path'); - projectDirectory.childDirectory('ios').createSync(recursive: true); - final FlutterManifest manifest = FakeFlutterManifest(isModule: true); - final FlutterProject project = FlutterProject(projectDirectory, manifest, manifest); - expect(project.usesSwiftPackageManager, isFalse); - }, overrides: { - FeatureFlags: () => TestFeatureFlags(isSwiftPackageManagerEnabled: true), - XcodeProjectInterpreter: () => FakeXcodeProjectInterpreter(version: Version(15, 0, 0)), - }); - }); - group('java gradle agp compatibility', () { Future configureGradleAgpForTest({ required String gradleV, @@ -1896,16 +1811,3 @@ class FakeAndroidSdkWithDir extends Fake implements AndroidSdk { @override Directory get directory => _directory; } - -class FakeFlutterManifest extends Fake implements FlutterManifest { - FakeFlutterManifest({ - this.disabledSwiftPackageManager = false, - this.isModule = false, - }); - - @override - bool disabledSwiftPackageManager; - - @override - bool isModule; -} diff --git a/packages/flutter_tools/test/general.shard/xcode_project_test.dart b/packages/flutter_tools/test/general.shard/xcode_project_test.dart index dfaf6096cc..fc8033fac8 100644 --- a/packages/flutter_tools/test/general.shard/xcode_project_test.dart +++ b/packages/flutter_tools/test/general.shard/xcode_project_test.dart @@ -6,13 +6,16 @@ import 'package:file/file.dart'; import 'package:file/memory.dart'; import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/logger.dart'; +import 'package:flutter_tools/src/base/version.dart'; +import 'package:flutter_tools/src/features.dart'; +import 'package:flutter_tools/src/flutter_manifest.dart'; import 'package:flutter_tools/src/ios/xcodeproj.dart'; import 'package:flutter_tools/src/project.dart'; import 'package:test/fake.dart'; import '../src/common.dart'; import '../src/context.dart'; - +import '../src/fakes.dart'; void main() { group('IosProject', () { @@ -127,6 +130,79 @@ void main() { XcodeProjectInterpreter: () => FakeXcodeProjectInterpreter(), }); }); + + group('usesSwiftPackageManager', () { + testUsingContext('is true when iOS project exists', () async { + final MemoryFileSystem fs = MemoryFileSystem.test(); + final Directory projectDirectory = fs.directory('path'); + projectDirectory.childDirectory('ios').createSync(recursive: true); + final FlutterManifest manifest = FakeFlutterManifest(); + final FlutterProject project = FlutterProject(projectDirectory, manifest, manifest); + expect(project.ios.usesSwiftPackageManager, isTrue); + }, overrides: { + FeatureFlags: () => TestFeatureFlags(isSwiftPackageManagerEnabled: true), + XcodeProjectInterpreter: () => FakeXcodeProjectInterpreter(version: Version(15, 0, 0)), + }); + + testUsingContext("is false when iOS project doesn't exist", () async { + final MemoryFileSystem fs = MemoryFileSystem.test(); + final Directory projectDirectory = fs.directory('path'); + final FlutterManifest manifest = FakeFlutterManifest(); + final FlutterProject project = FlutterProject(projectDirectory, manifest, manifest); + expect(project.ios.usesSwiftPackageManager, isFalse); + }, overrides: { + FeatureFlags: () => TestFeatureFlags(isSwiftPackageManagerEnabled: true), + XcodeProjectInterpreter: () => FakeXcodeProjectInterpreter(version: Version(15, 0, 0)), + }); + + testUsingContext('is false when disabled via manifest', () async { + final MemoryFileSystem fs = MemoryFileSystem.test(); + final Directory projectDirectory = fs.directory('path'); + projectDirectory.childDirectory('ios').createSync(recursive: true); + final FlutterManifest manifest = FakeFlutterManifest(disabledSwiftPackageManager: true); + final FlutterProject project = FlutterProject(projectDirectory, manifest, manifest); + expect(project.ios.usesSwiftPackageManager, isFalse); + }, overrides: { + FeatureFlags: () => TestFeatureFlags(isSwiftPackageManagerEnabled: true), + XcodeProjectInterpreter: () => FakeXcodeProjectInterpreter(version: Version(15, 0, 0)), + }); + + testUsingContext('is false when Xcode is less than 15', () async { + final MemoryFileSystem fs = MemoryFileSystem.test(); + final Directory projectDirectory = fs.directory('path'); + projectDirectory.childDirectory('ios').createSync(recursive: true); + final FlutterManifest manifest = FakeFlutterManifest(); + final FlutterProject project = FlutterProject(projectDirectory, manifest, manifest); + expect(project.ios.usesSwiftPackageManager, isFalse); + }, overrides: { + FeatureFlags: () => TestFeatureFlags(isSwiftPackageManagerEnabled: true), + XcodeProjectInterpreter: () => FakeXcodeProjectInterpreter(version: Version(14, 0, 0)), + }); + + testUsingContext('is false when Swift Package Manager feature is not enabled', () async { + final MemoryFileSystem fs = MemoryFileSystem.test(); + final Directory projectDirectory = fs.directory('path'); + projectDirectory.childDirectory('ios').createSync(recursive: true); + final FlutterManifest manifest = FakeFlutterManifest(); + final FlutterProject project = FlutterProject(projectDirectory, manifest, manifest); + expect(project.ios.usesSwiftPackageManager, isFalse); + }, overrides: { + FeatureFlags: () => TestFeatureFlags(), + XcodeProjectInterpreter: () => FakeXcodeProjectInterpreter(version: Version(15, 0, 0)), + }); + + testUsingContext('is false when project is a module', () async { + final MemoryFileSystem fs = MemoryFileSystem.test(); + final Directory projectDirectory = fs.directory('path'); + projectDirectory.childDirectory('ios').createSync(recursive: true); + final FlutterManifest manifest = FakeFlutterManifest(isModule: true); + final FlutterProject project = FlutterProject(projectDirectory, manifest, manifest); + expect(project.ios.usesSwiftPackageManager, isFalse); + }, overrides: { + FeatureFlags: () => TestFeatureFlags(isSwiftPackageManagerEnabled: true), + XcodeProjectInterpreter: () => FakeXcodeProjectInterpreter(version: Version(15, 0, 0)), + }); + }); }); group('MacOSProject', () { @@ -175,6 +251,80 @@ void main() { 'app_name/macos/Flutter/Flutter-Debug.xcconfig', ); }); + + group('usesSwiftPackageManager', () { + testUsingContext('is true when macOS project exists', () async { + final MemoryFileSystem fs = MemoryFileSystem.test(); + final Directory projectDirectory = fs.directory('path'); + projectDirectory.childDirectory('macos').createSync(recursive: true); + final FlutterManifest manifest = FakeFlutterManifest(); + final FlutterProject project = FlutterProject(projectDirectory, manifest, manifest); + expect(project.macos.usesSwiftPackageManager, isTrue); + }, overrides: { + FeatureFlags: () => TestFeatureFlags(isSwiftPackageManagerEnabled: true), + XcodeProjectInterpreter: () => FakeXcodeProjectInterpreter(version: Version(15, 0, 0)), + }); + + testUsingContext("is false when macOS project doesn't exist", () async { + final MemoryFileSystem fs = MemoryFileSystem.test(); + final Directory projectDirectory = fs.directory('path'); + final FlutterManifest manifest = FakeFlutterManifest(); + final FlutterProject project = FlutterProject(projectDirectory, manifest, manifest); + expect(project.ios.usesSwiftPackageManager, isFalse); + expect(project.macos.usesSwiftPackageManager, isFalse); + }, overrides: { + FeatureFlags: () => TestFeatureFlags(isSwiftPackageManagerEnabled: true), + XcodeProjectInterpreter: () => FakeXcodeProjectInterpreter(version: Version(15, 0, 0)), + }); + + testUsingContext('is false when disabled via manifest', () async { + final MemoryFileSystem fs = MemoryFileSystem.test(); + final Directory projectDirectory = fs.directory('path'); + projectDirectory.childDirectory('macos').createSync(recursive: true); + final FlutterManifest manifest = FakeFlutterManifest(disabledSwiftPackageManager: true); + final FlutterProject project = FlutterProject(projectDirectory, manifest, manifest); + expect(project.macos.usesSwiftPackageManager, isFalse); + }, overrides: { + FeatureFlags: () => TestFeatureFlags(isSwiftPackageManagerEnabled: true), + XcodeProjectInterpreter: () => FakeXcodeProjectInterpreter(version: Version(15, 0, 0)), + }); + + testUsingContext('is false when Xcode is less than 15', () async { + final MemoryFileSystem fs = MemoryFileSystem.test(); + final Directory projectDirectory = fs.directory('path'); + projectDirectory.childDirectory('macos').createSync(recursive: true); + final FlutterManifest manifest = FakeFlutterManifest(); + final FlutterProject project = FlutterProject(projectDirectory, manifest, manifest); + expect(project.macos.usesSwiftPackageManager, isFalse); + }, overrides: { + FeatureFlags: () => TestFeatureFlags(isSwiftPackageManagerEnabled: true), + XcodeProjectInterpreter: () => FakeXcodeProjectInterpreter(version: Version(14, 0, 0)), + }); + + testUsingContext('is false when Swift Package Manager feature is not enabled', () async { + final MemoryFileSystem fs = MemoryFileSystem.test(); + final Directory projectDirectory = fs.directory('path'); + projectDirectory.childDirectory('macos').createSync(recursive: true); + final FlutterManifest manifest = FakeFlutterManifest(); + final FlutterProject project = FlutterProject(projectDirectory, manifest, manifest); + expect(project.macos.usesSwiftPackageManager, isFalse); + }, overrides: { + FeatureFlags: () => TestFeatureFlags(), + XcodeProjectInterpreter: () => FakeXcodeProjectInterpreter(version: Version(15, 0, 0)), + }); + + testUsingContext('is false when project is a module', () async { + final MemoryFileSystem fs = MemoryFileSystem.test(); + final Directory projectDirectory = fs.directory('path'); + projectDirectory.childDirectory('macos').createSync(recursive: true); + final FlutterManifest manifest = FakeFlutterManifest(isModule: true); + final FlutterProject project = FlutterProject(projectDirectory, manifest, manifest); + expect(project.macos.usesSwiftPackageManager, isFalse); + }, overrides: { + FeatureFlags: () => TestFeatureFlags(isSwiftPackageManagerEnabled: true), + XcodeProjectInterpreter: () => FakeXcodeProjectInterpreter(version: Version(15, 0, 0)), + }); + }); }); } @@ -196,11 +346,15 @@ class FakeFlutterProject extends Fake implements FlutterProject { class FakeXcodeProjectInterpreter extends Fake implements XcodeProjectInterpreter { FakeXcodeProjectInterpreter({ this.isInstalled = true, + this.version, }); @override final bool isInstalled; + @override + final Version? version; + @override Future getInfo(String projectPath, {String? projectFilename}) async { return XcodeProjectInfo( @@ -211,3 +365,16 @@ class FakeXcodeProjectInterpreter extends Fake implements XcodeProjectInterprete ); } } + +class FakeFlutterManifest extends Fake implements FlutterManifest { + FakeFlutterManifest({ + this.disabledSwiftPackageManager = false, + this.isModule = false, + }); + + @override + bool disabledSwiftPackageManager; + + @override + bool isModule; +}