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