[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.
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);

View File

@ -166,7 +166,7 @@ Future<XcodeBuildResult> 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<XcodeBuildResult> 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<bool> _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<bool> _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<String> swiftPackageOnlyPlugins = <String>[];
for (final String module in missingModules) {

View File

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

View File

@ -22,7 +22,7 @@ Future<void> 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<void> 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.');

View File

@ -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');

View File

@ -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) {

View File

@ -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<SupportedPlatform> getSupportedPlatforms({bool includeRoot = false}) {
return <SupportedPlatform>[

View File

@ -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<XcodeProjectInfo?> projectInfo() async {
final XcodeProjectInterpreter? xcodeProjectInterpreter = globals.xcodeProjectInterpreter;
if (!xcodeProject.existsSync() || xcodeProjectInterpreter == null || !xcodeProjectInterpreter.isInstalled) {

View File

@ -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;

View File

@ -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 {

View File

@ -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;
}

View File

@ -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 <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 {
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 {

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', () {
Future<FlutterProject?> 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;
}

View File

@ -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: <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', () {
@ -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: <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 {
FakeXcodeProjectInterpreter({
this.isInstalled = true,
this.version,
});
@override
final bool isInstalled;
@override
final Version? version;
@override
Future<XcodeProjectInfo?> 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;
}