Skip to content
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 support #3924

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions build.mill
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ object Settings {
val docUrl = "https://mill-build.org"
// the exact branches containing a doc root
val docBranches = Seq()
// the exact tags containing a doc root. Publish docs for
// the exact tags containing a doc root. Publish docs for
// the last point version in each minor release series
val legacyDocTags: Seq[String] = Seq(
"0.9.12",
Expand Down Expand Up @@ -114,7 +114,7 @@ object Deps {
val asmTree = ivy"org.ow2.asm:asm-tree:9.7.1"
val bloopConfig = ivy"ch.epfl.scala::bloop-config:1.5.5"

val coursier = ivy"io.get-coursier::coursier:2.1.14"
val coursier = ivy"io.get-coursier::coursier:2.1.17"
val coursierInterface = ivy"io.get-coursier:interface:1.0.22"

val cask = ivy"com.lihaoyi::cask:0.9.4"
Expand Down
34 changes: 34 additions & 0 deletions integration/feature/bom/resources/build.mill
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package build

import mill._
import mill.scalalib._

object `google-cloud-java` extends JavaModule {
def bomDeps = Agg(
ivy"com.google.cloud:libraries-bom:26.50.0"
)
def ivyDeps = Agg(
ivy"com.google.protobuf:protobuf-java:_"
)
}

object `google-cloud-java-no-bom` extends JavaModule {
def ivyDeps = Agg(
ivy"com.google.protobuf:protobuf-java:_"
)
}

object `google-cloud-scala` extends JavaModule {
def bomDeps = Agg(
ivy"com.google.cloud:libraries-bom:26.50.0"
)
def ivyDeps = Agg(
ivy"com.thesamet.scalapb:scalapbc_2.13:0.9.8"
)
}

object `google-cloud-scala-no-bom` extends JavaModule {
def ivyDeps = Agg(
ivy"com.thesamet.scalapb:scalapbc_2.13:0.9.8"
)
}
58 changes: 58 additions & 0 deletions integration/feature/bom/src/BomTests.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package mill.integration

import mill.testkit.{IntegrationTester, UtestIntegrationTestSuite}
import utest._

object BomTests extends UtestIntegrationTestSuite {

def tests: Tests = Tests {

def expectedProtobufJavaVersion = "4.28.3"

def compileClasspathFileNames(tester: IntegrationTester, moduleName: String): Seq[String] = {
import tester._
val res = eval(
("show", s"$moduleName.compileClasspath"),
stderr = os.Inherit,
check = true
)
ujson.read(res.out).arr.map(v => os.Path(v.str.split(":").last).last).toSeq
}

test("googleCloudJavaCheck") - integrationTest { tester =>
import tester._

val res = eval(
("show", "google-cloud-java-no-bom.compileClasspath"),
check = false
)
assert(
res.err.contains(
"not found: https://repo1.maven.org/maven2/com/google/protobuf/protobuf-java/_/protobuf-java-_.pom"
)
)
}

test("googleCloudJava") - integrationTest { tester =>
val compileClasspathFileNames0 = compileClasspathFileNames(tester, "google-cloud-java")
assert(compileClasspathFileNames0.contains(s"protobuf-java-$expectedProtobufJavaVersion.jar"))
}

test("googleCloudScalaCheck") - integrationTest { tester =>
val compileClasspathFileNames0 =
compileClasspathFileNames(tester, "google-cloud-scala-no-bom")
assert(
compileClasspathFileNames0.exists(v => v.startsWith("protobuf-java-") && v.endsWith(".jar"))
)
assert(
!compileClasspathFileNames0.contains(s"protobuf-java-$expectedProtobufJavaVersion.jar")
)
}

test("googleCloudScala") - integrationTest { tester =>
val compileClasspathFileNames0 = compileClasspathFileNames(tester, "google-cloud-scala")
assert(compileClasspathFileNames0.contains(s"protobuf-java-$expectedProtobufJavaVersion.jar"))
}

}
}
25 changes: 15 additions & 10 deletions main/util/src/mill/util/CoursierSupport.scala
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ trait CoursierSupport {
ctx: Option[mill.api.Ctx.Log] = None,
coursierCacheCustomizer: Option[FileCache[Task] => FileCache[Task]] = None,
resolveFilter: os.Path => Boolean = _ => true,
artifactTypes: Option[Set[Type]] = None
artifactTypes: Option[Set[Type]] = None,
bomDeps: IterableOnce[Dependency] = Nil
): Result[Agg[PathRef]] = {
def isLocalTestDep(dep: Dependency): Option[Seq[PathRef]] = {
val org = dep.module.organization.value
Expand All @@ -61,12 +62,8 @@ trait CoursierSupport {
classpathResourceText.map(_.linesIterator.map(s => PathRef(os.Path(s))).toSeq)
}

val (localTestDeps, remoteDeps) = deps.iterator.toSeq.partitionMap(d =>
isLocalTestDep(d) match {
case None => Right(d)
case Some(vs) => Left(vs)
}
)
val (localTestDeps, remoteDeps) =
deps.iterator.toSeq.partitionMap(d => isLocalTestDep(d).toLeft(d))

val resolutionRes = resolveDependenciesMetadataSafe(
repositories,
Expand All @@ -75,7 +72,8 @@ trait CoursierSupport {
mapDependencies,
customizer,
ctx,
coursierCacheCustomizer
coursierCacheCustomizer,
bomDeps
)

resolutionRes.flatMap { resolution =>
Expand Down Expand Up @@ -159,7 +157,8 @@ trait CoursierSupport {
mapDependencies,
customizer,
ctx,
coursierCacheCustomizer
coursierCacheCustomizer,
Nil
)
(deps0, res.getOrThrow)
}
Expand All @@ -171,13 +170,18 @@ trait CoursierSupport {
mapDependencies: Option[Dependency => Dependency] = None,
customizer: Option[Resolution => Resolution] = None,
ctx: Option[mill.api.Ctx.Log] = None,
coursierCacheCustomizer: Option[FileCache[Task] => FileCache[Task]] = None
coursierCacheCustomizer: Option[FileCache[Task] => FileCache[Task]] = None,
bomDeps: IterableOnce[Dependency] = Nil
): Result[Resolution] = {

val rootDeps = deps.iterator
.map(d => mapDependencies.fold(d)(_.apply(d)))
.toSeq

val bomDeps0 = bomDeps.iterator
.map(d => mapDependencies.fold(d)(_.apply(d)))
.toSeq

val forceVersions = force.iterator
.map(mapDependencies.getOrElse(identity[Dependency](_)))
.map { d => d.module -> d.version }
Expand All @@ -191,6 +195,7 @@ trait CoursierSupport {
val resolve = Resolve()
.withCache(coursierCache0)
.withDependencies(rootDeps)
.withBomDependencies(bomDeps0)
.withRepositories(repositories)
.withResolutionParams(resolutionParams)
.withMapDependenciesOpt(mapDependencies)
Expand Down
19 changes: 15 additions & 4 deletions scalalib/src/mill/scalalib/CoursierModule.scala
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,14 @@ trait CoursierModule extends mill.Module {
def resolveDeps(
deps: Task[Agg[BoundDep]],
sources: Boolean = false,
artifactTypes: Option[Set[Type]] = None
artifactTypes: Option[Set[Type]] = None,
bomDeps: Task[Agg[BoundDep]] = Task.Anon(Agg.empty[BoundDep])
): Task[Agg[PathRef]] =
Task.Anon {
Lib.resolveDependencies(
repositories = repositoriesTask(),
deps = deps(),
bomDeps = bomDeps(),
sources = sources,
artifactTypes = artifactTypes,
mapDependencies = Some(mapDependencies()),
Expand All @@ -74,7 +76,7 @@ trait CoursierModule extends mill.Module {
deps: Task[Agg[BoundDep]],
sources: Boolean
): Task[Agg[PathRef]] =
resolveDeps(deps, sources, None)
resolveDeps(deps, sources, None, Task.Anon(Agg.empty[BoundDep]))

/**
* Map dependencies before resolving them.
Expand Down Expand Up @@ -148,11 +150,13 @@ object CoursierModule {
def resolveDeps[T: CoursierModule.Resolvable](
deps: IterableOnce[T],
sources: Boolean = false,
artifactTypes: Option[Set[coursier.Type]] = None
artifactTypes: Option[Set[coursier.Type]] = None,
bomDeps: IterableOnce[T] = Nil
): Agg[PathRef] = {
Lib.resolveDependencies(
repositories = repositories,
deps = deps.map(implicitly[CoursierModule.Resolvable[T]].bind(_, bind)),
bomDeps = bomDeps.map(implicitly[CoursierModule.Resolvable[T]].bind(_, bind)),
sources = sources,
artifactTypes = artifactTypes,
mapDependencies = mapDependencies,
Expand All @@ -162,12 +166,19 @@ object CoursierModule {
).getOrThrow
}

def resolveDeps[T: CoursierModule.Resolvable](
deps: IterableOnce[T],
sources: Boolean,
artifactTypes: Option[Set[coursier.Type]]
): Agg[PathRef] =
resolveDeps(deps, sources, artifactTypes, Nil)

@deprecated("Use the override accepting artifactTypes", "Mill after 0.12.0-RC3")
def resolveDeps[T: CoursierModule.Resolvable](
deps: IterableOnce[T],
sources: Boolean
): Agg[PathRef] =
resolveDeps(deps, sources, None)
resolveDeps(deps, sources, None, Nil)
}

sealed trait Resolvable[T] {
Expand Down
20 changes: 15 additions & 5 deletions scalalib/src/mill/scalalib/JavaModule.scala
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,12 @@ trait JavaModule
*/
def runIvyDeps: T[Agg[Dep]] = Task { Agg.empty[Dep] }

/**
* Any BOM dependencies you want to add to this Module, in the format
* ivy"org:name:version"
*/
def bomDeps: T[Agg[Dep]] = Task { Agg.empty[Dep] }

/**
* Default artifact types to fetch and put in the classpath. Add extra types
* here if you'd like fancy artifact extensions to be fetched.
Expand Down Expand Up @@ -327,7 +333,7 @@ trait JavaModule
* This is calculated from [[ivyDeps]], [[mandatoryIvyDeps]] and recursively from [[moduleDeps]].
*/
def transitiveIvyDeps: T[Agg[BoundDep]] = Task {
(ivyDeps() ++ mandatoryIvyDeps()).map(bindDependency()) ++
allIvyDeps().map(bindDependency()) ++
T.traverse(moduleDepsChecked)(_.transitiveIvyDeps)().flatten
}

Expand Down Expand Up @@ -604,7 +610,8 @@ trait JavaModule
def resolvedIvyDeps: T[Agg[PathRef]] = Task {
defaultResolver().resolveDeps(
transitiveCompileIvyDeps() ++ transitiveIvyDeps(),
artifactTypes = Some(artifactTypes())
artifactTypes = Some(artifactTypes()),
bomDeps = bomDeps().map(bindDependency())
)
}

Expand All @@ -619,7 +626,8 @@ trait JavaModule
def resolvedRunIvyDeps: T[Agg[PathRef]] = Task {
defaultResolver().resolveDeps(
transitiveRunIvyDeps() ++ transitiveIvyDeps(),
artifactTypes = Some(artifactTypes())
artifactTypes = Some(artifactTypes()),
bomDeps = bomDeps().map(bindDependency())
)
}

Expand Down Expand Up @@ -1096,13 +1104,15 @@ trait JavaModule
Task.Anon {
defaultResolver().resolveDeps(
transitiveCompileIvyDeps() ++ transitiveIvyDeps(),
sources = true
sources = true,
bomDeps = bomDeps().map(bindDependency())
)
},
Task.Anon {
defaultResolver().resolveDeps(
transitiveRunIvyDeps() ++ transitiveIvyDeps(),
sources = true
sources = true,
bomDeps = bomDeps().map(bindDependency())
)
}
)
Expand Down
4 changes: 4 additions & 0 deletions scalalib/src/mill/scalalib/JsonFormatters.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ trait JsonFormatters {
implicit lazy val configurationFormat: RW[coursier.core.Configuration] = upickle.default.macroRW
implicit lazy val typeFormat: RW[coursier.core.Type] = upickle.default.macroRW
implicit lazy val classifierFormat: RW[coursier.core.Classifier] = upickle.default.macroRW
implicit lazy val depMgmtKeyFormat: RW[coursier.core.DependencyManagement.Key] =
upickle.default.macroRW
implicit lazy val depMgmtValuesFormat: RW[coursier.core.DependencyManagement.Values] =
upickle.default.macroRW

}
object JsonFormatters extends JsonFormatters
13 changes: 9 additions & 4 deletions scalalib/src/mill/scalalib/Lib.scala
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ object Lib {
ctx: Option[Ctx.Log] = None,
coursierCacheCustomizer: Option[
coursier.cache.FileCache[Task] => coursier.cache.FileCache[Task]
] = None
] = None,
bomDeps: IterableOnce[BoundDep] = Nil
): Result[Resolution] = {
val depSeq = deps.iterator.toSeq
mill.util.Jvm.resolveDependenciesMetadataSafe(
Expand All @@ -69,7 +70,8 @@ object Lib {
mapDependencies = mapDependencies,
customizer = customizer,
ctx = ctx,
coursierCacheCustomizer = coursierCacheCustomizer
coursierCacheCustomizer = coursierCacheCustomizer,
bomDeps = bomDeps.iterator.toSeq.map(_.dep)
)
}

Expand All @@ -90,12 +92,14 @@ object Lib {
coursierCacheCustomizer: Option[
coursier.cache.FileCache[Task] => coursier.cache.FileCache[Task]
] = None,
artifactTypes: Option[Set[Type]] = None
artifactTypes: Option[Set[Type]] = None,
bomDeps: IterableOnce[BoundDep] = Nil
): Result[Agg[PathRef]] = {
val depSeq = deps.iterator.toSeq
mill.util.Jvm.resolveDependencies(
repositories = repositories,
deps = depSeq.map(_.dep),
bomDeps = bomDeps.iterator.map(_.dep).toSeq,
force = depSeq.filter(_.force).map(_.dep),
sources = sources,
artifactTypes = artifactTypes,
Expand Down Expand Up @@ -126,7 +130,8 @@ object Lib {
customizer,
ctx,
coursierCacheCustomizer,
None
None,
Nil
)

def scalaCompilerIvyDeps(scalaOrganization: String, scalaVersion: String): Loose.Agg[Dep] =
Expand Down
7 changes: 6 additions & 1 deletion scalalib/src/mill/scalalib/PublishModule.scala
Original file line number Diff line number Diff line change
Expand Up @@ -82,14 +82,19 @@ trait PublishModule extends JavaModule { outer =>
compileModulePomDeps.map(Dependency(_, Scope.Provided))
}

def publishXmlBomDeps: Task[Agg[Dependency]] = Task.Anon {
bomDeps().map(resolvePublishDependency.apply().apply(_))
}

def pom: T[PathRef] = Task {
val pom = Pom(
artifactMetadata(),
publishXmlDeps(),
artifactId(),
pomSettings(),
publishProperties(),
packagingType = pomPackagingType
packagingType = pomPackagingType,
bomDependencies = publishXmlBomDeps()
)
val pomPath = T.dest / s"${artifactId()}-${publishVersion()}.pom"
os.write.over(pomPath, pom)
Expand Down
Loading
Loading