-
Notifications
You must be signed in to change notification settings - Fork 29.8k
Add --platforms to flutter create -t plugin command
#59507
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
flutter add-platform command
jonahwilliams
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.
This PR is really hard to review, because I'm not really sure which code was changed and which was original. I'm not convinced that doing a massive refactor ahead of trying to change the behavior is the best way to go about this.
Could I suggest a more incremental approach: make the methods you need to override on create visibleForTesting, then create an AddPackage command that extends flutter create.
Then, you could gradually refactor shared functionality with huge diffs
| /// Perform an initial validation of the environment and args. | ||
| /// | ||
| /// Should be called as early as possible in [FlutterCommand.runCommand]. | ||
| void doInitialValidation() { |
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.
There is already a hook for this: FlutterCommand.verifyThenRunCommand
packages/flutter_tools/test/commands.shard/permeable/add_platform_test.dart
Outdated
Show resolved
Hide resolved
flutter add-platform command--platforms to flutter create -t plugin command
| /// Adding argument is optional if the command does not require the user to explicitly state what platforms the project supports. | ||
| void _addPlatformsOptions() { | ||
| argParser.addMultiOption('platforms', | ||
| help: 'the platforms supported by this project.', |
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 expand this comment a bit for each kind of template. Talk about what it does/does not generate
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, see if you like this.
| globals.printError(''' | ||
| You have generated a new plugin project without | ||
| specifying the `--platforms` flag. A plugin project supports no platforms is generated. | ||
| The project will not compile until you add platforms implementation. |
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.
project -> plugin?
The project will not compile until you add platforms implementation.
This isn't quite true, it just isn't useful
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.
Or remove this line?
| generatedCount += await _renderTemplate('plugin', directory, templateContext, overwrite: overwrite); | ||
| if (!argResults.wasParsed('platforms')) { | ||
| globals.printError(''' | ||
| You have generated a new plugin project without |
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.
Can you hoist this out into a constant so it is easier to read?
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
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.
please move to the top of the file
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
| context['android'] = true; | ||
| break; | ||
| case 'web': | ||
| context['web'] = featureFlags.isWebEnabled && true; |
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.
Rather than conditionally checking here, you might be able to only add web, linux, macos, windows to the list itself if the experiment is enabled. Then you can do:
for (final String platform in platforms) {
context[platform] = true;
}
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 with a slightly different approach, please take a look and see if you like it.
packages/flutter_tools/test/commands.shard/permeable/create_test.dart
Outdated
Show resolved
Hide resolved
cyanglaz
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.
@jonahwilliams I updated the PR with some of your suggestions. I'll work on the other half of the comments and ping you once they are all addressed.
| globals.printError(''' | ||
| You have generated a new plugin project without | ||
| specifying the `--platforms` flag. A plugin project supports no platforms is generated. | ||
| The project will not compile until you add platforms implementation. |
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.
Or remove this line?
| generatedCount += await _renderTemplate('plugin', directory, templateContext, overwrite: overwrite); | ||
| if (!argResults.wasParsed('platforms')) { | ||
| globals.printError(''' | ||
| You have generated a new plugin project without |
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
| /// Adding argument is optional if the command does not require the user to explicitly state what platforms the project supports. | ||
| void _addPlatformsOptions() { | ||
| argParser.addMultiOption('platforms', | ||
| help: 'the platforms supported by this project.', |
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, see if you like this.
| if (argResults.wasParsed('platforms') && (generateModule || generatePackage)) { | ||
| final String template = generateModule?'module':'package'; | ||
| throwToolExit( | ||
| 'The "--platforms" argument is not supported in $template template. please remove the argument and try again.', |
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.
nit, remove : please remove the argument and try again
| final List<String> platforms = stringsArg('platforms'); | ||
| // `--platforms` does not support module and package. | ||
| if (argResults.wasParsed('platforms') && (generateModule || generatePackage)) { | ||
| final String template = generateModule?'module':'package'; |
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.
formatting
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
|
|
||
| final List<String> platforms = _getSupportedPlatformsFromTemplateContext(templateContext); | ||
| String platformsString = ''; | ||
| for (int i = 0; i < platforms.length; i++) { |
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.
platforms.join(',') ?
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. Good call!
| argParser.addMultiOption('platforms', | ||
| help: 'the platforms supported by this project.' | ||
| 'This argument only works when the --template is set to app or plugin.' | ||
| 'Respective folders will be generated in the target project.' |
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.
clarify "respective folders" - "platform folders (android/ ) ?
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
| 'Respective folders will be generated in the target project.' | ||
| 'When adding platforms to a plugin project, the pubspec.yaml will be updated with the requested platform.' | ||
| 'When adding windows, linux or macos platforms, the platform feature has to be enable. Otherwise it would be a no-op.' | ||
| 'To enable desktop features, run `flutter config --enable-<desktopName>-desktop.' |
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.
Maybe: Adding desktop platforms requires the corresponding desktop config setting to be enabled
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
| generatedCount += await _renderTemplate('plugin', directory, templateContext, overwrite: overwrite); | ||
| if (!argResults.wasParsed('platforms')) { | ||
| globals.printError(''' | ||
| You have generated a new plugin project without |
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.
please move to the top of the file
| if (boolArg('pub')) { | ||
| await pub.get(context: PubContext.create, directory: directory.path, offline: boolArg('offline')); | ||
| await project.ensureReadyForPlatformSpecificTooling(checkProjects: false); | ||
| await project.ensureReadyForPlatformSpecificTooling(checkProjects: pluginExampleApp); |
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 is confusing, why do we need to only check projects if we're creating an example app
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.
As a side effect, this method always generates the GeneratedPluginRegstraint files for iOS and Android unless the checkProject flag is true. When the plugin is not for mobile, this method still generate those files, including iOS and Android.
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.
(ew.)
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
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 you guys think pluginExampleApp is confusing? I am thinking it might be better just to call it checkProjects or checkProjectsWhileEnsuringTooling
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 think the non specificity of "checkProjects" is what's throwing me off (I know you didn't name it). Like, check what? If that was a better name, pluginExampleApp might make 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.
By looking at the code of ensureReadyForPlatformSpecificTooling, I also not sure what that meant. @jonahwilliams do you have any idea? Maybe we can add a TODO comment on that method to either fix the param name or add some doc/comments on the checkProjects 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.
yeah, TODO for now.
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
| return generatedCount; | ||
| } | ||
|
|
||
| void _updateTemplateContextWithPlatforms(Map<String, dynamic> context, List<String> platforms) { |
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 is confusing, everything gets set to false by default, then everything get sets to true, then some are conditional set to false.
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.
you are right, I deleted this method and updated the caller a bit. Please take a look see if it makes more sense.
| /// | ||
| /// Throws ToolExit if cannot find the `platforms:` map. | ||
| static YamlMap getPlatformsYamlMap(YamlMap pubspec) { | ||
| if (pubspec == null) { |
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.
Assuming that someone is migrating an old plugin and trying to use add-platform, it would be nice to at least link to page explaining the formatting
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. We only need to show this error if the platforms map is missing or not valid, other failing scenarios are just general invalid pubspec.
| throwToolExit('Did not find valid `platforms` map in pubspec.yaml', exitCode: 2); | ||
| } | ||
|
|
||
| return pluginConfig['platforms'] as YamlMap; |
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.
missing a type check here, platforms could be anything
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
| final List<String> platformsToAdd = List<String>.from(platforms); | ||
| final YamlMap platformsMap = getPlatformsYamlMap(pubspec); | ||
| final List<String> existingPlatforms = platformsMap.keys.cast<String>().toList(); | ||
| platformsToAdd.removeWhere((String platform) => existingPlatforms.contains(platform)); |
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.
platformsToAdd.removeWhere(existingPlatforms.contains);
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
| frontSpaces = line.split('platforms:').first; | ||
| index = i + 1; | ||
| } | ||
| if (line.contains('some_platform:')) { |
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.
Does this rely on on having a fake platform key?
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 logic doesn't. however it does remove the some_platform.
The reason that some_platform needs to be there is that we need to keep the pubspec.yaml valid. Otherwise, other steps of the create command would fail (pub get for example)
Another way is to perhaps not having the whole flutter map in the pubspec if there are no --platforms specified, then add the flutter map programmatically when --platforms is specified.
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 seems pretty fragile, what if someone develops android/ios and then wants to use add-platform to add a new desktop or web implementation?
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 also work as the code is to look for the platforms: key and add an entry under it. i think I have a test covers this scenario: 'create a plugin with ios, then add macos'.
It only removes 'some_platform' map, which is an invalid platform.
| /// ``` | ||
| /// | ||
| /// This method doesn't support the legacy plugin pubspec format and will throw error if try to update a pubspec.yaml that uses the legacy format. | ||
| static Future<void> updatePubspecWithPlatforms(String projectDir, List<String> platforms, String pluginClass, String androidIdentifier) async { |
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.
It seems like there are a lot of ways for this method to fail, and it requires a sentinel data in the form of the comment. Is there a way this process could be simplified by ignoring comments? Is add-platforms going to be usable if it requires a very specific structure?
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.
one way to improve this is to ignore the lines that starts with a #
Other than that, I don't find a perfect solution to update this without breaking the existing yaml comments. Which I think we need to preserve because those are great instructions for new users.
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 say ignore the comments and focus on the structured data.
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.
Did you mean to overwrite all the comments when updating pubspec.yaml?
Something like
file.write(pubspecYamlMap.convertToString)?
(I'm making up APIs here)
Or did you mean to skip comments like
if (line.contains('#')) {
continue;
}
jmagman
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
|
Google testing passed! |
3 similar comments
|
Google testing passed! |
|
Google testing passed! |
|
Google testing passed! |
Description
Adds a
--platformsflag to theflutter create. The flag only works in-t pluginand-t appThis Pr doesn't make --platforms support web yet because web template is not ready.
Note: We need to decide what to do with the version constraint before landing this.
Related Issues
#59493
Tests
plugin supports ios if requested
plugin supports android if requested
create an empty plugin, then add ios
create an empty plugin, then add android
create an empty plugin, then add linux
create an empty plugin, then add macos
create an empty plugin, then add windows
create a plugin with ios, then add macos
create a plugin with ios and android
create a module with --platforms throws error.
create a package with --platforms throws error.
Also updated some existing tests to fit the new flag.
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.