Skip to content

Commit

Permalink
Support exclude attribute in Dep parser (#3492)
Browse files Browse the repository at this point in the history
You can give exclusions with `;exclude=org:name` or `;exclude=org:*` or
`;exclude=*:name`.

Pull request: #3492
  • Loading branch information
lefou committed Sep 9, 2024
1 parent fc2edf9 commit 7732b0f
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 6 deletions.
17 changes: 16 additions & 1 deletion docs/modules/ROOT/pages/Library_Dependencies.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,18 @@ For more details about coursier, refer to the {link-coursier-doc}[coursier docum

== Dependencies in General

Mill dependencies have the form:
Mill dependencies have the simple form:

----
ivy"{organization}:{name}:{version}"
----

Additional attributes are also supported:

----
ivy"{organization}:{name}:{version}[;{attribute}={value}]*"
----

When working in other Java and Scala projects, you will find some synonyms, which typically all mean the same.

For example in the Maven ecosystem, the `organization` is called the `group` and the `name` is called the `artifact`.
Expand Down Expand Up @@ -225,6 +231,15 @@ def deps = Agg(

You can also use `.excludeOrg` or `excludeName`:

There is also a short notation available:

.Example: Shot notation to exclude `fansi_2.12` library from transitive dependency set of `pprint`.
[source,scala]
----
def deps = Agg(
ivy"com.lihaoyi::pprint:0.5.3;exclude=com.lihaoyi:fansi_2.12"
)
----

.Example: Exclude all `com.lihaoyi` libraries from transitive dependency set of `pprint`.
[source,scala]
Expand Down
13 changes: 11 additions & 2 deletions scalalib/src/mill/scalalib/Dep.scala
Original file line number Diff line number Diff line change
Expand Up @@ -106,14 +106,17 @@ object Dep {
implicit def parse(signature: String): Dep = {
val parts = signature.split(';')
val module = parts.head
var exclusions = Seq.empty[(String, String)]
val attributes = parts.tail.foldLeft(coursier.Attributes()) { (as, s) =>
s.split('=') match {
case Array("classifier", v) => as.withClassifier(coursier.Classifier(v))
case Array("type", v) => as.withType(coursier.Type(v))
case Array("exclude", s"${org}:${name}") => exclusions ++= Seq((org, name)); as
case Array(k, v) => throw new Exception(s"Unrecognized attribute: [$s]")
case _ => throw new Exception(s"Unable to parse attribute specifier: [$s]")
}
}

(module.split(':') match {
case Array(a, b, c) => Dep(a, b, c, cross = empty(platformed = false))
case Array(a, b, "", c) => Dep(a, b, c, cross = empty(platformed = true))
Expand All @@ -122,7 +125,9 @@ object Dep {
case Array(a, "", "", b, c) => Dep(a, b, c, cross = Full(platformed = false))
case Array(a, "", "", b, "", c) => Dep(a, b, c, cross = Full(platformed = true))
case _ => throw new Exception(s"Unable to parse signature: [$signature]")
}).configure(attributes = attributes)
})
.exclude(exclusions.sorted: _*)
.configure(attributes = attributes)
}

@unused private implicit val depFormat: RW[Dependency] = mill.scalalib.JsonFormatters.depFormat
Expand All @@ -141,7 +146,11 @@ object Dep {
case "" => ""
case s => s";type=$s"
}
val attrs = classifierAttr + typeAttr

val excludeAttr =
dep.dep.exclusions().toSeq.sorted.map(e => s";exclude=${e._1.value}:${e._2.value}").mkString

val attrs = classifierAttr + typeAttr + excludeAttr

val prospective = dep.cross match {
case CrossVersion.Constant("", false) => Some(s"$org:$mod:$ver$attrs")
Expand Down
6 changes: 3 additions & 3 deletions scalalib/test/src/mill/scalalib/ResolveDepsTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,14 @@ object ResolveDepsTests extends TestSuite {

test("excludeTransitiveDeps") {
val deps = Agg(ivy"com.lihaoyi::pprint:0.5.3".exclude("com.lihaoyi" -> "fansi_2.12"))
assertRoundTrip(deps, simplified = false)
assertRoundTrip(deps, simplified = true)
val Success(paths) = evalDeps(deps)
assert(!paths.exists(_.path.toString.contains("fansi_2.12")))
}

test("excludeTransitiveDepsByOrg") {
val deps = Agg(ivy"com.lihaoyi::pprint:0.5.3".excludeOrg("com.lihaoyi"))
assertRoundTrip(deps, simplified = false)
assertRoundTrip(deps, simplified = true)
val Success(paths) = evalDeps(deps)
assert(!paths.exists(path =>
path.path.toString.contains("com/lihaoyi") && !path.path.toString.contains("pprint_2.12")
Expand All @@ -70,7 +70,7 @@ object ResolveDepsTests extends TestSuite {

test("excludeTransitiveDepsByName") {
val deps = Agg(ivy"com.lihaoyi::pprint:0.5.3".excludeName("fansi_2.12"))
assertRoundTrip(deps, simplified = false)
assertRoundTrip(deps, simplified = true)
val Success(paths) = evalDeps(deps)
assert(!paths.exists(_.path.toString.contains("fansi_2.12")))
}
Expand Down

0 comments on commit 7732b0f

Please sign in to comment.