diff --git a/example/thirdparty/android-compose-samples/build.mill b/example/thirdparty/android-compose-samples/build.mill index 4a2a16089786..62529f64f3d7 100644 --- a/example/thirdparty/android-compose-samples/build.mill +++ b/example/thirdparty/android-compose-samples/build.mill @@ -273,7 +273,6 @@ object Jetchat extends mill.api.Module { mvn"org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.2", mvn"androidx.activity:activity-compose:1.10.1", mvn"androidx.core:core-ktx:1.16.0", - mvn"androidx.appcompat:appcompat:1.7.0", mvn"androidx.compose.runtime:runtime-livedata", mvn"androidx.lifecycle:lifecycle-viewmodel-compose:2.9.0", mvn"androidx.lifecycle:lifecycle-runtime-compose:2.9.0", @@ -288,8 +287,7 @@ object Jetchat extends mill.api.Module { mvn"androidx.databinding:viewbinding:8.13.0", mvn"androidx.compose.ui:ui-tooling", mvn"androidx.compose.ui:ui-tooling-preview", - mvn"androidx.compose.ui:ui-test-manifest", - mvn"com.google.android.material:material:1.6.0" + mvn"androidx.compose.ui:ui-test-manifest" ) def androidSdkModule = mill.api.ModuleRef(androidSdkModule0) @@ -298,12 +296,6 @@ object Jetchat extends mill.api.Module { def androidMinSdk = Versions.androidMinSdk - /* - * FIXME Currently broken with - * Message 2) profileScreen_back_conversationScreen(com.example.compose.jetchat.NavigationTest) - * android.content.res.Resources$NotFoundException: String resource ID #0x7f0c002b - * Message android.content.res.Resources$NotFoundException: String resource ID #0x7f0c002b - */ object androidTest extends AndroidAppKotlinInstrumentedTests, AndroidR8AppModule { override def bomMvnDeps = super.mvnDeps() ++ Seq( mvn"androidx.compose:compose-bom:2025.08.00" @@ -317,9 +309,6 @@ object Jetchat extends mill.api.Module { true } - // FIXME: R8 should compile without missing classes errors - override def androidR8Args = Seq("--map-diagnostics", "error", "warning") - def mvnDeps = super.mvnDeps() ++ Seq( mvn"junit:junit:4.13.2", mvn"androidx.test:core:1.6.1", @@ -330,10 +319,8 @@ object Jetchat extends mill.api.Module { mvn"org.jetbrains.kotlinx:kotlinx-coroutines-test:1.10.2", mvn"androidx.compose.ui:ui-test", mvn"androidx.compose.ui:ui-test-junit4", - mvn"androidx.collection:collection-ktx:1.5.0", - mvn"androidx.savedstate:savedstate-ktx:1.3.0", - mvn"androidx.appcompat:appcompat:1.7.0", - mvn"androidx.lifecycle:lifecycle-viewmodel-compose:2.9.0" + // FIXME temporary workaround to avoid linking error + mvn"androidx.appcompat:appcompat:1.7.0" ) } diff --git a/example/thirdparty/androidtodo/build.mill b/example/thirdparty/androidtodo/build.mill index 4704d379c8c9..d2493022776c 100644 --- a/example/thirdparty/androidtodo/build.mill +++ b/example/thirdparty/androidtodo/build.mill @@ -67,7 +67,6 @@ object app def mvnDeps: T[Seq[Dep]] = super.mvnDeps() ++ Seq( mvn"androidx.core:core-ktx:1.15.0", - mvn"androidx.appcompat:appcompat:1.7.0", mvn"androidx.annotation:annotation:1.9.1", mvn"org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0", mvn"com.jakewharton.timber:timber:5.0.1", @@ -145,7 +144,7 @@ object app object androidTest extends AndroidAppKotlinInstrumentedTests, - AndroidR8AppModule, + AndroidR8InstrumentedTestsModule, AndroidHiltSupport { def moduleDeps = super.moduleDeps ++ Seq(`shared-test`) @@ -164,7 +163,6 @@ object app } override def androidProjectProguardFiles = Task.Sources( - "proguard-rules.pro", "proguardTest-rules.pro" ) override def androidDefaultProguardFileNames: Task[Seq[String]] = Task.Anon { @@ -184,6 +182,7 @@ object app // Dependencies for Android unit tests mvn"junit:junit:4.13.2", mvn"org.jetbrains.kotlinx:kotlinx-coroutines-test:1.9.0", + mvn"androidx.appcompat:appcompat:1.7.0", // AndroidX Test - Instrumented testing 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 a0db64148386..b88962a40679 100644 --- a/libs/androidlib/src/mill/androidlib/AndroidAppModule.scala +++ b/libs/androidlib/src/mill/androidlib/AndroidAppModule.scala @@ -12,13 +12,14 @@ import mill.javalib.* import os.{Path, RelPath, zip} import os.RelPath.stringRelPathValidated import upickle.* -import scala.concurrent.duration.* +import scala.concurrent.duration.* import scala.jdk.OptionConverters.RichOptional import scala.xml.* import mill.api.daemon.internal.bsp.BspBuildTarget import mill.api.daemon.internal.EvaluatorApi import mill.javalib.testrunner.TestResult + import scala.util.Properties.isWin /** @@ -134,12 +135,13 @@ trait AndroidAppModule extends AndroidModule { outer => ) /** - * Collect files from META-INF folder of classes.jar (not META-INF of aar in case of Android library). + * Collect files from META-INF folder of [[androidPackagedDeps]] (not META-INF of aar in case of Android library). + * to include in the apk */ def androidLibsClassesJarMetaInf: T[Seq[PathRef]] = Task { // ^ not the best name for the method, but this is to distinguish between META-INF of aar and META-INF // of classes.jar included in aar - compileClasspath() + androidPackagedDeps() .filter(ref => ref.path.ext == "jar" && ref != androidSdkModule().androidJarPath() @@ -178,7 +180,6 @@ trait AndroidAppModule extends AndroidModule { outer => } }) .map(PathRef(_)) - .toSeq } /** @@ -189,7 +190,7 @@ trait AndroidAppModule extends AndroidModule { outer => def androidPackageableExtraFiles: T[Seq[AndroidPackageableExtraFile]] = Task { Seq.empty[AndroidPackageableExtraFile] } - def androidPackageMetaInfoFiles: T[Seq[AndroidPackageableExtraFile]] = Task { + def androidPackagedMetaInfFiles: T[Seq[AndroidPackageableExtraFile]] = Task { def metaInfRoot(p: os.Path): os.Path = { var current = p while (!current.endsWith(os.rel / "META-INF")) { @@ -239,7 +240,7 @@ trait AndroidAppModule extends AndroidModule { outer => (androidPackageableExtraFile.source.path, androidPackageableExtraFile.destination.asSubPath) ) - val metaInf = androidPackageMetaInfoFiles().map(asZipSource) + val metaInf = androidPackagedMetaInfFiles().map(asZipSource) val nativeDeps = androidPackageableNativeDeps().map(asZipSource) @@ -894,9 +895,6 @@ 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" @@ -962,7 +960,7 @@ trait AndroidAppModule extends AndroidModule { outer => } private def androidxTestManifests: Task[Seq[PathRef]] = Task { - androidUnpackArchives().flatMap { + androidUnpackRunArchives().flatMap { unpackedArchive => unpackedArchive.manifest.map(_.path) }.filter { @@ -1040,8 +1038,8 @@ trait AndroidAppModule extends AndroidModule { outer => val device = androidTestInstall().apply() val instrumentOutput = os.proc( - ( - androidSdkModule().adbExe().path, + Seq( + androidSdkModule().adbExe().path.toString, "-s", device, "shell", @@ -1084,10 +1082,6 @@ trait AndroidAppModule extends AndroidModule { outer => .map(PathRef(_)) } - override def androidPackagedDeps: T[Seq[PathRef]] = Task { - androidResolvedRunMvnDeps() - } - /** * The instrumented tests are packaged with testClasspath which already contains the * user compiled classes diff --git a/libs/androidlib/src/mill/androidlib/AndroidModule.scala b/libs/androidlib/src/mill/androidlib/AndroidModule.scala index 9aacf28da088..0b737c1a107e 100644 --- a/libs/androidlib/src/mill/androidlib/AndroidModule.scala +++ b/libs/androidlib/src/mill/androidlib/AndroidModule.scala @@ -525,7 +525,7 @@ trait AndroidModule extends JavaModule { outer => sources = androidLibsRClasses().map(_.path), compileClasspath = Seq.empty, javacOptions = jOpts.compiler, - incrementalCompilation = zincIncrementalCompilation() + incrementalCompilation = true ), javaHome = javaHome().map(_.path), javaRuntimeOptions = jOpts.runtime, diff --git a/libs/androidlib/src/mill/androidlib/AndroidR8AppModule.scala b/libs/androidlib/src/mill/androidlib/AndroidR8AppModule.scala index baead298122c..15866297782d 100644 --- a/libs/androidlib/src/mill/androidlib/AndroidR8AppModule.scala +++ b/libs/androidlib/src/mill/androidlib/AndroidR8AppModule.scala @@ -1,13 +1,18 @@ package mill.androidlib +import coursier.core as cs import mill.* import mill.api.{PathRef, Task} +import mill.PathRef.jsonFormatter +import mill.javalib.{Dep, JavaModule} +import os.Path + import scala.xml.* @mill.api.experimental -trait AndroidR8AppModule extends AndroidAppModule { +trait AndroidR8AppModule extends AndroidAppModule { outer => - override def androidPackageMetaInfoFiles: T[Seq[AndroidPackageableExtraFile]] = + override def androidPackagedMetaInfFiles: T[Seq[AndroidPackageableExtraFile]] = androidR8PackageMetaInfoFiles() /** @@ -72,15 +77,20 @@ trait AndroidR8AppModule extends AndroidAppModule { } /** - * Creates a file for letting know R8 that [[compileModuleDeps]] and + * The list to let 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) { + def androidR8CompileOnlyClasspath: T[Seq[PathRef]] = + androidResolvedCompileMvnDeps() ++ androidTransitiveCompileOnlyClasspath() ++ androidTransitiveModuleRClasspath() + + /** + * Creates a file of [[androidR8CompileOnlyClasspath]] for CLI compatibility reasons (e.g. windows arg limit) + */ + def androidR8CompileOnlyClasspathFile: T[Option[PathRef]] = Task { + val resolvedCompileMvnDeps = androidR8CompileOnlyClasspath() + if (resolvedCompileMvnDeps.nonEmpty) { val compiledMvnDepsFile = Task.dest / "compile-only-classpath.txt" os.write.over( compiledMvnDepsFile, @@ -152,126 +162,169 @@ trait AndroidR8AppModule extends AndroidAppModule { /** * Prepares the R8 cli command to build this android app! - * @return */ private def androidR8Dex - : Task[(outPath: PathRef, dexCliArgs: Seq[String], appCompiledFiles: Seq[PathRef])] = Task { - val destDir = Task.dest / "minify" - os.makeDir.all(destDir) - - val outputPath = destDir - - Task.log.debug("outputPath: " + outputPath) - - // Define diagnostic output file paths - val mappingOut = destDir / "mapping.txt" - val seedsOut = destDir / "seeds.txt" - val usageOut = destDir / "usage.txt" - val configOut = destDir / "configuration.txt" - destDir / "missing_rules.txt" - val baselineOutOpt = destDir / "baseline-profile-rewritten.txt" - destDir / "res" - - // Extra ProGuard rules - val extraRules = - Seq( - // Instruct R8 to print seeds and usage. - s"-printseeds $seedsOut", - s"-printusage $usageOut" - ) ++ - (if (androidBuildSettings().isMinifyEnabled) then androidGeneratedMinifyKeepRules() - else Seq()) - // Create an extra ProGuard config file - val extraRulesFile = destDir / "extra-rules.pro" - val extraRulesContent = extraRules.mkString("\n") - os.write.over(extraRulesFile, extraRulesContent) - - val classpathClassFiles: Seq[PathRef] = androidPackagedClassfiles() - .filter(_.path.ext == "class") - - val appCompiledFiles: Seq[PathRef] = androidPackagedCompiledClasses() - .filter(_.path.ext == "class") - - val allClassFilesPathRefs = - classpathClassFiles ++ appCompiledFiles ++ androidPackagedDeps() - - val allClassFiles = allClassFilesPathRefs.map(_.path.toString) - val allClassFilesFile = Task.dest / "all-classes.txt" - os.write.over(allClassFilesFile, allClassFiles.mkString("\n")) - - val r8ArgsBuilder = Seq.newBuilder[String] - - r8ArgsBuilder += androidSdkModule().r8Exe().path.toString + : Task[(outPath: PathRef, dexCliArgs: Seq[String], appCompiledFiles: Seq[PathRef])] = + Task.Anon { + androidR8Build(Task.Anon("--dex"))() + } - if (androidIsDebug()) - r8ArgsBuilder += "--debug" - else - r8ArgsBuilder += "--release" - - r8ArgsBuilder ++= Seq( - "--output", - outputPath.toString, - "--pg-map-output", - mappingOut.toString, - "--pg-conf-output", - configOut.toString + /** + * The jdk-home to be used in r8 in case of building a + * jar (using --classfile) instead of a dex. Used in + * [[androidR8Jar]] + */ + def androidR8JavaHome: T[PathRef] = + javaHome().getOrElse(PathRef(Path(sys.props("java.home")), quick = true)) + + /** + * Prepares the R8 cli command to build this android app as a java class file (jar)! + * Useful for building APKs that are dynamically linked to other apks (e.g. the test apk) + */ + def androidR8Jar: T[PathRef] = Task { + val minifyDir = androidR8Build(Task.Anon("--classfile"))() + val cli = minifyDir.dexCliArgs + os.call(cli) + val jarDest = Task.dest / s"${moduleSegments.render}.jar" + val zipFiles = os.walk(minifyDir.outPath.path).map(p => + os.zip.ZipSource.fromPathTuple(p -> p.subRelativeTo(minifyDir.outPath.path)) ) + os.zip.apply(jarDest, zipFiles) + PathRef(jarDest) + } - if (!androidBuildSettings().enableDesugaring) { - r8ArgsBuilder += "--no-desugaring" - } + private def androidR8Build(buildType: Task[String]) + : Task[(outPath: PathRef, dexCliArgs: Seq[String], appCompiledFiles: Seq[PathRef])] = + Task.Anon { + val destDir = Task.dest / "minify" + os.makeDir.all(destDir) + + val outputPath = destDir + + Task.log.debug("outputPath: " + outputPath) + + // Define diagnostic output file paths + val mappingOut = destDir / "mapping.txt" + val seedsOut = destDir / "seeds.txt" + val usageOut = destDir / "usage.txt" + val configOut = destDir / "configuration.txt" + destDir / "missing_rules.txt" + val baselineOutOpt = destDir / "baseline-profile-rewritten.txt" + destDir / "res" + + // Extra ProGuard rules + val extraRules = + Seq( + // Instruct R8 to print seeds and usage. + s"-printseeds $seedsOut", + s"-printusage $usageOut" + ) ++ + (if (androidBuildSettings().isMinifyEnabled) then androidGeneratedMinifyKeepRules() + else Seq()) + // Create an extra ProGuard config file + val extraRulesFile = destDir / "extra-rules.pro" + val extraRulesContent = extraRules.mkString("\n") + os.write.over(extraRulesFile, extraRulesContent) + + val classpathClassFiles: Seq[PathRef] = androidPackagedClassfiles() + .filter(_.path.ext == "class") + + val appCompiledFiles: Seq[PathRef] = androidPackagedCompiledClasses() + .filter(_.path.ext == "class") + + val allClassFilesPathRefs = + classpathClassFiles ++ appCompiledFiles ++ androidPackagedDeps() + + val allClassFiles = allClassFilesPathRefs.map(_.path.toString) + val allClassFilesFile = Task.dest / "all-classes.txt" + os.write.over(allClassFilesFile, allClassFiles.mkString("\n")) + + val r8ArgsBuilder = Seq.newBuilder[String] + + r8ArgsBuilder += androidSdkModule().r8Exe().path.toString + + if (androidIsDebug()) + r8ArgsBuilder += "--debug" + else + r8ArgsBuilder += "--release" + + r8ArgsBuilder ++= Seq( + "--output", + outputPath.toString, + "--pg-map-output", + mappingOut.toString, + "--pg-conf-output", + configOut.toString + ) - if (!androidBuildSettings().isMinifyEnabled) { - r8ArgsBuilder ++= Seq("--no-minification", "--no-tree-shaking") - } + if (!androidBuildSettings().enableDesugaring) { + r8ArgsBuilder += "--no-desugaring" + } - r8ArgsBuilder ++= Seq( - "--min-api", - androidMinSdk().toString, - "--dex" - ) + if (!androidBuildSettings().isMinifyEnabled) { + r8ArgsBuilder ++= Seq("--no-minification", "--no-tree-shaking") + } - // Baseline profile rewriting arguments, if a baseline profile is provided. - val baselineArgs = baselineProfile().map { bp => - Seq("--art-profile", bp.path.toString, baselineOutOpt.toString) - }.getOrElse(Seq.empty) + // R8 does not support --min-api when compiling to class files + if (buildType() == "--dex") { + r8ArgsBuilder ++= Seq( + "--min-api", + androidMinSdk().toString + ) + } - r8ArgsBuilder ++= baselineArgs + r8ArgsBuilder ++= Seq( + buildType() + ) - // Library arguments: pass each bootclasspath and any additional library classes as --lib. - val libArgs = libraryClassesPaths().flatMap(ref => Seq("--lib", ref.path.toString)) + // Baseline profile rewriting arguments, if a baseline profile is provided. + val baselineArgs = baselineProfile().map { bp => + Seq("--art-profile", bp.path.toString, baselineOutOpt.toString) + }.getOrElse(Seq.empty) - r8ArgsBuilder ++= libArgs + r8ArgsBuilder ++= baselineArgs - // ProGuard configuration files: add our extra rules file, - // all provided config files and the common rules. - val pgArgs = - Seq( - "--pg-conf", - androidProguard().path.toString, - "--pg-conf", - extraRulesFile.toString - ) ++ androidCommonProguardFiles().flatMap(pgf => Seq("--pg-conf", pgf.path.toString)) + // Library arguments: pass each bootclasspath and any additional library classes as --lib. + val libArgs = libraryClassesPaths().flatMap(ref => Seq("--lib", ref.path.toString)) - r8ArgsBuilder ++= pgArgs + r8ArgsBuilder ++= libArgs - val compileOnlyClasspath = androidR8CompileOnlyClasspath() + if (buildType() == "--classfile") { + r8ArgsBuilder ++= Seq( + "--lib", + androidR8JavaHome().path.toString + ) + } - r8ArgsBuilder ++= compileOnlyClasspath.toSeq.flatMap(compiledMvnDepsFile => - Seq( - "--classpath", - "@" + compiledMvnDepsFile.path.toString + // ProGuard configuration files: add our extra rules file, + // all provided config files and the common rules. + val pgArgs = + Seq( + "--pg-conf", + androidProguard().path.toString, + "--pg-conf", + extraRulesFile.toString + ) ++ androidCommonProguardFiles().flatMap(pgf => Seq("--pg-conf", pgf.path.toString)) + + r8ArgsBuilder ++= pgArgs + + val compileOnlyClasspath = androidR8CompileOnlyClasspath() + + r8ArgsBuilder ++= compileOnlyClasspath.filter(_.path.ext == "jar").flatMap(compiledMvnDeps => + Seq( + "--classpath", + compiledMvnDeps.path.toString + ) ) - ) - r8ArgsBuilder ++= androidR8Args() + r8ArgsBuilder ++= androidR8Args() - r8ArgsBuilder += "@" + allClassFilesFile.toString + r8ArgsBuilder += "@" + allClassFilesFile.toString - val r8Args = r8ArgsBuilder.result() + val r8Args = r8ArgsBuilder.result() - (PathRef(outputPath), r8Args, allClassFilesPathRefs) - } + (PathRef(outputPath), r8Args, allClassFilesPathRefs) + } /** * Generates ProGuard/R8 keep rules to keep classes that are referenced in the AndroidManifest.xml @@ -350,4 +403,49 @@ trait AndroidR8AppModule extends AndroidAppModule { } } + trait AndroidR8InstrumentedTestsModule extends AndroidAppInstrumentedTests, AndroidR8AppModule { + + def moduleDeps: Seq[JavaModule] = super.moduleDeps.filterNot(_ == outer) + def compileModuleDeps: Seq[JavaModule] = super.compileModuleDeps ++ Seq(outer) + + def androidPackagableDepsExclusionRules: T[Seq[(String, String)]] = Task { + val baseResolvedDependencies = defaultResolver().resolution( + Task.traverse(compileModuleDepsChecked)(_.mvnDeps)().flatten, + boms = allBomDeps() + ) + baseResolvedDependencies.dependencies + .map(d => d.module.organization.value -> d.module.name.value).toSeq + } + + def androidPackagableMvnDeps: T[Seq[Dep]] = Task { + mvnDeps().map(_.exclude(androidPackagableDepsExclusionRules()*)) + } + + def androidResolvedPackagableMvnDeps: Task.Simple[Seq[PathRef]] = Task { + defaultResolver().classpath( + androidPackagableMvnDeps(), + artifactTypes = Some(artifactTypes()), + resolutionParamsMapOpt = + Some { params => + params + .withDefaultConfiguration(coursier.core.Configuration.runtime) + .withDefaultVariantAttributes( + cs.VariantSelector.AttributesBased( + params.defaultVariantAttributes.map(_.matchers).getOrElse(Map()) ++ Seq( + "org.gradle.usage" -> cs.VariantSelector.VariantMatcher.Runtime + ) + ) + ) + }, + boms = allBomDeps() + ) + } + + override def resolvedRunMvnDeps: Task.Simple[Seq[PathRef]] = androidResolvedPackagableMvnDeps() + + override def androidR8CompileOnlyClasspath: T[Seq[PathRef]] = Task { + Seq(outer.androidR8Jar()) + } + } + } diff --git a/libs/javalib/src/mill/javalib/CoursierModule.scala b/libs/javalib/src/mill/javalib/CoursierModule.scala index 0e2da484cf8d..986bfd78db72 100644 --- a/libs/javalib/src/mill/javalib/CoursierModule.scala +++ b/libs/javalib/src/mill/javalib/CoursierModule.scala @@ -5,7 +5,7 @@ import coursier.cache.FileCache import coursier.core.Resolution import coursier.core.VariantSelector.VariantMatcher import coursier.params.ResolutionParams -import coursier.{Dependency, Repository} +import coursier.{BomDependency, Dependency, Repository} import mill.api.{ModuleRef, PathRef, Result, Task} import mill.util.Jvm import mill.T @@ -250,7 +250,8 @@ object CoursierModule { sources: Boolean = false, artifactTypes: Option[Set[coursier.Type]] = None, resolutionParamsMapOpt: Option[ResolutionParams => ResolutionParams] = None, - mapDependencies: Option[Dependency => Dependency] = null + mapDependencies: Option[Dependency => Dependency] = null, + @unroll boms: IterableOnce[BomDependency] = Nil )(using ctx: mill.api.TaskCtx): Seq[PathRef] = Lib.resolveDependencies( repositories = repositories, @@ -263,7 +264,8 @@ object CoursierModule { ctx = Some(ctx), resolutionParams = resolutionParamsMapOpt.fold(resolutionParams)(_(resolutionParams)), checkGradleModules = checkGradleModules, - config = config + config = config, + boms = boms ).get /** @@ -272,7 +274,8 @@ object CoursierModule { * @param deps root dependencies */ def resolution[T: CoursierModule.Resolvable]( - deps: IterableOnce[T] + deps: IterableOnce[T], + @unroll boms: IterableOnce[BomDependency] = Nil )(using ctx: mill.api.TaskCtx): coursier.core.Resolution = { val deps0 = deps .iterator @@ -286,7 +289,7 @@ object CoursierModule { coursierCacheCustomizer = coursierCacheCustomizer, ctx = Some(ctx), resolutionParams = resolutionParams, - boms = Nil, + boms = boms, checkGradleModules = checkGradleModules, config = config ).get diff --git a/libs/javalib/src/mill/javalib/Lib.scala b/libs/javalib/src/mill/javalib/Lib.scala index e7cfe4e927ce..a4aa80667e4c 100644 --- a/libs/javalib/src/mill/javalib/Lib.scala +++ b/libs/javalib/src/mill/javalib/Lib.scala @@ -79,7 +79,8 @@ object Lib { artifactTypes: Option[Set[Type]] = None, resolutionParams: ResolutionParams = ResolutionParams(), checkGradleModules: Boolean = false, - @unroll config: mill.util.CoursierConfig = mill.util.CoursierConfig.default() + @unroll config: mill.util.CoursierConfig = mill.util.CoursierConfig.default(), + @unroll boms: IterableOnce[BomDependency] = Nil ): Result[Seq[PathRef]] = { val depSeq = deps.iterator.toSeq val res = mill.util.Jvm.resolveDependencies( @@ -94,7 +95,8 @@ object Lib { coursierCacheCustomizer = coursierCacheCustomizer, resolutionParams = resolutionParams, checkGradleModules = checkGradleModules, - config = config + config = config, + boms = boms ) res.map(_.map(_.withRevalidateOnce)) diff --git a/libs/util/src/mill/util/Jvm.scala b/libs/util/src/mill/util/Jvm.scala index f92979768c8c..eac359d7fa35 100644 --- a/libs/util/src/mill/util/Jvm.scala +++ b/libs/util/src/mill/util/Jvm.scala @@ -478,7 +478,8 @@ object Jvm { artifactTypes: Option[Set[Type]] = None, resolutionParams: ResolutionParams = ResolutionParams(), checkGradleModules: Boolean = false, - @unroll config: CoursierConfig = CoursierConfig.default() + @unroll config: CoursierConfig = CoursierConfig.default(), + @unroll boms: IterableOnce[BomDependency] = Nil ): Result[coursier.Artifacts.Result] = { val resolutionRes = resolveDependenciesMetadataSafe( repositories, @@ -489,6 +490,7 @@ object Jvm { ctx, coursierCacheCustomizer, resolutionParams, + boms = boms, checkGradleModules = checkGradleModules, config = config ) @@ -543,7 +545,8 @@ object Jvm { artifactTypes: Option[Set[Type]] = None, resolutionParams: ResolutionParams = ResolutionParams(), checkGradleModules: Boolean = false, - @unroll config: CoursierConfig = CoursierConfig.default() + @unroll config: CoursierConfig = CoursierConfig.default(), + @unroll boms: IterableOnce[BomDependency] = Nil ): Result[Seq[PathRef]] = getArtifacts( repositories, @@ -557,7 +560,8 @@ object Jvm { artifactTypes, resolutionParams, checkGradleModules = checkGradleModules, - config = config + config = config, + boms = boms ).map { res => BuildCtx.withFilesystemCheckerDisabled { res.files