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