Automatically wire dependencies for native plugins (#8891)
Go through all packages brought in by pub, and write the name and path of every one that is a flutter plugin into .flutter-plugins.
In android/settings.gradle and ios/Podfile, read in .flutter-plugins, if that file exists. The Android / iOS code from the plugins is automatically added as dependencies of the native code of the app.
diff --git a/packages/flutter_tools/gradle/flutter.gradle b/packages/flutter_tools/gradle/flutter.gradle
index 7e16be7e..29cc75a 100644
--- a/packages/flutter_tools/gradle/flutter.gradle
+++ b/packages/flutter_tools/gradle/flutter.gradle
@@ -34,13 +34,22 @@
private String localEngineSrcPath
private Properties localProperties
+ private File flutterJar
+ private File debugFlutterJar
+ private File profileFlutterJar
+ private File releaseFlutterJar
+
+ private Properties readPropertiesIfExist(File propertiesFile) {
+ Properties result = new Properties()
+ if (propertiesFile.exists()) {
+ propertiesFile.withInputStream { stream -> result.load(stream) }
+ }
+ return result
+ }
+
private String resolveProperty(Project project, String name, String defaultValue) {
if (localProperties == null) {
- localProperties = new Properties()
- def localPropertiesFile = project.rootProject.file("local.properties")
- if (localPropertiesFile.exists()) {
- localProperties.load(localPropertiesFile.newDataInputStream())
- }
+ localProperties = readPropertiesIfExist(project.rootProject.file("local.properties"))
}
String result
if (project.hasProperty(name)) {
@@ -82,7 +91,7 @@
if (!engineOut.isDirectory()) {
throw new GradleException('localEngineOut must point to a local engine build')
}
- File flutterJar = Paths.get(engineOut.absolutePath, "flutter.jar").toFile()
+ flutterJar = Paths.get(engineOut.absolutePath, "flutter.jar").toFile()
if (!flutterJar.isFile()) {
throw new GradleException('Local engine build does not contain flutter.jar')
}
@@ -95,9 +104,9 @@
}
} else {
Path baseEnginePath = Paths.get(flutterRoot.absolutePath, "bin", "cache", "artifacts", "engine")
- File debugFlutterJar = baseEnginePath.resolve("android-arm").resolve("flutter.jar").toFile()
- File profileFlutterJar = baseEnginePath.resolve("android-arm-profile").resolve("flutter.jar").toFile()
- File releaseFlutterJar = baseEnginePath.resolve("android-arm-release").resolve("flutter.jar").toFile()
+ debugFlutterJar = baseEnginePath.resolve("android-arm").resolve("flutter.jar").toFile()
+ profileFlutterJar = baseEnginePath.resolve("android-arm-profile").resolve("flutter.jar").toFile()
+ releaseFlutterJar = baseEnginePath.resolve("android-arm-release").resolve("flutter.jar").toFile()
if (!debugFlutterJar.isFile()) {
project.exec {
executable flutterExecutable.absolutePath
@@ -130,6 +139,32 @@
project.extensions.create("flutter", FlutterExtension)
project.afterEvaluate this.&addFlutterTask
+
+ File pluginsFile = new File(project.rootProject.projectDir.parentFile, '.flutter-plugins')
+ Properties plugins = readPropertiesIfExist(pluginsFile)
+
+ plugins.each { name, _ ->
+ def pluginProject = project.rootProject.findProject(":$name")
+ if (pluginProject != null) {
+ project.dependencies {
+ compile pluginProject
+ }
+ pluginProject.afterEvaluate this.&addFlutterJarDependency
+ } else {
+ project.logger.error("Plugin project :$name not found. Please update settings.gradle.")
+ }
+ }
+ }
+
+ private void addFlutterJarDependency(Project project) {
+ project.dependencies {
+ if (flutterJar != null) {
+ provided project.files(flutterJar)
+ } else {
+ debugProvided project.files(debugFlutterJar)
+ releaseProvided project.files(releaseFlutterJar)
+ }
+ }
}
private void addFlutterTask(Project project) {
diff --git a/packages/flutter_tools/lib/src/android/gradle.dart b/packages/flutter_tools/lib/src/android/gradle.dart
index 5e5984b..48e3e8d 100644
--- a/packages/flutter_tools/lib/src/android/gradle.dart
+++ b/packages/flutter_tools/lib/src/android/gradle.dart
@@ -15,6 +15,7 @@
import '../build_info.dart';
import '../cache.dart';
import '../globals.dart';
+import '../plugins.dart';
import 'android_sdk.dart';
import 'android_studio.dart';
@@ -154,6 +155,8 @@
settings.values['flutter.buildMode'] = buildModeName;
settings.writeContents(localProperties);
+ writeFlutterPluginsList();
+
final String gradle = ensureGradle();
switch (flutterPluginVersion) {
diff --git a/packages/flutter_tools/lib/src/ios/mac.dart b/packages/flutter_tools/lib/src/ios/mac.dart
index e20e793..e26e0d7 100644
--- a/packages/flutter_tools/lib/src/ios/mac.dart
+++ b/packages/flutter_tools/lib/src/ios/mac.dart
@@ -19,6 +19,7 @@
import '../doctor.dart';
import '../flx.dart' as flx;
import '../globals.dart';
+import '../plugins.dart';
import '../services.dart';
import 'xcodeproj.dart';
@@ -127,6 +128,7 @@
// copied over to a location that is suitable for Xcodebuild to find them.
final Directory appDirectory = fs.directory(app.appDirectory);
await _addServicesToBundle(appDirectory);
+ writeFlutterPluginsList();
_runPodInstall(appDirectory, flutterFrameworkDir(mode));
diff --git a/packages/flutter_tools/lib/src/plugins.dart b/packages/flutter_tools/lib/src/plugins.dart
new file mode 100644
index 0000000..0df1721
--- /dev/null
+++ b/packages/flutter_tools/lib/src/plugins.dart
@@ -0,0 +1,52 @@
+// Copyright 2017 The Chromium 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:yaml/yaml.dart';
+
+import 'base/file_system.dart';
+import 'dart/package_map.dart';
+import 'globals.dart';
+
+dynamic _loadYamlFile(String path) {
+ if (!fs.isFileSync(path))
+ return null;
+ final String manifestString = fs.file(path).readAsStringSync();
+ return loadYaml(manifestString);
+}
+
+String _generatePluginManifest() {
+ Map<String, Uri> packages;
+ try {
+ packages = new PackageMap(PackageMap.globalPackagesPath).map;
+ } on FormatException catch(e) {
+ printTrace('Invalid .packages file: $e');
+ return '';
+ }
+ final List<String> plugins = <String>[];
+ packages.forEach((String name, Uri uri) {
+ final Uri packageRoot = uri.resolve('..');
+ final dynamic packageConfig = _loadYamlFile(packageRoot.resolve('pubspec.yaml').path);
+ if (packageConfig != null) {
+ final dynamic flutterConfig = packageConfig['flutter'];
+ if (flutterConfig != null && flutterConfig.containsKey('plugin')) {
+ printTrace('Found plugin $name at ${packageRoot.path}');
+ plugins.add('$name=${packageRoot.path}');
+ }
+ }
+ });
+ return plugins.join('\n');
+}
+
+void writeFlutterPluginsList() {
+ final File pluginsProperties = fs.file('.flutter-plugins');
+
+ final String pluginManifest = _generatePluginManifest();
+ if (pluginManifest.isNotEmpty) {
+ pluginsProperties.writeAsStringSync('$pluginManifest\n');
+ } else {
+ if (pluginsProperties.existsSync()) {
+ pluginsProperties.deleteSync();
+ }
+ }
+}
diff --git a/packages/flutter_tools/templates/create/.gitignore.tmpl b/packages/flutter_tools/templates/create/.gitignore.tmpl
index 14c7d4c..eb15c3d 100644
--- a/packages/flutter_tools/templates/create/.gitignore.tmpl
+++ b/packages/flutter_tools/templates/create/.gitignore.tmpl
@@ -7,3 +7,4 @@
ios/.generated/
packages
pubspec.lock
+.flutter-plugins
diff --git a/packages/flutter_tools/templates/create/android.tmpl/settings.gradle b/packages/flutter_tools/templates/create/android.tmpl/settings.gradle
index e7b4def..115da6c 100644
--- a/packages/flutter_tools/templates/create/android.tmpl/settings.gradle
+++ b/packages/flutter_tools/templates/create/android.tmpl/settings.gradle
@@ -1 +1,15 @@
include ':app'
+
+def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
+
+def plugins = new Properties()
+def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
+if (pluginsFile.exists()) {
+ pluginsFile.withInputStream { stream -> plugins.load(stream) }
+}
+
+plugins.each { name, path ->
+ def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
+ include ":$name"
+ project(":$name").projectDir = pluginDirectory
+}
diff --git a/packages/flutter_tools/templates/create/ios.tmpl/Podfile b/packages/flutter_tools/templates/create/ios.tmpl/Podfile
index b6f1c98..74b3de0 100644
--- a/packages/flutter_tools/templates/create/ios.tmpl/Podfile
+++ b/packages/flutter_tools/templates/create/ios.tmpl/Podfile
@@ -1,10 +1,38 @@
# Uncomment this line to define a global platform for your project
# platform :ios, '9.0'
+if ENV['FLUTTER_FRAMEWORK_DIR'] == nil
+ abort('Please set FLUTTER_FRAMEWORK_DIR to the directory containing Flutter.framework')
+end
+
target 'Runner' do
- # Uncomment this line if you're using Swift or would like to use dynamic frameworks
- # use_frameworks!
+ use_frameworks!
# Pods for Runner
+ # Flutter Pods
+ pod 'Flutter', :path => ENV['FLUTTER_FRAMEWORK_DIR']
+
+ if File.exists? '../.flutter-plugins'
+ flutter_root = File.expand_path('..')
+ File.foreach('../.flutter-plugins') { |line|
+ plugin = line.split(pattern='=')
+ if plugin.length == 2
+ name = plugin[0].strip()
+ path = plugin[1].strip()
+ resolved_path = File.expand_path("#{path}/ios", flutter_root)
+ pod name, :path => resolved_path
+ else
+ puts "Invalid plugin specification: #{line}"
+ end
+ }
+ end
+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