diff --git a/.gitignore b/.gitignore index 75f33e5b02..cf476b11ba 100644 --- a/.gitignore +++ b/.gitignore @@ -41,7 +41,6 @@ unlinked.ds unlinked_spec.ds # Android related -**/android/**/GeneratedPluginRegistrant.java **/android/**/gradle-wrapper.jar **/android/.gradle **/android/captures/ @@ -60,8 +59,6 @@ unlinked_spec.ds **/ios/**/.tags* **/ios/**/.vagrant/ **/ios/**/DerivedData/ -**/ios/**/GeneratedPluginRegistrant.h -**/ios/**/GeneratedPluginRegistrant.m **/ios/**/Icon? **/ios/**/Pods/ **/ios/**/profile diff --git a/dev/bots/test.dart b/dev/bots/test.dart index c5c5cf7e43..9aafdcd29e 100644 --- a/dev/bots/test.dart +++ b/dev/bots/test.dart @@ -87,6 +87,7 @@ Future _verifyInternationalizations() async { } Future _analyzeRepo() async { + await _verifyGeneratedPluginRegistrants(flutterRoot); await _verifyNoBadImports(flutterRoot); await _verifyInternationalizations(); @@ -477,3 +478,63 @@ void _printProgress(String action, String workingDir, String command) { const String arrow = '⏩'; print('$arrow $action: cd $cyan$workingDir$reset; $yellow$command$reset'); } + +Future _verifyGeneratedPluginRegistrants(String flutterRoot) async { + final Directory flutterRootDir = new Directory(flutterRoot); + + final Map> packageToRegistrants = >{}; + + for (FileSystemEntity entity in flutterRootDir.listSync(recursive: true)) { + if (entity is! File) + continue; + if (_isGeneratedPluginRegistrant(entity)) { + final String package = _getPackageFor(entity, flutterRootDir); + final List registrants = packageToRegistrants.putIfAbsent(package, () => []); + registrants.add(entity); + } + } + + final Set outOfDate = new Set(); + + for (String package in packageToRegistrants.keys) { + final Map fileToContent = new Map.fromIterable(packageToRegistrants[package], + key: (File f) => f, + value: (File f) => f.readAsStringSync(), + ); + await _runCommand(flutter, ['inject-plugins'], + workingDirectory: package, + printOutput: false, + ); + for (File registrant in fileToContent.keys) { + if (registrant.readAsStringSync() != fileToContent[registrant]) { + outOfDate.add(registrant.path); + } + } + } + + if (outOfDate.isNotEmpty) { + print('$red━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━$reset'); + print('${bold}The following GeneratedPluginRegistrants are out of date:$reset'); + for (String registrant in outOfDate) { + print(' - $registrant'); + } + print('$red━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━$reset'); + exit(1); + } +} + +String _getPackageFor(File entity, Directory flutterRootDir) { + for (Directory dir = entity.parent; dir != flutterRootDir; dir = dir.parent) { + if (new File(path.join(dir.path, 'pubspec.yaml')).existsSync()) { + return dir.path; + } + } + throw new ArgumentError('$entity is not within a dart package.'); +} + +bool _isGeneratedPluginRegistrant(File file) { + final String filename = path.basename(file.path); + return filename == 'GeneratedPluginRegistrant.java' || + filename == 'GeneratedPluginRegistrant.h' || + filename == 'GeneratedPluginRegistrant.m'; +} diff --git a/dev/integration_tests/channels/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java b/dev/integration_tests/channels/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java index e07931dc42..d007606a44 100644 --- a/dev/integration_tests/channels/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java +++ b/dev/integration_tests/channels/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java @@ -7,5 +7,17 @@ import io.flutter.plugin.common.PluginRegistry; */ public final class GeneratedPluginRegistrant { public static void registerWith(PluginRegistry registry) { + if (alreadyRegisteredWith(registry)) { + return; + } + } + + private static boolean alreadyRegisteredWith(PluginRegistry registry) { + final String key = GeneratedPluginRegistrant.class.getCanonicalName(); + if (registry.hasPlugin(key)) { + return true; + } + registry.registrarFor(key); + return false; } } diff --git a/dev/integration_tests/ui/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java b/dev/integration_tests/ui/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java index e07931dc42..d007606a44 100644 --- a/dev/integration_tests/ui/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java +++ b/dev/integration_tests/ui/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java @@ -7,5 +7,17 @@ import io.flutter.plugin.common.PluginRegistry; */ public final class GeneratedPluginRegistrant { public static void registerWith(PluginRegistry registry) { + if (alreadyRegisteredWith(registry)) { + return; + } + } + + private static boolean alreadyRegisteredWith(PluginRegistry registry) { + final String key = GeneratedPluginRegistrant.class.getCanonicalName(); + if (registry.hasPlugin(key)) { + return true; + } + registry.registrarFor(key); + return false; } } diff --git a/dev/manual_tests/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java b/dev/manual_tests/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java index e07931dc42..d007606a44 100644 --- a/dev/manual_tests/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java +++ b/dev/manual_tests/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java @@ -7,5 +7,17 @@ import io.flutter.plugin.common.PluginRegistry; */ public final class GeneratedPluginRegistrant { public static void registerWith(PluginRegistry registry) { + if (alreadyRegisteredWith(registry)) { + return; + } + } + + private static boolean alreadyRegisteredWith(PluginRegistry registry) { + final String key = GeneratedPluginRegistrant.class.getCanonicalName(); + if (registry.hasPlugin(key)) { + return true; + } + registry.registrarFor(key); + return false; } } diff --git a/examples/flutter_gallery/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java b/examples/flutter_gallery/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java index cae2b1f93d..19a021fdfa 100644 --- a/examples/flutter_gallery/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java +++ b/examples/flutter_gallery/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java @@ -11,9 +11,21 @@ import io.flutter.videoplayer.VideoPlayerPlugin; */ public final class GeneratedPluginRegistrant { public static void registerWith(PluginRegistry registry) { + if (alreadyRegisteredWith(registry)) { + return; + } ConnectivityPlugin.registerWith(registry.registrarFor("io.flutter.plugins.connectivity.ConnectivityPlugin")); DeviceInfoPlugin.registerWith(registry.registrarFor("io.flutter.plugins.deviceinfo.DeviceInfoPlugin")); UrlLauncherPlugin.registerWith(registry.registrarFor("io.flutter.plugins.urllauncher.UrlLauncherPlugin")); VideoPlayerPlugin.registerWith(registry.registrarFor("io.flutter.videoplayer.VideoPlayerPlugin")); } + + private static boolean alreadyRegisteredWith(PluginRegistry registry) { + final String key = GeneratedPluginRegistrant.class.getCanonicalName(); + if (registry.hasPlugin(key)) { + return true; + } + registry.registrarFor(key); + return false; + } } diff --git a/examples/hello_world/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java b/examples/hello_world/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java index e07931dc42..d007606a44 100644 --- a/examples/hello_world/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java +++ b/examples/hello_world/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java @@ -7,5 +7,17 @@ import io.flutter.plugin.common.PluginRegistry; */ public final class GeneratedPluginRegistrant { public static void registerWith(PluginRegistry registry) { + if (alreadyRegisteredWith(registry)) { + return; + } + } + + private static boolean alreadyRegisteredWith(PluginRegistry registry) { + final String key = GeneratedPluginRegistrant.class.getCanonicalName(); + if (registry.hasPlugin(key)) { + return true; + } + registry.registrarFor(key); + return false; } } diff --git a/examples/layers/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java b/examples/layers/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java index e07931dc42..d007606a44 100644 --- a/examples/layers/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java +++ b/examples/layers/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java @@ -7,5 +7,17 @@ import io.flutter.plugin.common.PluginRegistry; */ public final class GeneratedPluginRegistrant { public static void registerWith(PluginRegistry registry) { + if (alreadyRegisteredWith(registry)) { + return; + } + } + + private static boolean alreadyRegisteredWith(PluginRegistry registry) { + final String key = GeneratedPluginRegistrant.class.getCanonicalName(); + if (registry.hasPlugin(key)) { + return true; + } + registry.registrarFor(key); + return false; } } diff --git a/examples/platform_channel/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java b/examples/platform_channel/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java index e07931dc42..d007606a44 100644 --- a/examples/platform_channel/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java +++ b/examples/platform_channel/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java @@ -7,5 +7,17 @@ import io.flutter.plugin.common.PluginRegistry; */ public final class GeneratedPluginRegistrant { public static void registerWith(PluginRegistry registry) { + if (alreadyRegisteredWith(registry)) { + return; + } + } + + private static boolean alreadyRegisteredWith(PluginRegistry registry) { + final String key = GeneratedPluginRegistrant.class.getCanonicalName(); + if (registry.hasPlugin(key)) { + return true; + } + registry.registrarFor(key); + return false; } } diff --git a/examples/platform_view/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java b/examples/platform_view/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java new file mode 100644 index 0000000000..d007606a44 --- /dev/null +++ b/examples/platform_view/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java @@ -0,0 +1,23 @@ +package io.flutter.plugins; + +import io.flutter.plugin.common.PluginRegistry; + +/** + * Generated file. Do not edit. + */ +public final class GeneratedPluginRegistrant { + public static void registerWith(PluginRegistry registry) { + if (alreadyRegisteredWith(registry)) { + return; + } + } + + private static boolean alreadyRegisteredWith(PluginRegistry registry) { + final String key = GeneratedPluginRegistrant.class.getCanonicalName(); + if (registry.hasPlugin(key)) { + return true; + } + registry.registrarFor(key); + return false; + } +} diff --git a/examples/stocks/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java b/examples/stocks/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java index e07931dc42..d007606a44 100644 --- a/examples/stocks/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java +++ b/examples/stocks/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java @@ -7,5 +7,17 @@ import io.flutter.plugin.common.PluginRegistry; */ public final class GeneratedPluginRegistrant { public static void registerWith(PluginRegistry registry) { + if (alreadyRegisteredWith(registry)) { + return; + } + } + + private static boolean alreadyRegisteredWith(PluginRegistry registry) { + final String key = GeneratedPluginRegistrant.class.getCanonicalName(); + if (registry.hasPlugin(key)) { + return true; + } + registry.registrarFor(key); + return false; } } diff --git a/packages/flutter_tools/lib/executable.dart b/packages/flutter_tools/lib/executable.dart index 43ca13b525..d20863c46c 100644 --- a/packages/flutter_tools/lib/executable.dart +++ b/packages/flutter_tools/lib/executable.dart @@ -18,6 +18,7 @@ import 'src/commands/drive.dart'; import 'src/commands/format.dart'; import 'src/commands/fuchsia_reload.dart'; import 'src/commands/ide_config.dart'; +import 'src/commands/inject_plugins.dart'; import 'src/commands/install.dart'; import 'src/commands/logs.dart'; import 'src/commands/packages.dart'; @@ -45,6 +46,7 @@ Future main(List args) async { new BuildCommand(verboseHelp: verboseHelp), new ChannelCommand(), new CleanCommand(), + new InjectPluginsCommand(hidden: !verboseHelp), new ConfigCommand(verboseHelp: verboseHelp), new CreateCommand(), new DaemonCommand(hidden: !verboseHelp), diff --git a/packages/flutter_tools/lib/src/commands/inject_plugins.dart b/packages/flutter_tools/lib/src/commands/inject_plugins.dart new file mode 100644 index 0000000000..bf53cb5164 --- /dev/null +++ b/packages/flutter_tools/lib/src/commands/inject_plugins.dart @@ -0,0 +1,34 @@ +// 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 'dart:async'; + +import '../globals.dart'; +import '../plugins.dart'; +import '../runner/flutter_command.dart'; + +class InjectPluginsCommand extends FlutterCommand { + InjectPluginsCommand({ this.hidden: false }) { + requiresPubspecYaml(); + } + + @override + final String name = 'inject-plugins'; + + @override + final String description = 'Re-generates the GeneratedPluginRegistrants.'; + + @override + final bool hidden; + + @override + Future runCommand() async { + final bool result = injectPlugins(); + if (result) { + printStatus('GeneratedPluginRegistrants successfully written.'); + } else { + printStatus('This project does not use plugins, no GeneratedPluginRegistrants have been created.'); + } + } +}