diff --git a/build.sc b/build.sc index d28672b388b..a067fbb89e7 100644 --- a/build.sc +++ b/build.sc @@ -934,7 +934,7 @@ object bsp extends MillPublishScalaModule with BuildInfo { } object worker extends MillPublishScalaModule { - def compileModuleDeps = Seq(bsp, scalalib, testrunner, runner) + def compileModuleDeps = Seq(bsp, scalalib, testrunner, runner) ++ scalalib.compileModuleDeps def ivyDeps = Agg(Deps.bsp4j, Deps.sbtTestInterface) } } diff --git a/example/basic/4-builtin-commands/build.sc b/example/basic/4-builtin-commands/build.sc index 379727e3e0e..8550d9a5a5e 100644 --- a/example/basic/4-builtin-commands/build.sc +++ b/example/basic/4-builtin-commands/build.sc @@ -129,9 +129,9 @@ Inputs: > ./mill show foo.compileClasspath [ - ".../foo/compile-resources", ".../org/scala-lang/scala-library/2.13.11/scala-library-2.13.11.jar", ... + ".../foo/compile-resources" ] */ @@ -150,9 +150,9 @@ Inputs: ".../foo/src" ], "foo.compileClasspath": [ - ".../foo/compile-resources", ".../org/scala-lang/scala-library/2.13.11/scala-library-2.13.11.jar", ... + ".../foo/compile-resources" ] } @@ -171,9 +171,9 @@ Inputs: ".../foo/src" ], "foo.compileClasspath": [ - ".../foo/compile-resources", ".../org/scala-lang/scala-library/2.13.11/scala-library-2.13.11.jar", ... + ".../foo/compile-resources" ] } diff --git a/example/cross/10-static-blog/build.sc b/example/cross/10-static-blog/build.sc index 139feb8c9ec..b9038fa0da4 100644 --- a/example/cross/10-static-blog/build.sc +++ b/example/cross/10-static-blog/build.sc @@ -3,7 +3,7 @@ // libraries - Commonmark and Scalatags - to deal with Markdown parsing and // HTML generation respectively: -import $ivy.`com.lihaoyi::scalatags:0.9.1`, scalatags.Text.all._ +import $ivy.`com.lihaoyi::scalatags:0.12.0`, scalatags.Text.all._ import $ivy.`com.atlassian.commonmark:commonmark:0.13.1` import org.commonmark.parser.Parser import org.commonmark.renderer.html.HtmlRenderer diff --git a/example/misc/3-import-file-ivy/build.sc b/example/misc/3-import-file-ivy/build.sc index 8212cf806cf..3ae2328d512 100644 --- a/example/misc/3-import-file-ivy/build.sc +++ b/example/misc/3-import-file-ivy/build.sc @@ -1,5 +1,5 @@ import mill._, scalalib._ -import $ivy.`com.lihaoyi::scalatags:0.8.2`, scalatags.Text.all._ +import $ivy.`com.lihaoyi::scalatags:0.12.0`, scalatags.Text.all._ import $file.scalaversion, scalaversion.myScalaVersion object foo extends RootModule with ScalaModule { diff --git a/example/misc/4-mill-build-folder/build.sc b/example/misc/4-mill-build-folder/build.sc index a7a1e92b563..5e153dbaa76 100644 --- a/example/misc/4-mill-build-folder/build.sc +++ b/example/misc/4-mill-build-folder/build.sc @@ -52,14 +52,14 @@ compiling 1 Scala source... > ./mill run Foo.value:

hello

-scalatagsVersion: 0.8.2 +scalatagsVersion: 0.12.0 > ./mill show assembly ".../out/assembly.dest/out.jar" > ./out/assembly.dest/out.jar # mac/linux Foo.value:

hello

-scalatagsVersion: 0.8.2 +scalatagsVersion: 0.12.0 */ diff --git a/example/misc/4-mill-build-folder/mill-build/build.sc b/example/misc/4-mill-build-folder/mill-build/build.sc index 01e68be833d..f7b600970de 100644 --- a/example/misc/4-mill-build-folder/mill-build/build.sc +++ b/example/misc/4-mill-build-folder/mill-build/build.sc @@ -1,7 +1,7 @@ import mill._, scalalib._ object millbuild extends MillBuildRootModule{ - val scalatagsVersion = "0.8.2" + val scalatagsVersion = "0.12.0" def ivyDeps = Agg(ivy"com.lihaoyi::scalatags:$scalatagsVersion") def generatedSources = T { diff --git a/example/thirdparty/jimfs/build.sc b/example/thirdparty/jimfs/build.sc index 980a997cbe8..839f3ff4353 100644 --- a/example/thirdparty/jimfs/build.sc +++ b/example/thirdparty/jimfs/build.sc @@ -1,6 +1,16 @@ import mill._, scalalib._, publish._ -object jimfs extends PublishModule with MavenModule{ +def sharedCompileIvyDeps = T{ + Agg( + ivy"com.google.auto.service:auto-service:1.0.1", + ivy"com.google.code.findbugs:jsr305:3.0.2", + ivy"org.checkerframework:checker-compat-qual:2.5.5", + ivy"com.ibm.icu:icu4j:73.1", + ) +} + + +object jimfs extends PublishModule with MavenModule { def publishVersion = "1.3.3.7" def pomSettings = PomSettings( @@ -12,21 +22,14 @@ object jimfs extends PublishModule with MavenModule{ developers = Nil ) - def ivyDeps = Agg( + def ivyDeps = sharedCompileIvyDeps() ++ Agg( ivy"com.google.guava:guava:31.1-android", ) - def compileIvyDeps = Agg( - ivy"com.google.auto.service:auto-service:1.0.1", - ivy"com.google.code.findbugs:jsr305:3.0.2", - ivy"org.checkerframework:checker-compat-qual:2.5.5", - ivy"com.ibm.icu:icu4j:73.1", - ) - def javacOptions = Seq("-processor", "com.google.auto.service.processor.AutoServiceProcessor") - object test extends MavenModuleTests{ - def ivyDeps = Agg( + object test extends MavenModuleTests { + def ivyDeps = sharedCompileIvyDeps() ++ Agg( ivy"junit:junit:4.13.2", ivy"com.google.guava:guava-testlib:31.1-android", ivy"com.google.truth:truth:1.1.3", diff --git a/scalalib/src/mill/scalalib/JavaModule.scala b/scalalib/src/mill/scalalib/JavaModule.scala index 7f4ac04a40f..b99e62c50ab 100644 --- a/scalalib/src/mill/scalalib/JavaModule.scala +++ b/scalalib/src/mill/scalalib/JavaModule.scala @@ -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 } /** @@ -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 @@ -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 } /** @@ -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 } /** @@ -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()) } @@ -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) } /** @@ -368,9 +368,8 @@ trait JavaModule */ @internal def bspLocalClasspath: T[Agg[UnresolvedPath]] = T { - (compileResources() ++ resources()).map(p => UnresolvedPath.ResolvedPath(p.path)) ++ Agg( - bspCompileClassesPath() - ) + (compileResources() ++ resources()).map(p => UnresolvedPath.ResolvedPath(p.path)) ++ + Agg(bspCompileClassesPath()) } /** @@ -379,10 +378,15 @@ 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. */ @@ -390,7 +394,7 @@ trait JavaModule @internal def bspCompileClasspath: T[Agg[UnresolvedPath]] = T { bspTransitiveCompileClasspath() ++ - (compileResources() ++ unmanagedClasspath() ++ resolvedIvyDeps()) + (localCompileClasspath() ++ resolvedIvyDeps()) .map(p => UnresolvedPath.ResolvedPath(p.path)) } @@ -398,9 +402,7 @@ trait JavaModule * Resolved dependencies based on [[transitiveIvyDeps]] and [[transitiveCompileIvyDeps]]. */ def resolvedIvyDeps: T[Agg[PathRef]] = T { - resolveDeps(T.task { - transitiveCompileIvyDeps() ++ transitiveIvyDeps() - })() + resolveDeps(T.task { transitiveCompileIvyDeps() ++ transitiveIvyDeps() })() } /** @@ -408,15 +410,11 @@ 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 { - resolveDeps(T.task { - runIvyDeps().map(bindDependency()) ++ transitiveIvyDeps() - })() + resolveDeps(T.task { runIvyDeps().map(bindDependency()) ++ transitiveIvyDeps() })() } /** @@ -424,8 +422,7 @@ trait JavaModule * necessary to run this module's code after compilation */ def runClasspath: T[Seq[PathRef]] = T { - localClasspath() ++ - upstreamAssemblyClasspath() + resolvedRunIvyDeps().toSeq ++ transitiveLocalClasspath() ++ localClasspath() } /** @@ -470,10 +467,7 @@ trait JavaModule * without those from upstream modules and dependencies */ def jar: T[PathRef] = T { - Jvm.createJar( - localClasspath().map(_.path).filter(os.exists), - manifest() - ) + Jvm.createJar(localClasspath().map(_.path).filter(os.exists), manifest()) } /** diff --git a/scalalib/test/src/mill/scalalib/HelloWorldTests.scala b/scalalib/test/src/mill/scalalib/HelloWorldTests.scala index 8b4a1beed1a..7498a8ced4e 100644 --- a/scalalib/test/src/mill/scalalib/HelloWorldTests.scala +++ b/scalalib/test/src/mill/scalalib/HelloWorldTests.scala @@ -329,6 +329,60 @@ object HelloWorldTests extends TestSuite { def checkedTuplePathRef: T[Tuple1[PathRef]] = T { Tuple1(mkDirWithFile().withRevalidateOnce) } } + object MultiModuleClasspaths extends HelloBase { + trait FooModule extends ScalaModule { + def scalaVersion = "2.13.12" + + def ivyDeps = Agg(ivy"com.lihaoyi::sourcecode:0.2.2") + def compileIvyDeps = Agg(ivy"com.lihaoyi::geny:0.4.2") + def runIvyDeps = Agg(ivy"com.lihaoyi::utest:0.7.2") + def unmanagedClasspath = T { Agg(PathRef(millSourcePath / "unmanaged")) } + } + trait BarModule extends ScalaModule { + def scalaVersion = "2.13.12" + + def ivyDeps = Agg(ivy"com.lihaoyi::sourcecode:0.2.1") + def compileIvyDeps = Agg(ivy"com.lihaoyi::geny:0.4.1") + def runIvyDeps = Agg(ivy"com.lihaoyi::utest:0.7.1") + def unmanagedClasspath = T { Agg(PathRef(millSourcePath / "unmanaged")) } + } + trait QuxModule extends ScalaModule { + def scalaVersion = "2.13.12" + + def ivyDeps = Agg(ivy"com.lihaoyi::sourcecode:0.2.0") + def compileIvyDeps = Agg(ivy"com.lihaoyi::geny:0.4.0") + def runIvyDeps = Agg(ivy"com.lihaoyi::utest:0.7.0") + def unmanagedClasspath = T { Agg(PathRef(millSourcePath / "unmanaged")) } + } + object ModMod extends Module { + object foo extends FooModule + object bar extends BarModule { + def moduleDeps = Seq(foo) + } + object qux extends QuxModule { + def moduleDeps = Seq(bar) + } + } + object ModCompile extends Module { + object foo extends FooModule + object bar extends BarModule { + def moduleDeps = Seq(foo) + } + object qux extends QuxModule { + def compileModuleDeps = Seq(bar) + } + } + object CompileMod extends Module { + object foo extends FooModule + object bar extends BarModule { + def compileModuleDeps = Seq(foo) + } + object qux extends QuxModule { + def moduleDeps = Seq(bar) + } + } + } + val resourcePath = os.pwd / "scalalib" / "test" / "resources" / "hello-world" def jarMainClass(jar: JarFile): Option[String] = { @@ -1341,5 +1395,223 @@ object HelloWorldTests extends TestSuite { } } + + "multiModuleClasspaths" - { + // Make sure that a bunch of modules dependent on each other has their various + // {classpaths,moduleDeps,ivyDeps}x{run,compile,normal} properly aggregated + def check( + eval: TestEvaluator, + mod: ScalaModule, + expectedRunClasspath: Seq[String], + expectedCompileClasspath: Seq[String], + expectedLocalClasspath: Seq[String] + ) = { + val Right((runClasspath, _)) = eval.apply(mod.runClasspath) + val Right((compileClasspath, _)) = eval.apply(mod.compileClasspath) + val Right((upstreamAssemblyClasspath, _)) = eval.apply(mod.upstreamAssemblyClasspath) + val Right((localClasspath, _)) = eval.apply(mod.localClasspath) + + val start = Set("org", "com", "MultiModuleClasspaths", "multiModuleClasspaths") + def simplify(cp: Seq[PathRef]) = { + cp.map(_.path.segments.dropWhile(!start.contains(_)).mkString("/")) + } + + val simplerRunClasspath = simplify(runClasspath) + val simplerCompileClasspath = simplify(compileClasspath.toSeq) + val simplerLocalClasspath = simplify(localClasspath) + + assert(expectedRunClasspath == simplerRunClasspath) + assert(expectedCompileClasspath == simplerCompileClasspath) + assert(expectedLocalClasspath == simplerLocalClasspath) + // invariant: the `upstreamAssemblyClasspath` used to make the `upstreamAssembly` + // and the `localClasspath` used to complete it to make the final `assembly` must + // have the same entries as the `runClasspath` used to execute things + assert(runClasspath == upstreamAssemblyClasspath.toSeq ++ localClasspath) + } + + "modMod" - workspaceTest(MultiModuleClasspaths) { eval => + // Make sure that `compileClasspath` has all the same things as `runClasspath`, + // but without the `/resources` + check( + eval, + MultiModuleClasspaths.ModMod.qux, + expectedRunClasspath = List( + // We pick up the oldest version of utest 0.7.0 from the current module, because + // utest is a `runIvyDeps` and not picked up transitively + "com/lihaoyi/utest_2.13/0.7.0/utest_2.13-0.7.0.jar", + // We pick up the newest version of sourcecode 0.2.4 from the upstream module, because + // sourcecode is a `ivyDeps` and `runIvyDeps` and those are picked up transitively + "com/lihaoyi/sourcecode_2.13/0.2.2/sourcecode_2.13-0.2.2.jar", + // + "org/scala-lang/scala-library/2.13.12/scala-library-2.13.12.jar", + "org/scala-sbt/test-interface/1.0/test-interface-1.0.jar", + "org/portable-scala/portable-scala-reflect_2.13/0.1.0/portable-scala-reflect_2.13-0.1.0.jar", + "org/scala-lang/scala-reflect/2.13.12/scala-reflect-2.13.12.jar", + // + "MultiModuleClasspaths/ModMod/bar/compile-resources", + "MultiModuleClasspaths/ModMod/bar/unmanaged", + "MultiModuleClasspaths/ModMod/bar/resources", + "multiModuleClasspaths/modMod/ModMod/bar/compile.dest/classes", + // + "MultiModuleClasspaths/ModMod/foo/compile-resources", + "MultiModuleClasspaths/ModMod/foo/unmanaged", + "MultiModuleClasspaths/ModMod/foo/resources", + "multiModuleClasspaths/modMod/ModMod/foo/compile.dest/classes", + // + "MultiModuleClasspaths/ModMod/qux/compile-resources", + "MultiModuleClasspaths/ModMod/qux/unmanaged", + "MultiModuleClasspaths/ModMod/qux/resources", + "multiModuleClasspaths/modMod/ModMod/qux/compile.dest/classes" + ), + expectedCompileClasspath = List( + // Make sure we only have geny 0.6.4 from the current module, and not newer + // versions pulled in by the upstream modules, because as `compileIvyDeps` it + // is not picked up transitively + "com/lihaoyi/geny_2.13/0.4.0/geny_2.13-0.4.0.jar", + "com/lihaoyi/sourcecode_2.13/0.2.2/sourcecode_2.13-0.2.2.jar", + // + "org/scala-lang/scala-library/2.13.12/scala-library-2.13.12.jar", + // + "MultiModuleClasspaths/ModMod/bar/compile-resources", + "MultiModuleClasspaths/ModMod/bar/unmanaged", + "multiModuleClasspaths/modMod/ModMod/bar/compile.dest/classes", + // + "MultiModuleClasspaths/ModMod/foo/compile-resources", + "MultiModuleClasspaths/ModMod/foo/unmanaged", + "multiModuleClasspaths/modMod/ModMod/foo/compile.dest/classes", + // + "MultiModuleClasspaths/ModMod/qux/compile-resources", + "MultiModuleClasspaths/ModMod/qux/unmanaged" + // We do not include `qux/compile.dest/classes` here, because this is the input + // that is required to compile `qux` in the first place + ), + expectedLocalClasspath = List( + "MultiModuleClasspaths/ModMod/qux/compile-resources", + "MultiModuleClasspaths/ModMod/qux/unmanaged", + "MultiModuleClasspaths/ModMod/qux/resources", + "multiModuleClasspaths/modMod/ModMod/qux/compile.dest/classes" + ) + ) + } + + "modCompile" - workspaceTest(MultiModuleClasspaths) { eval => + // Mostly the same as `modMod` above, but with the dependency + // from `qux` to `bar` being a `compileModuleDeps` + check( + eval, + MultiModuleClasspaths.ModCompile.qux, + expectedRunClasspath = List( + // `utest` is a `runIvyDeps` and not picked up transitively + "com/lihaoyi/utest_2.13/0.7.0/utest_2.13-0.7.0.jar", + // Because `sourcecode` comes from `ivyDeps`, and the dependency from + // `qux` to `bar` is a `compileModuleDeps`, we do not include its + // dependencies for `qux`'s `runClasspath` + "com/lihaoyi/sourcecode_2.13/0.2.0/sourcecode_2.13-0.2.0.jar", + // + "org/scala-lang/scala-library/2.13.12/scala-library-2.13.12.jar", + "org/scala-sbt/test-interface/1.0/test-interface-1.0.jar", + "org/portable-scala/portable-scala-reflect_2.13/0.1.0/portable-scala-reflect_2.13-0.1.0.jar", + "org/scala-lang/scala-reflect/2.13.12/scala-reflect-2.13.12.jar", + // + "MultiModuleClasspaths/ModCompile/bar/compile-resources", + "MultiModuleClasspaths/ModCompile/bar/unmanaged", + "MultiModuleClasspaths/ModCompile/bar/resources", + "multiModuleClasspaths/modCompile/ModCompile/bar/compile.dest/classes", + // + "MultiModuleClasspaths/ModCompile/foo/compile-resources", + "MultiModuleClasspaths/ModCompile/foo/unmanaged", + "MultiModuleClasspaths/ModCompile/foo/resources", + "multiModuleClasspaths/modCompile/ModCompile/foo/compile.dest/classes", + // + "MultiModuleClasspaths/ModCompile/qux/compile-resources", + "MultiModuleClasspaths/ModCompile/qux/unmanaged", + "MultiModuleClasspaths/ModCompile/qux/resources", + "multiModuleClasspaths/modCompile/ModCompile/qux/compile.dest/classes" + ), + expectedCompileClasspath = List( + "com/lihaoyi/geny_2.13/0.4.0/geny_2.13-0.4.0.jar", + // `sourcecode` is a `ivyDeps` from a `compileModuleDeps, which still + // gets picked up transitively, but only for compilation. This is necessary + // in order to make sure that we can correctly compile against the upstream + // module's classes. + "com/lihaoyi/sourcecode_2.13/0.2.2/sourcecode_2.13-0.2.2.jar", + // + "org/scala-lang/scala-library/2.13.12/scala-library-2.13.12.jar", + // + "MultiModuleClasspaths/ModCompile/bar/compile-resources", + "MultiModuleClasspaths/ModCompile/bar/unmanaged", + "multiModuleClasspaths/modCompile/ModCompile/bar/compile.dest/classes", + // + "MultiModuleClasspaths/ModCompile/foo/compile-resources", + "MultiModuleClasspaths/ModCompile/foo/unmanaged", + "multiModuleClasspaths/modCompile/ModCompile/foo/compile.dest/classes", + // + "MultiModuleClasspaths/ModCompile/qux/compile-resources", + "MultiModuleClasspaths/ModCompile/qux/unmanaged" + ), + expectedLocalClasspath = List( + "MultiModuleClasspaths/ModCompile/qux/compile-resources", + "MultiModuleClasspaths/ModCompile/qux/unmanaged", + "MultiModuleClasspaths/ModCompile/qux/resources", + "multiModuleClasspaths/modCompile/ModCompile/qux/compile.dest/classes" + ) + ) + } + + "compileMod" - workspaceTest(MultiModuleClasspaths) { eval => + // Both the `runClasspath` and `compileClasspath` should not have `foo` on the + // classpath, nor should it have the versions of libraries pulled in by `foo` + // (e.g. `sourcecode-0.2.4`), because it is a `compileModuleDep` of an upstream + // module and thus it is not transitive + check( + eval, + MultiModuleClasspaths.CompileMod.qux, + expectedRunClasspath = List( + "com/lihaoyi/utest_2.13/0.7.0/utest_2.13-0.7.0.jar", + // We pick up the version of `sourcecode` from `ivyDeps` from `bar` because + // we have a normal `moduleDeps` from `qux` to `bar`, but do not pick it up + // from `foo` because it's a `compileIvyDeps` from `bar` to `foo` and + // `compileIvyDeps` are not transitive + "com/lihaoyi/sourcecode_2.13/0.2.1/sourcecode_2.13-0.2.1.jar", + // + "org/scala-lang/scala-library/2.13.12/scala-library-2.13.12.jar", + "org/scala-sbt/test-interface/1.0/test-interface-1.0.jar", + "org/portable-scala/portable-scala-reflect_2.13/0.1.0/portable-scala-reflect_2.13-0.1.0.jar", + "org/scala-lang/scala-reflect/2.13.12/scala-reflect-2.13.12.jar", + // + "MultiModuleClasspaths/CompileMod/bar/compile-resources", + "MultiModuleClasspaths/CompileMod/bar/unmanaged", + "MultiModuleClasspaths/CompileMod/bar/resources", + "multiModuleClasspaths/compileMod/CompileMod/bar/compile.dest/classes", + // + "MultiModuleClasspaths/CompileMod/qux/compile-resources", + "MultiModuleClasspaths/CompileMod/qux/unmanaged", + "MultiModuleClasspaths/CompileMod/qux/resources", + "multiModuleClasspaths/compileMod/CompileMod/qux/compile.dest/classes" + ), + expectedCompileClasspath = List( + "com/lihaoyi/geny_2.13/0.4.0/geny_2.13-0.4.0.jar", + "com/lihaoyi/sourcecode_2.13/0.2.1/sourcecode_2.13-0.2.1.jar", + // + "org/scala-lang/scala-library/2.13.12/scala-library-2.13.12.jar", + // We do not include `foo`s compile output here, because `foo` is a + // `compileModuleDep` of `bar`, and `compileModuleDep`s are non-transitive + // + "MultiModuleClasspaths/CompileMod/bar/compile-resources", + "MultiModuleClasspaths/CompileMod/bar/unmanaged", + "multiModuleClasspaths/compileMod/CompileMod/bar/compile.dest/classes", + // + "MultiModuleClasspaths/CompileMod/qux/compile-resources", + "MultiModuleClasspaths/CompileMod/qux/unmanaged" + ), + expectedLocalClasspath = List( + "MultiModuleClasspaths/CompileMod/qux/compile-resources", + "MultiModuleClasspaths/CompileMod/qux/unmanaged", + "MultiModuleClasspaths/CompileMod/qux/resources", + "multiModuleClasspaths/compileMod/CompileMod/qux/compile.dest/classes" + ) + ) + } + } } }