Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Refactor Flutter Gradle Plugin so it can be applied using the declarative plugins {} block #123511

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

Merged
merged 57 commits into from
Apr 19, 2023

Conversation

bartekpacia
Copy link
Member

@bartekpacia bartekpacia commented Mar 27, 2023

This PR aims to resolve #121552.

Resources used:

This PR also paves way for #121541, because apps will no longer have:

apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"

hardcoded. Instead, they'll use:

plugins {
    // ...
    id "dev.flutter.flutter-gradle-plugin" // the exact name is tentative
}

Pre-launch Checklist

  • I read the Contributor Guide and followed the process outlined there for submitting PRs.
  • I read the Tree Hygiene wiki page, which explains my responsibilities.
  • I read and followed the Flutter Style Guide, including Features we expect every widget to implement.
  • I signed the CLA.
  • I listed at least one issue that this PR fixes in the description above.
  • I updated/added relevant documentation (doc comments with ///).
  • I added new tests to check the change I am making, or this PR is test-exempt.
  • All existing and new tests are passing.

@flutter-dashboard flutter-dashboard bot added the tool Affects the "flutter" command-line tool. See also t: labels. label Mar 27, 2023
@bartekpacia bartekpacia force-pushed the gradle_plugin_proper branch from df323f9 to 0e11466 Compare March 27, 2023 01:50
@flutter-dashboard flutter-dashboard bot added d: examples Sample code and demos c: contributor-productivity Team-specific productivity, code health, technical debt. labels Mar 27, 2023
@bartekpacia
Copy link
Member Author

bartekpacia commented Mar 27, 2023

Successfully built examples/hello_world 🎉

Now I'm thinking what to do next:

1) All in

  • We migrate all example apps, integration test apps, benchmark apps in the flutter repo to the new, declarative way of applying the Flutter Gradle Plugin.
  • We also write a migration to make this transition seamless for customers.
  • The "new way of applying the plugin" becomes the only way, because all the existing projects are automatically migrated.

I think it won't be a breaking change since it'll be handled automatically by the migration. So we won't have to go through this process.

2) Gradual

We migrate some example apps, integration test apps, and benchmark apps in the flutter repo to the new, declarative way of applying the Flutter Gradle Plugin.

The "new way" and the "old way" coexist.

We should write the migration anyway, IMHO.

I prefer option 1) which will require less support.

cc @gnprice @reidbaker

@reidbaker
Copy link
Contributor

I am skeptical of requiring the migration for all apps even if we build a tool to do the migration. That said for code owned by flutter I think I could get behind requiring the migration.

@gnprice
Copy link
Member

gnprice commented Mar 30, 2023

Neat!

What would be the advantage of option 1 over option 2? What sorts of things would we be able to do because we're no longer supporting the old way, or what sorts of work would be required to continue supporting the old way?

I'd be nervous about going all at once to supporting only the new way. Doing so would mean that if for any particular project the automatic migration doesn't quite work 100% seamlessly out of the box, that project would be totally blocked on upgrading Flutter until they figure out what this transition is about well enough for them to make the migration manually.

Based on the diff in the example app, this will probably be a more complex migration than many migrations are. And the app build.gradle is a file that people regularly make manual changes to, for a variety of good and necessary reasons. So between those two factors, I think it's fairly likely that however well we write the auto-migration, there will be some projects where the file has been rearranged enough that the auto-migration isn't able to completely understand what's going on, and doesn't apply its changes.

OTOH if

  • we get to something that supports both the new way and the old way;
  • plus there's an auto-migration that works smoothly for most projects;
  • and there's also perhaps a writeup somewhere that clearly explains what this change is about and why it's good, and how to do it manually if the auto-migration didn't work, and the auto-migration links to that when it fails to apply;

then after that's been out in a stable release for some time, if there's a clear benefit we'd get (perhaps even just cleaning up the code) from dropping support for the old way, I think dropping it would be a lot more palatable at that point.

One point of comparison is Flutter's deprecation policy:

The lifetime for a Flutter deprecation is 1 year after reaching the stable channel, or after 4 stable releases, whichever is longer.

I think that policy doesn't directly apply here; it's intended for the framework's Dart API. But as long as there's an auto-migration, I think the situation is pretty comparable, and it'd be reasonable to take the same approach.

@bartekpacia bartekpacia force-pushed the gradle_plugin_proper branch from 4e7f9cd to 36dc5d8 Compare March 30, 2023 21:34
@bartekpacia bartekpacia changed the title [WIP] Trying to includeBuild() Flutter Gradle plugin Trying to includeBuild() Flutter Gradle plugin Mar 31, 2023
@bartekpacia bartekpacia force-pushed the gradle_plugin_proper branch from 97e00ad to daf30af Compare March 31, 2023 16:15
@bartekpacia
Copy link
Member Author

I got both the "new" and "old" style apply of Flutter Gradle Plugin to work (so, essentially, option (2)). Doing that felt like defusing a bomb btw.

Most tests pass except for add-to-app scenarios. Gonna work on fixing them now.

@bartekpacia bartekpacia changed the title Trying to includeBuild() Flutter Gradle plugin Refactor Flutter Gradle Plugin so it can be applied using the declarative plugins {} block Mar 31, 2023
@bartekpacia bartekpacia marked this pull request as ready for review March 31, 2023 22:00
@bartekpacia
Copy link
Member Author

bartekpacia commented Mar 31, 2023

Got all tests to pass.

I'd be happy to hear your opinions on the current progress.

I think the next steps are:

  • write an automatic migration so that most existing projects will be updated
  • update flutter create templates for the "new way" of applying the FGP (Flutter Gradle Plugin)
  • deprecate the "old way" of applying the FGP and advise developers to migrate to the "new way". After ~1 year (in line with the Deprecation Policy), support for the old way would be removed.

Automatic migration

Here's an overview of what it should do:

  • android/app/build.gradle file

    +plugins {
    +    id "dev.flutter.flutter-gradle-plugin"
    +}
    -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"

    More ambitious version:

    +plugins {
    +    id "com.android.application"
    +    id "dev.flutter.flutter-gradle-plugin"
    +}
    -apply plugin: 'com.android.application'
    -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"

    If the app uses apply for other plugins, I'd leave the migration up to the developers. It's hard/impossible to handle all the cases automatically.

  • android/settings.gradle file

    I'd like to extract this common boilerplate somewhere, but haven't found a way. See here for more details. So until we find a more concise snippet, it'd look like this:

    +pluginManagement {
    +    def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
    +    def properties = new Properties()
    +    assert localPropertiesFile.exists()
    +    localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
    +    def flutterSdkPath = properties.getProperty("flutter.sdk")
    +    assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
    +
    +    includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
    +
    +    plugins {
    +        id "dev.flutter.flutter-gradle-plugin" version "1.0.0" apply false
    +    }
    +}

@gnprice raised some important caveats in this comment - the Gradle build files are often edited for various reasons, even more so in larger, complex projects. I tried to keep the migration as lean as possible, but I'm still afraid some edge cases might exist that'd make it hard/impossible to make it work 100% of the time (for example, add-to-app scenarios). So my question is: what should happen if the project can't be automatically migrated? How to fail gracefully in such a case?

I'm also all in for writing a short article explaining (1) why this migration is good and (2) how to do it manually, in case it cannot be done automatically.

Upgrade default templates

That should be pretty straightforward - I've already done that in #122290 and #123426.

Copy link
Contributor

@reidbaker reidbaker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall this looks good. Thanks for your contribution.

@bartekpacia bartekpacia force-pushed the gradle_plugin_proper branch from 8f00d72 to c99259e Compare April 3, 2023 00:03
Copy link
Member

@gnprice gnprice left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Small comments on the code.

So my question is: what should happen if the project can't be automatically migrated? How to fail gracefully in such a case?

Since the old way will continue to work (for some time, anyway), in this case the migration can just leave the files untouched and that will be safe.

Then it might also print a message, something like:

Unable to automatically upgrade Android build files to declarative form.
Consider doing the upgrade manually:
  https://docs.flutter.dev/path/to/migration/guide

Copy link
Member

@gnprice gnprice left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On thinking about this again, I think it will make sense to merge this PR once the small remaining review comments are handled. The converted example validates that the new way works, and the other examples validate that the old way works.

Then the template changes and the migration can go in a followup PR or two.

@bartekpacia
Copy link
Member Author

Oof, Google testing failed. cc @reidbaker

@bartekpacia
Copy link
Member Author

bartekpacia commented Apr 3, 2023

Thanks for taking a look.

Then the template changes and the migration can go in a followup PR or two.

Actually, I'd prefer to include template changes at least in this PR. No strong opinions though.

So my question is: what should happen if the project can't be automatically migrated? How to fail gracefully in such a case?

Since the old way will continue to work (for some time, anyway), in this case the migration can just leave the files untouched and that will be safe.
Then it might also print a message, something like:

Unable to automatically upgrade Android build files to declarative form.
Consider doing the upgrade manually:
  https://docs.flutter.dev/path/to/migration/guide

The main problem is that we can't detect if the migration was successful just after we've performed it. The developer has to run Gradle build, and only after it passes/fails, we can be certain.

I'm not sure we could make this experience more seamless. The first idea off the top of my head:

  1. Create some temporary file ({gradle_migration_checked: false}) just after the migration has been performed
  2. The user runs flutter build apk/flutter run on Android device
  3. If the build fails because of any Gradle failure, and if the gradle_migration_checked in the temporary file is false, we print your error message about the migration and automatically rollback, suggesting to migrate manually.
  4. If the build succeeds, we delete the file or write true to it.

Honestly? I hate this idea :)

Another take: when the migration is performed, its logs are only printed when running with --verbose. I think this is not ideal for "larger", potentially breaking migrations, like this one. In such cases, migration logs should be always printed (no matter the --verbose flag). See relevant code.
The migration model could be extended so that individual migrations could override a new, hypothetical bool potentiallyBreaking, and if it's true, logger.printStatus() would be used instead of logger.printTrace().

EDIT: Actually I see that logger.printStatus() is used for logging that the migration is being performed. And in migrations that don't call processFileLines(), the logger.printStatus() is used anyway, e.g here.

@gnprice
Copy link
Member

gnprice commented Apr 3, 2023

Actually, I'd prefer to include template changes at least in this PR. No strong opinions though.

Sure, WFM.

The main problem is that we can't detect if the migration was successful just after we've performed it. The developer has to run Gradle build, and only after it passes/fails, we can be certain.

Ah. Yeah, the approach that's generally been taken in these migrations is to instead say: if we're not certain that the migration will be a success when it applies, then we make the patterns of what it's looking for in the file more specific until we are pretty much certain.

As you outline, the model for how these migrations run doesn't really allow for a great UX/DX for doing a migration that has more uncertainty like that. I think that'd call for an interaction that the user had to more explicitly trigger — along the lines of the Android Studio upgrade wizards, and that's perhaps what flutter migrate was projected to be as well (but it seems like that project didn't really get completed.)

I think it will be quite doable here to make a migration that we can be sure of in that way. It just means there'll be a significant swath of projects the auto-migration doesn't apply to; but the long tail which haven't changed much in this file from the template will still benefit.

@bartekpacia
Copy link
Member Author

bartekpacia commented Apr 3, 2023

if we're not certain that the migration will be a success when it applies, then we make the patterns of what it's looking for in the file more specific until we are pretty much certain.

We'll need a way to test the migrations on some real apps. For example, run all the migrations over apps in examples and dev and make sure nothing breaks.

It just means there'll be a significant swath of projects the auto-migration doesn't apply to

For them we could print a warning each time they flutter run/build on Android:

Your project is using the outdated, non-declarative form of applying Flutter Gradle Plugin. Support for it will be removed in a future version of Flutter.
Consider doing the upgrade manually:
  https://docs.flutter.dev/path/to/migration/guide

So to sum up todos:

  • This PR: refactor FGP to support declarative apply, update template (but imho, we've got a blocker here)
  • Future PR: write the laser-focused automatic migration, write the migration guide doc, print warning if using imperative apply
  • Far future PR: remove support for imperative apply

I can create issues for them (or you can, if you want to) once you and @reidbaker agree on this rough plan.

PS

along the lines of the Android Studio upgrade wizards, and that's perhaps what flutter migrate was projected to be as well (but it seems like that project didn't really get completed.)

I think that a separate flutter migrate command would be great (for "larger" migrations maybe?) if the flutter tool detected that some migrations have to be run and reminded the user to do them. It'd also "raise awareness" among the developers that such automatic migrations are a thing.

Comment on lines +87 to +100
/**
* Some apps don't set default compile options.
* Apps can change these values in android/app/build.gradle.
* This just ensures that default values are set.
*/
android {
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
Copy link
Member Author

@bartekpacia bartekpacia Apr 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has no effect when FGP is applied using the plugin {} block.

Verified by changing to some ancient JavaVersion.VERSION_1_1. examples/hello_world (using declarative apply) continued to build fine, examples/flutter_view (using imperative apply) started to spit out error: invalid source release: 1.1.

It shouldn't be a big thing - the templates contain these lines anyway.

@gnprice
Copy link
Member

gnprice commented Apr 3, 2023

We'll need a way to test the migrations on some real apps. For example, run all the migrations over apps in examples and dev and make sure nothing breaks.

Definitely. I think customer_testing, and also Google testing, will contribute here too.

There may be internal apps at Google where the overall app build doesn't get run in the presubmit "Google testing" check. In that case, once there's a draft of the migration, @reidbaker or another Googler can probably help by running the PR across those other Google-internal apps. That will confirm that the migration either succeeds, or successfully chooses not to apply itself.

So to sum up todos:

SGTM!

(but imho, we've got a blocker here)

I wouldn't consider that duplication a blocker. If the duplication is the best way to do it that we can find given how Gradle works, then that's OK.

If you have any more ideas for how to deduplicate, though, it'd make sense to try a bit more. Or if you want to wait another day to see if that Stack Overflow question gets an answer.

@reidbaker
Copy link
Contributor

Oof, Google testing failed. cc @reidbaker

Hey sorry I can dig in but it will probably be a week. This impending Flamingo launch and the work associated with it is my top priority until it is handled. That said maybe another googler can take a look.

@camsim99 After your gradle error catching work would you be willing to take a look at this?

engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Apr 20, 2023
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Apr 21, 2023
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Apr 21, 2023
@reidbaker reidbaker mentioned this pull request Apr 21, 2023
8 tasks
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Apr 22, 2023
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Apr 22, 2023
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Apr 23, 2023
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Apr 23, 2023
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Apr 24, 2023
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Apr 24, 2023
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Apr 24, 2023
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Apr 24, 2023
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Apr 24, 2023
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Apr 25, 2023
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Apr 25, 2023
fluttermirroringbot pushed a commit that referenced this pull request Nov 2, 2023
I'm removing an unneded block of configuration from the `settings.gradle` template. It was introduced by me in #123511. At that time, I did not know that it's unnecessary, and did not test removing it � sorry about that.

I learned that it's unnecessary recently, when [I asked a question on StackOverflow](https://stackoverflow.com/questions/77073596/whats-the-difference-between-plugins-and-pluginmanagement-plugins-in). More context there.
@balaji101010
Copy link

balaji101010 commented Nov 3, 2023

Since WHEN AND WHICH VERSION the declarative way plugins{} is added ?

I have lost a half a day figuring this out!!

Please someone mention the documentation or any useful link.

I am using Flutter 3.10

and my android studio generates this when creating a new project

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"

and my Colleague has this in their project

plugins {
    id "com.android.application"
    id "kotlin-android"
    id "dev.flutter.flutter-gradle-plugin"
}

Can somebody please tell me why my version generates apply plugin instead of plugins{}

@bartekpacia
Copy link
Member Author

Since WHEN AND WHICH VERSION the declarative way plugins{} is added ?

It was a gradual migration. See #135392 to see how Android build files differ between versions.

my android studio generates this when creating a new project (...) my Colleague has this in their project

Your colleague and you have different Flutter versions installed. Upgrade to the newest one to take advantage of the latest, modern-Gradle-style template.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
autosubmit Merge PR when tree becomes green via auto submit App c: contributor-productivity Team-specific productivity, code health, technical debt. d: examples Sample code and demos platform-android Android applications specifically t: gradle "flutter build" and "flutter run" on Android tool Affects the "flutter" command-line tool. See also t: labels.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Make it possible to apply Flutter's Gradle plugin using the declarative plugins block
5 participants