-
-
Notifications
You must be signed in to change notification settings - Fork 421
Use R8 optimization tool in Android modules #4892
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
Use R8 optimization tool in Android modules #4892
Conversation
|
Hi! I'm I would like to apply some automated changes to this pull request, but it looks like I don't have the necessary permissions to do so. To get this pull request into a mergeable state, please do one of the following two things:
|
80021b9 to
03252dc
Compare
lefou
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 looks good to me. I added some nits. Also, it would be good to have some scaladoc on at least those tasks that the user is supposed to override or to call.
0c3d90f to
eac130c
Compare
|
Have you tried to render the docs? It looks rather unreadable to me. Looks like the example itself is just a copy of the Also, to effectively see the docs, you need to include it into the website. diff --git a/website/docs/modules/ROOT/pages/android/java.adoc b/website/docs/modules/ROOT/pages/android/java.adoc
index aed8dd649e5..7e44f2df7de 100644
--- a/website/docs/modules/ROOT/pages/android/java.adoc
+++ b/website/docs/modules/ROOT/pages/android/java.adoc
@@ -67,3 +67,7 @@ The `AndroidAppBundle` trait is used to create and manage Android App Bundles (A
- **androidUnsignedBundle:** Uses the `bundleTool` to build an unsigned AAB from the bundle zip.
- **androidBundle:** Signs the AAB using a specified keystore with the `jarsigner` tool, producing a signed Android App Bundle (AAB).
+
+== Using the R8 optimization tool in Android modules
+
+include::partial$example/android/javalib/5-R8.adoc[]Then generate a small version of the site to be read locally: > mill website.fastPages
...
[2504] You can browse the pages at: file:///.../mill/out/website/fastPages.dest/site/index.htmlAlso, the example should pass the test. > mill example.android.javalib[5-R8].local.server.testForkedIt doesn't for me, but that's probably because I missed some steps to setup android dev at all. I think our own intro page isn't detailed enough for me. 😜 |
|
Feel free to ignore the callouts I requested before (I can put them in myself later). Just make it look reasonable. |
Okay. Thank you :) |
The fastPages doesn't work but the testForked its running okay |
What's the error message? You can also try |
|
I removed the generated I added some TODOs, where you should add a short explanation what R8 is and why a user should want to use it. Next comes your example project. It currently does not contain any specific configuration for R8. The point of the example is to show the user how to enable and configure it. If that is mostly done via additional files, e.g. proguard files, use a "See Also" line and explain how it works. |
|
Looks like I can't push to your branch. Here are my changes: diff --git a/website/docs/modules/ROOT/pages/android/build-docs.adoc b/website/docs/modules/ROOT/pages/android/build-docs.adoc
deleted file mode 100644
index 10fade4c452..00000000000
--- a/website/docs/modules/ROOT/pages/android/build-docs.adoc
+++ /dev/null
@@ -1,73 +0,0 @@
-= Android Build File
-
-This is an example of a build file for an Android application using Mill. It demonstrates how to set up an Android project, configure the SDK, and define tasks for building and testing the application.
-
-[source,scala]
-----
-package build
-
-import mill._, javalib._
-import mill.javalib.android.{AndroidAppModule, AndroidSdkModule}
-import mill.javalib.android.AndroidTestModule
-
-// Create and configure an Android SDK module to manage Android SDK paths and tools. <3>
-object androidSdkModule0 extends AndroidSdkModule {
- def buildToolsVersion = "35.0.0"
-}
-
-// Actual android application <4>
-object app extends AndroidAppModule {
- def androidSdkModule = mill.define.ModuleRef(androidSdkModule0)
- def androidMinSdk = 19
- def androidCompileSdk = 35
-
- /**
- * Configuration for ReleaseKey
- * WARNING: Replace these default values with secure and private credentials before using in production.
- * Never use these defaults in a production environment as they are not secure.
- * This is just for testing purposes.
- */
- def androidReleaseKeyName: T[Option[String]] = Task { Some("releaseKey.jks") } // <5>
- def androidReleaseKeyAlias: T[Option[String]] = Task { Some("releaseKey") }
- def androidReleaseKeyPass: T[Option[String]] = Task { Some("MillBuildTool") }
- def androidReleaseKeyStorePass: T[Option[String]] = Task { Some("MillBuildTool") }
-
- override def androidVirtualDeviceIdentifier: String = "java-test"
-
- // Unit tests for the application <6>
- object test extends AndroidAppTests with TestModule.Junit4 {
- def ivyDeps = super.ivyDeps() ++ Seq(
- ivy"junit:junit:4.13.2"
- )
- }
-
- // Instrumented tests (runs on device/emulator) <7>
- object it extends AndroidAppInstrumentedTests with AndroidTestModule.AndroidJUnit {
- def androidSdkModule = mill.define.ModuleRef(androidSdkModule0)
- override def instrumentationPackage = "com.helloworld.app"
-
- /* TODO currently the dependency resolution ignores the platform type and kotlinx-coroutines-core has
- * conflicting classes with kotlinx-coroutines-core-jvm . Remove the exclusions once the dependency
- * resolution resolves conflicts between androidJvm and jvm platform types
- */
- def ivyDeps = super.ivyDeps() ++ Seq(
- ivy"androidx.test.ext:junit:1.2.1".exclude(
- ("org.jetbrains.kotlinx", "kotlinx-coroutines-core-jvm")
- ),
- ivy"androidx.test:runner:1.6.2",
- ivy"androidx.test.espresso:espresso-core:3.5.1".exclude(
- ("org.jetbrains.kotlinx", "kotlinx-coroutines-core-jvm")
- ),
- ivy"junit:junit:4.13.2"
- )
- }
-
-}
-----
-<1> High-level summary of the build file, describing how it sets up an Android project in Mill.
-<2> Explanation of how `AndroidAppModule` automatically provides tasks for building an Android app (resource generation, APK building, signing, etc.).
-<3> The `androidSdkModule0` object extends `AndroidSdkModule`, controlling SDK versioning and tool paths.
-<4> The `app` object defines the main Android application module by extending `AndroidAppModule`.
-<5> **Release key configuration**. These lines demonstrate how to declare release key credentials (FOR DEMO ONLY - DO NOT use in production).
-<6> The `test` object includes local JUnit-based unit tests.
-<7> The `it` object includes instrumented tests, which run on an emulator
diff --git a/example/android/javalib/5-R8/build.mill b/example/android/javalib/5-R8/build.mill
index 13d1f2e8088..9fca9959ea3 100644
--- a/example/android/javalib/5-R8/build.mill
+++ b/example/android/javalib/5-R8/build.mill
@@ -1,11 +1,4 @@
-//// SNIPPET:BUILD
-// This section sets up a basic Android project using Mill.
-// We utilize `AndroidAppModule` and `AndroidSdkModule` to streamline the process of
-// building an Android application with minimal configuration.
-//
-// By extending `AndroidAppModule`, we inherit all Android-related tasks such as
-// resource generation, APK building, DEX conversion, and APK signing.
-// Additionally, `AndroidSdkModule` is embedded, making SDK management seamless.
+// TODO: Some docs about R8 should go here
package build
@@ -13,18 +6,16 @@ import mill._, javalib._
import mill.javalib.android.{AndroidAppModule, AndroidSdkModule}
import mill.javalib.android.AndroidTestModule
-// Create and configure an Android SDK module to manage Android SDK paths and tools.
-object androidSdkModule0 extends AndroidSdkModule {
+ object androidSdkModule0 extends AndroidSdkModule { // <1>
def buildToolsVersion = "35.0.0"
}
-// Actual android application
-object app extends AndroidAppModule {
+object app extends AndroidAppModule { // <2>
def androidSdkModule = mill.define.ModuleRef(androidSdkModule0)
def androidMinSdk = 19
def androidCompileSdk = 35
- /**
+ /*
* Configuration for ReleaseKey
* WARNING: Replace these default values with secure and private credentials before using in production.
* Never use these defaults in a production environment as they are not secure.
@@ -66,109 +57,13 @@ object app extends AndroidAppModule {
}
}
-//// SNIPPET:END
-////SNIPPET:END
+// <1> Create and configure an Android SDK module to manage Android SDK paths and tools.
+// <2> The actual Android application
-/** Usage
-> ./mill show app.androidApk
-".../out/app/androidApk.dest/app.apk"
+// TODO: Add some example how to use the R8 tool. To refer to other resources, use the "See Also" section like below.
-> ./mill show app.createAndroidVirtualDevice
-...Name: java-test, DeviceId: medium_phone...
+/** See Also: app/proguard-rules.pro */
-> ./mill show app.startAndroidEmulator
-
-> ./mill show app.androidReleaseInstall
-...All files should be loaded. Notifying the device...
-
-> ./mill show app.stopAndroidEmulator
-
-> ./mill show app.deleteAndroidVirtualDevice
-
-*/
-
-// This command triggers the build process, which installs the Android Setup, compiles the Java
-// code, generates Android resources, converts Java bytecode to DEX format, packages everything
-// into an APK, optimizes the APK using `zipalign`, and finally signs it.
-//
-// This Mill build configuration is designed to build a simple "Hello World" Android application.
-// By extending `AndroidAppModule`, we leverage its predefined Android build tasks, ensuring that
-// all necessary steps (resource generation, APK creation, and signing) are executed automatically.
-//
-// #### Project Structure:
-// The project follows the standard Android app layout. Below is a typical project folder structure:
-//
-// ----
-// .
-//├── app
-//│ └── src
-//│ ├── androidTest/java/com/helloworld/app/ExampleInstrumentedTest.java
-//│ ├── main
-//│ │ ├── AndroidManifest.xml
-//│ │ ├── java/com/helloworld/app/MainActivity.java
-//│ │ └── res
-//│ │ └── values
-//│ │ ├── colors.xml
-//│ │ └── strings.xml
-//│ └── test/java/com/helloworld/app/ExampleUnitTest.java
-//└── build.mill
-// ----
-//
-
-/** Usage
-
-> ./mill show app.test
-...compiling 2 Java source...
-
-> cat out/app/test/testForked.dest/worker-0/out.json
-["",[{"fullyQualifiedName":"com.helloworld.ExampleUnitTest.textSize_isCorrect","selector":"com.helloworld.ExampleUnitTest.textSize_isCorrect","duration":...,"status":"Success"}]]
-
-*/
-
-// This command runs unit tests on your local environment.
-
-/** Usage
-
-> ./mill show app.createAndroidVirtualDevice
-...Name: java-test, DeviceId: medium_phone...
-
-> ./mill show app.startAndroidEmulator
-
-> ./mill show app.adbDevices
-...emulator-5554...device...
-
-> ./mill show app.it
-...
-[
- "",
- [
- {
- "fullyQualifiedName": "com.helloworld.app.ExampleInstrumentedTest.useAppContext",
- "selector": "com.helloworld.app.ExampleInstrumentedTest.useAppContext",
- "duration": ...,
- "status": "Success"
- }
- ]
-]
-...
-
-> cat out/app/it/testTask.dest/test-report.xml
-...
-<?xml version='1.0' encoding='UTF-8'?>
-<testsuites tests="1" failures="0" errors="0" skipped="0" time="...">
- <testsuite name="com.helloworld.app.ExampleInstrumentedTest.useAppContext" tests="1" failures="0" errors="0" skipped="0" time="0.0" timestamp="...">
- <properties>
- </properties>
- <testcase classname="com.helloworld.app.ExampleInstrumentedTest.useAppContext" name="com.helloworld.app.ExampleInstrumentedTest.useAppContext" time="...">
- </testcase>
- </testsuite>
- </testsuites>
-...
-
-> ./mill show app.stopAndroidEmulator
-
-> ./mill show app.deleteAndroidVirtualDevice
-
-*/
+// TODO: you should explain the essential lines of that files. (Callouts might come in handy)
diff --git a/website/docs/modules/ROOT/pages/android/java.adoc b/website/docs/modules/ROOT/pages/android/java.adoc
index aed8dd649e5..8d0a37cb55e 100644
--- a/website/docs/modules/ROOT/pages/android/java.adoc
+++ b/website/docs/modules/ROOT/pages/android/java.adoc
@@ -67,3 +67,8 @@ The `AndroidAppBundle` trait is used to create and manage Android App Bundles (A
- **androidUnsignedBundle:** Uses the `bundleTool` to build an unsigned AAB from the bundle zip.
- **androidBundle:** Signs the AAB using a specified keystore with the `jarsigner` tool, producing a signed Android App Bundle (AAB).
+
+
+== Using the R8 optimization tool in Android modules
+
+include::partial$example/android/javalib/5-R8.adoc[] |
Thanks for the changes. I just added comments in the build file for the user. If you think something is not specified or needs more explanation, let me know |
|
You haven't run the local page generator, have you? Is there some way we can test/assert that R8 worked sufficiently well? E.g. check that the size is smaller than the size of the un-optimited build result. Something we could add to the tests? |
|
This can also be helpful @irodotos7 to add more context . For example, gradle as part of the release apk creation runs Some ideasI think for the time being there's not much value documenting this extensively, we can hide the runR8 task instead (just have it as something that runs internally with androidReleaseInstall) . For testing, we can do androidReleaseInstall and androidInstall and add an assertion that androidReleaseInstall generated APK is smaller? EDIT: I wouldn't even have a separate R8 example, we know we haven't done a proper separation of release and debug install - yet -, I'd just have an androidReleaseApk in the basic example and do the size comparison until we complete the feature WDYT? |
| .map(ref => { | ||
| def metaInfRoot(p: os.Path): os.Path = { | ||
| var current = p | ||
| while (!current.endsWith(os.rel / "META-INF")) { |
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 this needed? and if it does, on first look it seems there will be an issue if there's no META-INF
3de95b6 to
97a8f8f
Compare
|
I think this looks fine, @irodotos7 if you could rebase this on top of latest |
3a3cb53 to
221a24b
Compare
# Conflicts: # example/androidlib/kotlin/1-hello-kotlin/app/proguard-rules.pro # example/androidlib/kotlin/1-hello-kotlin/app/test-proguard-rules.pro # libs/androidlib/src/mill/androidlib/AndroidModule.scala
c41b4ff to
bcbb8d0
Compare
@lihaoyi done |
## Summary - **better organisation** between androidmodule and androidappmodule (separate properly what things are used in apps and things that are shared between lib building and app building) - R8 and D8 **dex generation** depending on build type settings - **Instrumented tests** now use the android application namespace and id and adding a "variant" indicator - introduced a lite version of **build type settings** with main motivation to control r8 and d8 but also serve as a scaffolding for build variants - **Android manifest** has less hardcoded parameters (uses-sdk and instrumentation tags are now generated) - Minor change into **testTask** to keep it consistent with the rest of modules(Task.Anon) and thus the test-report is now in `testForked.dest` ## R8 and Proguard Followed similar configuration style with gradle  Although there's still work to be done in that area, if minification is enabled, we use the R8 tool to generate the dex otherwise the d8 (which also uses proguard for now) . This needs further work and clarification as AGP seems to be using R8 by default when minification is enabled, otherwise d8 is used. Proguard default files are also fetched (as an opt-in setting) from the Android SDK directories ## Build settings I've introduced a first very minimal step for doing build variants, mainly out of necessity to manage R8 and D8 variations better, but this will be expanded once we need to fully support build variants. The settings so far concerned the R8 use case at hand and it's a case class that has the parameters to feed proguard files and flags into R8. ## R8 enhancements Extending the work from #4892 , I've made some usability enhancements and enriched the examples to demonstrate how instrumented tests can be used alongside R8 and release builds. I think R8 now can be fairly usable to generate minified and optimised release builds without getting in the middle of development (e.g. running tests and testing in the emulator) although more work needs to be done towards that area, but we'll follow up after we do more exploratory usability testing! ## Future work Following this work, we can move to complete the work on architecture samples by testing instrumented tests, unit tests and later on debug variants. The cleanup also reduced a lot of duplication between `AndroidAppModule` and `AndroidModule` so it will make our development efforts a bit easier to add more features! More discovery work will follow to align the instrumented test apk installation more in line with AGP
A step back from this PR #4743
I created a smaller, clear PR with the R8 task only and without the separation of Debug and Release Install paths
Motivation
As part of Mill’s Android integration, we want to use the R8 optimization tool to create smaller dex files
Provided in this PR