-
-
Notifications
You must be signed in to change notification settings - Fork 359
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add BOM / dependency management support #3924
base: main
Are you sure you want to change the base?
Conversation
Sir @alexarchambault, will this add bom support for jetpack compose libs also? |
If it only relies on a "BOM", that should yes. But this PR isn't about the Gradle module metadata stuff, that was mentioned too in one of your PRs. |
Actually i think this will work(not sure) lets see after it gets merged then i will try this once |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't we need to take transitive bomDeps
into account? In it's current draft, bomDeps
is only used intransitively.
As a rule of thumb, transitive moduleDeps
should behave the same as transitive ivyDeps
. If an upstream dependency import a BOM and that BOM is considered by coursier when resolving dependencies, it should also work, if that upstream dependency is a local module.
@lefou Yes, that's what I meant by "make things working for multi-module projects". In the ITs, I intend to publish locally things in a directory, and check that resolution by coursier of stuff published locally gives the same class path as Mill computes internally. |
Conflicts: build.mill
Conflicts: scalalib/src/mill/scalalib/PublishModule.scala scalalib/src/mill/scalalib/publish/Pom.scala
This comment was marked as outdated.
This comment was marked as outdated.
2843192
to
067d84d
Compare
This comment was marked as outdated.
This comment was marked as outdated.
@lihaoyi @lefou This is almost ready for review, you can already have a look if you wish. There a two things I'd like to tweak though: some examples should be added in the doc This PR adds many features. I have a branch with these changes with a cleaner git history here. If needed, to ease review, I can drop some features (and send subsequent PRs later…), so that these can be reviewed one-by-one. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not a full review, but some remarks.
@@ -31,6 +31,20 @@ trait CoursierSupport { | |||
ctx.fold(cache)(c => cache.withLogger(new TickerResolutionLogger(c))) | |||
} | |||
|
|||
def isLocalTestDep(dep: Dependency): Option[Seq[PathRef]] = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we make this private
or private[mill]
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, private
actually (with minor refactoring so that it's not used elsewhere)
def allIvyDeps: T[Agg[Dep]] = Task { | ||
val bomDeps0 = allBomDeps().toSeq | ||
val rawDeps = ivyDeps() ++ mandatoryIvyDeps() | ||
val depsWithBoms = | ||
if (bomDeps0.isEmpty) rawDeps | ||
else rawDeps.map(dep => dep.copy(dep = dep.dep.addBoms(bomDeps0))) | ||
val depMgmt = dependencyManagementDict() | ||
if (depMgmt.isEmpty) | ||
depsWithBoms | ||
else { | ||
lazy val depMgmtMap = depMgmt.toMap | ||
depsWithBoms | ||
.map(dep => dep.copy(dep = dep.dep.withOverrides(dep.dep.overrides ++ depMgmt))) | ||
.map { dep => | ||
val key = DependencyManagement.Key( | ||
dep.dep.module.organization, | ||
dep.dep.module.name, | ||
coursier.core.Type.jar, | ||
dep.dep.publication.classifier | ||
) | ||
val versionOverride = | ||
if (dep.dep.version == "_") depMgmtMap.get(key).map(_.version) | ||
else None | ||
val exclusions = depMgmtMap.get(key) | ||
.map(_.minimizedExclusions) | ||
.map(dep.dep.minimizedExclusions.join(_)) | ||
.getOrElse(dep.dep.minimizedExclusions) | ||
dep.copy( | ||
dep = dep.dep | ||
.withVersion(versionOverride.getOrElse(dep.dep.version)) | ||
.withMinimizedExclusions(exclusions) | ||
) | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This large-ish task is hard to digest. Maybe, we can make it more compact and use some def
s with meaningful names. Or add some in-line comments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe this logic could be moved into a helper method?
* Data from dependencyManagement, converted to a type ready to be passed to coursier | ||
* for dependency resolution | ||
*/ | ||
def dependencyManagementDict: Task[Seq[(DependencyManagement.Key, DependencyManagement.Values)]] = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks a bit like utility task we could move to CoursierSupport
(might not work as-is due to uses of Dep
) or CoursierModule
.
ivy"com.google.cloud:libraries-bom:26.50.0" | ||
) | ||
def ivyDeps = Agg( | ||
ivy"com.google.protobuf:protobuf-java:_" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we leave out the :_
and just use ivy"com.google.protobuf:protobuf-java"
? That would make our syntax consistent with Maven and Gradle, both of which just leave out the version segment without any placeholder
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Trying that
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Leaving out the version entirely would also be consistent with how we currently handle missing versions for contrib plugins. We could remove the special handling and instead auto-include a BOM configuring all contrib plugins.
@@ -0,0 +1,200 @@ | |||
package build |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this needs to be an integration test. Let's cover all the edge cases in a unit test, and have a single example test under example/{lang}lib/dependencies/
as both documentation for the happy paths and end-to-end verification that the feature works from a user point of view
If I'm not mistaken, there are a few places we should assert that the BOM takes effect end-to-end in a JavaModule/ScalaModule/KotlinModule:
compile
run
jar
assembly
launcher
publishArtifacts
We can run these tasks in the example module for each language and verify that they work (e.g. library resolves and the right version is used)
@@ -159,6 +194,109 @@ trait JavaModule | |||
*/ | |||
def runIvyDeps: T[Agg[Dep]] = Task { Agg.empty[Dep] } | |||
|
|||
def parentDep: T[Option[Dep]] = Task { None } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's call this bomParentDep
? To make it clear what it's used for
Left some comments. Some high level questions
|
They apply only to the module they're added to. If one wants the BOMs to apply to dependencies added in a second module that depends on the first, the BOMs have to be added to it too. But the BOMs of the first module still apply to dependencies pulled via it by the second module. |
(Last push may not need reviews yet. It needs a bit of cleanup, I'm just checking if the CI passes) |
This PR adds support for user-specified BOMs and dependency management in Mill.
BOM support allows users to pass the coordinates of an existing Maven "Bill of Material" (BOM), such as this one, that contains versions of dependencies, meant to override those pulled during dependency resolution. (They can also add exclusions to dependencies.)
It also allows users to specify the coordinates of a parent POM, which are taken into account just like a BOM:
(in line with
PublishModule#pomParentProject
that's been added recently)It allows users to specify "dependency management", which act like the dependencies listed in a BOM: versions in dependency management override those pulled transitively during dependency resolution, and exclusions in its dependencies are added to the same dependencies during dependency resolution.
BOM and dependency management also allow for "placeholder" versions: users can use
_
as version in theirivyDeps
, and the version of that dependency will be picked either in dependency management or in BOMs:A tricky aspect of that PR is that details about BOMs and dependency management have to be passed around via several paths:
moduleDeps
: BOMs and dependency management of module dependencies have to be applied to the dependencies of the module they come fromto transitive modules pulled via(worked out-of-the-box with the previous point, viamoduleDeps
: BOMs and dependency management of a module dependency have to be applied to the dependencies of modules they pull transitively (if A depends on B and B depends on C, from A, the BOMs and dep mgmt of B apply to C's dependencies too)transitiveIvyDeps
)ivy.xml
: when publishing to Ivy repositories (like duringpubishLocal
), BOMs and dep mgmt details need to be written in theivy.xml
file, so that they're taken into account when resolving that module from the Ivy repoFixes #1975