From cdfadf2ec239e1970b859a4a99f6fde4dbfa19ce Mon Sep 17 00:00:00 2001 From: Alex Archambault Date: Mon, 3 Feb 2025 23:35:36 +0100 Subject: [PATCH] Break the dependency between repositoriesTask and ivyDeps mill-scalablytyped (but possibly other plugins or user setups too) adds a dependency on `repositoriesTask` from `ivyDeps`. Since the introduction of `JavaModule#coursierProject`, Mill had a dependency the other way around, `repositoriesTask` depends on `ivyDeps`. This creates a cycle, leading to StackOverflowException-s. In order to work around that, this PR splits both `repositoriesTask` and `defaultResolver`, adding: - `allRepositoriesTask`: basically `repositoriesTask`, with the Mill internal repository added - `internalResolver`: same as `defaultResolver`, with the Mill internal repository added (via `allRepositoriesTask`) If users need to resolve purely external modules (most common case it seems), they can keep using `repositoriesTask` or `defaultResolver`. If they need to resolve some Mill internal modules (usually brought in via `JavaModule#coursierDependency`), they now need to use `allRepositoriesTask` and `internalResolver` instead of `repositoriesTask` and `defaultResolver`. That way, no cycle is introduced when users only need to resolve external modules. Fixes #4457 --- .../src/mill/bsp/worker/MillBuildServer.scala | 4 +- .../src/mill/contrib/bloop/BloopImpl.scala | 2 +- .../contrib/scalapblib/ScalaPBModule.scala | 2 +- idea/src/mill/idea/GenIdeaImpl.scala | 2 +- .../src/mill/scalalib/CoursierModule.scala | 48 ++++++++++++++++++- scalalib/src/mill/scalalib/JavaModule.scala | 12 ++--- 6 files changed, 58 insertions(+), 12 deletions(-) diff --git a/bsp/worker/src/mill/bsp/worker/MillBuildServer.scala b/bsp/worker/src/mill/bsp/worker/MillBuildServer.scala index ee870de6fbb..0c03266e641 100644 --- a/bsp/worker/src/mill/bsp/worker/MillBuildServer.scala +++ b/bsp/worker/src/mill/bsp/worker/MillBuildServer.scala @@ -296,7 +296,7 @@ private class MillBuildServer( case m: JavaModule => Task.Anon { ( - m.defaultResolver().resolveDeps( + m.internalResolver().resolveDeps( Seq( m.coursierDependency.withConfiguration(coursier.core.Configuration.provided), m.coursierDependency @@ -304,7 +304,7 @@ private class MillBuildServer( sources = true ), m.unmanagedClasspath(), - m.repositoriesTask() + m.allRepositoriesTask() ) } } diff --git a/contrib/bloop/src/mill/contrib/bloop/BloopImpl.scala b/contrib/bloop/src/mill/contrib/bloop/BloopImpl.scala index 2dcf81c7e5c..601154d6090 100644 --- a/contrib/bloop/src/mill/contrib/bloop/BloopImpl.scala +++ b/contrib/bloop/src/mill/contrib/bloop/BloopImpl.scala @@ -390,7 +390,7 @@ class BloopImpl(evs: () => Seq[Evaluator], wd: os.Path) extends ExternalModule { } val bloopResolution: Task[BloopConfig.Resolution] = Task.Anon { - val repos = module.repositoriesTask() + val repos = module.allRepositoriesTask() // same as input of resolvedIvyDeps val coursierDeps = Seq( module.coursierDependency.withConfiguration(coursier.core.Configuration.provided), diff --git a/contrib/scalapblib/src/mill/contrib/scalapblib/ScalaPBModule.scala b/contrib/scalapblib/src/mill/contrib/scalapblib/ScalaPBModule.scala index a88655a6df6..f02bdc8497f 100644 --- a/contrib/scalapblib/src/mill/contrib/scalapblib/ScalaPBModule.scala +++ b/contrib/scalapblib/src/mill/contrib/scalapblib/ScalaPBModule.scala @@ -91,7 +91,7 @@ trait ScalaPBModule extends ScalaModule { } def scalaPBProtoClasspath: T[Agg[PathRef]] = Task { - defaultResolver().resolveDeps( + internalResolver().resolveDeps( Seq( coursierDependency.withConfiguration(coursier.core.Configuration.provided), coursierDependency diff --git a/idea/src/mill/idea/GenIdeaImpl.scala b/idea/src/mill/idea/GenIdeaImpl.scala index 915dd752402..279fe6309bb 100755 --- a/idea/src/mill/idea/GenIdeaImpl.scala +++ b/idea/src/mill/idea/GenIdeaImpl.scala @@ -114,7 +114,7 @@ case class GenIdeaImpl( GenIdeaException( s"Failure during resolving repositories: ${Evaluator.formatFailing(r)}" ) - )(modules.map(_._2.repositoriesTask)) + )(modules.map(_._2.allRepositoriesTask)) } Lib.resolveMillBuildDeps(moduleRepos.flatten, Option(ctx), useSources = true) Lib.resolveMillBuildDeps(moduleRepos.flatten, Option(ctx), useSources = false) diff --git a/scalalib/src/mill/scalalib/CoursierModule.scala b/scalalib/src/mill/scalalib/CoursierModule.scala index 0d5be1bfa89..65786e480d5 100644 --- a/scalalib/src/mill/scalalib/CoursierModule.scala +++ b/scalalib/src/mill/scalalib/CoursierModule.scala @@ -33,6 +33,34 @@ trait CoursierModule extends mill.Module { Lib.depToDependencyJava(_: Dep) } + /** + * A `CoursierModule.Resolver` to resolve dependencies. + * + * Unlike `defaultResolver`, this resolver can resolve Mill internal modules + * (obtained via `JavaModule#coursierDependency`). + * + * @return `CoursierModule.Resolver` instance + */ + def internalResolver: Task[CoursierModule.Resolver] = Task.Anon { + new CoursierModule.Resolver( + repositories = allRepositoriesTask(), + bind = bindDependency(), + mapDependencies = Some(mapDependencies()), + customizer = resolutionCustomizer(), + coursierCacheCustomizer = coursierCacheCustomizer(), + ctx = Some(implicitly[mill.api.Ctx.Log]), + resolutionParams = resolutionParams() + ) + } + + /** + * A `CoursierModule.Resolver` to resolve dependencies. + * + * Can be used to resolve external dependencies, if you need to download an external + * tool from Maven or Ivy repositories, by calling `CoursierModule.Resolver#resolveDeps`. + * + * @return `CoursierModule.Resolver` instance + */ def defaultResolver: Task[CoursierModule.Resolver] = Task.Anon { new CoursierModule.Resolver( repositories = repositoriesTask(), @@ -94,6 +122,8 @@ trait CoursierModule extends mill.Module { /** * The repositories used to resolved dependencies with [[resolveDeps()]]. + * + * See [[allRepositoriesTask]] if you need to resolve Mill internal modules. */ def repositoriesTask: Task[Seq[Repository]] = Task.Anon { val resolve = Resolve() @@ -101,7 +131,23 @@ trait CoursierModule extends mill.Module { resolve.finalRepositories.future()(resolve.cache.ec), Duration.Inf ) - internalRepositories() ++ repos + repos + } + + /** + * The repositories used to resolved dependencies + * + * Unlike [[repositoriesTask]], this includes the Mill internal repositories, + * which allow to resolve Mill internal modules (usually brought in via + * `JavaModule#coursierDependency`). + * + * Beware that this needs to evaluate `JavaModule#coursierProject` of all + * module dependencies of the current module, which itself evaluates `JavaModule#ivyDeps` + * and related tasks. You shouldn't depend on this task from implementations of `ivyDeps`, + * which would introduce cycles between Mill tasks. + */ + def allRepositoriesTask: Task[Seq[Repository]] = Task.Anon { + internalRepositories() ++ repositoriesTask() } /** diff --git a/scalalib/src/mill/scalalib/JavaModule.scala b/scalalib/src/mill/scalalib/JavaModule.scala index 8a166bef3e4..0d0669f6cba 100644 --- a/scalalib/src/mill/scalalib/JavaModule.scala +++ b/scalalib/src/mill/scalalib/JavaModule.scala @@ -49,7 +49,7 @@ trait JavaModule override def resources = super[JavaModule].resources override def moduleDeps: Seq[JavaModule] = Seq(outer) override def repositoriesTask: Task[Seq[Repository]] = Task.Anon { - internalRepositories() ++ outer.repositoriesTask() + outer.repositoriesTask() } override def resolutionCustomizer: Task[Option[coursier.Resolution => coursier.Resolution]] = outer.resolutionCustomizer @@ -1001,7 +1001,7 @@ trait JavaModule * Resolved dependencies */ def resolvedIvyDeps: T[Agg[PathRef]] = Task { - defaultResolver().resolveDeps( + internalResolver().resolveDeps( Seq( BoundDep( coursierDependency.withConfiguration(cs.Configuration.provided), @@ -1024,7 +1024,7 @@ trait JavaModule } def resolvedRunIvyDeps: T[Agg[PathRef]] = Task { - defaultResolver().resolveDeps( + internalResolver().resolveDeps( Seq( BoundDep( coursierDependency.withConfiguration(cs.Configuration.runtime), @@ -1222,7 +1222,7 @@ trait JavaModule val dependencies = (additionalDeps() ++ Seq(BoundDep(coursierDependency, force = false))).iterator.to(Seq) val resolution: Resolution = Lib.resolveDependenciesMetadataSafe( - repositoriesTask(), + allRepositoriesTask(), dependencies, Some(mapDependencies()), customizer = resolutionCustomizer(), @@ -1465,7 +1465,7 @@ trait JavaModule val tasks = if (all.value) Seq( Task.Anon { - defaultResolver().resolveDeps( + internalResolver().resolveDeps( Seq( coursierDependency.withConfiguration(cs.Configuration.provided), coursierDependency @@ -1478,7 +1478,7 @@ trait JavaModule ) }, Task.Anon { - defaultResolver().resolveDeps( + internalResolver().resolveDeps( Seq(coursierDependency.withConfiguration(cs.Configuration.runtime)), sources = true )