Skip to content

Commit b6e9200

Browse files
author
Emmanuel Garcia
authored
Add .flutter-plugins-dependencies to the project, which contains the app's plugin dependency graph (flutter#45379)
1 parent 161e4df commit b6e9200

File tree

15 files changed

+245
-38
lines changed

15 files changed

+245
-38
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ version
4444
**/doc/api/
4545
.dart_tool/
4646
.flutter-plugins
47+
.flutter-plugins-dependencies
4748
.packages
4849
.pub-cache/
4950
.pub/

dev/integration_tests/android_embedding_v2_smoke_test/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
**/doc/api/
2525
.dart_tool/
2626
.flutter-plugins
27+
.flutter-plugins-dependencies
2728
.packages
2829
.pub-cache/
2930
.pub/

dev/integration_tests/android_splash_screens/splash_screen_kitchen_sink/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
**/doc/api/
2828
.dart_tool/
2929
.flutter-plugins
30+
.flutter-plugins-dependencies
3031
.packages
3132
.pub-cache/
3233
.pub/

dev/integration_tests/android_splash_screens/splash_screen_load_rotate/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
**/doc/api/
2525
.dart_tool/
2626
.flutter-plugins
27+
.flutter-plugins-dependencies
2728
.packages
2829
.pub-cache/
2930
.pub/

dev/integration_tests/android_splash_screens/splash_screen_trans_rotate/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
**/doc/api/
2525
.dart_tool/
2626
.flutter-plugins
27+
.flutter-plugins-dependencies
2728
.packages
2829
.pub-cache/
2930
.pub/

dev/integration_tests/ios_add2app/flutterapp/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,4 @@ build/
3939
.android/
4040
.ios/
4141
.flutter-plugins
42+
.flutter-plugins-dependencies

dev/integration_tests/release_smoke_test/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
**/doc/api/
2525
.dart_tool/
2626
.flutter-plugins
27+
.flutter-plugins-dependencies
2728
.packages
2829
.pub-cache/
2930
.pub/

packages/flutter_tools/gradle/flutter.gradle

Lines changed: 69 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import static groovy.io.FileType.FILES
66

77
import com.android.builder.model.AndroidProject
88
import com.android.build.OutputFile
9+
import groovy.json.JsonSlurper
910
import java.nio.file.Path
1011
import java.nio.file.Paths
1112
import java.util.regex.Matcher
@@ -253,6 +254,7 @@ class FlutterPlugin implements Plugin<Project> {
253254
private void configurePlugins() {
254255
if (!buildPluginAsAar()) {
255256
getPluginList().each this.&configurePluginProject
257+
getPluginDependencies().each this.&configurePluginDependencies
256258
return
257259
}
258260
project.repositories {
@@ -298,19 +300,15 @@ class FlutterPlugin implements Plugin<Project> {
298300
}
299301

300302
// Adds the plugin project dependency to the app project .
301-
private void configurePluginProject(String name, String _) {
302-
Project pluginProject = project.rootProject.findProject(":$name")
303+
private void configurePluginProject(String pluginName, String _) {
304+
Project pluginProject = project.rootProject.findProject(":$pluginName")
303305
if (pluginProject == null) {
304-
project.logger.error("Plugin project :$name not found. Please update settings.gradle.")
306+
project.logger.error("Plugin project :$pluginName not found. Please update settings.gradle.")
305307
return
306308
}
307309
// Add plugin dependency to the app project.
308310
project.dependencies {
309-
if (project.getConfigurations().findByName("implementation")) {
310-
implementation pluginProject
311-
} else {
312-
compile pluginProject
313-
}
311+
implementation pluginProject
314312
}
315313
Closure addEmbeddingCompileOnlyDependency = { buildType ->
316314
String flutterBuildMode = buildModeFor(buildType)
@@ -337,6 +335,36 @@ class FlutterPlugin implements Plugin<Project> {
337335
}
338336
}
339337

338+
// Add the dependencies on other plugin projects to the plugin project.
339+
// A plugin A can depend on plugin B. As a result, this dependency must be surfaced by
340+
// making the Gradle plugin project A depend on the Gradle plugin project B.
341+
private void configurePluginDependencies(Object dependencyObject) {
342+
assert dependencyObject.name instanceof String
343+
Project pluginProject = project.rootProject.findProject(":${dependencyObject.name}")
344+
if (pluginProject == null) {
345+
// Ignore plugins that don't have a project since most likely they don't
346+
// have an android/ directory.
347+
return
348+
}
349+
assert dependencyObject.dependencies instanceof List
350+
dependencyObject.dependencies.each { pluginDependencyName ->
351+
assert pluginDependencyName instanceof String
352+
if (pluginDependencyName.empty) {
353+
return
354+
}
355+
Project dependencyProject = project.rootProject.findProject(":$pluginDependencyName")
356+
if (dependencyProject == null) {
357+
return
358+
}
359+
// Wait for the Android plugin to load and add the dependency to the plugin project.
360+
pluginProject.afterEvaluate {
361+
pluginProject.dependencies {
362+
implementation dependencyProject
363+
}
364+
}
365+
}
366+
}
367+
340368
private Properties getPluginList() {
341369
File pluginsFile = new File(project.projectDir.parentFile.parentFile, '.flutter-plugins')
342370
Properties allPlugins = readPropertiesIfExist(pluginsFile)
@@ -353,6 +381,39 @@ class FlutterPlugin implements Plugin<Project> {
353381
return androidPlugins
354382
}
355383

384+
// Gets the plugins dependencies from `.flutter-plugins-dependencies`.
385+
private List getPluginDependencies() {
386+
// Consider a `.flutter-plugins-dependencies` file with the following content:
387+
// {
388+
// "dependencyGraph": [
389+
// {
390+
// "name": "plugin-a",
391+
// "dependencies": ["plugin-b","plugin-c"]
392+
// },
393+
// {
394+
// "name": "plugin-b",
395+
// "dependencies": ["plugin-c"]
396+
// },
397+
// {
398+
// "name": "plugin-c",
399+
// "dependencies": []'
400+
// }
401+
// ]
402+
// }
403+
//
404+
// This means, `plugin-a` depends on `plugin-b` and `plugin-c`.
405+
// `plugin-b` depends on `plugin-c`.
406+
// `plugin-c` doesn't depend on anything.
407+
File pluginsDependencyFile = new File(project.projectDir.parentFile.parentFile, '.flutter-plugins-dependencies')
408+
if (pluginsDependencyFile.exists()) {
409+
def object = new JsonSlurper().parseText(pluginsDependencyFile.text)
410+
assert object instanceof Map
411+
assert object.dependencyGraph instanceof List
412+
return object.dependencyGraph
413+
}
414+
return []
415+
}
416+
356417
private static String toCammelCase(List<String> parts) {
357418
if (parts.empty) {
358419
return ""

packages/flutter_tools/lib/src/plugins.dart

Lines changed: 81 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@
44

55
import 'dart:async';
66

7+
import 'package:meta/meta.dart';
78
import 'package:mustache/mustache.dart' as mustache;
89
import 'package:yaml/yaml.dart';
910

1011
import 'android/gradle.dart';
1112
import 'base/common.dart';
1213
import 'base/file_system.dart';
14+
import 'convert.dart';
1315
import 'dart/package_map.dart';
1416
import 'features.dart';
1517
import 'globals.dart';
@@ -27,10 +29,14 @@ void _renderTemplateToFile(String template, dynamic context, String filePath) {
2729

2830
class Plugin {
2931
Plugin({
30-
this.name,
31-
this.path,
32-
this.platforms,
33-
});
32+
@required this.name,
33+
@required this.path,
34+
@required this.platforms,
35+
@required this.dependencies,
36+
}) : assert(name != null),
37+
assert(path != null),
38+
assert(platforms != null),
39+
assert(dependencies != null);
3440

3541
/// Parses [Plugin] specification from the provided pluginYaml.
3642
///
@@ -60,18 +66,28 @@ class Plugin {
6066
/// pluginClass: SamplePlugin
6167
/// windows:
6268
/// pluginClass: SamplePlugin
63-
factory Plugin.fromYaml(String name, String path, YamlMap pluginYaml) {
69+
factory Plugin.fromYaml(
70+
String name,
71+
String path,
72+
YamlMap pluginYaml,
73+
List<String> dependencies,
74+
) {
6475
final List<String> errors = validatePluginYaml(pluginYaml);
6576
if (errors.isNotEmpty) {
6677
throwToolExit('Invalid plugin specification.\n${errors.join('\n')}');
6778
}
6879
if (pluginYaml != null && pluginYaml['platforms'] != null) {
69-
return Plugin._fromMultiPlatformYaml(name, path, pluginYaml);
80+
return Plugin._fromMultiPlatformYaml(name, path, pluginYaml, dependencies);
7081
}
71-
return Plugin._fromLegacyYaml(name, path, pluginYaml);
82+
return Plugin._fromLegacyYaml(name, path, pluginYaml, dependencies);
7283
}
7384

74-
factory Plugin._fromMultiPlatformYaml(String name, String path, dynamic pluginYaml) {
85+
factory Plugin._fromMultiPlatformYaml(
86+
String name,
87+
String path,
88+
dynamic pluginYaml,
89+
List<String> dependencies,
90+
) {
7591
assert (pluginYaml != null && pluginYaml['platforms'] != null,
7692
'Invalid multi-platform plugin specification.');
7793
final YamlMap platformsYaml = pluginYaml['platforms'] as YamlMap;
@@ -118,10 +134,16 @@ class Plugin {
118134
name: name,
119135
path: path,
120136
platforms: platforms,
137+
dependencies: dependencies,
121138
);
122139
}
123140

124-
factory Plugin._fromLegacyYaml(String name, String path, dynamic pluginYaml) {
141+
factory Plugin._fromLegacyYaml(
142+
String name,
143+
String path,
144+
dynamic pluginYaml,
145+
List<String> dependencies,
146+
) {
125147
final Map<String, PluginPlatform> platforms = <String, PluginPlatform>{};
126148
final String pluginClass = pluginYaml['pluginClass'] as String;
127149
if (pluginYaml != null && pluginClass != null) {
@@ -147,6 +169,7 @@ class Plugin {
147169
name: name,
148170
path: path,
149171
platforms: platforms,
172+
dependencies: dependencies,
150173
);
151174
}
152175

@@ -232,6 +255,9 @@ class Plugin {
232255
final String name;
233256
final String path;
234257

258+
/// The name of the packages this plugin depends on.
259+
final List<String> dependencies;
260+
235261
/// This is a mapping from platform config key to the plugin platform spec.
236262
final Map<String, PluginPlatform> platforms;
237263
}
@@ -250,11 +276,13 @@ Plugin _pluginFromPubspec(String name, Uri packageRoot) {
250276
return null;
251277
}
252278
final String packageRootPath = fs.path.fromUri(packageRoot);
279+
final YamlMap dependencies = pubspec['dependencies'];
253280
printTrace('Found plugin $name at $packageRootPath');
254281
return Plugin.fromYaml(
255282
name,
256283
packageRootPath,
257284
flutterConfig['plugin'] as YamlMap,
285+
dependencies == null ? <String>[] : <String>[...dependencies.keys],
258286
);
259287
}
260288

@@ -281,29 +309,58 @@ List<Plugin> findPlugins(FlutterProject project) {
281309
return plugins;
282310
}
283311

284-
/// Returns true if .flutter-plugins has changed, otherwise returns false.
312+
/// Writes the .flutter-plugins and .flutter-plugins-dependencies files based on the list of plugins.
313+
/// If there aren't any plugins, then the files aren't written to disk.
314+
///
315+
/// Finally, returns [true] if .flutter-plugins or .flutter-plugins-dependencies have changed,
316+
/// otherwise returns [false].
285317
bool _writeFlutterPluginsList(FlutterProject project, List<Plugin> plugins) {
318+
final List<dynamic> directAppDependencies = <dynamic>[];
319+
final StringBuffer flutterPluginsBuffer = StringBuffer();
320+
321+
final Set<String> pluginNames = <String>{};
322+
for (Plugin plugin in plugins) {
323+
pluginNames.add(plugin.name);
324+
}
325+
for (Plugin plugin in plugins) {
326+
flutterPluginsBuffer.write('${plugin.name}=${escapePath(plugin.path)}\n');
327+
directAppDependencies.add(<String, dynamic>{
328+
'name': plugin.name,
329+
// Extract the plugin dependencies which happen to be plugins.
330+
'dependencies': <String>[...plugin.dependencies.where(pluginNames.contains)],
331+
});
332+
}
286333
final File pluginsFile = project.flutterPluginsFile;
287-
final String oldContents = _readFlutterPluginsList(project);
288-
final String pluginManifest =
289-
plugins.map<String>((Plugin p) => '${p.name}=${escapePath(p.path)}').join('\n');
290-
if (pluginManifest.isNotEmpty) {
291-
pluginsFile.writeAsStringSync('$pluginManifest\n', flush: true);
334+
final String oldPluginFileContent = _readFileContent(pluginsFile);
335+
final String pluginFileContent = flutterPluginsBuffer.toString();
336+
if (pluginFileContent.isNotEmpty) {
337+
pluginsFile.writeAsStringSync(pluginFileContent, flush: true);
292338
} else {
293339
if (pluginsFile.existsSync()) {
294340
pluginsFile.deleteSync();
295341
}
296342
}
297-
final String newContents = _readFlutterPluginsList(project);
298-
return oldContents != newContents;
343+
344+
final File dependenciesFile = project.flutterPluginsDependenciesFile;
345+
final String oldDependenciesFileContent = _readFileContent(dependenciesFile);
346+
final String dependenciesFileContent = json.encode(<String, dynamic>{
347+
'dependencyGraph': directAppDependencies,
348+
});
349+
if (pluginFileContent.isNotEmpty) {
350+
dependenciesFile.writeAsStringSync(dependenciesFileContent, flush: true);
351+
} else {
352+
if (dependenciesFile.existsSync()) {
353+
dependenciesFile.deleteSync();
354+
}
355+
}
356+
357+
return oldPluginFileContent != _readFileContent(pluginsFile)
358+
|| oldDependenciesFileContent != _readFileContent(dependenciesFile);
299359
}
300360

301-
/// Returns the contents of the `.flutter-plugins` file in [project], or
302-
/// null if that file does not exist.
303-
String _readFlutterPluginsList(FlutterProject project) {
304-
return project.flutterPluginsFile.existsSync()
305-
? project.flutterPluginsFile.readAsStringSync()
306-
: null;
361+
/// Returns the contents of [File] or [null] if that file does not exist.
362+
String _readFileContent(File file) {
363+
return file.existsSync() ? file.readAsStringSync() : null;
307364
}
308365

309366
const String _androidPluginRegistryTemplateOldEmbedding = '''package io.flutter.plugins;
@@ -782,5 +839,5 @@ Future<void> injectPlugins(FlutterProject project, {bool checkProjects = false})
782839
///
783840
/// Assumes [refreshPluginsList] has been called since last change to `pubspec.yaml`.
784841
bool hasPlugins(FlutterProject project) {
785-
return _readFlutterPluginsList(project) != null;
842+
return _readFileContent(project.flutterPluginsFile) != null;
786843
}

packages/flutter_tools/lib/src/project.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,10 @@ class FlutterProject {
146146
/// The `.flutter-plugins` file of this project.
147147
File get flutterPluginsFile => directory.childFile('.flutter-plugins');
148148

149+
/// The `.flutter-plugins-dependencies` file of this project,
150+
/// which contains the dependencies each plugin depends on.
151+
File get flutterPluginsDependenciesFile => directory.childFile('.flutter-plugins-dependencies');
152+
149153
/// The `.dart-tool` directory of this project.
150154
Directory get dartTool => directory.childDirectory('.dart_tool');
151155

packages/flutter_tools/templates/app/.gitignore.tmpl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
**/doc/api/
2525
.dart_tool/
2626
.flutter-plugins
27+
.flutter-plugins-dependencies
2728
.packages
2829
.pub-cache/
2930
.pub/

packages/flutter_tools/templates/module/common/.gitignore.tmpl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,4 @@ build/
3939
.android/
4040
.ios/
4141
.flutter-plugins
42+
.flutter-plugins-dependencies

packages/flutter_tools/templates/package/.gitignore.tmpl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
**/doc/api/
2525
.dart_tool/
2626
.flutter-plugins
27+
.flutter-plugins-dependencies
2728
.packages
2829
.pub-cache/
2930
.pub/

0 commit comments

Comments
 (0)