Skip to content

Commit

Permalink
Support customizing coursier dependency resolution (make OS specific …
Browse files Browse the repository at this point in the history
…classifiers work) (#775)

Added CoursierModule.resolutionCustomizer task.

This helps in customizing coursier resolution process, e.g.
to add OS or JDK specific resolution properties sometimes used by Maven

Pull request: #775
  • Loading branch information
lefou authored Apr 26, 2021
1 parent 8e91fef commit ab4d61a
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 33 deletions.
49 changes: 43 additions & 6 deletions main/src/modules/Jvm.scala
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,7 @@ object Jvm {
PathRef(outputPath)
}


/**
* Resolve dependencies using Coursier.
*
Expand All @@ -486,10 +487,11 @@ object Jvm {
force: IterableOnce[coursier.Dependency],
sources: Boolean = false,
mapDependencies: Option[Dependency => Dependency] = None,
ctx: Option[mill.util.Ctx.Log] = None): Result[Agg[PathRef]] = {
customizer: Option[coursier.core.Resolution => coursier.core.Resolution] = None,
ctx: Option[mill.api.Ctx.Log] = None): Result[Agg[PathRef]] = {

val (_, resolution) = resolveDependenciesMetadata(
repositories, deps, force, mapDependencies, ctx
repositories, deps, force, mapDependencies, customizer, ctx
)
val errs = resolution.errors

Expand Down Expand Up @@ -552,12 +554,12 @@ object Jvm {
}
}


def resolveDependenciesMetadata(repositories: Seq[Repository],
deps: IterableOnce[coursier.Dependency],
force: IterableOnce[coursier.Dependency],
mapDependencies: Option[Dependency => Dependency] = None,
ctx: Option[mill.util.Ctx.Log] = None) = {
customizer: Option[coursier.core.Resolution => coursier.core.Resolution] = None,
ctx: Option[mill.api.Ctx.Log] = None): (Seq[Dependency], Resolution) = {

val cachePolicies = coursier.cache.CacheDefaults.cachePolicies

Expand All @@ -566,11 +568,13 @@ object Jvm {
.map{d => d.module -> d.version}
.toMap

val start = Resolution()
.withRootDependencies(deps.map(mapDependencies.getOrElse(identity[Dependency](_))).toSeq)
val start0 = Resolution()
.withRootDependencies(deps.iterator.map(mapDependencies.getOrElse(identity[Dependency](_))).toSeq)
.withForceVersions(forceVersions)
.withMapDependencies(mapDependencies)

val start = customizer.getOrElse(identity[Resolution](_)).apply(start0)

val resolutionLogger = ctx.map(c => new TickerResolutionLogger(c))
val cache = resolutionLogger match {
case None => coursier.cache.FileCache[Task].withCachePolicies(cachePolicies)
Expand Down Expand Up @@ -673,4 +677,37 @@ object Jvm {
manifest
}
}

@deprecated("Use alternative overload. This one is only for binary backwards compatibility.", "mill after 0.9.6")
def resolveDependencies(repositories: Seq[Repository],
deps: IterableOnce[coursier.Dependency],
force: IterableOnce[coursier.Dependency],
sources: Boolean,
mapDependencies: Option[Dependency => Dependency],
ctx: Option[mill.api.Ctx.Log]): Result[Agg[PathRef]] =
resolveDependencies(
repositories = repositories,
deps = deps,
force = force,
sources = sources,
mapDependencies = mapDependencies,
customizer = None,
ctx = ctx
)

@deprecated("Use alternative overload. This one is only for binary backwards compatibility.", "mill after 0.9.6")
def resolveDependenciesMetadata(repositories: Seq[Repository],
deps: IterableOnce[coursier.Dependency],
force: IterableOnce[coursier.Dependency],
mapDependencies: Option[Dependency => Dependency],
ctx: Option[mill.api.Ctx.Log]): (Seq[Dependency], Resolution) =
resolveDependenciesMetadata(
repositories = repositories,
deps = deps,
force = force,
mapDependencies = mapDependencies,
customizer = None,
ctx = ctx
)

}
39 changes: 30 additions & 9 deletions scalalib/src/CoursierModule.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package mill.scalalib

import coursier.{Dependency, Repository, Resolve}
import coursier.core.{Resolution}
import mill.{Agg, T}
import mill.define.Task
import mill.api.PathRef
Expand All @@ -26,30 +27,50 @@ trait CoursierModule extends mill.Module {
*/
def resolveDeps(deps: Task[Agg[Dep]], sources: Boolean = false): Task[Agg[PathRef]] = T.task {
Lib.resolveDependencies(
repositoriesTask(),
resolveCoursierDependency().apply(_),
deps(),
sources,
repositories = repositoriesTask(),
depToDependency = resolveCoursierDependency().apply(_),
deps = deps(),
sources = sources,
mapDependencies = Some(mapDependencies()),
Some(implicitly[mill.util.Ctx.Log])
customizer = resolutionCustomizer(),
ctx = Some(implicitly[mill.api.Ctx.Log])
)
}

/**
* Map dependencies before resolving them.
* Override this to customize the set of dependencies.
*/
def mapDependencies: Task[Dependency => Dependency] = T.task { d: coursier.Dependency => d }
def mapDependencies: Task[Dependency => Dependency] = T.task { d: Dependency => d }

/**
* The repositories used to resolved dependencies with [[resolveDeps()]].
*/
@deprecated("Use repositoriesTask instead", "after mill 0.8.0")
def repositories: Seq[Repository] = Resolve.defaultRepositories
def repositoriesTask: Task[Seq[Repository]] = T.task { repositories }

/**
* Customize the coursier resolution resolution process.
* This is rarely needed to changed, as the default try to provide a
* highly reproducible resolution process. But sometime, you need
* more control, e.g. you want to add some OS or JDK specific resolution properties
* which are sometimes used by Maven and therefore found in dependency artifact metadata.
* For example, the JavaFX artifacts are known to use OS specific properties.
* To fix resolution for JavaFX, you could override this task like the following:
* {{{
* override def resolutionCustomizer = T.task {
* Some( (r: coursier.core.Resolution) =>
* r.withOsInfo(coursier.core.Activation.Os.fromProperties(sys.props.toMap))
* )
* }
* }}}
* @return
*/
def resolutionCustomizer: Task[Option[Resolution => Resolution]] = T.task { None }

/**
* The repositories used to resolved dependencies with [[resolveDeps()]].
*/
def repositoriesTask: Task[Seq[Repository]] = T.task { repositories }
@deprecated("Use repositoriesTask instead", "after mill 0.8.0")
def repositories: Seq[Repository] = Resolve.defaultRepositories

}
3 changes: 2 additions & 1 deletion scalalib/src/JavaModule.scala
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,8 @@ trait JavaModule
repositoriesTask(),
resolveCoursierDependency().apply(_),
additionalDeps() ++ transitiveIvyDeps(),
Some(mapDependencies())
Some(mapDependencies()),
customizer = resolutionCustomizer()
)

println(
Expand Down
66 changes: 52 additions & 14 deletions scalalib/src/Lib.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import mill.api.{Ctx, Loose, PathRef, Result}
import sbt.testing._


object Lib{
object Lib {
def depToDependencyJava(dep: Dep, platformSuffix: String = ""): Dependency = {
assert(dep.cross.isConstant, s"Not a Java dependency: $dep")
depToDependency(dep, "", platformSuffix)
Expand All @@ -28,16 +28,19 @@ object Lib{
depToDependency: Dep => coursier.Dependency,
deps: IterableOnce[Dep],
mapDependencies: Option[Dependency => Dependency] = None,
customizer: Option[coursier.core.Resolution => coursier.core.Resolution] = None,
ctx: Option[Ctx.Log] = None): (Seq[Dependency], Resolution) = {
val depSeq = deps.toSeq
val depSeq = deps.iterator.toSeq
mill.modules.Jvm.resolveDependenciesMetadata(
repositories,
depSeq.map(depToDependency),
depSeq.filter(_.force).map(depToDependency),
mapDependencies,
ctx
repositories = repositories,
deps = depSeq.map(depToDependency),
force = depSeq.filter(_.force).map(depToDependency),
mapDependencies = mapDependencies,
customizer = customizer,
ctx = ctx
)
}

/**
* Resolve dependencies using Coursier.
*
Expand All @@ -50,17 +53,20 @@ object Lib{
deps: IterableOnce[Dep],
sources: Boolean = false,
mapDependencies: Option[Dependency => Dependency] = None,
customizer: Option[coursier.core.Resolution => coursier.core.Resolution] = None,
ctx: Option[Ctx.Log] = None): Result[Agg[PathRef]] = {
val depSeq = deps.toSeq
val depSeq = deps.iterator.toSeq
mill.modules.Jvm.resolveDependencies(
repositories,
depSeq.map(depToDependency),
depSeq.filter(_.force).map(depToDependency),
sources,
mapDependencies,
ctx
repositories = repositories,
deps = depSeq.map(depToDependency),
force = depSeq.filter(_.force).map(depToDependency),
sources = sources,
mapDependencies = mapDependencies,
customizer = customizer,
ctx = ctx
)
}

def scalaCompilerIvyDeps(scalaOrganization: String, scalaVersion: String): Loose.Agg[Dep] =
if (mill.scalalib.api.Util.isDotty(scalaVersion))
Agg(
Expand Down Expand Up @@ -165,4 +171,36 @@ object Lib{
}.map { f => (cls, f) }
}

@deprecated("User other overload instead. Only for binary backward compatibility.", "mill after 0.9.6")
def resolveDependenciesMetadata(repositories: Seq[Repository],
depToDependency: Dep => coursier.Dependency,
deps: IterableOnce[Dep],
mapDependencies: Option[Dependency => Dependency],
ctx: Option[Ctx.Log]): (Seq[Dependency], Resolution) =
resolveDependenciesMetadata(
repositories = repositories,
depToDependency = depToDependency,
deps = deps,
mapDependencies = mapDependencies,
customizer = None,
ctx = ctx
)

@deprecated("User other overload instead. Only for binary backward compatibility.", "mill after 0.9.6")
def resolveDependencies(repositories: Seq[Repository],
depToDependency: Dep => coursier.Dependency,
deps: IterableOnce[Dep],
sources: Boolean,
mapDependencies: Option[Dependency => Dependency],
ctx: Option[Ctx.Log]): Result[Agg[PathRef]] =
resolveDependencies(
repositories = repositories,
depToDependency = depToDependency,
deps = deps,
sources = sources,
mapDependencies = mapDependencies,
customizer = None,
ctx = ctx
)

}
14 changes: 11 additions & 3 deletions scalalib/src/dependency/versions/VersionsFinder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,20 @@ private[dependency] object VersionsFinder {
javaModules.map { javaModule =>
val depToDependency = eval(evaluator, javaModule.resolveCoursierDependency)
val deps = evalOrElse(evaluator, javaModule.ivyDeps, Loose.Agg.empty[Dep])
val compileIvyDeps = evalOrElse(evaluator, javaModule.compileIvyDeps, Loose.Agg.empty[Dep])
val runIvyDeps = evalOrElse(evaluator, javaModule.runIvyDeps, Loose.Agg.empty[Dep])
val repos = evalOrElse(evaluator, javaModule.repositoriesTask, Seq.empty[Repository])
val mapDeps = evalOrElse(evaluator, javaModule.mapDependencies, (d: coursier.Dependency) => d)
val custom = evalOrElse(evaluator, javaModule.resolutionCustomizer, None)

val (dependencies, _) =
Lib.resolveDependenciesMetadata(repos,
depToDependency,
deps)
Lib.resolveDependenciesMetadata(
repositories = repos,
depToDependency = depToDependency,
deps = deps ++ compileIvyDeps ++ runIvyDeps,
mapDependencies = Some(mapDeps),
customizer = custom
)

(javaModule, dependencies)
}
Expand Down

0 comments on commit ab4d61a

Please sign in to comment.