diff --git a/example/thirdparty/androidtodo/build.mill b/example/thirdparty/androidtodo/build.mill index faa0c67ddb25..4704d379c8c9 100644 --- a/example/thirdparty/androidtodo/build.mill +++ b/example/thirdparty/androidtodo/build.mill @@ -133,7 +133,6 @@ object app def mvnDeps = super.mvnDeps() ++ composeDeps ++ Seq( mvn"junit:junit:4.13.2", mvn"androidx.arch.core:core-testing:2.2.0", - mvn"org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0", mvn"org.jetbrains.kotlinx:kotlinx-coroutines-test:1.9.0", mvn"androidx.navigation:navigation-testing:2.8.5", mvn"androidx.test.espresso:espresso-core:3.6.1", @@ -185,7 +184,6 @@ object app // Dependencies for Android unit tests mvn"junit:junit:4.13.2", mvn"org.jetbrains.kotlinx:kotlinx-coroutines-test:1.9.0", - mvn"org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0", // AndroidX Test - Instrumented testing mvn"androidx.test:core-ktx:1.6.1", mvn"androidx.test.ext:junit-ktx:1.2.1", @@ -227,7 +225,7 @@ object `shared-test` extends AndroidKotlinModule, AndroidHiltSupport { def androidEnableCompose = true - def androidNamespace = "com.example.android.architecture.blueprints.todoapp.daemon.test" + def androidNamespace = "com.example.android.architecture.blueprints.todoapp.shared.test" def kotlinSymbolProcessors: T[Seq[Dep]] = Seq( mvn"androidx.room:room-compiler:2.7.1", @@ -237,7 +235,6 @@ object `shared-test` extends AndroidKotlinModule, AndroidHiltSupport { def mvnDeps = super.mvnDeps() ++ Seq( mvn"junit:junit:4.13.2", mvn"androidx.arch.core:core-testing:2.2.0", - mvn"org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0", mvn"org.jetbrains.kotlinx:kotlinx-coroutines-test:1.9.0", mvn"androidx.test:core-ktx:1.6.1", mvn"androidx.test.ext:junit-ktx:1.2.1", diff --git a/libs/androidlib/src/mill/androidlib/AndroidAppModule.scala b/libs/androidlib/src/mill/androidlib/AndroidAppModule.scala index 9ee05478fb45..1b99b692468e 100644 --- a/libs/androidlib/src/mill/androidlib/AndroidAppModule.scala +++ b/libs/androidlib/src/mill/androidlib/AndroidAppModule.scala @@ -822,7 +822,7 @@ trait AndroidAppModule extends AndroidModule { outer => } - def knownProguardRules: T[String] = Task { + def androidKnownProguardRules: T[String] = Task { // TODO need also pick proguard files from // [[moduleDeps]] androidUnpackArchives() @@ -833,15 +833,13 @@ trait AndroidAppModule extends AndroidModule { outer => .mkString("\n") } - override def androidProguard: T[PathRef] = Task { - val inheritedProguardFile = super.androidProguard() - val proguardFile = Task.dest / "proguard-rules.pro" - - os.write(proguardFile, os.read(inheritedProguardFile.path)) - - os.write.append(proguardFile, knownProguardRules()) - - PathRef(proguardFile) + /** + * File names that are provided by the Android SDK in `androidSdkModule().androidProguardPath().path` + * + * For now, it's only used by [[AndroidR8AppModule]] + */ + def androidDefaultProguardFileNames: Task[Seq[String]] = Task.Anon { + Seq.empty[String] } // uses the d8 tool to generate the dex file, when minification is disabled @@ -896,6 +894,9 @@ trait AndroidAppModule extends AndroidModule { outer => override def androidIsDebug: T[Boolean] = Task { true } + override def moduleDeps: Seq[JavaModule] = Seq.empty + override def compileModuleDeps: Seq[JavaModule] = Seq(outer) + override def resolutionParams: Task[ResolutionParams] = Task.Anon(outer.resolutionParams()) override def androidApplicationId: String = s"${outer.androidApplicationId}.test" @@ -913,6 +914,10 @@ trait AndroidAppModule extends AndroidModule { outer => def androidResources: T[Seq[PathRef]] = Task.Sources("src/androidTest/res") + override def androidDefaultProguardFileNames: Task[Seq[String]] = Task.Anon { + outer.androidDefaultProguardFileNames() + } + override def testFramework: T[String] = Task { "androidx.test.runner.AndroidJUnitRunner" } @@ -1063,7 +1068,7 @@ trait AndroidAppModule extends AndroidModule { outer => * as its apk is installed separately */ def androidTransitiveTestClasspath: T[Seq[PathRef]] = Task { - Task.traverse(transitiveModuleCompileModuleDeps) { + Task.traverse(transitiveRunModuleDeps) { m => Task.Anon(m.localRunClasspath()) }().flatten @@ -1071,7 +1076,7 @@ trait AndroidAppModule extends AndroidModule { outer => /** The instrumented dex should just contain the test dependencies and locally tested files */ override def androidPackagedClassfiles: T[Seq[PathRef]] = Task { - (testClasspath() ++ androidTransitiveTestClasspath()) + androidTransitiveTestClasspath() .map(_.path).filter(os.isDir) .flatMap(os.walk(_)) .filter(os.isFile) @@ -1080,7 +1085,7 @@ trait AndroidAppModule extends AndroidModule { outer => } override def androidPackagedDeps: T[Seq[PathRef]] = Task { - androidResolvedMvnDeps() + androidResolvedRunMvnDeps() } /** diff --git a/libs/androidlib/src/mill/androidlib/AndroidModule.scala b/libs/androidlib/src/mill/androidlib/AndroidModule.scala index fa19291a3d3b..c2fad558330f 100644 --- a/libs/androidlib/src/mill/androidlib/AndroidModule.scala +++ b/libs/androidlib/src/mill/androidlib/AndroidModule.scala @@ -193,11 +193,11 @@ trait AndroidModule extends JavaModule { outer => /** * Gets all the compiled Android resources (typically in res/ directory) - * from the [[transitiveModuleCompileModuleDeps]] + * from the [[transitiveModuleRunModuleDeps]] * @return a sequence of PathRef to the compiled resources */ def androidTransitiveCompiledResources: T[Seq[PathRef]] = Task { - Task.traverse(transitiveModuleCompileModuleDeps) { + Task.traverse(transitiveModuleRunModuleDeps) { case m: AndroidModule => Task.Anon(m.androidCompiledModuleResources()) case _ => @@ -264,7 +264,7 @@ trait AndroidModule extends JavaModule { outer => override def compileClasspath: T[Seq[PathRef]] = Task { // TODO process metadata shipped with Android libs. It can have some rules with Target SDK, for example. // TODO support baseline profiles shipped with Android libs. - androidDepsClasspath() ++ androidTransitiveLibRClasspath() + androidDepsClasspath() ++ androidTransitiveLibRClasspath() ++ androidTransitiveModuleRClasspath() } /** @@ -522,6 +522,24 @@ trait AndroidModule extends JavaModule { outer => }().flatten } + def androidTransitiveModuleRClasspath: T[Seq[PathRef]] = Task { + Task.traverse(compileModuleDepsChecked) { + case m: AndroidModule => + Task.Anon(Seq(m.androidProcessedResources())) + case _ => + Task.Anon(Seq.empty[PathRef]) + }().flatten + } + + def androidTransitiveCompileOnlyClasspath: T[Seq[PathRef]] = Task { + Task.traverse(compileModuleDepsChecked) { + case m: AndroidModule => + Task.Anon(Seq(m.compile().classes)) + case _ => + Task.Anon(Seq.empty[PathRef]) + }().flatten + } + /** * Namespace of the Android module. * Used in manifest package and also used as the package to place the generated R sources diff --git a/libs/androidlib/src/mill/androidlib/AndroidR8AppModule.scala b/libs/androidlib/src/mill/androidlib/AndroidR8AppModule.scala index 857052417cdd..baead298122c 100644 --- a/libs/androidlib/src/mill/androidlib/AndroidR8AppModule.scala +++ b/libs/androidlib/src/mill/androidlib/AndroidR8AppModule.scala @@ -54,7 +54,7 @@ trait AndroidR8AppModule extends AndroidAppModule { } def androidLibraryProguardConfigs: Task[Seq[PathRef]] = Task { - androidUnpackArchives() + androidUnpackRunArchives() // TODO need also collect rules from other modules, // but Android lib module doesn't yet exist .flatMap(_.proguardRules) @@ -71,6 +71,27 @@ trait AndroidR8AppModule extends AndroidAppModule { androidDefaultProguardFiles() ++ androidProjectProguardFiles() ++ androidLibraryProguardConfigs() } + /** + * Creates a file for letting know R8 that [[compileModuleDeps]] and + * [[compileMvnDeps]] are in compile classpath only and not packaged with the apps. + * Useful for dependencies that are provided in devices and compile only module deps + * such as for avoiding to package main sources in the androidTest apk. + */ + def androidR8CompileOnlyClasspath: T[Option[PathRef]] = Task { + val resolvedCompileMvnDeps = + androidResolvedCompileMvnDeps() ++ androidTransitiveCompileOnlyClasspath() ++ androidTransitiveModuleRClasspath() + if (!resolvedCompileMvnDeps.isEmpty) { + val compiledMvnDepsFile = Task.dest / "compile-only-classpath.txt" + os.write.over( + compiledMvnDepsFile, + resolvedCompileMvnDeps.map(_.path.toString()).mkString("\n") + ) + Some(PathRef(compiledMvnDepsFile)) + } else + None + + } + /** Concatenates all rules into one file */ override def androidProguard: T[PathRef] = Task { val inheritedProguardFile = super.androidProguard() @@ -98,14 +119,6 @@ trait AndroidR8AppModule extends AndroidAppModule { ) } - /** - * File names that are provided by the Android SDK in `androidSdkModule().androidProguardPath().path` - * @return - */ - def androidDefaultProguardFileNames: Task[Seq[String]] = Task.Anon { - Seq.empty[String] - } - private def androidDefaultProguardFiles: Task[Seq[PathRef]] = Task.Anon { val dest = Task.dest androidDefaultProguardFileNames().map { fileName => @@ -242,18 +255,14 @@ trait AndroidR8AppModule extends AndroidAppModule { r8ArgsBuilder ++= pgArgs - val resolvedCompileMvnDeps = androidResolvedCompileMvnDeps() - if (!resolvedCompileMvnDeps.isEmpty) { - val compiledMvnDepsFile = Task.dest / "compiled-mvndeps.txt" - os.write.over( - compiledMvnDepsFile, - androidResolvedCompileMvnDeps().map(_.path.toString()).mkString("\n") - ) - r8ArgsBuilder ++= Seq( + val compileOnlyClasspath = androidR8CompileOnlyClasspath() + + r8ArgsBuilder ++= compileOnlyClasspath.toSeq.flatMap(compiledMvnDepsFile => + Seq( "--classpath", - "@" + compiledMvnDepsFile.toString + "@" + compiledMvnDepsFile.path.toString ) - } + ) r8ArgsBuilder ++= androidR8Args()