From 596a660e19b40ad0835a5b4a46b3828eb031fabf Mon Sep 17 00:00:00 2001 From: Tobias Roeser Date: Mon, 9 Sep 2024 09:27:55 +0200 Subject: [PATCH 1/2] Support `exclude` attribte in `Dep` parser You can give exclusions with `;exclude=org:name` or `;exclude=org:*` or `;exclude=*;name`. --- scalalib/src/mill/scalalib/Dep.scala | 13 +++++++++++-- .../test/src/mill/scalalib/ResolveDepsTests.scala | 6 +++--- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/scalalib/src/mill/scalalib/Dep.scala b/scalalib/src/mill/scalalib/Dep.scala index e00e5a74b21..b857131bc50 100644 --- a/scalalib/src/mill/scalalib/Dep.scala +++ b/scalalib/src/mill/scalalib/Dep.scala @@ -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)) @@ -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 @@ -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") diff --git a/scalalib/test/src/mill/scalalib/ResolveDepsTests.scala b/scalalib/test/src/mill/scalalib/ResolveDepsTests.scala index 98be5c2efba..cd55d1b14b5 100644 --- a/scalalib/test/src/mill/scalalib/ResolveDepsTests.scala +++ b/scalalib/test/src/mill/scalalib/ResolveDepsTests.scala @@ -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") @@ -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"))) } From 23ae305112a8893d5ca5bd641e099d562a6bd82e Mon Sep 17 00:00:00 2001 From: Tobias Roeser Date: Mon, 9 Sep 2024 09:33:46 +0200 Subject: [PATCH 2/2] docs --- .../ROOT/pages/Library_Dependencies.adoc | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/docs/modules/ROOT/pages/Library_Dependencies.adoc b/docs/modules/ROOT/pages/Library_Dependencies.adoc index e0baba3c380..cff5e929e8a 100644 --- a/docs/modules/ROOT/pages/Library_Dependencies.adoc +++ b/docs/modules/ROOT/pages/Library_Dependencies.adoc @@ -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`. @@ -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]