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.
This commit is contained in:
Jakob Andersen 2017-03-23 14:59:12 +01:00 committed by GitHub
parent 2d29c62590
commit b61e169011
7 changed files with 146 additions and 11 deletions

View file

@ -34,13 +34,22 @@ class FlutterPlugin implements Plugin<Project> {
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 @@ class FlutterPlugin implements Plugin<Project> {
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 @@ class FlutterPlugin implements Plugin<Project> {
}
} 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 @@ class FlutterPlugin implements Plugin<Project> {
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) {

View file

@ -15,6 +15,7 @@ import '../base/utils.dart';
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 @@ Future<Null> buildGradleProject(BuildMode buildMode, String target, String kerne
settings.values['flutter.buildMode'] = buildModeName;
settings.writeContents(localProperties);
writeFlutterPluginsList();
final String gradle = ensureGradle();
switch (flutterPluginVersion) {

View file

@ -19,6 +19,7 @@ import '../build_info.dart';
import '../doctor.dart';
import '../flx.dart' as flx;
import '../globals.dart';
import '../plugins.dart';
import '../services.dart';
import 'xcodeproj.dart';
@ -127,6 +128,7 @@ Future<XcodeBuildResult> buildXcodeProject({
// 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));

View file

@ -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();
}
}
}

View file

@ -7,3 +7,4 @@ build/
ios/.generated/
packages
pubspec.lock
.flutter-plugins

View file

@ -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
}

View file

@ -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