[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
This commit is contained in:
Loïc Sharma 2024-11-13 13:45:07 -08:00 committed by GitHub
parent 5a9c2b4dd1
commit 8a55c941cf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 341 additions and 166 deletions

View File

@ -1071,12 +1071,22 @@ Future<void> refreshPluginsList(
// Write the legacy plugin files to avoid breaking existing apps. // Write the legacy plugin files to avoid breaking existing apps.
final bool legacyChanged = useImplicitPubspecResolution && _writeFlutterPluginsListLegacy(project, plugins); 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( final bool changed = _writeFlutterPluginsList(
project, project,
plugins, plugins,
swiftPackageManagerEnabledIos: swiftPackageManagerEnabled, swiftPackageManagerEnabledIos: swiftPackageManagerEnabledIos,
swiftPackageManagerEnabledMacos: swiftPackageManagerEnabled, swiftPackageManagerEnabledMacos: swiftPackageManagerEnabledMacos,
); );
if (changed || legacyChanged || forceCocoaPodsOnly) { if (changed || legacyChanged || forceCocoaPodsOnly) {
createPluginSymlinks(project, force: true); createPluginSymlinks(project, force: true);

View File

@ -166,7 +166,7 @@ Future<XcodeBuildResult> buildXcodeProject({
RemoveBitcodeMigration(app.project, globals.logger), RemoveBitcodeMigration(app.project, globals.logger),
XcodeThinBinaryBuildPhaseInputPathsMigration(app.project, globals.logger), XcodeThinBinaryBuildPhaseInputPathsMigration(app.project, globals.logger),
UIApplicationMainDeprecationMigration(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( SwiftPackageManagerIntegrationMigration(
app.project, app.project,
SupportedPlatform.ios, SupportedPlatform.ios,
@ -267,7 +267,7 @@ Future<XcodeBuildResult> buildXcodeProject({
targetOverride: targetOverride, targetOverride: targetOverride,
buildInfo: buildInfo, buildInfo: buildInfo,
); );
if (project.usesSwiftPackageManager) { if (app.project.usesSwiftPackageManager) {
final String? iosDeploymentTarget = buildSettings['IPHONEOS_DEPLOYMENT_TARGET']; final String? iosDeploymentTarget = buildSettings['IPHONEOS_DEPLOYMENT_TARGET'];
if (iosDeploymentTarget != null) { if (iosDeploymentTarget != null) {
SwiftPackageManager.updateMinimumDeployment( SwiftPackageManager.updateMinimumDeployment(
@ -888,7 +888,7 @@ Future<bool> _handleIssues(
logger.printError(missingPlatformInstructions(missingPlatform), emphasis: true); logger.printError(missingPlatformInstructions(missingPlatform), emphasis: true);
} else if (duplicateModules.isNotEmpty) { } else if (duplicateModules.isNotEmpty) {
final bool usesCocoapods = xcodeProject.podfile.existsSync(); final bool usesCocoapods = xcodeProject.podfile.existsSync();
final bool usesSwiftPackageManager = project.usesSwiftPackageManager; final bool usesSwiftPackageManager = xcodeProject.usesSwiftPackageManager;
if (usesCocoapods && usesSwiftPackageManager) { if (usesCocoapods && usesSwiftPackageManager) {
logger.printError( logger.printError(
'Your project uses both CocoaPods and Swift Package Manager, which can ' 'Your project uses both CocoaPods and Swift Package Manager, which can '
@ -906,7 +906,7 @@ Future<bool> _handleIssues(
} }
} else if (missingModules.isNotEmpty) { } else if (missingModules.isNotEmpty) {
final bool usesCocoapods = xcodeProject.podfile.existsSync(); final bool usesCocoapods = xcodeProject.podfile.existsSync();
final bool usesSwiftPackageManager = project.usesSwiftPackageManager; final bool usesSwiftPackageManager = xcodeProject.usesSwiftPackageManager;
if (usesCocoapods && !usesSwiftPackageManager) { if (usesCocoapods && !usesSwiftPackageManager) {
final List<String> swiftPackageOnlyPlugins = <String>[]; final List<String> swiftPackageOnlyPlugins = <String>[];
for (final String module in missingModules) { for (final String module in missingModules) {

View File

@ -91,7 +91,7 @@ Future<void> buildMacOS({
FlutterApplicationMigration(flutterProject.macos, globals.logger), FlutterApplicationMigration(flutterProject.macos, globals.logger),
NSApplicationMainDeprecationMigration(flutterProject.macos, globals.logger), NSApplicationMainDeprecationMigration(flutterProject.macos, globals.logger),
SecureRestorableStateMigration(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( SwiftPackageManagerIntegrationMigration(
flutterProject.macos, flutterProject.macos,
SupportedPlatform.macos, SupportedPlatform.macos,
@ -145,7 +145,7 @@ Future<void> buildMacOS({
useMacOSConfig: true, useMacOSConfig: true,
); );
if (flutterProject.usesSwiftPackageManager) { if (flutterProject.macos.usesSwiftPackageManager) {
final String? macOSDeploymentTarget = buildSettings['MACOSX_DEPLOYMENT_TARGET']; final String? macOSDeploymentTarget = buildSettings['MACOSX_DEPLOYMENT_TARGET'];
if (macOSDeploymentTarget != null) { if (macOSDeploymentTarget != null) {
SwiftPackageManager.updateMinimumDeployment( SwiftPackageManager.updateMinimumDeployment(

View File

@ -22,7 +22,7 @@ Future<void> processPodsIfNeeded(
// When using Swift Package Manager, the Podfile may not exist so if there // When using Swift Package Manager, the Podfile may not exist so if there
// isn't a Podfile, skip processing pods. // isn't a Podfile, skip processing pods.
if (project.usesSwiftPackageManager && if (xcodeProject.usesSwiftPackageManager &&
!xcodeProject.podfile.existsSync() && !xcodeProject.podfile.existsSync() &&
!forceCocoaPodsOnly) { !forceCocoaPodsOnly) {
return; return;
@ -50,7 +50,7 @@ Future<void> processPodsIfNeeded(
// If forcing the use of only CocoaPods, but the project is using Swift // If forcing the use of only CocoaPods, but the project is using Swift
// Package Manager, print a warning that CocoaPods will be used. // Package Manager, print a warning that CocoaPods will be used.
if (forceCocoaPodsOnly && project.usesSwiftPackageManager) { if (forceCocoaPodsOnly && xcodeProject.usesSwiftPackageManager) {
globals.logger.printWarning( globals.logger.printWarning(
'Swift Package Manager does not yet support this command. ' 'Swift Package Manager does not yet support this command. '
'CocoaPods will be used instead.'); 'CocoaPods will be used instead.');

View File

@ -167,7 +167,7 @@ class CocoaPods {
}) async { }) async {
if (!xcodeProject.podfile.existsSync()) { if (!xcodeProject.podfile.existsSync()) {
// Swift Package Manager doesn't need Podfile, so don't error. // Swift Package Manager doesn't need Podfile, so don't error.
if (xcodeProject.parent.usesSwiftPackageManager) { if (xcodeProject.usesSwiftPackageManager) {
return false; return false;
} }
throwToolExit('Podfile missing'); throwToolExit('Podfile missing');

View File

@ -60,7 +60,7 @@ class DarwinDependencyManagement {
final XcodeBasedProject xcodeProject = platform == SupportedPlatform.ios final XcodeBasedProject xcodeProject = platform == SupportedPlatform.ios
? _project.ios ? _project.ios
: _project.macos; : _project.macos;
if (_project.usesSwiftPackageManager) { if (xcodeProject.usesSwiftPackageManager) {
await _swiftPackageManager.generatePluginsSwiftPackage( await _swiftPackageManager.generatePluginsSwiftPackage(
_plugins, _plugins,
platform, platform,
@ -89,7 +89,7 @@ class DarwinDependencyManagement {
); );
final bool useCocoapods; final bool useCocoapods;
if (_project.usesSwiftPackageManager) { if (xcodeProject.usesSwiftPackageManager) {
useCocoapods = swiftPackageCount < totalCount; useCocoapods = swiftPackageCount < totalCount;
} else { } else {
// When Swift Package Manager is not enabled, set up Podfile if plugins // 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 // 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 // but does have a Package.swift, throw an error. Otherwise, it'll error
// when it builds. // when it builds.
if (!_project.usesSwiftPackageManager && if (!xcodeProject.usesSwiftPackageManager &&
!cocoaPodsCompatible && !cocoaPodsCompatible &&
swiftPackageManagerCompatible) { swiftPackageManagerCompatible) {
throwToolExit( throwToolExit(
@ -175,7 +175,7 @@ class DarwinDependencyManagement {
// Only show warnings to remove CocoaPods if the project is using Swift // Only show warnings to remove CocoaPods if the project is using Swift
// Package Manager, has already been migrated to have SPM integration, and // Package Manager, has already been migrated to have SPM integration, and
// all plugins are Swift Packages. // all plugins are Swift Packages.
if (_project.usesSwiftPackageManager && if (xcodeProject.usesSwiftPackageManager &&
xcodeProject.flutterPluginSwiftPackageInProjectSettings && xcodeProject.flutterPluginSwiftPackageInProjectSettings &&
pluginCount == swiftPackageCount && pluginCount == swiftPackageCount &&
swiftPackageCount != 0) { swiftPackageCount != 0) {

View File

@ -21,7 +21,6 @@ import 'features.dart';
import 'flutter_manifest.dart'; import 'flutter_manifest.dart';
import 'flutter_plugins.dart'; import 'flutter_plugins.dart';
import 'globals.dart' as globals; import 'globals.dart' as globals;
import 'macos/xcode.dart';
import 'platform_plugins.dart'; import 'platform_plugins.dart';
import 'project_validator_result.dart'; import 'project_validator_result.dart';
import 'template.dart'; import 'template.dart';
@ -262,24 +261,6 @@ class FlutterProject {
/// True if this project has an example application. /// True if this project has an example application.
bool get hasExampleApp => _exampleDirectory(directory).existsSync(); 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. /// Returns a list of platform names that are supported by the project.
List<SupportedPlatform> getSupportedPlatforms({bool includeRoot = false}) { List<SupportedPlatform> getSupportedPlatforms({bool includeRoot = false}) {
return <SupportedPlatform>[ return <SupportedPlatform>[

View File

@ -5,15 +5,18 @@
import 'base/error_handling_io.dart'; import 'base/error_handling_io.dart';
import 'base/file_system.dart'; import 'base/file_system.dart';
import 'base/utils.dart'; import 'base/utils.dart';
import 'base/version.dart';
import 'build_info.dart'; import 'build_info.dart';
import 'bundle.dart' as bundle; import 'bundle.dart' as bundle;
import 'convert.dart'; import 'convert.dart';
import 'features.dart';
import 'flutter_plugins.dart'; import 'flutter_plugins.dart';
import 'globals.dart' as globals; import 'globals.dart' as globals;
import 'ios/code_signing.dart'; import 'ios/code_signing.dart';
import 'ios/plist_parser.dart'; import 'ios/plist_parser.dart';
import 'ios/xcode_build_settings.dart' as xcode; import 'ios/xcode_build_settings.dart' as xcode;
import 'ios/xcodeproj.dart'; import 'ios/xcodeproj.dart';
import 'macos/xcode.dart';
import 'platform_plugins.dart'; import 'platform_plugins.dart';
import 'project.dart'; import 'project.dart';
import 'template.dart'; import 'template.dart';
@ -147,6 +150,40 @@ abstract class XcodeBasedProject extends FlutterProjectPlatform {
.contains('FlutterGeneratedPluginSwiftPackage'); .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<XcodeProjectInfo?> projectInfo() async { Future<XcodeProjectInfo?> projectInfo() async {
final XcodeProjectInterpreter? xcodeProjectInterpreter = globals.xcodeProjectInterpreter; final XcodeProjectInterpreter? xcodeProjectInterpreter = globals.xcodeProjectInterpreter;
if (!xcodeProject.existsSync() || xcodeProjectInterpreter == null || !xcodeProjectInterpreter.isInstalled) { if (!xcodeProject.existsSync() || xcodeProjectInterpreter == null || !xcodeProjectInterpreter.isInstalled) {

View File

@ -816,6 +816,7 @@ void createFakePlugins(
class FakeIosProject extends Fake implements IosProject { class FakeIosProject extends Fake implements IosProject {
FakeIosProject({ FakeIosProject({
required MemoryFileSystem fileSystem, required MemoryFileSystem fileSystem,
this.usesSwiftPackageManager = false,
}) : hostAppRoot = fileSystem.directory('app_name').childDirectory('ios'); }) : hostAppRoot = fileSystem.directory('app_name').childDirectory('ios');
@override @override
@ -832,6 +833,9 @@ class FakeIosProject extends Fake implements IosProject {
@override @override
File get podfile => hostAppRoot.childFile('Podfile'); File get podfile => hostAppRoot.childFile('Podfile');
@override
final bool usesSwiftPackageManager;
} }
class FakeFlutterProject extends Fake implements FlutterProject { class FakeFlutterProject extends Fake implements FlutterProject {
@ -841,7 +845,8 @@ class FakeFlutterProject extends Fake implements FlutterProject {
this.isModule = false, this.isModule = false,
}); });
MemoryFileSystem fileSystem; final MemoryFileSystem fileSystem;
final bool usesSwiftPackageManager;
@override @override
late final Directory directory = fileSystem.directory('app_name'); 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'); File get flutterPluginsDependenciesFile => directory.childFile('.flutter-plugins-dependencies');
@override @override
late final IosProject ios = FakeIosProject(fileSystem: fileSystem); late final IosProject ios = FakeIosProject(
fileSystem: fileSystem,
@override usesSwiftPackageManager: usesSwiftPackageManager,
final bool usesSwiftPackageManager; );
@override @override
final bool isModule; final bool isModule;

View File

@ -177,7 +177,7 @@ void main() {
'plugin_one', 'plugin_one',
'plugin_two' 'plugin_two'
]); ]);
flutterProject.usesSwiftPackageManager = true; flutterProject.ios.usesSwiftPackageManager = true;
flutterProject.ios.podfile.createSync(recursive: true); flutterProject.ios.podfile.createSync(recursive: true);
await processPodsIfNeeded( await processPodsIfNeeded(
@ -200,7 +200,7 @@ void main() {
'plugin_one', 'plugin_one',
'plugin_two' 'plugin_two'
]); ]);
flutterProject.usesSwiftPackageManager = true; flutterProject.ios.usesSwiftPackageManager = true;
await processPodsIfNeeded( await processPodsIfNeeded(
flutterProject.ios, flutterProject.ios,
@ -221,7 +221,7 @@ void main() {
'plugin_one', 'plugin_one',
'plugin_two' 'plugin_two'
]); ]);
flutterProject.usesSwiftPackageManager = true; flutterProject.ios.usesSwiftPackageManager = true;
flutterProject.ios.flutterPluginSwiftPackageManifest.createSync(recursive: true); flutterProject.ios.flutterPluginSwiftPackageManifest.createSync(recursive: true);
await processPodsIfNeeded( await processPodsIfNeeded(
@ -336,7 +336,7 @@ void main() {
'plugin_one', 'plugin_one',
'plugin_two' 'plugin_two'
]); ]);
flutterProject.usesSwiftPackageManager = true; flutterProject.macos.usesSwiftPackageManager = true;
flutterProject.macos.podfile.createSync(recursive: true); flutterProject.macos.podfile.createSync(recursive: true);
await processPodsIfNeeded( await processPodsIfNeeded(
@ -359,7 +359,7 @@ void main() {
'plugin_one', 'plugin_one',
'plugin_two' 'plugin_two'
]); ]);
flutterProject.usesSwiftPackageManager = true; flutterProject.macos.usesSwiftPackageManager = true;
await processPodsIfNeeded( await processPodsIfNeeded(
flutterProject.macos, flutterProject.macos,
@ -380,7 +380,7 @@ void main() {
'plugin_one', 'plugin_one',
'plugin_two' 'plugin_two'
]); ]);
flutterProject.usesSwiftPackageManager = true; flutterProject.macos.usesSwiftPackageManager = true;
flutterProject.macos.flutterPluginSwiftPackageManifest.createSync(recursive: true); flutterProject.macos.flutterPluginSwiftPackageManifest.createSync(recursive: true);
await processPodsIfNeeded( await processPodsIfNeeded(
@ -420,9 +420,6 @@ class FakeFlutterProject extends Fake implements FlutterProject {
@override @override
bool isModule = false; bool isModule = false;
@override
bool usesSwiftPackageManager = false;
@override @override
late FlutterManifest manifest; late FlutterManifest manifest;
@ -436,10 +433,10 @@ class FakeFlutterProject extends Fake implements FlutterProject {
late File flutterPluginsDependenciesFile; late File flutterPluginsDependenciesFile;
@override @override
late IosProject ios; late FakeIosProject ios;
@override @override
late MacOSProject macos; late FakeMacOSProject macos;
@override @override
late AndroidProject android; late AndroidProject android;
@ -489,6 +486,9 @@ class FakeMacOSProject extends Fake implements MacOSProject {
.childDirectory('Packages') .childDirectory('Packages')
.childDirectory('FlutterGeneratedPluginSwiftPackage') .childDirectory('FlutterGeneratedPluginSwiftPackage')
.childFile('Package.swift'); .childFile('Package.swift');
@override
bool usesSwiftPackageManager = false;
} }
class FakeIosProject extends Fake implements IosProject { class FakeIosProject extends Fake implements IosProject {
@ -527,6 +527,9 @@ class FakeIosProject extends Fake implements IosProject {
.childDirectory('Packages') .childDirectory('Packages')
.childDirectory('FlutterGeneratedPluginSwiftPackage') .childDirectory('FlutterGeneratedPluginSwiftPackage')
.childFile('Package.swift'); .childFile('Package.swift');
@override
bool usesSwiftPackageManager = false;
} }
class FakeAndroidProject extends Fake implements AndroidProject { class FakeAndroidProject extends Fake implements AndroidProject {

View File

@ -505,6 +505,7 @@ void main() {
class FakeIosProject extends Fake implements IosProject { class FakeIosProject extends Fake implements IosProject {
FakeIosProject({ FakeIosProject({
required MemoryFileSystem fileSystem, required MemoryFileSystem fileSystem,
required this.usesSwiftPackageManager,
}) : hostAppRoot = fileSystem.directory('app_name').childDirectory('ios'); }) : hostAppRoot = fileSystem.directory('app_name').childDirectory('ios');
@override @override
@ -530,6 +531,9 @@ class FakeIosProject extends Fake implements IosProject {
.contains('FlutterGeneratedPluginSwiftPackage'); .contains('FlutterGeneratedPluginSwiftPackage');
} }
@override
bool usesSwiftPackageManager;
@override @override
Directory get managedDirectory => hostAppRoot.childDirectory('Flutter'); Directory get managedDirectory => hostAppRoot.childDirectory('Flutter');
@ -540,6 +544,7 @@ class FakeIosProject extends Fake implements IosProject {
class FakeMacOSProject extends Fake implements MacOSProject { class FakeMacOSProject extends Fake implements MacOSProject {
FakeMacOSProject({ FakeMacOSProject({
required MemoryFileSystem fileSystem, required MemoryFileSystem fileSystem,
required this.usesSwiftPackageManager,
}) : hostAppRoot = fileSystem.directory('app_name').childDirectory('macos'); }) : hostAppRoot = fileSystem.directory('app_name').childDirectory('macos');
@override @override
@ -565,6 +570,9 @@ class FakeMacOSProject extends Fake implements MacOSProject {
.contains('FlutterGeneratedPluginSwiftPackage'); .contains('FlutterGeneratedPluginSwiftPackage');
} }
@override
bool usesSwiftPackageManager;
@override @override
Directory get managedDirectory => hostAppRoot.childDirectory('Flutter'); Directory get managedDirectory => hostAppRoot.childDirectory('Flutter');
@ -579,18 +587,21 @@ class FakeFlutterProject extends Fake implements FlutterProject {
this.isModule = false, this.isModule = false,
}); });
MemoryFileSystem fileSystem; final MemoryFileSystem fileSystem;
@override
late final IosProject ios = FakeIosProject(fileSystem: fileSystem);
@override
late final MacOSProject macos = FakeMacOSProject(fileSystem: fileSystem);
@override
final bool usesSwiftPackageManager; final bool usesSwiftPackageManager;
@override
late final FakeIosProject ios = FakeIosProject(
fileSystem: fileSystem,
usesSwiftPackageManager: usesSwiftPackageManager,
);
@override
late final FakeMacOSProject macos = FakeMacOSProject(
fileSystem: fileSystem,
usesSwiftPackageManager: usesSwiftPackageManager,
);
@override @override
final bool isModule; final bool isModule;
} }

View File

@ -667,9 +667,15 @@ dependencies:
final DateTime dateCreated = DateTime(1970); final DateTime dateCreated = DateTime(1970);
systemClock.currentTime = dateCreated; 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); expect(flutterProject.flutterPluginsDependenciesFile.existsSync(), true);
final String pluginsString = flutterProject.flutterPluginsDependenciesFile final String pluginsString = flutterProject.flutterPluginsDependenciesFile
@ -711,7 +717,8 @@ dependencies:
final DateTime dateCreated = DateTime(1970); final DateTime dateCreated = DateTime(1970);
systemClock.currentTime = dateCreated; systemClock.currentTime = dateCreated;
flutterProject.usesSwiftPackageManager = true; iosProject.usesSwiftPackageManager = true;
macosProject.usesSwiftPackageManager = true;
await refreshPluginsList(flutterProject, forceCocoaPodsOnly: true, useImplicitPubspecResolution: true); await refreshPluginsList(flutterProject, forceCocoaPodsOnly: true, useImplicitPubspecResolution: true);
@ -735,6 +742,55 @@ dependencies:
FlutterVersion: () => flutterVersion, 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 <String, _PluginPlatformInfo>{
// 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<String, dynamic> jsonContent = json.decode(pluginsString) as Map<String, dynamic>;
final Map<String, dynamic> expectedSwiftPackageManagerEnabled = <String, dynamic>{
'ios': true,
'macos': false,
};
expect(
jsonContent['swift_package_manager_enabled'],
expectedSwiftPackageManagerEnabled,
);
}, overrides: <Type, Generator>{
FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(),
SystemClock: () => systemClock,
FlutterVersion: () => flutterVersion,
});
testUsingContext('Changes to the plugin list invalidates the Cocoapod lockfiles', () async { testUsingContext('Changes to the plugin list invalidates the Cocoapod lockfiles', () async {
simulatePodInstallRun(iosProject); simulatePodInstallRun(iosProject);
simulatePodInstallRun(macosProject); simulatePodInstallRun(macosProject);
@ -2108,9 +2164,6 @@ class FakeFlutterProject extends Fake implements FlutterProject {
@override @override
bool isModule = false; bool isModule = false;
@override
bool usesSwiftPackageManager = false;
@override @override
late FlutterManifest manifest; late FlutterManifest manifest;
@ -2154,6 +2207,9 @@ class FakeMacOSProject extends Fake implements MacOSProject {
@override @override
late File podManifestLock; late File podManifestLock;
@override
bool usesSwiftPackageManager = false;
@override @override
late Directory managedDirectory; late Directory managedDirectory;
@ -2187,6 +2243,9 @@ class FakeIosProject extends Fake implements IosProject {
@override @override
late File podManifestLock; late File podManifestLock;
@override
bool usesSwiftPackageManager = false;
} }
class FakeAndroidProject extends Fake implements AndroidProject { class FakeAndroidProject extends Fake implements AndroidProject {

View File

@ -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: <Type, Generator>{
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: <Type, Generator>{
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: <Type, Generator>{
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: <Type, Generator>{
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: <Type, Generator>{
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: <Type, Generator>{
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: <Type, Generator>{
FeatureFlags: () => TestFeatureFlags(isSwiftPackageManagerEnabled: true),
XcodeProjectInterpreter: () => FakeXcodeProjectInterpreter(version: Version(15, 0, 0)),
});
});
group('java gradle agp compatibility', () { group('java gradle agp compatibility', () {
Future<FlutterProject?> configureGradleAgpForTest({ Future<FlutterProject?> configureGradleAgpForTest({
required String gradleV, required String gradleV,
@ -1896,16 +1811,3 @@ class FakeAndroidSdkWithDir extends Fake implements AndroidSdk {
@override @override
Directory get directory => _directory; Directory get directory => _directory;
} }
class FakeFlutterManifest extends Fake implements FlutterManifest {
FakeFlutterManifest({
this.disabledSwiftPackageManager = false,
this.isModule = false,
});
@override
bool disabledSwiftPackageManager;
@override
bool isModule;
}

View File

@ -6,13 +6,16 @@ import 'package:file/file.dart';
import 'package:file/memory.dart'; import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/logger.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/ios/xcodeproj.dart';
import 'package:flutter_tools/src/project.dart'; import 'package:flutter_tools/src/project.dart';
import 'package:test/fake.dart'; import 'package:test/fake.dart';
import '../src/common.dart'; import '../src/common.dart';
import '../src/context.dart'; import '../src/context.dart';
import '../src/fakes.dart';
void main() { void main() {
group('IosProject', () { group('IosProject', () {
@ -127,6 +130,79 @@ void main() {
XcodeProjectInterpreter: () => FakeXcodeProjectInterpreter(), 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: <Type, Generator>{
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: <Type, Generator>{
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: <Type, Generator>{
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: <Type, Generator>{
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: <Type, Generator>{
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: <Type, Generator>{
FeatureFlags: () => TestFeatureFlags(isSwiftPackageManagerEnabled: true),
XcodeProjectInterpreter: () => FakeXcodeProjectInterpreter(version: Version(15, 0, 0)),
});
});
}); });
group('MacOSProject', () { group('MacOSProject', () {
@ -175,6 +251,80 @@ void main() {
'app_name/macos/Flutter/Flutter-Debug.xcconfig', '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: <Type, Generator>{
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: <Type, Generator>{
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: <Type, Generator>{
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: <Type, Generator>{
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: <Type, Generator>{
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: <Type, Generator>{
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 { class FakeXcodeProjectInterpreter extends Fake implements XcodeProjectInterpreter {
FakeXcodeProjectInterpreter({ FakeXcodeProjectInterpreter({
this.isInstalled = true, this.isInstalled = true,
this.version,
}); });
@override @override
final bool isInstalled; final bool isInstalled;
@override
final Version? version;
@override @override
Future<XcodeProjectInfo?> getInfo(String projectPath, {String? projectFilename}) async { Future<XcodeProjectInfo?> getInfo(String projectPath, {String? projectFilename}) async {
return XcodeProjectInfo( 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;
}