Unverified Commit bd47a31e authored by Jenn Magder's avatar Jenn Magder Committed by GitHub

Vend Flutter module App.framework as a local CocoaPod pod to be installed by a host app (#36793)

parent 47eeaac1
platform :ios, '12.0'
# Prevent Cocoapods from embedding a second Flutter framework and causing an error with the new Xcode build system.
install! 'cocoapods', :disable_input_output_paths => true
flutter_application_path = 'flutterapp/'
framework_dir = File.join(flutter_application_path, '.ios', 'Flutter')
engine_dir = File.join(framework_dir, 'engine')
if !File.exist?(engine_dir)
# Copy the debug engine to have something to link against if the xcode backend script has not run yet.
debug_framework_dir = File.join(flutter_root(flutter_application_path), 'bin', 'cache', 'artifacts', 'engine', 'ios')
FileUtils.mkdir_p(engine_dir)
FileUtils.cp_r(File.join(debug_framework_dir, 'Flutter.framework'), engine_dir)
FileUtils.cp(File.join(debug_framework_dir, 'Flutter.podspec'), engine_dir)
end
load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')
target 'ios_add2app' do
eval(File.read(File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')), binding)
install_all_flutter_pods(flutter_application_path, 'ios_add2app_flutter')
end
target 'ios_add2appTests' do
pod 'Flutter', :path => engine_dir
inherit! :search_paths
install_flutter_engine_pod
pod 'EarlGrey'
end
#include "Flutter.xcconfig"
#include "../Pods/Target Support Files/Pods-Host/Pods-Host.debug.xcconfig"
#include "../../hello/.ios/Flutter/Generated.xcconfig"
ENABLE_BITCODE=NO
#include "Flutter.xcconfig"
#include "../Pods/Target Support Files/Pods-Host/Pods-Host.release.xcconfig"
FLUTTER_BUILD_MODE=release
......@@ -13,26 +13,9 @@
74F9786E215AB9E9005A0F04 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 74F9786D215AB9E9005A0F04 /* Assets.xcassets */; };
74F97871215AB9E9005A0F04 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 74F9786F215AB9E9005A0F04 /* LaunchScreen.storyboard */; };
74F97874215AB9E9005A0F04 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 74F97873215AB9E9005A0F04 /* main.m */; };
74F978AF215AD6F9005A0F04 /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 74F978AC215AD6E8005A0F04 /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
DAEA7B95412864C7F3A4AE98 /* libPods-Host.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2499C53F0BD30E24745E2F6B /* libPods-Host.a */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
74F978A2215ACF9B005A0F04 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
74F978AF215AD6F9005A0F04 /* App.framework in Embed Frameworks */,
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
2499C53F0BD30E24745E2F6B /* libPods-Host.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Host.a"; sourceTree = BUILT_PRODUCTS_DIR; };
74F97861215AB9E8005A0F04 /* Host.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Host.app; sourceTree = BUILT_PRODUCTS_DIR; };
74F97864215AB9E8005A0F04 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
74F97865215AB9E8005A0F04 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
......@@ -43,10 +26,6 @@
74F97870215AB9E9005A0F04 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
74F97872215AB9E9005A0F04 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
74F97873215AB9E9005A0F04 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
74F9787B215ABA08005A0F04 /* Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
74F9787C215ABA73005A0F04 /* Flutter.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Flutter.xcconfig; sourceTree = "<group>"; };
74F9787D215ABA9D005A0F04 /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
74F978AC215AD6E8005A0F04 /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = ../../hello/.ios/Flutter/App.framework; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
......@@ -54,7 +33,6 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
DAEA7B95412864C7F3A4AE98 /* libPods-Host.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
......@@ -64,8 +42,6 @@
74F97858215AB9E8005A0F04 = {
isa = PBXGroup;
children = (
74F978A3215AD111005A0F04 /* Flutter */,
74F9787A215AB9F3005A0F04 /* Config */,
74F97863215AB9E8005A0F04 /* Host */,
74F97862215AB9E8005A0F04 /* Products */,
74F9788B215AC328005A0F04 /* Frameworks */,
......@@ -97,32 +73,13 @@
path = Host;
sourceTree = "<group>";
};
74F9787A215AB9F3005A0F04 /* Config */ = {
isa = PBXGroup;
children = (
74F9787C215ABA73005A0F04 /* Flutter.xcconfig */,
74F9787B215ABA08005A0F04 /* Debug.xcconfig */,
74F9787D215ABA9D005A0F04 /* Release.xcconfig */,
);
path = Config;
sourceTree = "<group>";
};
74F9788B215AC328005A0F04 /* Frameworks */ = {
isa = PBXGroup;
children = (
2499C53F0BD30E24745E2F6B /* libPods-Host.a */,
);
name = Frameworks;
sourceTree = "<group>";
};
74F978A3215AD111005A0F04 /* Flutter */ = {
isa = PBXGroup;
children = (
74F978AC215AD6E8005A0F04 /* App.framework */,
);
path = Flutter;
sourceTree = "<group>";
};
A4A9971F50C4EE357B74B6E0 /* Pods */ = {
isa = PBXGroup;
children = (
......@@ -137,13 +94,9 @@
isa = PBXNativeTarget;
buildConfigurationList = 74F97877215AB9E9005A0F04 /* Build configuration list for PBXNativeTarget "Host" */;
buildPhases = (
EDACAC7378E52BD5BBDB34D5 /* [CP] Check Pods Manifest.lock */,
74F9787E215ABB1B005A0F04 /* Run Script */,
74F9785D215AB9E8005A0F04 /* Sources */,
74F9785E215AB9E8005A0F04 /* Frameworks */,
74F9785F215AB9E8005A0F04 /* Resources */,
53642CAE5328D86A50FDAD59 /* [CP] Embed Pods Frameworks */,
74F978A2215ACF9B005A0F04 /* Embed Frameworks */,
);
buildRules = (
);
......@@ -199,56 +152,6 @@
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
53642CAE5328D86A50FDAD59 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Host/Pods-Host-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
74F9787E215ABB1B005A0F04 /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Run Script";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n\"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed\n";
};
EDACAC7378E52BD5BBDB34D5 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-Host-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
74F9785D215AB9E8005A0F04 /* Sources */ = {
isa = PBXSourcesBuildPhase;
......@@ -317,6 +220,7 @@
CODE_SIGN_IDENTITY = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_BITCODE = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
......@@ -376,6 +280,7 @@
CODE_SIGN_IDENTITY = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_BITCODE = NO;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
......@@ -396,7 +301,6 @@
};
74F97878215AB9E9005A0F04 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 74F9787B215ABA08005A0F04 /* Debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
......@@ -414,7 +318,6 @@
};
74F97879215AB9E9005A0F04 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 74F9787D215ABA9D005A0F04 /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
......
#import "ViewController.h"
#import "Flutter/Flutter.h"
#import "FlutterPluginRegistrant/GeneratedPluginRegistrant.h"
#import <Flutter/Flutter.h>
#import <FlutterPluginRegistrant/GeneratedPluginRegistrant.h>
@implementation ViewController
......
platform :ios, '9.0'
# Prevent Cocoapods from embedding a second Flutter framework and causing an error with the new Xcode build system.
install! 'cocoapods', :disable_input_output_paths => true
flutter_application_path = '../hello'
load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')
target 'Host' do
flutter_application_path = '../hello'
eval(File.read("#{flutter_application_path}/.ios/Flutter/podhelper.rb"))
install_all_flutter_pods flutter_application_path
end
......@@ -10,6 +10,7 @@ import '../artifacts.dart';
import '../base/context.dart';
import '../base/file_system.dart';
import '../base/io.dart';
import '../base/os.dart';
import '../base/platform.dart';
import '../base/process.dart';
import '../base/process_manager.dart';
......@@ -48,25 +49,96 @@ Future<void> updateGeneratedXcodeProperties({
bool useMacOSConfig = false,
bool setSymroot = true,
}) async {
final List<String> xcodeBuildSettings = _xcodeBuildSettingsLines(
project: project,
buildInfo: buildInfo,
targetOverride: targetOverride,
useMacOSConfig: useMacOSConfig,
setSymroot: setSymroot
);
_updateGeneratedXcodePropertiesFile(
project: project,
xcodeBuildSettings: xcodeBuildSettings,
useMacOSConfig: useMacOSConfig,
);
_updateGeneratedEnvironmentVariablesScript(
project: project,
xcodeBuildSettings: xcodeBuildSettings,
useMacOSConfig: useMacOSConfig,
);
}
/// Generate a xcconfig file to inherit FLUTTER_ build settings
/// for Xcode targets that need them.
/// See [XcodeBasedProject.generatedXcodePropertiesFile].
void _updateGeneratedXcodePropertiesFile({
@required FlutterProject project,
@required List<String> xcodeBuildSettings,
bool useMacOSConfig = false,
}) {
final StringBuffer localsBuffer = StringBuffer();
localsBuffer.writeln('// This is a generated file; do not edit or check into version control.');
xcodeBuildSettings.forEach(localsBuffer.writeln);
final File generatedXcodePropertiesFile = useMacOSConfig
? project.macos.generatedXcodePropertiesFile
: project.ios.generatedXcodePropertiesFile;
generatedXcodePropertiesFile.createSync(recursive: true);
generatedXcodePropertiesFile.writeAsStringSync(localsBuffer.toString());
}
/// Generate a script to export all the FLUTTER_ environment variables needed
/// as flags for Flutter tools.
/// See [XcodeBasedProject.generatedEnvironmentVariableExportScript].
void _updateGeneratedEnvironmentVariablesScript({
@required FlutterProject project,
@required List<String> xcodeBuildSettings,
bool useMacOSConfig = false,
}) {
final StringBuffer localsBuffer = StringBuffer();
localsBuffer.writeln('#!/bin/sh');
localsBuffer.writeln('# This is a generated file; do not edit or check into version control.');
for (String line in xcodeBuildSettings) {
localsBuffer.writeln('export "$line"');
}
final File generatedModuleBuildPhaseScript = useMacOSConfig
? project.macos.generatedEnvironmentVariableExportScript
: project.ios.generatedEnvironmentVariableExportScript;
generatedModuleBuildPhaseScript.createSync(recursive: true);
generatedModuleBuildPhaseScript.writeAsStringSync(localsBuffer.toString());
os.chmod(generatedModuleBuildPhaseScript, '755');
}
/// List of lines of build settings. Example: 'FLUTTER_BUILD_DIR=build'
List<String> _xcodeBuildSettingsLines({
@required FlutterProject project,
@required BuildInfo buildInfo,
String targetOverride,
bool useMacOSConfig = false,
bool setSymroot = true,
}) {
final List<String> xcodeBuildSettings = <String>[];
final String flutterRoot = fs.path.normalize(Cache.flutterRoot);
localsBuffer.writeln('FLUTTER_ROOT=$flutterRoot');
xcodeBuildSettings.add('FLUTTER_ROOT=$flutterRoot');
// This holds because requiresProjectRoot is true for this command
localsBuffer.writeln('FLUTTER_APPLICATION_PATH=${fs.path.normalize(project.directory.path)}');
xcodeBuildSettings.add('FLUTTER_APPLICATION_PATH=${fs.path.normalize(project.directory.path)}');
// Relative to FLUTTER_APPLICATION_PATH, which is [Directory.current].
if (targetOverride != null)
localsBuffer.writeln('FLUTTER_TARGET=$targetOverride');
xcodeBuildSettings.add('FLUTTER_TARGET=$targetOverride');
// The build outputs directory, relative to FLUTTER_APPLICATION_PATH.
localsBuffer.writeln('FLUTTER_BUILD_DIR=${getBuildDirectory()}');
xcodeBuildSettings.add('FLUTTER_BUILD_DIR=${getBuildDirectory()}');
if (setSymroot) {
localsBuffer.writeln('SYMROOT=\${SOURCE_ROOT}/../${getIosBuildDirectory()}');
xcodeBuildSettings.add('SYMROOT=\${SOURCE_ROOT}/../${getIosBuildDirectory()}');
}
if (!project.isModule) {
......@@ -75,26 +147,26 @@ Future<void> updateGeneratedXcodeProperties({
// logic to derive it from FLUTTER_ROOT and FLUTTER_BUILD_MODE.
// However, this is necessary for regular projects using Cocoapods.
final String frameworkDir = useMacOSConfig
? flutterMacOSFrameworkDir(buildInfo.mode)
: flutterFrameworkDir(buildInfo.mode);
localsBuffer.writeln('FLUTTER_FRAMEWORK_DIR=$frameworkDir');
? flutterMacOSFrameworkDir(buildInfo.mode)
: flutterFrameworkDir(buildInfo.mode);
xcodeBuildSettings.add('FLUTTER_FRAMEWORK_DIR=$frameworkDir');
}
final String buildName = validatedBuildNameForPlatform(TargetPlatform.ios, buildInfo?.buildName ?? project.manifest.buildName);
if (buildName != null) {
localsBuffer.writeln('FLUTTER_BUILD_NAME=$buildName');
xcodeBuildSettings.add('FLUTTER_BUILD_NAME=$buildName');
}
final String buildNumber = validatedBuildNumberForPlatform(TargetPlatform.ios, buildInfo?.buildNumber ?? project.manifest.buildNumber);
if (buildNumber != null) {
localsBuffer.writeln('FLUTTER_BUILD_NUMBER=$buildNumber');
xcodeBuildSettings.add('FLUTTER_BUILD_NUMBER=$buildNumber');
}
if (artifacts is LocalEngineArtifacts) {
final LocalEngineArtifacts localEngineArtifacts = artifacts;
final String engineOutPath = localEngineArtifacts.engineOutPath;
localsBuffer.writeln('FLUTTER_ENGINE=${fs.path.dirname(fs.path.dirname(engineOutPath))}');
localsBuffer.writeln('LOCAL_ENGINE=${fs.path.basename(engineOutPath)}');
xcodeBuildSettings.add('FLUTTER_ENGINE=${fs.path.dirname(fs.path.dirname(engineOutPath))}');
xcodeBuildSettings.add('LOCAL_ENGINE=${fs.path.basename(engineOutPath)}');
// Tell Xcode not to build universal binaries for local engines, which are
// single-architecture.
......@@ -106,19 +178,15 @@ Future<void> updateGeneratedXcodeProperties({
// Skip this step for macOS builds.
if (!useMacOSConfig) {
final String arch = engineOutPath.endsWith('_arm') ? 'armv7' : 'arm64';
localsBuffer.writeln('ARCHS=$arch');
xcodeBuildSettings.add('ARCHS=$arch');
}
}
if (buildInfo.trackWidgetCreation) {
localsBuffer.writeln('TRACK_WIDGET_CREATION=true');
xcodeBuildSettings.add('TRACK_WIDGET_CREATION=true');
}
final File generatedXcodePropertiesFile = useMacOSConfig
? project.macos.generatedXcodePropertiesFile
: project.ios.generatedXcodePropertiesFile;
generatedXcodePropertiesFile.createSync(recursive: true);
generatedXcodePropertiesFile.writeAsStringSync(localsBuffer.toString());
return xcodeBuildSettings;
}
XcodeProjectInterpreter get xcodeProjectInterpreter => context.get<XcodeProjectInterpreter>();
......
......@@ -260,6 +260,12 @@ abstract class XcodeBasedProject {
/// The Flutter-managed Xcode config file for [mode].
File xcodeConfigFor(String mode);
/// The script that exports environment variables needed for Flutter tools.
/// Can be run first in a Xcode Script build phase to make FLUTTER_ROOT,
/// LOCAL_ENGINE, and other Flutter variables available to any flutter
/// tooling (`flutter build`, etc) to convert into flags.
File get generatedEnvironmentVariableExportScript;
/// The CocoaPods 'Podfile'.
File get podfile;
......@@ -317,6 +323,9 @@ class IosProject implements XcodeBasedProject {
@override
File xcodeConfigFor(String mode) => _flutterLibRoot.childDirectory('Flutter').childFile('$mode.xcconfig');
@override
File get generatedEnvironmentVariableExportScript => _flutterLibRoot.childDirectory('Flutter').childFile('flutter_export_environment.sh');
@override
File get podfile => hostAppRoot.childFile('Podfile');
......@@ -661,6 +670,9 @@ class MacOSProject implements XcodeBasedProject {
@override
File xcodeConfigFor(String mode) => managedDirectory.childFile('Flutter-$mode.xcconfig');
@override
File get generatedEnvironmentVariableExportScript => managedDirectory.childFile('flutter_export_environment.sh');
@override
File get podfile => _macOSDirectory.childFile('Podfile');
......
platform :ios, '8.0'
flutter_application_path = '../'
load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')
target 'Runner' do
flutter_application_path = '../'
eval(File.read(File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')), binding)
install_flutter_plugin_pods flutter_application_path
end
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['ENABLE_BITCODE'] = 'NO'
end
end
end
# Prevent Cocoapods from embedding a second Flutter framework and causing an error with the new Xcode build system.
......
def parse_KV_file(file, separator='=')
file_abs_path = File.expand_path(file)
if !File.exists? file_abs_path
return [];
end
pods_array = []
skip_line_start_symbols = ["#", "/"]
File.foreach(file_abs_path) { |line|
next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ }
plugin = line.split(pattern=separator)
if plugin.length == 2
podname = plugin[0].strip()
path = plugin[1].strip()
podpath = File.expand_path("#{path}", file_abs_path)
pods_array.push({:name => podname, :path => podpath});
else
puts "Invalid plugin specification: #{line}"
end
}
return pods_array
end
def flutter_root(f)
generated_xcode_build_settings = parse_KV_file(File.join(f, File.join('.ios', 'Flutter', 'Generated.xcconfig')))
if generated_xcode_build_settings.empty?
puts "Generated.xcconfig must exist. Make sure `flutter pub get` is executed in #{f}."
exit
end
generated_xcode_build_settings.map { |p|
if p[:name] == 'FLUTTER_ROOT'
return p[:path]
end
}
# Install pods needed to embed Flutter application, Flutter engine, and plugins
# from the host application Podfile.
#
# @example
# target 'MyApp' do
# install_all_flutter_pods 'my_flutter'
# end
# @param [String] flutter_application_path Path of the root directory of the Flutter module.
# Optional, defaults to two levels up from the directory of this script.
# MyApp/my_flutter/.ios/Flutter/../..
# @param [String] flutter_application_name Name of the Flutter module. Optional, defaults to module directory name.
def install_all_flutter_pods(flutter_application_path = nil, flutter_application_name = nil)
flutter_application_name ||= File.basename(File.expand_path(flutter_application_path))
flutter_application_path ||= File.join('..', '..')
install_flutter_engine_pod
install_flutter_plugin_pods(flutter_application_path)
install_flutter_application_pod(flutter_application_path, flutter_application_name)
end
# If this wasn't specified, assume it's two levels up from the directory of this script.
flutter_application_path ||= File.join(__dir__, '..', '..')
framework_dir = File.join(flutter_application_path, '.ios', 'Flutter')
engine_dir = File.join(framework_dir, 'engine')
if !File.exist?(engine_dir)
# Install Flutter engine pod.
#
# @example
# target 'MyApp' do
# install_flutter_engine_pod
# end
def install_flutter_engine_pod
engine_dir = File.join(__dir__, 'engine')
if !File.exist?(engine_dir)
# Copy the debug engine to have something to link against if the xcode backend script has not run yet.
debug_framework_dir = File.join(flutter_root(flutter_application_path), 'bin', 'cache', 'artifacts', 'engine', 'ios')
# CocoaPods will not embed the framework on pod install (before any build phases can generate) if the dylib does not exist.
debug_framework_dir = File.join(flutter_root, 'bin', 'cache', 'artifacts', 'engine', 'ios')
FileUtils.mkdir_p(engine_dir)
FileUtils.cp_r(File.join(debug_framework_dir, 'Flutter.framework'), engine_dir)
FileUtils.cp(File.join(debug_framework_dir, 'Flutter.podspec'), engine_dir)
end
pod 'Flutter', :path => engine_dir, :inhibit_warnings => true
end
pod 'Flutter', :path => engine_dir
pod 'FlutterPluginRegistrant', :path => File.join(framework_dir, 'FlutterPluginRegistrant')
# Install Flutter plugin pods.
#
# @example
# target 'MyApp' do
# install_flutter_plugin_pods 'my_flutter'
# end
# @param [String] flutter_application_path Path of the root directory of the Flutter module.
# Optional, defaults to two levels up from the directory of this script.
# MyApp/my_flutter/.ios/Flutter/../..
def install_flutter_plugin_pods(flutter_application_path)
flutter_application_path ||= File.join('..', '..')
pod 'FlutterPluginRegistrant', :path => File.join(__dir__, 'FlutterPluginRegistrant'), :inhibit_warnings => true
symlinks_dir = File.join(framework_dir, '.symlinks')
FileUtils.mkdir_p(symlinks_dir)
plugin_pods = parse_KV_file(File.join(flutter_application_path, '.flutter-plugins'))
plugin_pods.map { |r|
symlinks_dir = File.join(__dir__, '.symlinks')
FileUtils.mkdir_p(symlinks_dir)
plugin_pods = parse_KV_file(File.join(flutter_application_path, '.flutter-plugins'))
plugin_pods.map do |r|
symlink = File.join(symlinks_dir, r[:name])
FileUtils.rm_f(symlink)
File.symlink(r[:path], symlink)
pod r[:name], :path => File.join(symlink, 'ios')
}
pod r[:name], :path => File.join(symlink, 'ios'), :inhibit_warnings => true
end
end
# Install Flutter application pod.
#
# @example
# target 'MyApp' do
# install_flutter_application_pod '../flutter_settings_repository', 'settings_module'
# end
# @param [String] flutter_application_path Path of the root directory of the Flutter module.
# Optional, defaults to two levels up from the directory of this script.
# MyApp/my_flutter/.ios/Flutter/../..
def install_flutter_application_pod(flutter_application_path, flutter_application_name)
app_framework_dir = File.join(__dir__, 'App.framework')
app_framework_dylib = File.join(app_framework_dir, 'App')
if !File.exist?(app_framework_dylib)
# Fake an App.framework to have something to link against if the xcode backend script has not run yet.
# CocoaPods will not embed the framework on pod install (before any build phases can run) if the dylib does not exist.
# Create a dummy dylib.
FileUtils.mkdir_p(app_framework_dir)
`echo "static const int Moo = 88;" | xcrun clang -x c -dynamiclib -o "#{app_framework_dylib}" -`
end
pod flutter_application_name, :path => __dir__, :inhibit_warnings => true
# Use relative paths for script phase paths since these strings will likely be checked into source controls.
# Process will be run from project directory.
current_directory_pathname = Pathname.new __dir__.to_s
project_directory_pathname = Pathname.new Dir.pwd
relative = current_directory_pathname.relative_path_from project_directory_pathname
flutter_export_environment_path = File.join('${SRCROOT}', relative, 'flutter_export_environment.sh');
script_phase :name => 'Run Flutter Build Script',
:script => "source \"#{flutter_export_environment_path}\"\n\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/xcode_backend.sh build",
:input_files => [
File.join('${SRCROOT}', flutter_application_path, '.metadata'),
File.join('${SRCROOT}', relative, 'App.framework', 'App'),
File.join('${SRCROOT}', relative, 'engine', 'Flutter.framework', 'Flutter'),
flutter_export_environment_path
],
:execution_position => :before_compile
end
def parse_KV_file(file, separator='=')
file_abs_path = File.expand_path(file)
if !File.exists? file_abs_path
return [];
end
pods_array = []
skip_line_start_symbols = ["#", "/"]
File.foreach(file_abs_path) { |line|
next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ }
plugin = line.split(pattern=separator)
if plugin.length == 2
podname = plugin[0].strip()
path = plugin[1].strip()
podpath = File.expand_path("#{path}", file_abs_path)
pods_array.push({:name => podname, :path => podpath});
else
puts "Invalid plugin specification: #{line}"
end
}
return pods_array
end
# Ensure that ENABLE_BITCODE is set to NO, add a #include to Generated.xcconfig, and
# add a run script to the Build Phases.
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['ENABLE_BITCODE'] = 'NO'
next if config.base_configuration_reference == nil
xcconfig_path = config.base_configuration_reference.real_path
File.open(xcconfig_path, 'a+') do |file|
file.puts "#include \"#{File.realpath(File.join(framework_dir, 'Generated.xcconfig'))}\""
end
end
def flutter_root
generated_xcode_build_settings = parse_KV_file(File.join(__dir__, 'Generated.xcconfig'))
if generated_xcode_build_settings.empty?
puts "Generated.xcconfig must exist. Make sure `flutter pub get` is executed in the Flutter module."
exit
end
generated_xcode_build_settings.map { |p|
if p[:name] == 'FLUTTER_ROOT'
return p[:path]
end
}
end
Pod::Spec.new do |s|
s.name = '{{projectName}}'
s.version = '0.0.1'
s.summary = 'Flutter module'
s.description = 'Flutter module - {{projectName}}'
s.homepage = 'https://flutter.dev'
s.license = { :type => 'BSD' }
s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' }
s.source = { :path => '.' }
s.ios.deployment_target = '8.0'
s.vendored_frameworks = 'App.framework'
s.dependency 'Flutter'
end
......@@ -100,6 +100,7 @@ void main() {
'.android/app/',
'.gitignore',
'.ios/Flutter',
'.ios/Flutter/flutter_project.podspec',
'.metadata',
'lib/main.dart',
'pubspec.yaml',
......@@ -545,6 +546,23 @@ void main() {
expect(xcodeConfig, contains('FLUTTER_ROOT='));
expect(xcodeConfig, contains('FLUTTER_APPLICATION_PATH='));
expect(xcodeConfig, contains('FLUTTER_TARGET='));
// Generated export environment variables script
final String buildPhaseScriptPath = fs.path.join('.ios', 'Flutter', 'flutter_export_environment.sh');
expectExists(buildPhaseScriptPath);
final File buildPhaseScriptFile = fs.file(fs.path.join(projectDir.path, buildPhaseScriptPath));
final String buildPhaseScript = buildPhaseScriptFile.readAsStringSync();
expect(buildPhaseScript, contains('FLUTTER_ROOT='));
expect(buildPhaseScript, contains('FLUTTER_APPLICATION_PATH='));
expect(buildPhaseScript, contains('FLUTTER_TARGET='));
// Generated podspec
final String podspecPath = fs.path.join('.ios', 'Flutter', 'flutter_project.podspec');
expectExists(podspecPath);
final File podspecFile = fs.file(fs.path.join(projectDir.path, podspecPath));
final String podspec = podspecFile.readAsStringSync();
expect(podspec, contains('Flutter module - flutter_project'));
// App identification
final String xcodeProjectPath = fs.path.join('.ios', 'Runner.xcodeproj', 'project.pbxproj');
expectExists(xcodeProjectPath);
......
......@@ -304,6 +304,12 @@ Information about project "Runner":
final String contents = config.readAsStringSync();
expect(contents.contains('ARCHS=armv7'), isTrue);
final File buildPhaseScript = fs.file('path/to/project/ios/Flutter/flutter_export_environment.sh');
expect(buildPhaseScript.existsSync(), isTrue);
final String buildPhaseScriptContents = buildPhaseScript.readAsStringSync();
expect(buildPhaseScriptContents.contains('ARCHS=armv7'), isTrue);
});
testUsingOsxContext('sets TRACK_WIDGET_CREATION=true when trackWidgetCreation is true', () async {
......@@ -322,6 +328,12 @@ Information about project "Runner":
final String contents = config.readAsStringSync();
expect(contents.contains('TRACK_WIDGET_CREATION=true'), isTrue);
final File buildPhaseScript = fs.file('path/to/project/ios/Flutter/flutter_export_environment.sh');
expect(buildPhaseScript.existsSync(), isTrue);
final String buildPhaseScriptContents = buildPhaseScript.readAsStringSync();
expect(buildPhaseScriptContents.contains('TRACK_WIDGET_CREATION=true'), isTrue);
});
testUsingOsxContext('does not set TRACK_WIDGET_CREATION when trackWidgetCreation is false', () async {
......@@ -340,6 +352,12 @@ Information about project "Runner":
final String contents = config.readAsStringSync();
expect(contents.contains('TRACK_WIDGET_CREATION=true'), isFalse);
final File buildPhaseScript = fs.file('path/to/project/ios/Flutter/flutter_export_environment.sh');
expect(buildPhaseScript.existsSync(), isTrue);
final String buildPhaseScriptContents = buildPhaseScript.readAsStringSync();
expect(buildPhaseScriptContents.contains('TRACK_WIDGET_CREATION=true'), isFalse);
});
testUsingOsxContext('sets ARCHS=armv7 when armv7 local engine is set', () async {
......
......@@ -200,6 +200,8 @@ void main() {
await project.ensureReadyForPlatformSpecificTooling();
final Directory flutter = project.ios.hostAppRoot.childDirectory('Flutter');
expectExists(flutter.childFile('podhelper.rb'));
expectExists(flutter.childFile('flutter_export_environment.sh'));
expectExists(flutter.childFile('${project.manifest.appName}.podspec'));
expectExists(flutter.childFile('Generated.xcconfig'));
final Directory pluginRegistrantClasses = flutter
.childDirectory('FlutterPluginRegistrant')
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment