[jnigen] Remove the need to run flutter build apk#3303
Conversation
PR HealthChangelog Entry ✔️
Changes to files need to be accounted for in their respective changelogs. This check can be disabled by tagging the PR with Breaking changes ✔️
This check can be disabled by tagging the PR with API leaks ✔️The following packages contain symbols visible in the public API, but not exported by the library. Export these symbols or remove them from your publicly visible API.
This check can be disabled by tagging the PR with |
dcharkes
left a comment
There was a problem hiding this comment.
It's also valid to check these files into your github repo. In fact that's the recommended policy according to the gradle docs. It has been suggested that Flutter should change this default gitignore, but there's been some push back. The push back in Flutter seems fairly mild to me, so if this becomes an issue for many users we can probably file an issue about it.
@reidbaker What are your thoughts on checking in these files in Flutter plugins that depend on JNIgen so that JNIgen can run without having to run flutter build apk?
| # Allow Gradle wrapper | ||
| !gradlew | ||
| !gradlew.bat | ||
| !gradle-wrapper.jar |
There was a problem hiding this comment.
These are 52kb jars? Why do we need to commit them?
There was a problem hiding this comment.
This is a part of bootstrapping Gradle and making sure the intended version of gradle is the one that is used to build the underlying project.
There was a problem hiding this comment.
Another option would be to do flutter build apk --config-only in the workflows. This creates the gradlew files without actually doing a build.
There was a problem hiding this comment.
As discussed offline:
- We can check in one project with all the generated files.
- And for the other projects we can detect it's a flutter project and run
flutter build apk --config-onlyinside JNIgen.
For a future where we have hooks/generate.dart, Flutter should make sure flutter build apk --config-only is run before the generate hook so that automatic detection would become a noop.
| @@ -0,0 +1 @@ | |||
| TODO: Add your license here. | |||
There was a problem hiding this comment.
Do we need to have the Dart license for our example projecs?
There was a problem hiding this comment.
The other examples don't have a LICENSE file. Deleted.
| throw e | ||
| } | ||
| } | ||
| System.err.println("If you are seeing this error in `flutter build` output, it is likely that JNIgen left some stubs in the build.gradle file. Please restore that file from your version control system or manually remove the stub functions named getReleaseCompileClasspath and / or getSources.") |
There was a problem hiding this comment.
Would it be useful to have a docs markdown file where we can point from the error message so that we can add docs there. Or even a GitHub issue?
There was a problem hiding this comment.
🤷 Not sure what needs to be expanded into a doc in this error message. Is there more we need to say here?
|
|
||
| static void _runFlutterConfigOnly(String androidProject) { | ||
| // Only run if it looks like a flutter project. | ||
| if (!File(join(androidProject, 'pubspec.yaml')).existsSync()) { |
There was a problem hiding this comment.
Haha, best heuristic ever 😆
| jni$_.JMethodIDPtr, | ||
| jni$_.VarArgs<(jni$_.Pointer<jni$_.Void>,)>, | ||
| )>>('globalEnv_CallObjectMethod') | ||
| jni$_.NativeFunction< |
There was a problem hiding this comment.
did the formatter change? what version of the formatter are we using?
There was a problem hiding this comment.
Some combination of flutter create and gemini CLI initially created the examples with the SDK version set to the most recent Dart version. This caused problems on CI, so I had to change it to match the bounds of the other examples, which caused formatting changes.
goderbauer
left a comment
There was a problem hiding this comment.
Maybe also update the PR description for future archeologists (i.e. the section about "gradlew files" and how we now attempt to autogenerate them)?
|
|
||
| static void _runFlutterConfigOnly(String androidProject) { | ||
| // Only run if it looks like a flutter project. | ||
| if (!File(join(androidProject, 'pubspec.yaml')).existsSync()) { |
There was a problem hiding this comment.
Why's the presence of a pubspec.yaml enough to assume a Flutter project? How is it garanteed that it isn't a standalone dart project?
There was a problem hiding this comment.
This whole flow is gated by Config.androidSdkConfig being non-null, so it should only be run for Android projects. Though I guess by that argument we probably don't need the pubspec check at all. Probably should just check the flutter: key exists.
This PR removes the need to run
flutter build apk.The only important changes are in
pkgs/jnigen/lib/src/tools/android_sdk_tools.dart, in the gradle stubs.JNIgen gathers dependencies by injecting a temporary stub into the build.gradle file. The goal of the stub is to find all the relevant files, and print their paths stdout. Gradle is then run in a subprocess and the paths are read from its stdout. There are 4 stubs, because we need to support loading JARs (classpath stub) vs loading source files, and we need to support both Groovy and Kotlin build.gradle files.
The stubs have been completely rewritten. All four now use the same 3 step process:
Steps 1 and 2 happen at config time, and step 3 happens at
doLasttime. Unfortunately this leads to some code duplication, because we need very similar looking queries to find all the JARs in steps 2 and 3.The classpath stub now has to explicitly extract all the JAR files that are stored in AARs. That used to happen automatically during
flutter build apk.The old classpath stubs made sure to list the
android.getBootClasspathfirst, so that JNIgen would see them first. That way, any duplicates of the core libraries would be ignored. This is particularly important for dealing with the Jetifier on Android, which produces a bunch of stub classes for core libraries. The new classpath stubs make sure to maintain the same ordering.Examples
I added
example/maven_libandexample/maven_lib_groovyto test the new stubs, because I found the existing examples were a bit too simple. They're identical except that they use Kotlin and Groovy respectively in their build.gradle filesThey're plugins that depend on GSON and OkHttp as maven dependencies (rather than checking in their source code into third_party). The plugin depends on GSON, and the plugin's example app depends on OkHttp, but the bindings for both exist in the plugin. It's a weird dependency layout designed to stress test the stubs.
gradlew files
There's one other thing that
flutter build apkdoes that we didn't anticipate: create the gradelw files if they're missing. The default Flutter .gitignore policy is to exclude these from the repo, though it's also valid to check these files into your github repo (in fact that's the recommended policy according to the gradle docs).We work around this by automatically running
flutter build apk --config-onlywhen JNIgen is run in a flutter app. This is a lightweight command to run, and creates the gradle wrappers if they're missing. It also implicitly runsflutter pub get, which is handy.To test the checked-in workflow, I'm also checking in the gradle wrappers for the maven_libs_groovy example.
Fixes #628
Fixes #1661
Fixes #1660
Related #1972
Related #576