Skip to content

Commit

Permalink
Rework android support example (#3676)
Browse files Browse the repository at this point in the history
Tries to address some of the issues in
#3675


* [ ] Various tasks like `sdkUrl` or `buildToolsVersion` should be
prefixed with `android`
* [x] Hardcoded `buildToolsVersion` defaults to `35.0.0`. I suggest to
just remove the default.
* [x] Hardcoded `platformsVersion` defaults to `android-35`. We could
derive it from `buildToolsVersion`.
* [ ] In general lots of various paths which are just sub-paths of
`buildToolsPath` have their own tasks. Instead we could have some
"config" class, which returns common paths from a `buildToolsPath`, e.g.
by applying a (version specific) layout/sub-paths.
* [x] `AndroidAppModule.androidSdkModule` should have the type
`ModuleRef[AndroidSdkModule]`.
* [x] `androidResources` hardcodes a path to
`src/main/AndroidManifest.xml`. It should either lookup the file from
`sources` task, or be it's own `androidManifest: Source` task.
* [x] `androidKeystore` is probably meant to be a persistent task
  • Loading branch information
lihaoyi authored Oct 5, 2024
1 parent 4d59769 commit 070e908
Show file tree
Hide file tree
Showing 5 changed files with 35 additions and 29 deletions.
26 changes: 13 additions & 13 deletions example/javalib/android/1-hello-world/build.mill
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,23 @@ package build
import mill._
import mill.javalib.android.{AndroidAppModule, AndroidSdkModule}

object `package` extends RootModule with AndroidAppModule {

// Create and configure an Android SDK module to manage Android SDK paths and tools.
object androidSdkModule extends AndroidSdkModule
// Create and configure an Android SDK module to manage Android SDK paths and tools.
object androidSdkModule0 extends AndroidSdkModule{
def buildToolsVersion = "35.0.0"
}

// Actual android application
object app extends AndroidAppModule {
def androidSdkModule = mill.define.ModuleRef(androidSdkModule0)
}

////SNIPPET:END


/** Usage

> ./mill androidApk
> ./mill show app.androidApk
".../out/app/androidApk.dest/app.apk"

*/

Expand All @@ -42,13 +46,9 @@ object `package` extends RootModule with AndroidAppModule {
// ----
// .
// ├── build.mill
// └── src
// └── main
// ├── AndroidManifest.xml
// └── java
// └── com
// └── helloworld
// └── app
// └── MainActivity.java
// ├── AndroidManifest.xml
// └── src/main/java
// └── com/helloworld/app
// └── MainActivity.java
// ----
//
34 changes: 20 additions & 14 deletions scalalib/src/mill/javalib/android/AndroidAppModule.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package mill.javalib.android

import mill._
import mill.api.PathRef
import mill.define.ModuleRef
import mill.scalalib.JavaModule

/**
Expand All @@ -27,7 +28,12 @@ trait AndroidAppModule extends JavaModule {
*
* @return The Android SDK module that is used across the project.
*/
def androidSdkModule: AndroidSdkModule
def androidSdkModule: ModuleRef[AndroidSdkModule]

/**
* An XML file containing configuration and metadata about your android application
*/
def androidManifest: Task[PathRef] = Task.Source(millSourcePath / "AndroidManifest.xml")

/**
* Generates the Android resources (such as layouts, strings, and other assets) needed
Expand All @@ -45,16 +51,16 @@ trait AndroidAppModule extends JavaModule {
val genDir: os.Path = T.dest // Directory to store generated resources.

os.call(Seq(
androidSdkModule.aaptPath().path.toString, // Call aapt tool
androidSdkModule().aaptPath().path.toString, // Call aapt tool
"package",
"-f",
"-m",
"-J",
genDir.toString, // Generate R.java files
"-M",
(millSourcePath / "src/main/AndroidManifest.xml").toString, // Use AndroidManifest.xml
androidManifest().path.toString, // Use AndroidManifest.xml
"-I",
androidSdkModule.androidJarPath().path.toString // Include Android SDK JAR
androidSdkModule().androidJarPath().path.toString // Include Android SDK JAR
))

PathRef(genDir)
Expand All @@ -64,7 +70,7 @@ trait AndroidAppModule extends JavaModule {
* Adds the Android SDK JAR file to the classpath during the compilation process.
*/
def unmanagedClasspath: T[Agg[PathRef]] = Task {
Agg(androidSdkModule.androidJarPath())
Agg(androidSdkModule().androidJarPath())
}

/**
Expand Down Expand Up @@ -93,7 +99,7 @@ trait AndroidAppModule extends JavaModule {

os.call(
Seq(
androidSdkModule.d8Path().path.toString, // Call d8 tool
androidSdkModule().d8Path().path.toString, // Call d8 tool
"--output",
jarFile.toString, // Output JAR file
"--no-desugaring" // Disable desugaring
Expand All @@ -112,10 +118,10 @@ trait AndroidAppModule extends JavaModule {
val dexOutputDir: os.Path = T.dest

os.call(
Seq(androidSdkModule.d8Path().path.toString, "--output", dexOutputDir.toString) ++
Seq(androidSdkModule().d8Path().path.toString, "--output", dexOutputDir.toString) ++
Seq(
androidJar().path.toString, // Use the JAR file from the previous step
androidSdkModule.androidJarPath().path.toString // Include Android framework classes
androidSdkModule().androidJarPath().path.toString // Include Android framework classes
)
)

Expand All @@ -134,13 +140,13 @@ trait AndroidAppModule extends JavaModule {

os.call(
Seq(
androidSdkModule.aaptPath().path.toString,
androidSdkModule().aaptPath().path.toString,
"package",
"-f",
"-M",
(millSourcePath / "src/main/AndroidManifest.xml").toString, // Path to AndroidManifest.xml
androidManifest().path.toString, // Path to AndroidManifest.xml
"-I",
androidSdkModule.androidJarPath().path.toString, // Include Android JAR
androidSdkModule().androidJarPath().path.toString, // Include Android JAR
"-F",
unsignedApk.toString // Output APK
) ++ Seq(androidDex().path.toString) // Include DEX files
Expand All @@ -160,7 +166,7 @@ trait AndroidAppModule extends JavaModule {

os.call(
Seq(
androidSdkModule.zipalignPath().path.toString, // Call zipalign tool
androidSdkModule().zipalignPath().path.toString, // Call zipalign tool
"-f",
"-p",
"4", // Force overwrite, align with 4-byte boundary
Expand Down Expand Up @@ -189,7 +195,7 @@ trait AndroidAppModule extends JavaModule {

os.call(
Seq(
androidSdkModule.apksignerPath().path.toString,
androidSdkModule().apksignerPath().path.toString,
"sign", // Call apksigner tool
"--ks",
androidKeystore().path.toString, // Path to keystore
Expand Down Expand Up @@ -218,7 +224,7 @@ trait AndroidAppModule extends JavaModule {
* For more details on the keytool utility, refer to:
* [[https://docs.oracle.com/javase/8/docs/technotes/tools/windows/keytool.html keytool Documentation]]
*/
def androidKeystore: T[PathRef] = Task {
def androidKeystore: T[PathRef] = Task(persistent = true) {
val keystoreFile: os.Path = T.dest / "keystore.jks"

if (!os.exists(keystoreFile)) {
Expand Down
4 changes: 2 additions & 2 deletions scalalib/src/mill/javalib/android/AndroidSdkModule.scala
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ trait AndroidSdkModule extends Module {
/**
* Specifies the version of the Android build tools to be used.
*/
def buildToolsVersion: T[String] = Task { "35.0.0" }
def buildToolsVersion: T[String]

/**
* Specifies the Android platform version (e.g., Android API level).
*/
def platformsVersion: T[String] = Task { "android-35" }
def platformsVersion: T[String] = Task { "android-" + buildToolsVersion().split('.').head }

/**
* Provides the path to the `android.jar` file, necessary for compiling Android apps.
Expand Down

0 comments on commit 070e908

Please sign in to comment.