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

Fix conflicting dependencies between upstream JavaModules #2735

Merged
merged 27 commits into from
Sep 14, 2023
Merged
96 changes: 49 additions & 47 deletions scalalib/src/mill/scalalib/JavaModule.scala
Original file line number Diff line number Diff line change
Expand Up @@ -128,12 +128,34 @@ trait JavaModule
/** The compile-only direct dependencies of this module. */
def compileModuleDeps: Seq[JavaModule] = Seq.empty

/** The direct and indirect dependencies of this module */
def recursiveModuleDeps: Seq[JavaModule] = {
moduleDeps.flatMap(_.transitiveModuleDeps).distinct
}

/**
* Like `recursiveModuleDeps` but also include the module itself,
* basically the modules whose classpath are needed at runtime
*/
def transitiveModuleDeps: Seq[JavaModule] = Seq(this) ++ recursiveModuleDeps

/**
* All direct and indirect module dependencies of this module, including
* compile-only dependencies: basically the modules whose classpath are needed
* at compile-time.
*
* Note that `compileModuleDeps` are defined to be non-transitive, so we only
* look at the direct `compileModuleDeps` when assembling this list
*/
def transitiveModuleCompileModuleDeps: Seq[JavaModule] = {
(moduleDeps ++ compileModuleDeps).flatMap(_.transitiveModuleDeps).distinct
}

/** The compile-only transitive ivy dependencies of this module and all it's upstream compile-only modules. */
def transitiveCompileIvyDeps: T[Agg[BoundDep]] = T {
// We never include compile-only dependencies transitively, but we must include normal transitive dependencies!
compileIvyDeps().map(bindDependency()) ++ T
.traverse(compileModuleDeps)(_.transitiveIvyDeps)()
.flatten
compileIvyDeps().map(bindDependency()) ++
T.traverse(compileModuleDeps)(_.transitiveIvyDeps)().flatten
}

/**
Expand All @@ -158,16 +180,6 @@ trait JavaModule
T.log.outputStream.println(asString)
}

/** The direct and indirect dependencies of this module */
def recursiveModuleDeps: Seq[JavaModule] = {
moduleDeps.flatMap(_.transitiveModuleDeps).distinct
}

/** Like `recursiveModuleDeps` but also include the module itself */
def transitiveModuleDeps: Seq[JavaModule] = {
Seq(this) ++ recursiveModuleDeps
}

/**
* Additional jars, classfiles or resources to add to the classpath directly
* from disk rather than being downloaded from Maven Central or other package
Expand All @@ -180,28 +192,22 @@ trait JavaModule
* This is calculated from [[ivyDeps]], [[mandatoryIvyDeps]] and recursively from [[moduleDeps]].
*/
def transitiveIvyDeps: T[Agg[BoundDep]] = T {
(ivyDeps() ++ mandatoryIvyDeps()).map(bindDependency()) ++ T.traverse(moduleDeps)(
_.transitiveIvyDeps
)().flatten
(ivyDeps() ++ mandatoryIvyDeps()).map(bindDependency()) ++
T.traverse(moduleDeps)(_.transitiveIvyDeps)().flatten
}

/**
* The upstream compilation output of all this module's upstream modules
*/
def upstreamCompileOutput: T[Seq[CompilationResult]] = T {
T.traverse((recursiveModuleDeps ++ compileModuleDeps.flatMap(
_.transitiveModuleDeps
)).distinct)(_.compile)
T.traverse(transitiveModuleCompileModuleDeps)(_.compile)
}

/**
* The transitive version of `localClasspath`
*/
def transitiveLocalClasspath: T[Agg[PathRef]] = T {
T.traverse(
(moduleDeps ++ compileModuleDeps).flatMap(_.transitiveModuleDeps).distinct
)(m => m.localClasspath)()
.flatten
T.traverse(transitiveModuleCompileModuleDeps)(_.localClasspath)().flatten
}

/**
Expand All @@ -210,20 +216,16 @@ trait JavaModule
// Keep in sync with [[transitiveLocalClasspath]]
@internal
def bspTransitiveLocalClasspath: T[Agg[UnresolvedPath]] = T {
T.traverse(
(moduleDeps ++ compileModuleDeps).flatMap(_.transitiveModuleDeps).distinct
)(m => m.bspLocalClasspath)()
.flatten
T.traverse(transitiveModuleCompileModuleDeps)(_.bspLocalClasspath)().flatten
}

/**
* The transitive version of `compileClasspath`
*/
def transitiveCompileClasspath: T[Agg[PathRef]] = T {
T.traverse(
(moduleDeps ++ compileModuleDeps).flatMap(_.transitiveModuleDeps).distinct
)(m => T.task { m.compileClasspath() ++ Agg(m.compile().classes) })()
.flatten
T.traverse(transitiveModuleCompileModuleDeps)(m =>
T.task { m.localCompileClasspath() ++ Agg(m.compile().classes) }
)().flatten
}

/**
Expand All @@ -232,9 +234,7 @@ trait JavaModule
// Keep in sync with [[transitiveCompileClasspath]]
@internal
def bspTransitiveCompileClasspath: T[Agg[UnresolvedPath]] = T {
T.traverse(
(moduleDeps ++ compileModuleDeps).flatMap(_.transitiveModuleDeps).distinct
)(m =>
T.traverse(transitiveModuleCompileModuleDeps)(m =>
T.task {
m.bspCompileClasspath() ++ Agg(m.bspCompileClassesPath())
}
Expand Down Expand Up @@ -355,11 +355,11 @@ trait JavaModule
}

/**
* The output classfiles/resources from this module, excluding upstream
* modules and third-party dependencies
* The *output* classfiles/resources from this module, used for execution,
* excluding upstream modules and third-party dependencies
*/
def localClasspath: T[Seq[PathRef]] = T {
compileResources() ++ resources() ++ Agg(compile().classes)
localCompileClasspath().toSeq ++ resources() ++ Agg(compile().classes)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change adds unmanagedClasspath to localClasspath. This is probably the right thing to do, since it means it'll probably be aggregated from upstream modules into upstreamAssemblyClasspath, where it wouldn't before

}

/**
Expand All @@ -379,18 +379,23 @@ trait JavaModule
*/
// Keep in sync with [[bspCompileClasspath]]
def compileClasspath: T[Agg[PathRef]] = T {
transitiveCompileClasspath() ++
compileResources() ++
unmanagedClasspath() ++
resolvedIvyDeps()
resolvedIvyDeps() ++ transitiveCompileClasspath() ++ localCompileClasspath()
}

/**
* The *input* classfiles/resources from this module, used during compilation,
* excluding upstream modules and third-party dependencies
*/
def localCompileClasspath: T[Agg[PathRef]] = T {
compileResources() ++ unmanagedClasspath()
}

/** Same as [[compileClasspath]], but does not trigger compilation targets, if possible. */
// Keep in sync with [[compileClasspath]]
@internal
def bspCompileClasspath: T[Agg[UnresolvedPath]] = T {
bspTransitiveCompileClasspath() ++
(compileResources() ++ unmanagedClasspath() ++ resolvedIvyDeps())
(localCompileClasspath() ++ resolvedIvyDeps())
.map(p => UnresolvedPath.ResolvedPath(p.path))
}

Expand All @@ -408,9 +413,7 @@ trait JavaModule
* assembly, but without this module's contribution
*/
def upstreamAssemblyClasspath: T[Agg[PathRef]] = T {
transitiveLocalClasspath() ++
unmanagedClasspath() ++
resolvedRunIvyDeps()
resolvedRunIvyDeps() ++ transitiveLocalClasspath()
}

def resolvedRunIvyDeps: T[Agg[PathRef]] = T {
Expand All @@ -424,8 +427,7 @@ trait JavaModule
* necessary to run this module's code after compilation
*/
def runClasspath: T[Seq[PathRef]] = T {
localClasspath() ++
upstreamAssemblyClasspath()
resolvedRunIvyDeps().toSeq ++ transitiveLocalClasspath() ++ localClasspath()
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We remove upstreamAssemblyClasspath so we can remove the dependency on unmanagedClasspath, since it now also comes from localClasspath, and we want to avoid duplicate classpath entries

}

/**
Expand Down
Loading
Loading