From 5a63b1df955bba1939bd7eab2bced5fe016ca429 Mon Sep 17 00:00:00 2001 From: Victoria Ashworth <15619084+vashworth@users.noreply.github.com> Date: Wed, 24 Apr 2024 17:46:20 -0500 Subject: [PATCH] Add create app and plugin templates for Swift Package Manager (#147082) When Swift Package Manager feature is enabled, create app and create plugin will have Swift Package Manager integration already added and will not need to undergo a migration. Fixes https://github.com/flutter/flutter/issues/146371. ``` flutter config --enable-swift-package-manager flutter create --ios-language swift --platforms ios,macos swift_app_name flutter create --ios-language objc --platforms ios objc_app_name flutter create --template=plugin --ios-language swift --platforms ios,macos swift_plugin_name flutter create --template=plugin --ios-language objc --platforms ios objc_plugin_name ``` --- .../lib/src/commands/create.dart | 19 +- .../lib/src/commands/create_base.dart | 2 + .../lib/src/macos/swift_package_manager.dart | 12 +- .../Runner.xcodeproj/project.pbxproj.tmpl | 32 ++ .../{Runner.xcscheme => Runner.xcscheme.tmpl} | 20 + .../RunnerTests/RunnerTests.m.tmpl | 5 + .../Runner.xcodeproj/project.pbxproj.tmpl | 32 ++ .../{Runner.xcscheme => Runner.xcscheme.tmpl} | 20 + .../RunnerTests/RunnerTests.swift.tmpl | 5 + .../Runner.xcodeproj/project.pbxproj.tmpl | 32 ++ .../xcschemes/Runner.xcscheme.tmpl | 20 + .../RunnerTests/RunnerTests.swift.tmpl | 5 + .../ios-objc.tmpl/projectName.podspec.tmpl | 6 + .../ios-swift.tmpl/projectName.podspec.tmpl | 5 + .../ios-objc.tmpl/Classes/pluginClass.h.tmpl | 0 .../ios-objc.tmpl/Classes/pluginClass.m.tmpl | 0 .../Classes/pluginClass.swift.tmpl | 0 .../ios.tmpl/Assets/.gitkeep | 0 .../macos.tmpl/Classes/pluginClass.swift.tmpl | 0 .../macos.tmpl/projectName.podspec.tmpl | 7 +- .../projectName.tmpl/Package.swift.tmpl | 27 + .../projectName.tmpl/pluginClass.h.tmpl | 4 + .../projectName.tmpl/pluginClass.m.tmpl | 20 + .../projectName.tmpl/Package.swift.tmpl | 24 + .../projectName.tmpl/pluginClass.swift.tmpl | 19 + .../projectName.tmpl/Resources/.gitkeep | 0 .../projectName.tmpl/Package.swift.tmpl | 24 + .../projectName.tmpl/Resources/.gitkeep | 0 .../projectName.tmpl/pluginClass.swift.tmpl | 19 + .../templates/template_manifest.json | 25 +- .../hermetic/create_usage_test.dart | 1 + .../commands.shard/permeable/create_test.dart | 49 ++ ...swift_package_manager_create_app_test.dart | 151 ++++++ ...ft_package_manager_create_plugin_test.dart | 175 ++++++ .../swift_package_manager_test.dart | 509 +++--------------- .../swift_package_manager_utils.dart | 379 +++++++++++++ 36 files changed, 1197 insertions(+), 451 deletions(-) rename packages/flutter_tools/templates/app_shared/ios-objc.tmpl/Runner.xcodeproj/xcshareddata/xcschemes/{Runner.xcscheme => Runner.xcscheme.tmpl} (79%) rename packages/flutter_tools/templates/app_shared/ios-swift.tmpl/Runner.xcodeproj/xcshareddata/xcschemes/{Runner.xcscheme => Runner.xcscheme.tmpl} (79%) rename packages/flutter_tools/templates/{plugin => plugin_cocoapods}/ios-objc.tmpl/Classes/pluginClass.h.tmpl (100%) rename packages/flutter_tools/templates/{plugin => plugin_cocoapods}/ios-objc.tmpl/Classes/pluginClass.m.tmpl (100%) rename packages/flutter_tools/templates/{plugin => plugin_cocoapods}/ios-swift.tmpl/Classes/pluginClass.swift.tmpl (100%) rename packages/flutter_tools/templates/{plugin => plugin_cocoapods}/ios.tmpl/Assets/.gitkeep (100%) rename packages/flutter_tools/templates/{plugin => plugin_cocoapods}/macos.tmpl/Classes/pluginClass.swift.tmpl (100%) create mode 100644 packages/flutter_tools/templates/plugin_swift_package_manager/ios-objc.tmpl/projectName.tmpl/Package.swift.tmpl create mode 100644 packages/flutter_tools/templates/plugin_swift_package_manager/ios-objc.tmpl/projectName.tmpl/Sources/projectName.tmpl/include/projectName.tmpl/pluginClass.h.tmpl create mode 100644 packages/flutter_tools/templates/plugin_swift_package_manager/ios-objc.tmpl/projectName.tmpl/Sources/projectName.tmpl/pluginClass.m.tmpl create mode 100644 packages/flutter_tools/templates/plugin_swift_package_manager/ios-swift.tmpl/projectName.tmpl/Package.swift.tmpl create mode 100644 packages/flutter_tools/templates/plugin_swift_package_manager/ios-swift.tmpl/projectName.tmpl/Sources/projectName.tmpl/pluginClass.swift.tmpl create mode 100644 packages/flutter_tools/templates/plugin_swift_package_manager/ios.tmpl/projectName.tmpl/Sources/projectName.tmpl/Resources/.gitkeep create mode 100644 packages/flutter_tools/templates/plugin_swift_package_manager/macos.tmpl/projectName.tmpl/Package.swift.tmpl create mode 100644 packages/flutter_tools/templates/plugin_swift_package_manager/macos.tmpl/projectName.tmpl/Sources/projectName.tmpl/Resources/.gitkeep create mode 100644 packages/flutter_tools/templates/plugin_swift_package_manager/macos.tmpl/projectName.tmpl/Sources/projectName.tmpl/pluginClass.swift.tmpl create mode 100644 packages/flutter_tools/test/integration.shard/swift_package_manager_create_app_test.dart create mode 100644 packages/flutter_tools/test/integration.shard/swift_package_manager_create_plugin_test.dart create mode 100644 packages/flutter_tools/test/integration.shard/swift_package_manager_utils.dart diff --git a/packages/flutter_tools/lib/src/commands/create.dart b/packages/flutter_tools/lib/src/commands/create.dart index e470fa222dc..6eed33fb657 100644 --- a/packages/flutter_tools/lib/src/commands/create.dart +++ b/packages/flutter_tools/lib/src/commands/create.dart @@ -21,6 +21,8 @@ import '../flutter_manifest.dart'; import '../flutter_project_metadata.dart'; import '../globals.dart' as globals; import '../ios/code_signing.dart'; +import '../macos/swift_package_manager.dart'; +import '../macos/swift_packages.dart'; import '../project.dart'; import '../reporting/reporting.dart'; import '../runner/flutter_command.dart'; @@ -324,6 +326,7 @@ class CreateCommand extends CreateBase { projectDescription: stringArg('description'), flutterRoot: flutterRoot, withPlatformChannelPluginHook: generateMethodChannelsPlugin, + withSwiftPackageManager: featureFlags.isSwiftPackageManagerEnabled, withFfiPluginHook: generateFfiPlugin, withFfiPackage: generateFfiPackage, withEmptyMain: emptyArgument, @@ -603,8 +606,21 @@ Your $application code is in $relativeAppMain. ? stringArg('description') : 'A new Flutter plugin project.'; templateContext['description'] = description; + + final String? projectName = templateContext['projectName'] as String?; + final List templates = ['plugin', 'plugin_shared']; + if ((templateContext['ios'] == true || templateContext['macos'] == true) && featureFlags.isSwiftPackageManagerEnabled) { + templates.add('plugin_swift_package_manager'); + templateContext['swiftLibraryName'] = projectName?.replaceAll('_', '-'); + templateContext['swiftToolsVersion'] = minimumSwiftToolchainVersion; + templateContext['iosSupportedPlatform'] = SwiftPackageManager.iosSwiftPackageSupportedPlatform.format(); + templateContext['macosSupportedPlatform'] = SwiftPackageManager.macosSwiftPackageSupportedPlatform.format(); + } else { + templates.add('plugin_cocoapods'); + } + generatedCount += await renderMerged( - ['plugin', 'plugin_shared'], + templates, directory, templateContext, overwrite: overwrite, @@ -618,7 +634,6 @@ Your $application code is in $relativeAppMain. project: project, requireAndroidSdk: false); } - final String? projectName = templateContext['projectName'] as String?; final String organization = templateContext['organization']! as String; // Required to make the context. final String? androidPluginIdentifier = templateContext['androidIdentifier'] as String?; final String exampleProjectName = '${projectName}_example'; diff --git a/packages/flutter_tools/lib/src/commands/create_base.dart b/packages/flutter_tools/lib/src/commands/create_base.dart index 93c9050c9b0..67dd988012f 100644 --- a/packages/flutter_tools/lib/src/commands/create_base.dart +++ b/packages/flutter_tools/lib/src/commands/create_base.dart @@ -351,6 +351,7 @@ abstract class CreateBase extends FlutterCommand { String? kotlinVersion, String? gradleVersion, bool withPlatformChannelPluginHook = false, + bool withSwiftPackageManager = false, bool withFfiPluginHook = false, bool withFfiPackage = false, bool withEmptyMain = false, @@ -404,6 +405,7 @@ abstract class CreateBase extends FlutterCommand { 'withFfiPackage': withFfiPackage, 'withFfiPluginHook': withFfiPluginHook, 'withPlatformChannelPluginHook': withPlatformChannelPluginHook, + 'withSwiftPackageManager': withSwiftPackageManager, 'withPluginHook': withFfiPluginHook || withFfiPackage || withPlatformChannelPluginHook, 'withEmptyMain': withEmptyMain, 'androidLanguage': androidLanguage, diff --git a/packages/flutter_tools/lib/src/macos/swift_package_manager.dart b/packages/flutter_tools/lib/src/macos/swift_package_manager.dart index b5575c11bbb..98ba51b592b 100644 --- a/packages/flutter_tools/lib/src/macos/swift_package_manager.dart +++ b/packages/flutter_tools/lib/src/macos/swift_package_manager.dart @@ -30,12 +30,12 @@ class SwiftPackageManager { static const String _defaultFlutterPluginsSwiftPackageName = 'FlutterGeneratedPluginSwiftPackage'; - static final SwiftPackageSupportedPlatform _iosSwiftPackageSupportedPlatform = SwiftPackageSupportedPlatform( + static final SwiftPackageSupportedPlatform iosSwiftPackageSupportedPlatform = SwiftPackageSupportedPlatform( platform: SwiftPackagePlatform.ios, version: Version(12, 0, null), ); - static final SwiftPackageSupportedPlatform _macosSwiftPackageSupportedPlatform = SwiftPackageSupportedPlatform( + static final SwiftPackageSupportedPlatform macosSwiftPackageSupportedPlatform = SwiftPackageSupportedPlatform( platform: SwiftPackagePlatform.macos, version: Version(10, 14, null), ); @@ -66,9 +66,9 @@ class SwiftPackageManager { final SwiftPackageSupportedPlatform swiftSupportedPlatform; if (platform == SupportedPlatform.ios) { - swiftSupportedPlatform = _iosSwiftPackageSupportedPlatform; + swiftSupportedPlatform = iosSwiftPackageSupportedPlatform; } else { - swiftSupportedPlatform = _macosSwiftPackageSupportedPlatform; + swiftSupportedPlatform = macosSwiftPackageSupportedPlatform; } // FlutterGeneratedPluginSwiftPackage must be statically linked to ensure @@ -169,10 +169,10 @@ class SwiftPackageManager { final SwiftPackageSupportedPlatform defaultPlatform; final SwiftPackagePlatform packagePlatform; if (platform == SupportedPlatform.ios) { - defaultPlatform = _iosSwiftPackageSupportedPlatform; + defaultPlatform = iosSwiftPackageSupportedPlatform; packagePlatform = SwiftPackagePlatform.ios; } else { - defaultPlatform = _macosSwiftPackageSupportedPlatform; + defaultPlatform = macosSwiftPackageSupportedPlatform; packagePlatform = SwiftPackagePlatform.macos; } diff --git a/packages/flutter_tools/templates/app_shared/ios-objc.tmpl/Runner.xcodeproj/project.pbxproj.tmpl b/packages/flutter_tools/templates/app_shared/ios-objc.tmpl/Runner.xcodeproj/project.pbxproj.tmpl index 14af272eea6..1581057b21b 100644 --- a/packages/flutter_tools/templates/app_shared/ios-objc.tmpl/Runner.xcodeproj/project.pbxproj.tmpl +++ b/packages/flutter_tools/templates/app_shared/ios-objc.tmpl/Runner.xcodeproj/project.pbxproj.tmpl @@ -10,6 +10,9 @@ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 331C80F4294D02FB00263BE5 /* RunnerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 331C80F3294D02FB00263BE5 /* RunnerTests.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + {{#withSwiftPackageManager}} + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; + {{/withSwiftPackageManager}} 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; @@ -71,6 +74,9 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + {{#withSwiftPackageManager}} + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, + {{/withSwiftPackageManager}} ); runOnlyForDeploymentPostprocessing = 0; }; @@ -176,6 +182,11 @@ dependencies = ( ); name = Runner; + {{#withSwiftPackageManager}} + packageProductDependencies = ( + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + ); + {{/withSwiftPackageManager}} productName = Runner; productReference = 97C146EE1CF9000F007C117D /* Runner.app */; productType = "com.apple.product-type.application"; @@ -208,6 +219,11 @@ Base, ); mainGroup = 97C146E51CF9000F007C117D; + {{#withSwiftPackageManager}} + packageReferences = ( + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */, + ); + {{/withSwiftPackageManager}} productRefGroup = 97C146EF1CF9000F007C117D /* Products */; projectDirPath = ""; projectRoot = ""; @@ -619,6 +635,22 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ +{{#withSwiftPackageManager}} + +/* Begin XCLocalSwiftPackageReference section */ + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = FlutterGeneratedPluginSwiftPackage; + }; +/* End XCSwiftPackageProductDependency section */ +{{/withSwiftPackageManager}} }; rootObject = 97C146E61CF9000F007C117D /* Project object */; } diff --git a/packages/flutter_tools/templates/app_shared/ios-objc.tmpl/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/flutter_tools/templates/app_shared/ios-objc.tmpl/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme.tmpl similarity index 79% rename from packages/flutter_tools/templates/app_shared/ios-objc.tmpl/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme rename to packages/flutter_tools/templates/app_shared/ios-objc.tmpl/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme.tmpl index 3d0fb007b17..542e2ae7000 100644 --- a/packages/flutter_tools/templates/app_shared/ios-objc.tmpl/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/flutter_tools/templates/app_shared/ios-objc.tmpl/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme.tmpl @@ -5,6 +5,26 @@ + {{#withSwiftPackageManager}} + + + + + + + + + + + {{/withSwiftPackageManager}} {{#withPlatformChannelPluginHook}} +{{#withSwiftPackageManager}} +// If your plugin has been explicitly set to "type: .dynamic" in the Package.swift, +// you will need to add your plugin as a dependency of RunnerTests within Xcode. +{{/withSwiftPackageManager}} + @import {{pluginProjectName}}; // This demonstrates a simple unit test of the Objective-C portion of this plugin's implementation. diff --git a/packages/flutter_tools/templates/app_shared/ios-swift.tmpl/Runner.xcodeproj/project.pbxproj.tmpl b/packages/flutter_tools/templates/app_shared/ios-swift.tmpl/Runner.xcodeproj/project.pbxproj.tmpl index d54d3bc9b3a..8043764061d 100644 --- a/packages/flutter_tools/templates/app_shared/ios-swift.tmpl/Runner.xcodeproj/project.pbxproj.tmpl +++ b/packages/flutter_tools/templates/app_shared/ios-swift.tmpl/Runner.xcodeproj/project.pbxproj.tmpl @@ -11,6 +11,9 @@ 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + {{#withSwiftPackageManager}} + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; + {{/withSwiftPackageManager}} 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; @@ -62,6 +65,9 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + {{#withSwiftPackageManager}} + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, + {{/withSwiftPackageManager}} ); runOnlyForDeploymentPostprocessing = 0; }; @@ -157,6 +163,11 @@ dependencies = ( ); name = Runner; + {{#withSwiftPackageManager}} + packageProductDependencies = ( + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + ); + {{/withSwiftPackageManager}} productName = Runner; productReference = 97C146EE1CF9000F007C117D /* Runner.app */; productType = "com.apple.product-type.application"; @@ -190,6 +201,11 @@ Base, ); mainGroup = 97C146E51CF9000F007C117D; + {{#withSwiftPackageManager}} + packageReferences = ( + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */, + ); + {{/withSwiftPackageManager}} productRefGroup = 97C146EF1CF9000F007C117D /* Products */; projectDirPath = ""; projectRoot = ""; @@ -620,6 +636,22 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ +{{#withSwiftPackageManager}} + +/* Begin XCLocalSwiftPackageReference section */ + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = FlutterGeneratedPluginSwiftPackage; + }; +/* End XCSwiftPackageProductDependency section */ +{{/withSwiftPackageManager}} }; rootObject = 97C146E61CF9000F007C117D /* Project object */; } diff --git a/packages/flutter_tools/templates/app_shared/ios-swift.tmpl/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/flutter_tools/templates/app_shared/ios-swift.tmpl/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme.tmpl similarity index 79% rename from packages/flutter_tools/templates/app_shared/ios-swift.tmpl/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme rename to packages/flutter_tools/templates/app_shared/ios-swift.tmpl/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme.tmpl index 8e3ca5dfe19..844b54e6c06 100644 --- a/packages/flutter_tools/templates/app_shared/ios-swift.tmpl/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/flutter_tools/templates/app_shared/ios-swift.tmpl/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme.tmpl @@ -5,6 +5,26 @@ + {{#withSwiftPackageManager}} + + + + + + + + + + + {{/withSwiftPackageManager}} + {{#withSwiftPackageManager}} + + + + + + + + + + + {{/withSwiftPackageManager}} '../LICENSE' } s.author = { 'Your Company' => 'email@example.com' } s.source = { :path => '.' } + {{#withSwiftPackageManager}} + s.source_files = '{{projectName}}/Sources/{{projectName}}/**/*' + s.public_header_files = '{{projectName}}/Sources/{{projectName}}/include/{{projectName}}/**/*.h' + {{/withSwiftPackageManager}} + {{^withSwiftPackageManager}} s.source_files = 'Classes/**/*' s.public_header_files = 'Classes/**/*.h' + {{/withSwiftPackageManager}} s.dependency 'Flutter' s.platform = :ios, '12.0' diff --git a/packages/flutter_tools/templates/plugin/ios-swift.tmpl/projectName.podspec.tmpl b/packages/flutter_tools/templates/plugin/ios-swift.tmpl/projectName.podspec.tmpl index c47c9b0d869..c1cde67129a 100644 --- a/packages/flutter_tools/templates/plugin/ios-swift.tmpl/projectName.podspec.tmpl +++ b/packages/flutter_tools/templates/plugin/ios-swift.tmpl/projectName.podspec.tmpl @@ -13,7 +13,12 @@ Pod::Spec.new do |s| s.license = { :file => '../LICENSE' } s.author = { 'Your Company' => 'email@example.com' } s.source = { :path => '.' } + {{#withSwiftPackageManager}} + s.source_files = '{{projectName}}/Sources/{{projectName}}/**/*' + {{/withSwiftPackageManager}} + {{^withSwiftPackageManager}} s.source_files = 'Classes/**/*' + {{/withSwiftPackageManager}} s.dependency 'Flutter' s.platform = :ios, '12.0' diff --git a/packages/flutter_tools/templates/plugin/ios-objc.tmpl/Classes/pluginClass.h.tmpl b/packages/flutter_tools/templates/plugin_cocoapods/ios-objc.tmpl/Classes/pluginClass.h.tmpl similarity index 100% rename from packages/flutter_tools/templates/plugin/ios-objc.tmpl/Classes/pluginClass.h.tmpl rename to packages/flutter_tools/templates/plugin_cocoapods/ios-objc.tmpl/Classes/pluginClass.h.tmpl diff --git a/packages/flutter_tools/templates/plugin/ios-objc.tmpl/Classes/pluginClass.m.tmpl b/packages/flutter_tools/templates/plugin_cocoapods/ios-objc.tmpl/Classes/pluginClass.m.tmpl similarity index 100% rename from packages/flutter_tools/templates/plugin/ios-objc.tmpl/Classes/pluginClass.m.tmpl rename to packages/flutter_tools/templates/plugin_cocoapods/ios-objc.tmpl/Classes/pluginClass.m.tmpl diff --git a/packages/flutter_tools/templates/plugin/ios-swift.tmpl/Classes/pluginClass.swift.tmpl b/packages/flutter_tools/templates/plugin_cocoapods/ios-swift.tmpl/Classes/pluginClass.swift.tmpl similarity index 100% rename from packages/flutter_tools/templates/plugin/ios-swift.tmpl/Classes/pluginClass.swift.tmpl rename to packages/flutter_tools/templates/plugin_cocoapods/ios-swift.tmpl/Classes/pluginClass.swift.tmpl diff --git a/packages/flutter_tools/templates/plugin/ios.tmpl/Assets/.gitkeep b/packages/flutter_tools/templates/plugin_cocoapods/ios.tmpl/Assets/.gitkeep similarity index 100% rename from packages/flutter_tools/templates/plugin/ios.tmpl/Assets/.gitkeep rename to packages/flutter_tools/templates/plugin_cocoapods/ios.tmpl/Assets/.gitkeep diff --git a/packages/flutter_tools/templates/plugin/macos.tmpl/Classes/pluginClass.swift.tmpl b/packages/flutter_tools/templates/plugin_cocoapods/macos.tmpl/Classes/pluginClass.swift.tmpl similarity index 100% rename from packages/flutter_tools/templates/plugin/macos.tmpl/Classes/pluginClass.swift.tmpl rename to packages/flutter_tools/templates/plugin_cocoapods/macos.tmpl/Classes/pluginClass.swift.tmpl diff --git a/packages/flutter_tools/templates/plugin_shared/macos.tmpl/projectName.podspec.tmpl b/packages/flutter_tools/templates/plugin_shared/macos.tmpl/projectName.podspec.tmpl index 72bae61e8a7..dc63236d227 100644 --- a/packages/flutter_tools/templates/plugin_shared/macos.tmpl/projectName.podspec.tmpl +++ b/packages/flutter_tools/templates/plugin_shared/macos.tmpl/projectName.podspec.tmpl @@ -20,7 +20,12 @@ Pod::Spec.new do |s| # `../src/*` so that the C sources can be shared among all target platforms. {{/withFfiPluginHook}} s.source = { :path => '.' } - s.source_files = 'Classes/**/*' + {{#withSwiftPackageManager}} + s.source_files = '{{projectName}}/Sources/{{projectName}}/**/*' + {{/withSwiftPackageManager}} + {{^withSwiftPackageManager}} + s.source_files = 'Classes/**/*' + {{/withSwiftPackageManager}} s.dependency 'FlutterMacOS' s.platform = :osx, '10.11' diff --git a/packages/flutter_tools/templates/plugin_swift_package_manager/ios-objc.tmpl/projectName.tmpl/Package.swift.tmpl b/packages/flutter_tools/templates/plugin_swift_package_manager/ios-objc.tmpl/projectName.tmpl/Package.swift.tmpl new file mode 100644 index 00000000000..31b08529fc2 --- /dev/null +++ b/packages/flutter_tools/templates/plugin_swift_package_manager/ios-objc.tmpl/projectName.tmpl/Package.swift.tmpl @@ -0,0 +1,27 @@ +// swift-tools-version: {{swiftToolsVersion}} +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "{{projectName}}", + platforms: [ + {{iosSupportedPlatform}} + ], + products: [ + .library(name: "{{swiftLibraryName}}", targets: ["{{projectName}}"]) + ], + dependencies: [], + targets: [ + .target( + name: "{{projectName}}", + dependencies: [], + resources: [ + .process("Resources"), + ], + cSettings: [ + .headerSearchPath("include/{{projectName}}"), + ] + ) + ] +) diff --git a/packages/flutter_tools/templates/plugin_swift_package_manager/ios-objc.tmpl/projectName.tmpl/Sources/projectName.tmpl/include/projectName.tmpl/pluginClass.h.tmpl b/packages/flutter_tools/templates/plugin_swift_package_manager/ios-objc.tmpl/projectName.tmpl/Sources/projectName.tmpl/include/projectName.tmpl/pluginClass.h.tmpl new file mode 100644 index 00000000000..560ffa569c7 --- /dev/null +++ b/packages/flutter_tools/templates/plugin_swift_package_manager/ios-objc.tmpl/projectName.tmpl/Sources/projectName.tmpl/include/projectName.tmpl/pluginClass.h.tmpl @@ -0,0 +1,4 @@ +#import + +@interface {{pluginClass}} : NSObject +@end diff --git a/packages/flutter_tools/templates/plugin_swift_package_manager/ios-objc.tmpl/projectName.tmpl/Sources/projectName.tmpl/pluginClass.m.tmpl b/packages/flutter_tools/templates/plugin_swift_package_manager/ios-objc.tmpl/projectName.tmpl/Sources/projectName.tmpl/pluginClass.m.tmpl new file mode 100644 index 00000000000..1aa97c644ea --- /dev/null +++ b/packages/flutter_tools/templates/plugin_swift_package_manager/ios-objc.tmpl/projectName.tmpl/Sources/projectName.tmpl/pluginClass.m.tmpl @@ -0,0 +1,20 @@ +#import "{{pluginClass}}.h" + +@implementation {{pluginClass}} ++ (void)registerWithRegistrar:(NSObject*)registrar { + FlutterMethodChannel* channel = [FlutterMethodChannel + methodChannelWithName:@"{{projectName}}" + binaryMessenger:[registrar messenger]]; + {{pluginClass}}* instance = [[{{pluginClass}} alloc] init]; + [registrar addMethodCallDelegate:instance channel:channel]; +} + +- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { + if ([@"getPlatformVersion" isEqualToString:call.method]) { + result([@"iOS " stringByAppendingString:[[UIDevice currentDevice] systemVersion]]); + } else { + result(FlutterMethodNotImplemented); + } +} + +@end diff --git a/packages/flutter_tools/templates/plugin_swift_package_manager/ios-swift.tmpl/projectName.tmpl/Package.swift.tmpl b/packages/flutter_tools/templates/plugin_swift_package_manager/ios-swift.tmpl/projectName.tmpl/Package.swift.tmpl new file mode 100644 index 00000000000..b387147eb20 --- /dev/null +++ b/packages/flutter_tools/templates/plugin_swift_package_manager/ios-swift.tmpl/projectName.tmpl/Package.swift.tmpl @@ -0,0 +1,24 @@ +// swift-tools-version: {{swiftToolsVersion}} +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "{{projectName}}", + platforms: [ + {{iosSupportedPlatform}} + ], + products: [ + .library(name: "{{swiftLibraryName}}", targets: ["{{projectName}}"]) + ], + dependencies: [], + targets: [ + .target( + name: "{{projectName}}", + dependencies: [], + resources: [ + .process("Resources"), + ] + ) + ] +) diff --git a/packages/flutter_tools/templates/plugin_swift_package_manager/ios-swift.tmpl/projectName.tmpl/Sources/projectName.tmpl/pluginClass.swift.tmpl b/packages/flutter_tools/templates/plugin_swift_package_manager/ios-swift.tmpl/projectName.tmpl/Sources/projectName.tmpl/pluginClass.swift.tmpl new file mode 100644 index 00000000000..9b8e116b4f5 --- /dev/null +++ b/packages/flutter_tools/templates/plugin_swift_package_manager/ios-swift.tmpl/projectName.tmpl/Sources/projectName.tmpl/pluginClass.swift.tmpl @@ -0,0 +1,19 @@ +import Flutter +import UIKit + +public class {{pluginClass}}: NSObject, FlutterPlugin { + public static func register(with registrar: FlutterPluginRegistrar) { + let channel = FlutterMethodChannel(name: "{{projectName}}", binaryMessenger: registrar.messenger()) + let instance = {{pluginClass}}() + registrar.addMethodCallDelegate(instance, channel: channel) + } + + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { + switch call.method { + case "getPlatformVersion": + result("iOS " + UIDevice.current.systemVersion) + default: + result(FlutterMethodNotImplemented) + } + } +} diff --git a/packages/flutter_tools/templates/plugin_swift_package_manager/ios.tmpl/projectName.tmpl/Sources/projectName.tmpl/Resources/.gitkeep b/packages/flutter_tools/templates/plugin_swift_package_manager/ios.tmpl/projectName.tmpl/Sources/projectName.tmpl/Resources/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/flutter_tools/templates/plugin_swift_package_manager/macos.tmpl/projectName.tmpl/Package.swift.tmpl b/packages/flutter_tools/templates/plugin_swift_package_manager/macos.tmpl/projectName.tmpl/Package.swift.tmpl new file mode 100644 index 00000000000..8625943765b --- /dev/null +++ b/packages/flutter_tools/templates/plugin_swift_package_manager/macos.tmpl/projectName.tmpl/Package.swift.tmpl @@ -0,0 +1,24 @@ +// swift-tools-version: {{swiftToolsVersion}} +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "{{projectName}}", + platforms: [ + {{macosSupportedPlatform}} + ], + products: [ + .library(name: "{{swiftLibraryName}}", targets: ["{{projectName}}"]) + ], + dependencies: [], + targets: [ + .target( + name: "{{projectName}}", + dependencies: [], + resources: [ + .process("Resources"), + ] + ) + ] +) diff --git a/packages/flutter_tools/templates/plugin_swift_package_manager/macos.tmpl/projectName.tmpl/Sources/projectName.tmpl/Resources/.gitkeep b/packages/flutter_tools/templates/plugin_swift_package_manager/macos.tmpl/projectName.tmpl/Sources/projectName.tmpl/Resources/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/flutter_tools/templates/plugin_swift_package_manager/macos.tmpl/projectName.tmpl/Sources/projectName.tmpl/pluginClass.swift.tmpl b/packages/flutter_tools/templates/plugin_swift_package_manager/macos.tmpl/projectName.tmpl/Sources/projectName.tmpl/pluginClass.swift.tmpl new file mode 100644 index 00000000000..d782168ed28 --- /dev/null +++ b/packages/flutter_tools/templates/plugin_swift_package_manager/macos.tmpl/projectName.tmpl/Sources/projectName.tmpl/pluginClass.swift.tmpl @@ -0,0 +1,19 @@ +import Cocoa +import FlutterMacOS + +public class {{pluginClass}}: NSObject, FlutterPlugin { + public static func register(with registrar: FlutterPluginRegistrar) { + let channel = FlutterMethodChannel(name: "{{projectName}}", binaryMessenger: registrar.messenger) + let instance = {{pluginClass}}() + registrar.addMethodCallDelegate(instance, channel: channel) + } + + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { + switch call.method { + case "getPlatformVersion": + result("macOS " + ProcessInfo.processInfo.operatingSystemVersionString) + default: + result(FlutterMethodNotImplemented) + } + } +} diff --git a/packages/flutter_tools/templates/template_manifest.json b/packages/flutter_tools/templates/template_manifest.json index a42c182fc23..fe5c970e5d9 100644 --- a/packages/flutter_tools/templates/template_manifest.json +++ b/packages/flutter_tools/templates/template_manifest.json @@ -40,13 +40,13 @@ "templates/app_shared/android.tmpl/gradle/wrapper/gradle-wrapper.properties.tmpl", "templates/app_shared/android.tmpl/settings.gradle", "templates/app_shared/ios-objc.tmpl/Runner.xcodeproj/project.pbxproj.tmpl", - "templates/app_shared/ios-objc.tmpl/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme", + "templates/app_shared/ios-objc.tmpl/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme.tmpl", "templates/app_shared/ios-objc.tmpl/Runner/AppDelegate.h", "templates/app_shared/ios-objc.tmpl/Runner/AppDelegate.m", "templates/app_shared/ios-objc.tmpl/Runner/main.m", "templates/app_shared/ios-objc.tmpl/RunnerTests/RunnerTests.m.tmpl", "templates/app_shared/ios-swift.tmpl/Runner.xcodeproj/project.pbxproj.tmpl", - "templates/app_shared/ios-swift.tmpl/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme", + "templates/app_shared/ios-swift.tmpl/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme.tmpl", "templates/app_shared/ios-swift.tmpl/Runner/AppDelegate.swift", "templates/app_shared/ios-swift.tmpl/Runner/Runner-Bridging-Header.h", "templates/app_shared/ios-swift.tmpl/RunnerTests/RunnerTests.swift.tmpl", @@ -277,13 +277,9 @@ "templates/plugin/android.tmpl/gradle.properties.tmpl", "templates/plugin/android.tmpl/settings.gradle.tmpl", "templates/plugin/android.tmpl/src/main/AndroidManifest.xml.tmpl", - "templates/plugin/ios-objc.tmpl/Classes/pluginClass.h.tmpl", - "templates/plugin/ios-objc.tmpl/Classes/pluginClass.m.tmpl", "templates/plugin/ios-objc.tmpl/projectName.podspec.tmpl", - "templates/plugin/ios-swift.tmpl/Classes/pluginClass.swift.tmpl", "templates/plugin/ios-swift.tmpl/projectName.podspec.tmpl", "templates/plugin/ios.tmpl/.gitignore", - "templates/plugin/ios.tmpl/Assets/.gitkeep", "templates/plugin/lib/projectName.dart.tmpl", "templates/plugin/lib/projectName_platform_interface.dart.tmpl", "templates/plugin/lib/projectName_method_channel.dart.tmpl", @@ -292,7 +288,6 @@ "templates/plugin/linux.tmpl/pluginClassSnakeCase.cc.tmpl", "templates/plugin/linux.tmpl/pluginClassSnakeCase_private.h.tmpl", "templates/plugin/linux.tmpl/test/pluginClassSnakeCase_test.cc.tmpl", - "templates/plugin/macos.tmpl/Classes/pluginClass.swift.tmpl", "templates/plugin/README.md.tmpl", "templates/plugin/test/projectName_test.dart.tmpl", "templates/plugin/test/projectName_method_channel_test.dart.tmpl", @@ -338,6 +333,22 @@ "templates/plugin_shared/pubspec.yaml.tmpl", "templates/plugin_shared/windows.tmpl/.gitignore", + "templates/plugin_cocoapods/ios-objc.tmpl/Classes/pluginClass.h.tmpl", + "templates/plugin_cocoapods/ios-objc.tmpl/Classes/pluginClass.m.tmpl", + "templates/plugin_cocoapods/ios-swift.tmpl/Classes/pluginClass.swift.tmpl", + "templates/plugin_cocoapods/ios.tmpl/Assets/.gitkeep", + "templates/plugin_cocoapods/macos.tmpl/Classes/pluginClass.swift.tmpl", + + "templates/plugin_swift_package_manager/ios-objc.tmpl/projectName.tmpl/Sources/projectName.tmpl/include/projectName.tmpl/pluginClass.h.tmpl", + "templates/plugin_swift_package_manager/ios-objc.tmpl/projectName.tmpl/Sources/projectName.tmpl/pluginClass.m.tmpl", + "templates/plugin_swift_package_manager/ios-objc.tmpl/projectName.tmpl/Package.swift.tmpl", + "templates/plugin_swift_package_manager/ios-swift.tmpl/projectName.tmpl/Sources/projectName.tmpl/pluginClass.swift.tmpl", + "templates/plugin_swift_package_manager/ios-swift.tmpl/projectName.tmpl/Package.swift.tmpl", + "templates/plugin_swift_package_manager/ios.tmpl/projectName.tmpl/Sources/projectName.tmpl/Resources/.gitkeep", + "templates/plugin_swift_package_manager/macos.tmpl/projectName.tmpl/Sources/projectName.tmpl/Resources/.gitkeep", + "templates/plugin_swift_package_manager/macos.tmpl/projectName.tmpl/Sources/projectName.tmpl/pluginClass.swift.tmpl", + "templates/plugin_swift_package_manager/macos.tmpl/projectName.tmpl/Package.swift.tmpl", + "templates/skeleton/assets/images/2.0x/flutter_logo.png.img.tmpl", "templates/skeleton/assets/images/3.0x/flutter_logo.png.img.tmpl", "templates/skeleton/assets/images/flutter_logo.png.img.tmpl", diff --git a/packages/flutter_tools/test/commands.shard/hermetic/create_usage_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/create_usage_test.dart index a3230581de1..ad80e02bacc 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/create_usage_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/create_usage_test.dart @@ -87,6 +87,7 @@ void main() { globals.fs.path.join('flutter', 'packages', 'flutter_tools', 'templates', 'plugin'), globals.fs.path.join('flutter', 'packages', 'flutter_tools', 'templates', 'plugin_ffi'), globals.fs.path.join('flutter', 'packages', 'flutter_tools', 'templates', 'plugin_shared'), + globals.fs.path.join('flutter', 'packages', 'flutter_tools', 'templates', 'plugin_cocoapods'), ]; for (final String templatePath in templatePaths) { globals.fs.directory(templatePath).createSync(recursive: true); diff --git a/packages/flutter_tools/test/commands.shard/permeable/create_test.dart b/packages/flutter_tools/test/commands.shard/permeable/create_test.dart index 78a267a1d2b..106115991c8 100644 --- a/packages/flutter_tools/test/commands.shard/permeable/create_test.dart +++ b/packages/flutter_tools/test/commands.shard/permeable/create_test.dart @@ -718,6 +718,55 @@ void main() { ); }); + testUsingContext('swift plugin project with Swift Package Manager', () async { + return _createProject( + projectDir, + ['--no-pub', '--template=plugin', '--ios-language', 'swift', '--platforms', 'ios,macos'], + [ + 'ios/flutter_project/Package.swift', + 'ios/flutter_project/Sources/flutter_project/FlutterProjectPlugin.swift', + 'ios/flutter_project/Sources/flutter_project/Resources/.gitkeep', + 'macos/flutter_project/Package.swift', + 'macos/flutter_project/Sources/flutter_project/FlutterProjectPlugin.swift', + 'macos/flutter_project/Sources/flutter_project/Resources/.gitkeep', + ], + unexpectedPaths: [ + 'ios/Classes/FlutterProjectPlugin.swift', + 'macos/Classes/FlutterProjectPlugin.swift', + 'ios/Classes/FlutterProjectPlugin.h', + 'ios/Classes/FlutterProjectPlugin.m', + 'ios/Assets/.gitkeep', + 'macos/Assets/.gitkeep', + ], + ); + }, overrides: { + FeatureFlags: () => TestFeatureFlags( + isSwiftPackageManagerEnabled: true, + isMacOSEnabled: true, + ), + }); + + testUsingContext('objc plugin project with Swift Package Manager', () async { + return _createProject( + projectDir, + ['--no-pub', '--template=plugin', '--ios-language', 'objc', '--platforms', 'ios'], + [ + 'ios/flutter_project/Package.swift', + 'ios/flutter_project/Sources/flutter_project/include/flutter_project/FlutterProjectPlugin.h', + 'ios/flutter_project/Sources/flutter_project/FlutterProjectPlugin.m', + 'ios/flutter_project/Sources/flutter_project/Resources/.gitkeep', + ], + unexpectedPaths: [ + 'ios/Classes/FlutterProjectPlugin.swift', + 'ios/Classes/FlutterProjectPlugin.h', + 'ios/Classes/FlutterProjectPlugin.m', + 'ios/Assets/.gitkeep', + ], + ); + }, overrides: { + FeatureFlags: () => TestFeatureFlags(isSwiftPackageManagerEnabled: true), + }); + testUsingContext('plugin project with custom org', () async { return _createProject( projectDir, diff --git a/packages/flutter_tools/test/integration.shard/swift_package_manager_create_app_test.dart b/packages/flutter_tools/test/integration.shard/swift_package_manager_create_app_test.dart new file mode 100644 index 00000000000..f40e0f6de23 --- /dev/null +++ b/packages/flutter_tools/test/integration.shard/swift_package_manager_create_app_test.dart @@ -0,0 +1,151 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter_tools/src/base/error_handling_io.dart'; +import 'package:flutter_tools/src/base/file_system.dart'; + +import '../src/common.dart'; +import 'swift_package_manager_utils.dart'; +import 'test_utils.dart'; + +void main() { + final String flutterBin = fileSystem.path.join( + getFlutterRoot(), + 'bin', + 'flutter', + ); + + final List platforms = ['ios', 'macos']; + for (final String platformName in platforms) { + final List iosLanguages = [ + if (platformName == 'ios') 'objc', + 'swift', + ]; + + for (final String iosLanguage in iosLanguages) { + test('Create $platformName $iosLanguage app with Swift Package Manager disabled', () async { + final Directory workingDirectory = fileSystem.systemTempDirectory + .createTempSync('swift_package_manager_create_app_disabled.'); + final String workingDirectoryPath = workingDirectory.path; + try { + await SwiftPackageManagerUtils.disableSwiftPackageManager(flutterBin, workingDirectoryPath); + + final String appDirectoryPath = await SwiftPackageManagerUtils.createApp( + flutterBin, + workingDirectoryPath, + iosLanguage: iosLanguage, + platform: platformName, + options: ['--platforms=$platformName'], + ); + + final File pbxprojFile = fileSystem + .directory(appDirectoryPath) + .childDirectory(platformName) + .childDirectory('Runner.xcodeproj') + .childFile('project.pbxproj'); + expect(pbxprojFile.existsSync(), isTrue); + expect( + pbxprojFile.readAsStringSync().contains('FlutterGeneratedPluginSwiftPackage'), + isFalse, + ); + + final File xcschemeFile = fileSystem + .directory(appDirectoryPath) + .childDirectory(platformName) + .childDirectory('Runner.xcodeproj') + .childDirectory('xcshareddata') + .childDirectory('xcschemes') + .childFile('Runner.xcscheme'); + expect(xcschemeFile.existsSync(), isTrue); + expect( + xcschemeFile.readAsStringSync().contains('Run Prepare Flutter Framework Script'), + isFalse, + ); + + await SwiftPackageManagerUtils.buildApp( + flutterBin, + appDirectoryPath, + options: [platformName, '--debug', '-v'], + expectedLines: SwiftPackageManagerUtils.expectedLines( + platform: platformName, + appDirectoryPath: appDirectoryPath, + ), + unexpectedLines: SwiftPackageManagerUtils.unexpectedLines( + platform: platformName, + appDirectoryPath: appDirectoryPath, + ), + ); + } finally { + await SwiftPackageManagerUtils.disableSwiftPackageManager(flutterBin, workingDirectoryPath); + ErrorHandlingFileSystem.deleteIfExists( + workingDirectory, + recursive: true, + ); + } + }, skip: !platform.isMacOS); // [intended] Swift Package Manager only works on macos. + + test('Create $platformName $iosLanguage app with Swift Package Manager enabled', () async { + final Directory workingDirectory = fileSystem.systemTempDirectory + .createTempSync('swift_package_manager_create_app_enabled.'); + final String workingDirectoryPath = workingDirectory.path; + try { + await SwiftPackageManagerUtils.enableSwiftPackageManager(flutterBin, workingDirectoryPath); + + final String appDirectoryPath = await SwiftPackageManagerUtils.createApp( + flutterBin, + workingDirectoryPath, + iosLanguage: iosLanguage, + platform: platformName, + options: ['--platforms=$platformName'], + ); + + final File pbxprojFile = fileSystem + .directory(appDirectoryPath) + .childDirectory(platformName) + .childDirectory('Runner.xcodeproj') + .childFile('project.pbxproj'); + expect(pbxprojFile.existsSync(), isTrue); + expect( + pbxprojFile.readAsStringSync(), + contains('FlutterGeneratedPluginSwiftPackage'), + ); + + final File xcschemeFile = fileSystem + .directory(appDirectoryPath) + .childDirectory(platformName) + .childDirectory('Runner.xcodeproj') + .childDirectory('xcshareddata') + .childDirectory('xcschemes') + .childFile('Runner.xcscheme'); + expect(xcschemeFile.existsSync(), isTrue); + expect( + xcschemeFile.readAsStringSync(), + contains('Run Prepare Flutter Framework Script'), + ); + + await SwiftPackageManagerUtils.buildApp( + flutterBin, + appDirectoryPath, + options: [platformName, '--debug', '-v'], + expectedLines: SwiftPackageManagerUtils.expectedLines( + platform: platformName, + appDirectoryPath: appDirectoryPath, + ), + unexpectedLines: SwiftPackageManagerUtils.unexpectedLines( + platform: platformName, + appDirectoryPath: appDirectoryPath, + ), + ); + + } finally { + await SwiftPackageManagerUtils.disableSwiftPackageManager(flutterBin, workingDirectoryPath); + ErrorHandlingFileSystem.deleteIfExists( + workingDirectory, + recursive: true, + ); + } + }, skip: !platform.isMacOS); // [intended] Swift Package Manager only works on macos. + } + } +} diff --git a/packages/flutter_tools/test/integration.shard/swift_package_manager_create_plugin_test.dart b/packages/flutter_tools/test/integration.shard/swift_package_manager_create_plugin_test.dart new file mode 100644 index 00000000000..64b9761f97e --- /dev/null +++ b/packages/flutter_tools/test/integration.shard/swift_package_manager_create_plugin_test.dart @@ -0,0 +1,175 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter_tools/src/base/error_handling_io.dart'; +import 'package:flutter_tools/src/base/file_system.dart'; + +import '../src/common.dart'; +import 'swift_package_manager_utils.dart'; +import 'test_utils.dart'; + +void main() { + final String flutterBin = fileSystem.path.join( + getFlutterRoot(), + 'bin', + 'flutter', + ); + + final List platforms = ['ios', 'macos']; + for (final String platformName in platforms) { + final List iosLanguages = [ + if (platformName == 'ios') 'objc', + 'swift', + ]; + + for (final String iosLanguage in iosLanguages) { + test('Create $platformName $iosLanguage plugin with Swift Package Manager disabled', () async { + final Directory workingDirectory = fileSystem.systemTempDirectory + .createTempSync('swift_package_manager_create_plugin_disabled.'); + final String workingDirectoryPath = workingDirectory.path; + try { + await SwiftPackageManagerUtils.disableSwiftPackageManager(flutterBin, workingDirectoryPath); + + final SwiftPackageManagerPlugin createdCocoaPodsPlugin = await SwiftPackageManagerUtils.createPlugin( + flutterBin, + workingDirectoryPath, + platform: platformName, + iosLanguage: iosLanguage, + ); + + final String appDirectoryPath = createdCocoaPodsPlugin.exampleAppPath; + + final File pbxprojFile = fileSystem + .directory(appDirectoryPath) + .childDirectory(platformName) + .childDirectory('Runner.xcodeproj') + .childFile('project.pbxproj'); + expect(pbxprojFile.existsSync(), isTrue); + expect( + pbxprojFile.readAsStringSync().contains('FlutterGeneratedPluginSwiftPackage'), + isFalse, + ); + + final File xcschemeFile = fileSystem + .directory(appDirectoryPath) + .childDirectory(platformName) + .childDirectory('Runner.xcodeproj') + .childDirectory('xcshareddata') + .childDirectory('xcschemes') + .childFile('Runner.xcscheme'); + expect(xcschemeFile.existsSync(), isTrue); + expect( + xcschemeFile.readAsStringSync().contains('Run Prepare Flutter Framework Script'), + isFalse, + ); + + final File podspec = fileSystem + .directory(createdCocoaPodsPlugin.pluginPath) + .childDirectory(platformName) + .childFile('${createdCocoaPodsPlugin.pluginName}.podspec'); + expect(podspec.existsSync(), isTrue); + expect(podspec.readAsStringSync(), contains('Classes')); + expect(podspec.readAsStringSync().contains('Sources'), isFalse); + + await SwiftPackageManagerUtils.buildApp( + flutterBin, + appDirectoryPath, + options: [platformName, '--debug', '-v'], + expectedLines: SwiftPackageManagerUtils.expectedLines( + platform: platformName, + appDirectoryPath: appDirectoryPath, + cocoaPodsPlugin: createdCocoaPodsPlugin, + ), + unexpectedLines: SwiftPackageManagerUtils.unexpectedLines( + platform: platformName, + appDirectoryPath: appDirectoryPath, + cocoaPodsPlugin: createdCocoaPodsPlugin, + ), + ); + } finally { + await SwiftPackageManagerUtils.disableSwiftPackageManager(flutterBin, workingDirectoryPath); + ErrorHandlingFileSystem.deleteIfExists( + workingDirectory, + recursive: true, + ); + } + }, skip: !platform.isMacOS); // [intended] Swift Package Manager only works on macos. + + test('Create $platformName $iosLanguage plugin with Swift Package Manager enabled', () async { + final Directory workingDirectory = fileSystem.systemTempDirectory + .createTempSync('swift_package_manager_create_plugin_enabled.'); + final String workingDirectoryPath = workingDirectory.path; + try { + await SwiftPackageManagerUtils.enableSwiftPackageManager(flutterBin, workingDirectoryPath); + + final SwiftPackageManagerPlugin createdSwiftPackagePlugin = await SwiftPackageManagerUtils.createPlugin( + flutterBin, + workingDirectoryPath, + platform: platformName, + iosLanguage: iosLanguage, + ); + + final String appDirectoryPath = createdSwiftPackagePlugin.exampleAppPath; + + final File pbxprojFile = fileSystem + .directory(appDirectoryPath) + .childDirectory(platformName) + .childDirectory('Runner.xcodeproj') + .childFile('project.pbxproj'); + expect(pbxprojFile.existsSync(), isTrue); + expect( + pbxprojFile.readAsStringSync(), + contains('FlutterGeneratedPluginSwiftPackage'), + ); + + final File xcschemeFile = fileSystem + .directory(appDirectoryPath) + .childDirectory(platformName) + .childDirectory('Runner.xcodeproj') + .childDirectory('xcshareddata') + .childDirectory('xcschemes') + .childFile('Runner.xcscheme'); + expect(xcschemeFile.existsSync(), isTrue); + expect( + xcschemeFile.readAsStringSync(), + contains('Run Prepare Flutter Framework Script'), + ); + + final File podspec = fileSystem + .directory(createdSwiftPackagePlugin.pluginPath) + .childDirectory(platformName) + .childFile('${createdSwiftPackagePlugin.pluginName}.podspec'); + expect(podspec.existsSync(), isTrue); + expect(podspec.readAsStringSync(), contains('Sources')); + expect(podspec.readAsStringSync().contains('Classes'), isFalse); + + await SwiftPackageManagerUtils.buildApp( + flutterBin, + appDirectoryPath, + options: [platformName, '--debug', '-v'], + expectedLines: SwiftPackageManagerUtils.expectedLines( + platform: platformName, + appDirectoryPath: appDirectoryPath, + swiftPackagePlugin: createdSwiftPackagePlugin, + swiftPackageMangerEnabled: true, + ), + unexpectedLines: SwiftPackageManagerUtils.unexpectedLines( + platform: platformName, + appDirectoryPath: appDirectoryPath, + swiftPackagePlugin: createdSwiftPackagePlugin, + swiftPackageMangerEnabled: true, + ), + ); + + } finally { + await SwiftPackageManagerUtils.disableSwiftPackageManager(flutterBin, workingDirectoryPath); + ErrorHandlingFileSystem.deleteIfExists( + workingDirectory, + recursive: true, + ); + } + }, skip: !platform.isMacOS); // [intended] Swift Package Manager only works on macos. + } + } +} diff --git a/packages/flutter_tools/test/integration.shard/swift_package_manager_test.dart b/packages/flutter_tools/test/integration.shard/swift_package_manager_test.dart index 5c224e0eae1..0f6f585a997 100644 --- a/packages/flutter_tools/test/integration.shard/swift_package_manager_test.dart +++ b/packages/flutter_tools/test/integration.shard/swift_package_manager_test.dart @@ -2,13 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:convert'; - import 'package:flutter_tools/src/base/error_handling_io.dart'; import 'package:flutter_tools/src/base/file_system.dart'; -import 'package:flutter_tools/src/base/io.dart'; import '../src/common.dart'; +import 'swift_package_manager_utils.dart'; import 'test_utils.dart'; void main() { @@ -24,39 +22,36 @@ void main() { if (platformName == 'ios') 'objc', 'swift', ]; - final _Plugin integrationTestPlugin = _integrationTestPlugin(platformName); + final SwiftPackageManagerPlugin integrationTestPlugin = SwiftPackageManagerUtils.integrationTestPlugin(platformName); for (final String iosLanguage in iosLanguages) { - test('Swift Package Manager not used when feature is disabled for $platformName with $iosLanguage', () async { + test('Swift Package Manager integration for $platformName with $iosLanguage', () async { final Directory workingDirectory = fileSystem.systemTempDirectory - .createTempSync('swift_package_manager_disabled.'); + .createTempSync('swift_package_manager_enabled.'); final String workingDirectoryPath = workingDirectory.path; try { - await _disableSwiftPackageManager(flutterBin, workingDirectoryPath); - // Create and build an app using the CocoaPods version of // integration_test. - final String appDirectoryPath = await _createApp( + await SwiftPackageManagerUtils.disableSwiftPackageManager(flutterBin, workingDirectoryPath); + final String appDirectoryPath = await SwiftPackageManagerUtils.createApp( flutterBin, workingDirectoryPath, iosLanguage: iosLanguage, platform: platformName, + usesSwiftPackageManager: true, options: ['--platforms=$platformName'], ); - _addDependency( - appDirectoryPath: appDirectoryPath, - plugin: integrationTestPlugin, - ); - await _buildApp( + SwiftPackageManagerUtils.addDependency(appDirectoryPath: appDirectoryPath, plugin: integrationTestPlugin); + await SwiftPackageManagerUtils.buildApp( flutterBin, appDirectoryPath, options: [platformName, '--debug', '-v'], - expectedLines: _expectedLines( + expectedLines: SwiftPackageManagerUtils.expectedLines( platform: platformName, appDirectoryPath: appDirectoryPath, cocoaPodsPlugin: integrationTestPlugin, ), - unexpectedLines: _unexpectedLines( + unexpectedLines: SwiftPackageManagerUtils.unexpectedLines( platform: platformName, appDirectoryPath: appDirectoryPath, cocoaPodsPlugin: integrationTestPlugin, @@ -81,47 +76,34 @@ void main() { .existsSync(), isFalse, ); - } finally { - ErrorHandlingFileSystem.deleteIfExists( - workingDirectory, - recursive: true, - ); - } - }, skip: !platform.isMacOS); // [intended] Swift Package Manager only works on macos. - test('Swift Package Manager integration for $platformName with $iosLanguage', () async { - final Directory workingDirectory = fileSystem.systemTempDirectory - .createTempSync('swift_package_manager_enabled.'); - final String workingDirectoryPath = workingDirectory.path; - try { - // Create and build an app using the Swift Package Manager version of - // integration_test. - await _enableSwiftPackageManager(flutterBin, workingDirectoryPath); - - final String appDirectoryPath = await _createApp( + final SwiftPackageManagerPlugin createdCocoaPodsPlugin = await SwiftPackageManagerUtils.createPlugin( flutterBin, workingDirectoryPath, - iosLanguage: iosLanguage, platform: platformName, - usesSwiftPackageManager: true, - options: ['--platforms=$platformName'], + iosLanguage: iosLanguage, ); - _addDependency(appDirectoryPath: appDirectoryPath, plugin: integrationTestPlugin); - await _buildApp( + + // Rebuild app with Swift Package Manager enabled, migrating the app and using the Swift Package Manager version of + // integration_test. + await SwiftPackageManagerUtils.enableSwiftPackageManager(flutterBin, workingDirectoryPath); + await SwiftPackageManagerUtils.buildApp( flutterBin, appDirectoryPath, options: [platformName, '--debug', '-v'], - expectedLines: _expectedLines( + expectedLines: SwiftPackageManagerUtils.expectedLines( platform: platformName, appDirectoryPath: appDirectoryPath, swiftPackageMangerEnabled: true, swiftPackagePlugin: integrationTestPlugin, + migrated: true, ), - unexpectedLines: _unexpectedLines( + unexpectedLines: SwiftPackageManagerUtils.unexpectedLines( platform: platformName, appDirectoryPath: appDirectoryPath, swiftPackageMangerEnabled: true, swiftPackagePlugin: integrationTestPlugin, + migrated: true, ), ); @@ -131,7 +113,7 @@ void main() { .childDirectory(platformName) .childFile('Podfile') .existsSync(), - isFalse, + isTrue, ); expect( fileSystem @@ -146,29 +128,22 @@ void main() { ); // Build an app using both a CocoaPods and Swift Package Manager plugin. - await _cleanApp(flutterBin, appDirectoryPath); - final _Plugin createdCocoaPodsPlugin = await _createPlugin( - flutterBin, - workingDirectoryPath, - platform: platformName, - iosLanguage: iosLanguage, - ); - _addDependency( + SwiftPackageManagerUtils.addDependency( appDirectoryPath: appDirectoryPath, plugin: createdCocoaPodsPlugin, ); - await _buildApp( + await SwiftPackageManagerUtils.buildApp( flutterBin, appDirectoryPath, options: [platformName, '--debug', '-v'], - expectedLines: _expectedLines( + expectedLines: SwiftPackageManagerUtils.expectedLines( platform: platformName, appDirectoryPath: appDirectoryPath, cocoaPodsPlugin: createdCocoaPodsPlugin, swiftPackageMangerEnabled: true, swiftPackagePlugin: integrationTestPlugin, ), - unexpectedLines: _unexpectedLines( + unexpectedLines: SwiftPackageManagerUtils.unexpectedLines( platform: platformName, appDirectoryPath: appDirectoryPath, cocoaPodsPlugin: createdCocoaPodsPlugin, @@ -199,18 +174,18 @@ void main() { // Build app again but with Swift Package Manager disabled by config. // App will now use CocoaPods version of integration_test plugin. - await _disableSwiftPackageManager(flutterBin, workingDirectoryPath); - await _cleanApp(flutterBin, appDirectoryPath); - await _buildApp( + await SwiftPackageManagerUtils.disableSwiftPackageManager(flutterBin, workingDirectoryPath); + await SwiftPackageManagerUtils.cleanApp(flutterBin, appDirectoryPath); + await SwiftPackageManagerUtils.buildApp( flutterBin, appDirectoryPath, options: [platformName, '--debug', '-v'], - expectedLines: _expectedLines( + expectedLines: SwiftPackageManagerUtils.expectedLines( platform: platformName, appDirectoryPath: appDirectoryPath, cocoaPodsPlugin: integrationTestPlugin, ), - unexpectedLines: _unexpectedLines( + unexpectedLines: SwiftPackageManagerUtils.unexpectedLines( platform: platformName, appDirectoryPath: appDirectoryPath, cocoaPodsPlugin: integrationTestPlugin, @@ -219,26 +194,26 @@ void main() { // Build app again but with Swift Package Manager disabled by pubspec. // App will still use CocoaPods version of integration_test plugin. - await _enableSwiftPackageManager(flutterBin, workingDirectoryPath); - await _cleanApp(flutterBin, appDirectoryPath); - _disableSwiftPackageManagerByPubspec(appDirectoryPath: appDirectoryPath); - await _buildApp( + await SwiftPackageManagerUtils.enableSwiftPackageManager(flutterBin, workingDirectoryPath); + await SwiftPackageManagerUtils.cleanApp(flutterBin, appDirectoryPath); + SwiftPackageManagerUtils.disableSwiftPackageManagerByPubspec(appDirectoryPath: appDirectoryPath); + await SwiftPackageManagerUtils.buildApp( flutterBin, appDirectoryPath, options: [platformName, '--debug', '-v'], - expectedLines: _expectedLines( + expectedLines: SwiftPackageManagerUtils.expectedLines( platform: platformName, appDirectoryPath: appDirectoryPath, cocoaPodsPlugin: integrationTestPlugin, ), - unexpectedLines: _unexpectedLines( + unexpectedLines: SwiftPackageManagerUtils.unexpectedLines( platform: platformName, appDirectoryPath: appDirectoryPath, cocoaPodsPlugin: integrationTestPlugin, ), ); } finally { - await _disableSwiftPackageManager(flutterBin, workingDirectoryPath); + await SwiftPackageManagerUtils.disableSwiftPackageManager(flutterBin, workingDirectoryPath); ErrorHandlingFileSystem.deleteIfExists( workingDirectory, recursive: true, @@ -254,9 +229,9 @@ void main() { try { // Create and build an app using the Swift Package Manager version of // integration_test. - await _enableSwiftPackageManager(flutterBin, workingDirectoryPath); + await SwiftPackageManagerUtils.enableSwiftPackageManager(flutterBin, workingDirectoryPath); - final String appDirectoryPath = await _createApp( + final String appDirectoryPath = await SwiftPackageManagerUtils.createApp( flutterBin, workingDirectoryPath, iosLanguage: 'swift', @@ -264,15 +239,12 @@ void main() { usesSwiftPackageManager: true, options: ['--platforms=$platformName'], ); - _addDependency(appDirectoryPath: appDirectoryPath, plugin: integrationTestPlugin); + SwiftPackageManagerUtils.addDependency(appDirectoryPath: appDirectoryPath, plugin: integrationTestPlugin); - await _buildApp( + await SwiftPackageManagerUtils.buildApp( flutterBin, appDirectoryPath, options: [platformName, '--config-only', '-v'], - expectedLines: [ - 'Adding Swift Package Manager integration...' - ] ); expect( @@ -297,7 +269,7 @@ void main() { // Create and build framework using the CocoaPods version of // integration_test even though Swift Package Manager is enabled. - await _buildApp( + await SwiftPackageManagerUtils.buildApp( flutterBin, appDirectoryPath, options: [ @@ -323,7 +295,7 @@ void main() { isTrue, ); } finally { - await _disableSwiftPackageManager(flutterBin, workingDirectoryPath); + await SwiftPackageManagerUtils.disableSwiftPackageManager(flutterBin, workingDirectoryPath); ErrorHandlingFileSystem.deleteIfExists( workingDirectory, recursive: true, @@ -339,9 +311,9 @@ void main() { try { // Create and build module and framework using the CocoaPods version of // integration_test even though Swift Package Manager is enabled. - await _enableSwiftPackageManager(flutterBin, workingDirectoryPath); + await SwiftPackageManagerUtils.enableSwiftPackageManager(flutterBin, workingDirectoryPath); - final String appDirectoryPath = await _createApp( + final String appDirectoryPath = await SwiftPackageManagerUtils.createApp( flutterBin, workingDirectoryPath, iosLanguage: 'swift', @@ -349,16 +321,13 @@ void main() { usesSwiftPackageManager: true, options: ['--template=module'], ); - final _Plugin integrationTestPlugin = _integrationTestPlugin('ios'); - _addDependency(appDirectoryPath: appDirectoryPath, plugin: integrationTestPlugin); + final SwiftPackageManagerPlugin integrationTestPlugin = SwiftPackageManagerUtils.integrationTestPlugin('ios'); + SwiftPackageManagerUtils.addDependency(appDirectoryPath: appDirectoryPath, plugin: integrationTestPlugin); - await _buildApp( + await SwiftPackageManagerUtils.buildApp( flutterBin, appDirectoryPath, options: ['ios', '--config-only', '-v'], - unexpectedLines: [ - 'Adding Swift Package Manager integration...' - ] ); expect( @@ -380,8 +349,30 @@ void main() { .existsSync(), isFalse, ); + final File pbxprojFile = fileSystem + .directory(appDirectoryPath) + .childDirectory('.ios') + .childDirectory('Runner.xcodeproj') + .childFile('project.pbxproj'); + expect(pbxprojFile.existsSync(), isTrue); + expect( + pbxprojFile.readAsStringSync().contains('FlutterGeneratedPluginSwiftPackage'), + isFalse, + ); + final File xcschemeFile = fileSystem + .directory(appDirectoryPath) + .childDirectory('.ios') + .childDirectory('Runner.xcodeproj') + .childDirectory('xcshareddata') + .childDirectory('xcschemes') + .childFile('Runner.xcscheme'); + expect(xcschemeFile.existsSync(), isTrue); + expect( + xcschemeFile.readAsStringSync().contains('Run Prepare Flutter Framework Script'), + isFalse, + ); - await _buildApp( + await SwiftPackageManagerUtils.buildApp( flutterBin, appDirectoryPath, options: [ @@ -408,7 +399,7 @@ void main() { isTrue, ); } finally { - await _disableSwiftPackageManager(flutterBin, workingDirectoryPath); + await SwiftPackageManagerUtils.disableSwiftPackageManager(flutterBin, workingDirectoryPath); ErrorHandlingFileSystem.deleteIfExists( workingDirectory, recursive: true, @@ -416,355 +407,3 @@ void main() { } }, skip: !platform.isMacOS); // [intended] Swift Package Manager only works on macos. } - -Future _enableSwiftPackageManager( - String flutterBin, - String workingDirectory, -) async { - final ProcessResult result = await processManager.run( - [ - flutterBin, - ...getLocalEngineArguments(), - 'config', - '--enable-swift-package-manager', - '-v', - ], - workingDirectory: workingDirectory, - ); - expect( - result.exitCode, - 0, - reason: 'Failed to enable Swift Package Manager: \n' - 'stdout: \n${result.stdout}\n' - 'stderr: \n${result.stderr}\n', - verbose: true, - ); -} - -Future _disableSwiftPackageManager( - String flutterBin, - String workingDirectory, -) async { - final ProcessResult result = await processManager.run( - [ - flutterBin, - ...getLocalEngineArguments(), - 'config', - '--no-enable-swift-package-manager', - '-v', - ], - workingDirectory: workingDirectory, - ); - expect( - result.exitCode, - 0, - reason: 'Failed to disable Swift Package Manager: \n' - 'stdout: \n${result.stdout}\n' - 'stderr: \n${result.stderr}\n', - verbose: true, - ); -} - -Future _createApp( - String flutterBin, - String workingDirectory, { - required String platform, - required String iosLanguage, - required List options, - bool usesSwiftPackageManager = false, -}) async { - final String appTemplateType = usesSwiftPackageManager ? 'spm' : 'default'; - - final String appName = '${platform}_${iosLanguage}_${appTemplateType}_app'; - final ProcessResult result = await processManager.run( - [ - flutterBin, - ...getLocalEngineArguments(), - 'create', - '--org', - 'io.flutter.devicelab', - '-i', - iosLanguage, - ...options, - appName, - ], - workingDirectory: workingDirectory, - ); - - expect( - result.exitCode, - 0, - reason: 'Failed to create app: \n' - 'stdout: \n${result.stdout}\n' - 'stderr: \n${result.stderr}\n', - ); - - return fileSystem.path.join( - workingDirectory, - appName, - ); -} - -Future _buildApp( - String flutterBin, - String workingDirectory, { - required List options, - List? expectedLines, - List? unexpectedLines, -}) async { - final List remainingExpectedLines = expectedLines ?? []; - final List unexpectedLinesFound = []; - final List command = [ - flutterBin, - ...getLocalEngineArguments(), - 'build', - ...options, - ]; - - final ProcessResult result = await processManager.run( - command, - workingDirectory: workingDirectory, - ); - - final List stdout = LineSplitter.split(result.stdout.toString()).toList(); - final List stderr = LineSplitter.split(result.stderr.toString()).toList(); - final List output = stdout + stderr; - for (final String line in output) { - // Remove "[ +3 ms] " prefix - String trimmedLine = line.trim(); - if (trimmedLine.startsWith('[')) { - final int prefixEndIndex = trimmedLine.indexOf(']'); - if (prefixEndIndex > 0) { - trimmedLine = trimmedLine - .substring(prefixEndIndex + 1, trimmedLine.length) - .trim(); - } - } - remainingExpectedLines.remove(trimmedLine); - remainingExpectedLines.removeWhere((Pattern expectedLine) => trimmedLine.contains(expectedLine)); - if (unexpectedLines != null && unexpectedLines.contains(trimmedLine)) { - unexpectedLinesFound.add(trimmedLine); - } - } - expect( - result.exitCode, - 0, - reason: 'Failed to build app for "${command.join(' ')}":\n' - 'stdout: \n${result.stdout}\n' - 'stderr: \n${result.stderr}\n', - ); - expect( - remainingExpectedLines, - isEmpty, - reason: 'Did not find expected lines for "${command.join(' ')}":\n' - 'stdout: \n${result.stdout}\n' - 'stderr: \n${result.stderr}\n', - ); - expect( - unexpectedLinesFound, - isEmpty, - reason: 'Found unexpected lines for "${command.join(' ')}":\n' - 'stdout: \n${result.stdout}\n' - 'stderr: \n${result.stderr}\n', - ); -} - -Future _cleanApp(String flutterBin, String workingDirectory) async { - final ProcessResult result = await processManager.run( - [ - flutterBin, - ...getLocalEngineArguments(), - 'clean', - ], - workingDirectory: workingDirectory, - ); - expect( - result.exitCode, - 0, - reason: 'Failed to clean app: \n' - 'stdout: \n${result.stdout}\n' - 'stderr: \n${result.stderr}\n', - ); -} - -Future<_Plugin> _createPlugin( - String flutterBin, - String workingDirectory, { - required String platform, - required String iosLanguage, - bool usesSwiftPackageManager = false, -}) async { - final String dependencyManager = usesSwiftPackageManager ? 'spm' : 'cocoapods'; - - // Create plugin - final String pluginName = '${platform}_${iosLanguage}_${dependencyManager}_plugin'; - final ProcessResult result = await processManager.run( - [ - flutterBin, - ...getLocalEngineArguments(), - 'create', - '--org', - 'io.flutter.devicelab', - '--template=plugin', - '--platforms=$platform', - '-i', - iosLanguage, - pluginName, - ], - workingDirectory: workingDirectory, - ); - - expect( - result.exitCode, - 0, - reason: 'Failed to create plugin: \n' - 'stdout: \n${result.stdout}\n' - 'stderr: \n${result.stderr}\n', - ); - - final Directory pluginDirectory = fileSystem.directory( - fileSystem.path.join(workingDirectory, pluginName), - ); - - return _Plugin( - pluginName: pluginName, - pluginPath: pluginDirectory.path, - platform: platform, - ); -} - -void _addDependency({ - required _Plugin plugin, - required String appDirectoryPath, -}) { - final File pubspec = fileSystem.file( - fileSystem.path.join(appDirectoryPath, 'pubspec.yaml'), - ); - final String pubspecContent = pubspec.readAsStringSync(); - pubspec.writeAsStringSync( - pubspecContent.replaceFirst( - '\ndependencies:\n', - '\ndependencies:\n ${plugin.pluginName}:\n path: ${plugin.pluginPath}\n', - ), - ); -} - -void _disableSwiftPackageManagerByPubspec({ - required String appDirectoryPath, -}) { - final File pubspec = fileSystem.file( - fileSystem.path.join(appDirectoryPath, 'pubspec.yaml'), - ); - final String pubspecContent = pubspec.readAsStringSync(); - pubspec.writeAsStringSync( - pubspecContent.replaceFirst( - '\n# The following section is specific to Flutter packages.\nflutter:\n', - '\n# The following section is specific to Flutter packages.\nflutter:\n disable-swift-package-manager: true', - ), - ); -} - -_Plugin _integrationTestPlugin(String platform) { - final String flutterRoot = getFlutterRoot(); - return _Plugin( - platform: platform, - pluginName: - (platform == 'ios') ? 'integration_test' : 'integration_test_macos', - pluginPath: (platform == 'ios') - ? fileSystem.path.join(flutterRoot, 'packages', 'integration_test') - : fileSystem.path.join(flutterRoot, 'packages', 'integration_test', 'integration_test_macos'), - ); -} - -List _expectedLines({ - required String platform, - required String appDirectoryPath, - _Plugin? cocoaPodsPlugin, - _Plugin? swiftPackagePlugin, - bool swiftPackageMangerEnabled = false, -}) { - final String frameworkName = platform == 'ios' ? 'Flutter' : 'FlutterMacOS'; - final String appPlatformDirectoryPath = fileSystem.path.join( - appDirectoryPath, - platform, - ); - - final List expectedLines = []; - if (swiftPackageMangerEnabled) { - expectedLines.addAll([ - 'FlutterGeneratedPluginSwiftPackage: $appPlatformDirectoryPath/Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage', - "➜ Explicit dependency on target 'FlutterGeneratedPluginSwiftPackage' in project 'FlutterGeneratedPluginSwiftPackage'", - ]); - } - if (swiftPackagePlugin != null) { - // If using a Swift Package plugin, but Swift Package Manager is not enabled, it falls back to being used as a CocoaPods plugin. - if (swiftPackageMangerEnabled) { - expectedLines.addAll([ - RegExp('${swiftPackagePlugin.pluginName}: [/private]*${swiftPackagePlugin.pluginPath}/$platform/${swiftPackagePlugin.pluginName} @ local'), - "➜ Explicit dependency on target '${swiftPackagePlugin.pluginName}' in project '${swiftPackagePlugin.pluginName}'", - ]); - } else { - expectedLines.addAll([ - '-> Installing ${swiftPackagePlugin.pluginName} (0.0.1)', - "➜ Explicit dependency on target '${swiftPackagePlugin.pluginName}' in project 'Pods'", - ]); - } - } - if (cocoaPodsPlugin != null) { - expectedLines.addAll([ - 'Running pod install...', - '-> Installing $frameworkName (1.0.0)', - '-> Installing ${cocoaPodsPlugin.pluginName} (0.0.1)', - "Target 'Pods-Runner' in project 'Pods'", - "➜ Explicit dependency on target '$frameworkName' in project 'Pods'", - "➜ Explicit dependency on target '${cocoaPodsPlugin.pluginName}' in project 'Pods'", - ]); - } - return expectedLines; -} - -List _unexpectedLines({ - required String platform, - required String appDirectoryPath, - _Plugin? cocoaPodsPlugin, - _Plugin? swiftPackagePlugin, - bool swiftPackageMangerEnabled = false, -}) { - final String frameworkName = platform == 'ios' ? 'Flutter' : 'FlutterMacOS'; - final List unexpectedLines = []; - if (cocoaPodsPlugin == null) { - unexpectedLines.addAll([ - 'Running pod install...', - '-> Installing $frameworkName (1.0.0)', - "Target 'Pods-Runner' in project 'Pods'", - ]); - } - if (swiftPackagePlugin != null) { - if (swiftPackageMangerEnabled) { - unexpectedLines.addAll([ - '-> Installing ${swiftPackagePlugin.pluginName} (0.0.1)', - "➜ Explicit dependency on target '${swiftPackagePlugin.pluginName}' in project 'Pods'", - ]); - } else { - unexpectedLines.addAll([ - '${swiftPackagePlugin.pluginName}: ${swiftPackagePlugin.pluginPath}/$platform/${swiftPackagePlugin.pluginName} @ local', - "➜ Explicit dependency on target '${swiftPackagePlugin.pluginName}' in project '${swiftPackagePlugin.pluginName}'", - ]); - } - } - return unexpectedLines; -} - -class _Plugin { - _Plugin({ - required this.pluginName, - required this.pluginPath, - required this.platform, - }); - - final String pluginName; - final String pluginPath; - final String platform; - String get exampleAppPath => fileSystem.path.join(pluginPath, 'example'); - String get exampleAppPlatformPath => fileSystem.path.join(exampleAppPath, platform); -} diff --git a/packages/flutter_tools/test/integration.shard/swift_package_manager_utils.dart b/packages/flutter_tools/test/integration.shard/swift_package_manager_utils.dart new file mode 100644 index 00000000000..dd23eaed9f1 --- /dev/null +++ b/packages/flutter_tools/test/integration.shard/swift_package_manager_utils.dart @@ -0,0 +1,379 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:convert'; + +import 'package:flutter_tools/src/base/file_system.dart'; +import 'package:flutter_tools/src/base/io.dart'; + +import '../src/common.dart'; +import 'test_utils.dart'; + +class SwiftPackageManagerUtils { + static Future enableSwiftPackageManager( + String flutterBin, + String workingDirectory, + ) async { + final ProcessResult result = await processManager.run( + [ + flutterBin, + ...getLocalEngineArguments(), + 'config', + '--enable-swift-package-manager', + '-v', + ], + workingDirectory: workingDirectory, + ); + expect( + result.exitCode, + 0, + reason: 'Failed to enable Swift Package Manager: \n' + 'stdout: \n${result.stdout}\n' + 'stderr: \n${result.stderr}\n', + verbose: true, + ); + } + + static Future disableSwiftPackageManager( + String flutterBin, + String workingDirectory, + ) async { + final ProcessResult result = await processManager.run( + [ + flutterBin, + ...getLocalEngineArguments(), + 'config', + '--no-enable-swift-package-manager', + '-v', + ], + workingDirectory: workingDirectory, + ); + expect( + result.exitCode, + 0, + reason: 'Failed to disable Swift Package Manager: \n' + 'stdout: \n${result.stdout}\n' + 'stderr: \n${result.stderr}\n', + verbose: true, + ); + } + + static Future createApp( + String flutterBin, + String workingDirectory, { + required String platform, + required String iosLanguage, + required List options, + bool usesSwiftPackageManager = false, + }) async { + final String appTemplateType = usesSwiftPackageManager ? 'spm' : 'default'; + + final String appName = '${platform}_${iosLanguage}_${appTemplateType}_app'; + final ProcessResult result = await processManager.run( + [ + flutterBin, + ...getLocalEngineArguments(), + 'create', + '--org', + 'io.flutter.devicelab', + '-i', + iosLanguage, + ...options, + appName, + ], + workingDirectory: workingDirectory, + ); + + expect( + result.exitCode, + 0, + reason: 'Failed to create app: \n' + 'stdout: \n${result.stdout}\n' + 'stderr: \n${result.stderr}\n', + ); + + return fileSystem.path.join( + workingDirectory, + appName, + ); + } + + static Future buildApp( + String flutterBin, + String workingDirectory, { + required List options, + List? expectedLines, + List? unexpectedLines, + }) async { + final List remainingExpectedLines = expectedLines ?? []; + final List unexpectedLinesFound = []; + final List command = [ + flutterBin, + ...getLocalEngineArguments(), + 'build', + ...options, + ]; + + final ProcessResult result = await processManager.run( + command, + workingDirectory: workingDirectory, + ); + + final List stdout = LineSplitter.split(result.stdout.toString()).toList(); + final List stderr = LineSplitter.split(result.stderr.toString()).toList(); + final List output = stdout + stderr; + for (final String line in output) { + // Remove "[ +3 ms] " prefix + String trimmedLine = line.trim(); + if (trimmedLine.startsWith('[')) { + final int prefixEndIndex = trimmedLine.indexOf(']'); + if (prefixEndIndex > 0) { + trimmedLine = trimmedLine + .substring(prefixEndIndex + 1, trimmedLine.length) + .trim(); + } + } + remainingExpectedLines.remove(trimmedLine); + remainingExpectedLines.removeWhere((Pattern expectedLine) => trimmedLine.contains(expectedLine)); + if (unexpectedLines != null && unexpectedLines.contains(trimmedLine)) { + unexpectedLinesFound.add(trimmedLine); + } + } + expect( + result.exitCode, + 0, + reason: 'Failed to build app for "${command.join(' ')}":\n' + 'stdout: \n${result.stdout}\n' + 'stderr: \n${result.stderr}\n', + ); + expect( + remainingExpectedLines, + isEmpty, + reason: 'Did not find expected lines for "${command.join(' ')}":\n' + 'stdout: \n${result.stdout}\n' + 'stderr: \n${result.stderr}\n', + ); + expect( + unexpectedLinesFound, + isEmpty, + reason: 'Found unexpected lines for "${command.join(' ')}":\n' + 'stdout: \n${result.stdout}\n' + 'stderr: \n${result.stderr}\n', + ); + } + + static Future cleanApp(String flutterBin, String workingDirectory) async { + final ProcessResult result = await processManager.run( + [ + flutterBin, + ...getLocalEngineArguments(), + 'clean', + ], + workingDirectory: workingDirectory, + ); + expect( + result.exitCode, + 0, + reason: 'Failed to clean app: \n' + 'stdout: \n${result.stdout}\n' + 'stderr: \n${result.stderr}\n', + ); + } + + static Future createPlugin( + String flutterBin, + String workingDirectory, { + required String platform, + required String iosLanguage, + bool usesSwiftPackageManager = false, + }) async { + final String dependencyManager = usesSwiftPackageManager ? 'spm' : 'cocoapods'; + + // Create plugin + final String pluginName = '${platform}_${iosLanguage}_${dependencyManager}_plugin'; + final ProcessResult result = await processManager.run( + [ + flutterBin, + ...getLocalEngineArguments(), + 'create', + '--org', + 'io.flutter.devicelab', + '--template=plugin', + '--platforms=$platform', + '-i', + iosLanguage, + pluginName, + ], + workingDirectory: workingDirectory, + ); + + expect( + result.exitCode, + 0, + reason: 'Failed to create plugin: \n' + 'stdout: \n${result.stdout}\n' + 'stderr: \n${result.stderr}\n', + ); + + final Directory pluginDirectory = fileSystem.directory( + fileSystem.path.join(workingDirectory, pluginName), + ); + + return SwiftPackageManagerPlugin( + pluginName: pluginName, + pluginPath: pluginDirectory.path, + platform: platform, + ); + } + + static void addDependency({ + required SwiftPackageManagerPlugin plugin, + required String appDirectoryPath, + }) { + final File pubspec = fileSystem.file( + fileSystem.path.join(appDirectoryPath, 'pubspec.yaml'), + ); + final String pubspecContent = pubspec.readAsStringSync(); + pubspec.writeAsStringSync( + pubspecContent.replaceFirst( + '\ndependencies:\n', + '\ndependencies:\n ${plugin.pluginName}:\n path: ${plugin.pluginPath}\n', + ), + ); + } + + static void disableSwiftPackageManagerByPubspec({ + required String appDirectoryPath, + }) { + final File pubspec = fileSystem.file( + fileSystem.path.join(appDirectoryPath, 'pubspec.yaml'), + ); + final String pubspecContent = pubspec.readAsStringSync(); + pubspec.writeAsStringSync( + pubspecContent.replaceFirst( + '\n# The following section is specific to Flutter packages.\nflutter:\n', + '\n# The following section is specific to Flutter packages.\nflutter:\n disable-swift-package-manager: true', + ), + ); + } + + static SwiftPackageManagerPlugin integrationTestPlugin(String platform) { + final String flutterRoot = getFlutterRoot(); + return SwiftPackageManagerPlugin( + platform: platform, + pluginName: + (platform == 'ios') ? 'integration_test' : 'integration_test_macos', + pluginPath: (platform == 'ios') + ? fileSystem.path.join(flutterRoot, 'packages', 'integration_test') + : fileSystem.path.join(flutterRoot, 'packages', 'integration_test', 'integration_test_macos'), + ); + } + + static List expectedLines({ + required String platform, + required String appDirectoryPath, + SwiftPackageManagerPlugin? cocoaPodsPlugin, + SwiftPackageManagerPlugin? swiftPackagePlugin, + bool swiftPackageMangerEnabled = false, + bool migrated = false, + }) { + final String frameworkName = platform == 'ios' ? 'Flutter' : 'FlutterMacOS'; + final String appPlatformDirectoryPath = fileSystem.path.join( + appDirectoryPath, + platform, + ); + + final List expectedLines = []; + if (swiftPackageMangerEnabled) { + expectedLines.addAll([ + 'FlutterGeneratedPluginSwiftPackage: $appPlatformDirectoryPath/Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage', + "➜ Explicit dependency on target 'FlutterGeneratedPluginSwiftPackage' in project 'FlutterGeneratedPluginSwiftPackage'", + ]); + } + if (swiftPackagePlugin != null) { + // If using a Swift Package plugin, but Swift Package Manager is not enabled, it falls back to being used as a CocoaPods plugin. + if (swiftPackageMangerEnabled) { + expectedLines.addAll([ + RegExp('${swiftPackagePlugin.pluginName}: [/private]*${swiftPackagePlugin.pluginPath}/$platform/${swiftPackagePlugin.pluginName} @ local'), + "➜ Explicit dependency on target '${swiftPackagePlugin.pluginName}' in project '${swiftPackagePlugin.pluginName}'", + ]); + } else { + expectedLines.addAll([ + '-> Installing ${swiftPackagePlugin.pluginName} (0.0.1)', + "➜ Explicit dependency on target '${swiftPackagePlugin.pluginName}' in project 'Pods'", + ]); + } + } + if (cocoaPodsPlugin != null) { + expectedLines.addAll([ + 'Running pod install...', + '-> Installing $frameworkName (1.0.0)', + '-> Installing ${cocoaPodsPlugin.pluginName} (0.0.1)', + "Target 'Pods-Runner' in project 'Pods'", + "➜ Explicit dependency on target '$frameworkName' in project 'Pods'", + "➜ Explicit dependency on target '${cocoaPodsPlugin.pluginName}' in project 'Pods'", + ]); + } + if (migrated) { + expectedLines.addAll([ + 'Adding Swift Package Manager integration...', + 'Running pod install...', + "Target 'Pods-Runner' in project 'Pods'", + ]); + } + return expectedLines; + } + + static List unexpectedLines({ + required String platform, + required String appDirectoryPath, + SwiftPackageManagerPlugin? cocoaPodsPlugin, + SwiftPackageManagerPlugin? swiftPackagePlugin, + bool swiftPackageMangerEnabled = false, + bool migrated = false, + }) { + final String frameworkName = platform == 'ios' ? 'Flutter' : 'FlutterMacOS'; + final List unexpectedLines = []; + if (cocoaPodsPlugin == null && !migrated) { + unexpectedLines.addAll([ + 'Running pod install...', + '-> Installing $frameworkName (1.0.0)', + "Target 'Pods-Runner' in project 'Pods'", + ]); + } + if (swiftPackagePlugin != null) { + if (swiftPackageMangerEnabled) { + unexpectedLines.addAll([ + '-> Installing ${swiftPackagePlugin.pluginName} (0.0.1)', + "➜ Explicit dependency on target '${swiftPackagePlugin.pluginName}' in project 'Pods'", + ]); + } else { + unexpectedLines.addAll([ + '${swiftPackagePlugin.pluginName}: ${swiftPackagePlugin.pluginPath}/$platform/${swiftPackagePlugin.pluginName} @ local', + "➜ Explicit dependency on target '${swiftPackagePlugin.pluginName}' in project '${swiftPackagePlugin.pluginName}'", + ]); + } + } + if (!migrated) { + unexpectedLines.addAll([ + 'Adding Swift Package Manager integration...', + ]); + } + return unexpectedLines; + } +} + +class SwiftPackageManagerPlugin { + SwiftPackageManagerPlugin({ + required this.pluginName, + required this.pluginPath, + required this.platform, + }); + + final String pluginName; + final String pluginPath; + final String platform; + String get exampleAppPath => fileSystem.path.join(pluginPath, 'example'); + String get exampleAppPlatformPath => fileSystem.path.join(exampleAppPath, platform); +}