-
Notifications
You must be signed in to change notification settings - Fork 29.7k
[flutter_tools] Removes the need of a no-op plugin implementations #48614
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
/cc @amirh |
|
|
||
| import 'dart:async'; | ||
|
|
||
| import 'package:flutter_tools/src/platform_plugins.dart'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be a relative import
| /// If there aren't any plugins, then the files aren't written to disk. The resulting | ||
| /// file looks something like this: | ||
| /// { | ||
| /// "_info": "// This is a generated file; do not edit or check into version control.", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would also recommend embedding some sort of version string in the JSON, just in case
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also possibly the date it was generated on/ the version of the tool used to generate it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See .dart_tool/package_config.json:
"generated": "2020-01-13T22:42:48.743606Z",
"generator": "pub",
"generatorVersion": "2.8.0-dev.0.0.flutter-395daaa3ec"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
| } | ||
| } | ||
|
|
||
| /// Writes the .flutter-plugins.json file based on the list of plugins. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.flutter-plugins-dependencies since new projects on stable have this file in the .gitignore already.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But that file means something different; wouldn't changing the content break a bunch of projects?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed it to .flutter-plugins-dependencies, and won't break since I still add the dependencyGraph to support current projects. Once it's fully deprecated we can just remove it from the json
|
Was there discussion somewhere about using a single file that I missed while out? The plan I had proposed was to make a file per platform, in the platform directories, which seemed like it would be simpler for each platform to parse, as well as sticking to the current structure of platform-specific information generally being within platform directories. I'm curious what the pro/con evaluation of that vs. the approach here was. |
I think we talked briefly about this before break but I've lost context on the discussion. |
|
I don’t think I was involved in the discussion about deprecating .flutter-plugins. Unfortunately, in some situations, Android and iOS have project files that depend on the location and format of this file. I’m looking into addressing this specific problem (managing project files) in a separate effort. Maybe the rest of the platform can have their own file while iOS/Android continue to use .flutter-plugins until we migrate the affected projects. |
|
Actually, I remember now. I think the context was concerns about how updates to the platform directories (if any) would effect this file. @franciscojma86 correct me if wrong |
This file is generated on demand though; I would expect that for desktop it would be in the |
|
Yeah it was a brief discussion with @jonahwilliams , but I should've opened an issue and discussed there. Looked simple enough at the beginning (doesn't it all? 😄 ). There were no strong reasons to go either way. The one advantage of going this way, is to stop using the key-value file and turn it into a JSON which is more flexible and we can version. Specially since The only advantage I see of doing a per platform @stuartmorgan do you have any concerns with this method? The other PR is not far behind so I can continue that one if people think is a better options. Personally, I don't see any clear advantages. |
|
I have a couple of potential concerns, although nothing definite enough to prevent moving forward:
And I can see the argument for this being potentially more future-proof, since we can add new sections to a JSON file without breaking existing projects. Let's proceed with this, and if it's problematic for Linux or Windows later I can figure out how to adapt. |
| } | ||
| } | ||
|
|
||
| /// Writes the .flutter-plugins.json file based on the list of plugins. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But that file means something different; wouldn't changing the content break a bunch of projects?
| } | ||
|
|
||
| void _addPluginsToPlatform(List<Plugin> plugins, FlutterProjectPlatform platform, Map<String, dynamic> pluginsMap) { | ||
| if (!platform.existsSync()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the file is in a central location rather than sharded to the platform directories, why do we need this check?
It's not clear to me that if I add a new platform to an existing project this file would be regenerated unless I also change the plugins, so skipping platforms make cause weird errors for people adding a platform after the fact. And generating them all should be harmless, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I got rid of this and added to the map directly. I was trying to avoid writing platforms that have no plugins but as you say, there's no much point to that. If there are no plugins at all, the file won't be written.
| return plugins; | ||
| } | ||
|
|
||
| void _addPluginsToPlatform(List<Plugin> plugins, FlutterProjectPlatform platform, Map<String, dynamic> pluginsMap) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you document this? It's not entirely obvious to me how the arguments interact without looking at a bunch of code (e.g., that pluginsMap is basically an out param).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed it
| const String info = 'This is a generated file; do not edit or check into version control.'; | ||
|
|
||
| final Map<String, dynamic> result = <String, dynamic> {}; | ||
| result['_info'] = '// $info'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why does this have a C-style comment prefix, when it's just a string value in a JSON?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bad copy paste :)
| } | ||
| } | ||
|
|
||
| return oldPluginFileContent != _readFileContent(pluginsFile); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since there's encoding/decoding going on now, it would be nice to make this a little bit more efficient. You don't need to json.encode above if pluginsMap.isEmpty, and you don't need to do the file readback since you already have pluginFileContent. There's already branching for empty vs. non-empty, so being more efficient here would add minimal complexity. Something like:
final File pluginsFile = project.flutterPluginsJsonFile;
final String oldPluginFileContent = _readFileContent(pluginsFile);
final String pluginFileContent = pluginsMap.isNotEmpty ? json.encode(result) : null;
if (pluginFileContent != null) {
pluginsFile.writeAsStringSync(pluginFileContent, flush: true);
} else {
if (pluginsFile.existsSync()) {
pluginsFile.deleteSync();
}
}
return pluginFileContent != oldPluginFileContent;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I moved the check for empty plugins to the top. Feels a bit cleaner to me. WDYT?
| bool existsSync(); | ||
|
|
||
| /// Creates a list with the project plugins supported for the current platform. | ||
| List<Map<String, dynamic>> pluginsList(List<Plugin>plugins) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
filterPlugins? The current name suggests to me that it's returning a list of plugins that are a property of the project, rather than being driven by the input list. (It feels a bit odd for this to be part of the project, rather than a stand-alone utility or something on a plugins class, but I don't feel super strongly about that.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree it feels better as a helper function. Done
Isn't that why the legacy file is still being written in this patch? We should still be creating the new file for all platforms so there's something to migrate to later. |
6aba1b9 to
d3fd61a
Compare
stuartmorgan-g
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just small nits; LGTM
| /// If there aren't any plugins, then the files aren't written to disk. The resulting | ||
| /// file looks something like this: | ||
| /// { | ||
| /// "info": "This is a generated file; do not edit or check into version control.", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't the location of this line in the file going to be "random" (not from run to run unless the map implementation changes, but not guaranteed to be first)? I don't have a solution that doesn't break existing apps since we're re-using a file that's already a dictionary--otherwise we could wrap the real content in an array with the comment then the content--but I'm wondering how useful this will end up being without an order dependency. I guess better than nothing...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed the test to not require an order comparison and clarified in the comment that the order is not guaranteed
| return directAppDependencies; | ||
| } | ||
|
|
||
| // This method will be DEPRECATED in favor of _writeFlutterPluginsList. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this isn't an API surface, it won't ever actually be deprecated, will it, just removed? The API is the existence of the file, not the method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated
| 'plugin-c=/.tmp_rand0/plugin.rand2/\n' | ||
| '' | ||
| ); | ||
| expect(flutterProject.flutterPluginsDependenciesFile.readAsStringSync(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we actually want this test to break if the order of the elements in this file change? I.e., do we consider the order of elements in a JSON dictionary to be an API? That seems extreme; I know in some cases Ian has advocated for that, but for a file that's only intended for use as JSON I'm not sure we actually want that.
If we don't, this test should parse and then do a deep structure equality test, instead of comparing strings.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated test as discussed
|
|
||
| // https://github.com/flutter/flutter/issues/46898 | ||
| // https://github.com/flutter/flutter/issues/39657 | ||
| File(path.join(pluginCDirectory.path, 'android', 'build.gradle')).deleteSync(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed this line since the top issue is closed and the second one I assume would be fixed by this PR.
|
@blasten @jonahwilliams I updated it to include the plugins list in |
| /// "windows": [], | ||
| /// "web": [] | ||
| /// }, | ||
| /// "dependencyGraph": [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the plan to split "dependencyGraph" per platform in a separate PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it makes much sense to split it anymore. It doesn't really add any value. I started that way, but then we'd have change the way the dependencies are handled in gradle, which at that point felt unnecessary. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Gradle is working around the fact that dependencyGraph isn't split per platform. I think it would be ideal if Gradle didn't have to workaround that and instead just read this file. This could be done in a separate PR though if that makes sense.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense, I did it here :)
blasten
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM! + comment
Description
As mentioned in #46618, macOS plugins require an iOS no-op implementation and vice versa. This is due to some limitations in the generated
.flutter-pluginsfile.Updates
.flutter-plugin-dependenciesto include the plugins list and version information. Supports all platforms. Example:This PR doesn't include changes to the Podfile and gradle templates, since there are still some issues to consider regarding the migration strategy and CI changes.
Related Issues
#46618
Tests
I added the following tests:
Updated tests in
plugins-test.dartandplugin-dependencies-test.dart. Still supports tests for current logic.Checklist
Before you create this PR confirm that it meets all requirements listed below by checking the relevant checkboxes (
[x]). This will ensure a smooth and quick review process.///).flutter analyze --flutter-repo) does not report any problems on my PR.Breaking Change
Did any tests fail when you ran them? Please read [Handling breaking changes].