Move Flutter.framework to build directory instead of ios/Flutter (#70224)
diff --git a/packages/flutter_tools/bin/podhelper.rb b/packages/flutter_tools/bin/podhelper.rb
index 455bdb3..87b495c 100644
--- a/packages/flutter_tools/bin/podhelper.rb
+++ b/packages/flutter_tools/bin/podhelper.rb
@@ -31,7 +31,19 @@
# [target.deployment_target] is a [String] formatted as "8.0".
inherit_deployment_target = target.deployment_target[/\d+/].to_i < 9
+
+ # This podhelper script is at $FLUTTER_ROOT/packages/flutter_tools/bin.
+ # Add search paths from $FLUTTER_ROOT/bin/cache/artifacts/engine.
+ artifacts_dir = File.join('..', '..', '..', '..', 'bin', 'cache', 'artifacts', 'engine')
+ debug_framework_dir = File.expand_path(File.join(artifacts_dir, 'ios'), __FILE__)
+ release_framework_dir = File.expand_path(File.join(artifacts_dir, 'ios-release'), __FILE__)
+
target.build_configurations.each do |build_configuration|
+ # Profile can't be derived from the CocoaPods build configuration. Use release framework (for linking only).
+ configuration_engine_dir = build_configuration.type == :debug ? debug_framework_dir : release_framework_dir
+ build_configuration.build_settings['FRAMEWORK_SEARCH_PATHS'] = "\"#{configuration_engine_dir}\" $(inherited)"
+ build_configuration.build_settings['OTHER_LDFLAGS'] = '$(inherited) -framework Flutter'
+
build_configuration.build_settings['CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER'] = 'NO'
build_configuration.build_settings['ENABLE_BITCODE'] = 'NO'
# Suppress warning when pod supports a version lower than the minimum supported by Xcode (Xcode 12 - iOS 9).
@@ -68,28 +80,33 @@
ios_application_path ||= File.dirname(defined_in_file.realpath) if self.respond_to?(:defined_in_file)
raise 'Could not find iOS application path' unless ios_application_path
- copied_flutter_dir = File.join(ios_application_path, 'Flutter')
- copied_framework_path = File.join(copied_flutter_dir, 'Flutter.framework')
- copied_podspec_path = File.join(copied_flutter_dir, 'Flutter.podspec')
+ copied_podspec_path = File.expand_path('Flutter.podspec', File.join(ios_application_path, 'Flutter'))
- unless File.exist?(copied_framework_path) && File.exist?(copied_podspec_path)
- # Copy Flutter.framework and Flutter.podspec to Flutter/ to have something to link against if the xcode backend script has not run yet.
- # That script will copy the correct debug/profile/release version of the framework based on the currently selected Xcode configuration,
- # which can handle a local engine.
- # CocoaPods will not embed the framework on pod install (before any build phases can generate) if the dylib does not exist.
+ # Generate a fake podspec to represent the Flutter framework.
+ # This is only necessary because plugin podspecs contain `s.dependency 'Flutter'`, and if this Podfile
+ # does not add a `pod 'Flutter'` CocoaPods will try to download it from the CoocaPods trunk.
+ File.open(copied_podspec_path, 'w') { |podspec|
+ podspec.write <<~EOF
+ #
+ # NOTE: This podspec is NOT to be published. It is only used as a local source!
+ # This is a generated file; do not edit or check into version control.
+ #
- # This podhelper script is at $FLUTTER_ROOT/packages/flutter_tools/bin.
- # Copy frameworks from $FLUTTER_ROOT/bin/cache/artifacts/engine/ios (Debug).
- debug_framework_dir = File.expand_path(File.join('..', '..', '..', '..', 'bin', 'cache', 'artifacts', 'engine', 'ios'), __FILE__)
-
- unless File.exist?(copied_framework_path)
- # Avoid the complication of dependencies like FileUtils.
- system('cp', '-r', File.expand_path('Flutter.framework', debug_framework_dir), copied_flutter_dir)
- end
- unless File.exist?(copied_podspec_path)
- system('cp', File.expand_path('Flutter.podspec', debug_framework_dir), copied_flutter_dir)
- end
- end
+ Pod::Spec.new do |s|
+ s.name = 'Flutter'
+ s.version = '1.0.0'
+ s.summary = 'High-performance, high-fidelity mobile apps.'
+ s.homepage = 'https://flutter.io'
+ s.license = { :type => 'MIT' }
+ s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' }
+ s.source = { :git => 'https://github.com/flutter/engine', :tag => s.version.to_s }
+ s.ios.deployment_target = '8.0'
+ # Framework linking is handled by Flutter tooling, not CocoaPods.
+ # Add a placeholder to satisfy `s.dependency 'Flutter'` plugin podspecs.
+ s.vendored_frameworks = 'path/to/nothing'
+ end
+ EOF
+ }
# Keep pod path relative so it can be checked into Podfile.lock.
pod 'Flutter', :path => 'Flutter'
diff --git a/packages/flutter_tools/bin/xcode_backend.sh b/packages/flutter_tools/bin/xcode_backend.sh
index e86fb3f..3ff708e 100755
--- a/packages/flutter_tools/bin/xcode_backend.sh
+++ b/packages/flutter_tools/bin/xcode_backend.sh
@@ -145,16 +145,12 @@
bitcode_flag="true"
fi
- # TODO(jonahwilliams): move engine copying to build system.
+ # TODO(jmagman): use assemble copied engine in add-to-app.
if [[ -e "${project_path}/.ios" ]]; then
RunCommand rm -rf -- "${derived_dir}/engine"
mkdir "${derived_dir}/engine"
RunCommand cp -r -- "${flutter_podspec}" "${derived_dir}/engine"
RunCommand cp -r -- "${flutter_framework}" "${derived_dir}/engine"
- else
- RunCommand rm -rf -- "${derived_dir}/Flutter.framework"
- RunCommand cp -- "${flutter_podspec}" "${derived_dir}"
- RunCommand cp -r -- "${flutter_framework}" "${derived_dir}"
fi
RunCommand pushd "${project_path}" > /dev/null
@@ -297,20 +293,11 @@
# Embed the actual Flutter.framework that the Flutter app expects to run against,
# which could be a local build or an arch/type specific build.
- # Prefer the hidden .ios folder, but fallback to a visible ios folder if .ios
- # doesn't exist.
- local flutter_ios_engine_folder="${project_path}/.ios/Flutter/engine"
- if [[ ! -d ${flutter_ios_engine_folder} ]]; then
- flutter_ios_engine_folder="${project_path}/ios/Flutter"
- fi
-
- AssertExists "${flutter_ios_engine_folder}"
-
# Copy Xcode behavior and don't copy over headers or modules.
- RunCommand rsync -av --delete --filter "- .DS_Store/" --filter "- Headers/" --filter "- Modules/" "${flutter_ios_engine_folder}/Flutter.framework" "${xcode_frameworks_dir}/"
+ RunCommand rsync -av --delete --filter "- .DS_Store/" --filter "- Headers/" --filter "- Modules/" "${BUILT_PRODUCTS_DIR}/Flutter.framework" "${xcode_frameworks_dir}/"
if [[ "$ACTION" != "install" || "$ENABLE_BITCODE" == "NO" ]]; then
# Strip bitcode from the destination unless archiving, or if bitcode is disabled entirely.
- RunCommand "${DT_TOOLCHAIN_DIR}"/usr/bin/bitcode_strip "${flutter_ios_engine_folder}/Flutter.framework/Flutter" -r -o "${xcode_frameworks_dir}/Flutter.framework/Flutter"
+ RunCommand "${DT_TOOLCHAIN_DIR}"/usr/bin/bitcode_strip "${BUILT_PRODUCTS_DIR}/Flutter.framework/Flutter" -r -o "${xcode_frameworks_dir}/Flutter.framework/Flutter"
fi
# Sign the binaries we moved.
diff --git a/packages/flutter_tools/lib/src/build_system/targets/ios.dart b/packages/flutter_tools/lib/src/build_system/targets/ios.dart
index 4bd146a..eba8cdc 100644
--- a/packages/flutter_tools/lib/src/build_system/targets/ios.dart
+++ b/packages/flutter_tools/lib/src/build_system/targets/ios.dart
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+import 'package:meta/meta.dart';
+
import '../../artifacts.dart';
import '../../base/build.dart';
import '../../base/common.dart';
@@ -145,6 +147,7 @@
@override
List<Target> get dependencies => const <Target>[
+ ReleaseUnpackIOS(),
KernelSnapshot(),
];
}
@@ -179,6 +182,7 @@
@override
List<Target> get dependencies => const <Target>[
+ ProfileUnpackIOS(),
KernelSnapshot(),
];
}
@@ -192,6 +196,7 @@
@override
List<Target> get dependencies => const <Target>[
+ DebugUnpackIOS(),
KernelSnapshot(),
];
@@ -226,6 +231,95 @@
}
}
+/// Copy the iOS framework to the correct copy dir by invoking 'rsync'.
+///
+/// This class is abstract to share logic between the three concrete
+/// implementations. The shelling out is done to avoid complications with
+/// preserving special files (e.g., symbolic links) in the framework structure.
+abstract class UnpackIOS extends Target {
+ const UnpackIOS();
+
+ @override
+ List<Source> get inputs => <Source>[
+ const Source.pattern(
+ '{FLUTTER_ROOT}/packages/flutter_tools/lib/src/build_system/targets/ios.dart'),
+ Source.artifact(
+ Artifact.flutterFramework,
+ platform: TargetPlatform.ios,
+ mode: buildMode,
+ ),
+ ];
+
+ @override
+ List<Source> get outputs => const <Source>[
+ Source.pattern('{OUTPUT_DIR}/Flutter.framework/Flutter'),
+ ];
+
+ @override
+ List<Target> get dependencies => <Target>[];
+
+ @visibleForOverriding
+ BuildMode get buildMode;
+
+ @override
+ Future<void> build(Environment environment) async {
+ final String basePath = environment.artifacts.getArtifactPath(
+ Artifact.flutterFramework,
+ platform: TargetPlatform.ios,
+ mode: buildMode,
+ );
+
+ final ProcessResult result = environment.processManager.runSync(<String>[
+ 'rsync',
+ '-av',
+ '--delete',
+ '--filter',
+ '- .DS_Store/',
+ basePath,
+ environment.outputDir.path,
+ ]);
+ if (result.exitCode != 0) {
+ throw Exception(
+ 'Failed to copy framework (exit ${result.exitCode}:\n'
+ '${result.stdout}\n---\n${result.stderr}',
+ );
+ }
+ }
+}
+
+/// Unpack the release prebuilt engine framework.
+class ReleaseUnpackIOS extends UnpackIOS {
+ const ReleaseUnpackIOS();
+
+ @override
+ String get name => 'release_unpack_ios';
+
+ @override
+ BuildMode get buildMode => BuildMode.release;
+}
+
+/// Unpack the profile prebuilt engine framework.
+class ProfileUnpackIOS extends UnpackIOS {
+ const ProfileUnpackIOS();
+
+ @override
+ String get name => 'profile_unpack_ios';
+
+ @override
+ BuildMode get buildMode => BuildMode.profile;
+}
+
+/// Unpack the debug prebuilt engine framework.
+class DebugUnpackIOS extends UnpackIOS {
+ const DebugUnpackIOS();
+
+ @override
+ String get name => 'debug_unpack_ios';
+
+ @override
+ BuildMode get buildMode => BuildMode.debug;
+}
+
/// The base class for all iOS bundle targets.
///
/// This is responsible for setting up the basic App.framework structure, including:
diff --git a/packages/flutter_tools/lib/src/commands/clean.dart b/packages/flutter_tools/lib/src/commands/clean.dart
index 5e45dbb..6a4cc92 100644
--- a/packages/flutter_tools/lib/src/commands/clean.dart
+++ b/packages/flutter_tools/lib/src/commands/clean.dart
@@ -51,6 +51,7 @@
deleteFile(flutterProject.ios.generatedXcodePropertiesFile);
deleteFile(flutterProject.ios.generatedEnvironmentVariableExportScript);
deleteFile(flutterProject.ios.deprecatedCompiledDartFramework);
+ deleteFile(flutterProject.ios.deprecatedProjectFlutterFramework);
deleteFile(flutterProject.linux.ephemeralDirectory);
deleteFile(flutterProject.macos.ephemeralDirectory);
diff --git a/packages/flutter_tools/lib/src/project.dart b/packages/flutter_tools/lib/src/project.dart
index f32a77a..9e36c46 100644
--- a/packages/flutter_tools/lib/src/project.dart
+++ b/packages/flutter_tools/lib/src/project.dart
@@ -689,6 +689,13 @@
.childDirectory('Flutter')
.childDirectory('App.framework');
+ /// No longer copied to this location.
+ ///
+ /// Used only for "flutter clean" to remove old references.
+ Directory get deprecatedProjectFlutterFramework => _flutterLibRoot
+ .childDirectory('Flutter')
+ .childDirectory('Flutter.framework');
+
Directory get pluginRegistrantHost {
return isModule
? _flutterLibRoot
diff --git a/packages/flutter_tools/templates/app/ios-objc.tmpl/Runner.xcodeproj/project.pbxproj.tmpl b/packages/flutter_tools/templates/app/ios-objc.tmpl/Runner.xcodeproj/project.pbxproj.tmpl
index 1ac8e06..1e69d7c 100644
--- a/packages/flutter_tools/templates/app/ios-objc.tmpl/Runner.xcodeproj/project.pbxproj.tmpl
+++ b/packages/flutter_tools/templates/app/ios-objc.tmpl/Runner.xcodeproj/project.pbxproj.tmpl
@@ -300,16 +300,8 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
- FRAMEWORK_SEARCH_PATHS = (
- "$(inherited)",
- "$(PROJECT_DIR)/Flutter",
- );
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
- LIBRARY_SEARCH_PATHS = (
- "$(inherited)",
- "$(PROJECT_DIR)/Flutter",
- );
PRODUCT_BUNDLE_IDENTIFIER = {{iosIdentifier}};
PRODUCT_NAME = "$(TARGET_NAME)";
VERSIONING_SYSTEM = "apple-generic";
@@ -428,16 +420,8 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
- FRAMEWORK_SEARCH_PATHS = (
- "$(inherited)",
- "$(PROJECT_DIR)/Flutter",
- );
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
- LIBRARY_SEARCH_PATHS = (
- "$(inherited)",
- "$(PROJECT_DIR)/Flutter",
- );
PRODUCT_BUNDLE_IDENTIFIER = {{iosIdentifier}};
PRODUCT_NAME = "$(TARGET_NAME)";
VERSIONING_SYSTEM = "apple-generic";
@@ -451,16 +435,8 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
- FRAMEWORK_SEARCH_PATHS = (
- "$(inherited)",
- "$(PROJECT_DIR)/Flutter",
- );
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
- LIBRARY_SEARCH_PATHS = (
- "$(inherited)",
- "$(PROJECT_DIR)/Flutter",
- );
PRODUCT_BUNDLE_IDENTIFIER = {{iosIdentifier}};
PRODUCT_NAME = "$(TARGET_NAME)";
VERSIONING_SYSTEM = "apple-generic";
diff --git a/packages/flutter_tools/templates/app/ios-swift.tmpl/Runner.xcodeproj/project.pbxproj.tmpl b/packages/flutter_tools/templates/app/ios-swift.tmpl/Runner.xcodeproj/project.pbxproj.tmpl
index 5f6a5d4..6c82ff2 100644
--- a/packages/flutter_tools/templates/app/ios-swift.tmpl/Runner.xcodeproj/project.pbxproj.tmpl
+++ b/packages/flutter_tools/templates/app/ios-swift.tmpl/Runner.xcodeproj/project.pbxproj.tmpl
@@ -289,16 +289,8 @@
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
- FRAMEWORK_SEARCH_PATHS = (
- "$(inherited)",
- "$(PROJECT_DIR)/Flutter",
- );
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
- LIBRARY_SEARCH_PATHS = (
- "$(inherited)",
- "$(PROJECT_DIR)/Flutter",
- );
PRODUCT_BUNDLE_IDENTIFIER = {{iosIdentifier}};
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@@ -421,16 +413,8 @@
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
- FRAMEWORK_SEARCH_PATHS = (
- "$(inherited)",
- "$(PROJECT_DIR)/Flutter",
- );
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
- LIBRARY_SEARCH_PATHS = (
- "$(inherited)",
- "$(PROJECT_DIR)/Flutter",
- );
PRODUCT_BUNDLE_IDENTIFIER = {{iosIdentifier}};
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@@ -448,16 +432,8 @@
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
- FRAMEWORK_SEARCH_PATHS = (
- "$(inherited)",
- "$(PROJECT_DIR)/Flutter",
- );
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
- LIBRARY_SEARCH_PATHS = (
- "$(inherited)",
- "$(PROJECT_DIR)/Flutter",
- );
PRODUCT_BUNDLE_IDENTIFIER = {{iosIdentifier}};
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
diff --git a/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.xcodeproj.tmpl/project.pbxproj.tmpl b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.xcodeproj.tmpl/project.pbxproj.tmpl
index 8499e8b..326ad84 100644
--- a/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.xcodeproj.tmpl/project.pbxproj.tmpl
+++ b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.xcodeproj.tmpl/project.pbxproj.tmpl
@@ -297,17 +297,8 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
- FRAMEWORK_SEARCH_PATHS = (
- "$(inherited)",
- "$(PROJECT_DIR)/Flutter",
- "$(PROJECT_DIR)/Flutter/engine",
- );
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
- LIBRARY_SEARCH_PATHS = (
- "$(inherited)",
- "$(PROJECT_DIR)/Flutter",
- );
PRODUCT_BUNDLE_IDENTIFIER = {{iosIdentifier}};
PRODUCT_NAME = "$(TARGET_NAME)";
VERSIONING_SYSTEM = "apple-generic";
@@ -425,17 +416,8 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
- FRAMEWORK_SEARCH_PATHS = (
- "$(inherited)",
- "$(PROJECT_DIR)/Flutter",
- "$(PROJECT_DIR)/Flutter/engine",
- );
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
- LIBRARY_SEARCH_PATHS = (
- "$(inherited)",
- "$(PROJECT_DIR)/Flutter",
- );
PRODUCT_BUNDLE_IDENTIFIER = {{iosIdentifier}};
PRODUCT_NAME = "$(TARGET_NAME)";
VERSIONING_SYSTEM = "apple-generic";
@@ -448,17 +430,8 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
- FRAMEWORK_SEARCH_PATHS = (
- "$(inherited)",
- "$(PROJECT_DIR)/Flutter",
- "$(PROJECT_DIR)/Flutter/engine",
- );
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
- LIBRARY_SEARCH_PATHS = (
- "$(inherited)",
- "$(PROJECT_DIR)/Flutter",
- );
PRODUCT_BUNDLE_IDENTIFIER = {{iosIdentifier}};
PRODUCT_NAME = "$(TARGET_NAME)";
VERSIONING_SYSTEM = "apple-generic";
diff --git a/packages/flutter_tools/test/commands.shard/hermetic/clean_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/clean_test.dart
index 8c005e6..08eb7d4 100644
--- a/packages/flutter_tools/test/commands.shard/hermetic/clean_test.dart
+++ b/packages/flutter_tools/test/commands.shard/hermetic/clean_test.dart
@@ -49,6 +49,7 @@
projectUnderTest.ios.generatedXcodePropertiesFile.createSync(recursive: true);
projectUnderTest.ios.generatedEnvironmentVariableExportScript.createSync(recursive: true);
projectUnderTest.ios.deprecatedCompiledDartFramework.createSync(recursive: true);
+ projectUnderTest.ios.deprecatedProjectFlutterFramework.createSync(recursive: true);
projectUnderTest.linux.ephemeralDirectory.createSync(recursive: true);
projectUnderTest.macos.ephemeralDirectory.createSync(recursive: true);
@@ -69,6 +70,7 @@
expect(projectUnderTest.ios.generatedXcodePropertiesFile.existsSync(), isFalse);
expect(projectUnderTest.ios.generatedEnvironmentVariableExportScript.existsSync(), isFalse);
expect(projectUnderTest.ios.deprecatedCompiledDartFramework.existsSync(), isFalse);
+ expect(projectUnderTest.ios.deprecatedProjectFlutterFramework.existsSync(), isFalse);
expect(projectUnderTest.linux.ephemeralDirectory.existsSync(), isFalse);
expect(projectUnderTest.macos.ephemeralDirectory.existsSync(), isFalse);
diff --git a/packages/flutter_tools/test/general.shard/build_system/targets/ios_test.dart b/packages/flutter_tools/test/general.shard/build_system/targets/ios_test.dart
index bd99389..c553b3b 100644
--- a/packages/flutter_tools/test/general.shard/build_system/targets/ios_test.dart
+++ b/packages/flutter_tools/test/general.shard/build_system/targets/ios_test.dart
@@ -208,4 +208,31 @@
ProcessManager: () => processManager,
Platform: () => macPlatform,
});
+
+ testWithoutContext('Unpack copies Flutter.framework', () async {
+ final FileSystem fileSystem = MemoryFileSystem.test();
+ final Directory outputDir = fileSystem.directory('output');
+ final Environment environment = Environment.test(
+ fileSystem.currentDirectory,
+ processManager: processManager,
+ artifacts: artifacts,
+ logger: logger,
+ fileSystem: fileSystem,
+ outputDir: outputDir,
+ );
+
+ processManager.addCommand(
+ FakeCommand(command: <String>[
+ 'rsync',
+ '-av',
+ '--delete',
+ '--filter',
+ '- .DS_Store/',
+ 'Artifact.flutterFramework.TargetPlatform.ios.debug',
+ outputDir.path,
+ ]),
+ );
+
+ await const DebugUnpackIOS().build(environment);
+ });
}