diff options
Diffstat (limited to 'scalalib')
18 files changed, 666 insertions, 306 deletions
diff --git a/scalalib/src/mill/scalalib/Dep.scala b/scalalib/src/mill/scalalib/Dep.scala index f20480b7..9719bd2d 100644 --- a/scalalib/src/mill/scalalib/Dep.scala +++ b/scalalib/src/mill/scalalib/Dep.scala @@ -3,23 +3,29 @@ import mill.util.JsonFormatters._ import upickle.default.{macroRW, ReadWriter => RW} sealed trait Dep { def configure(attributes: coursier.Attributes): Dep - def exclude(exclusions: (String, String)*): Dep = - this match { - case dep : Dep.Java => dep.copy(dep = dep.dep.copy(exclusions = dep.dep.exclusions ++ exclusions)) - case dep : Dep.Scala => dep.copy(dep = dep.dep.copy(exclusions = dep.dep.exclusions ++ exclusions)) - case dep : Dep.Point => dep.copy(dep = dep.dep.copy(exclusions = dep.dep.exclusions ++ exclusions)) - } + def force: Boolean + def forceVersion(): Dep = this match { + case dep : Dep.Java => dep.copy(force = true) + case dep : Dep.Scala => dep.copy(force = true) + case dep : Dep.Point => dep.copy(force = true) + } + def exclude(exclusions: (String, String)*): Dep = this match { + case dep : Dep.Java => dep.copy(dep = dep.dep.copy(exclusions = dep.dep.exclusions ++ exclusions)) + case dep : Dep.Scala => dep.copy(dep = dep.dep.copy(exclusions = dep.dep.exclusions ++ exclusions)) + case dep : Dep.Point => dep.copy(dep = dep.dep.copy(exclusions = dep.dep.exclusions ++ exclusions)) + } def excludeOrg(organizations: String*): Dep = exclude(organizations.map(_ -> "*"): _*) def excludeName(names: String*): Dep = exclude(names.map("*" -> _): _*) - def withConfiguration(configuration: String): Dep = - this match { - case dep : Dep.Java => dep.copy(dep = dep.dep.copy(configuration = configuration)) - case dep : Dep.Scala => dep.copy(dep = dep.dep.copy(configuration = configuration)) - case dep : Dep.Point => dep.copy(dep = dep.dep.copy(configuration = configuration)) - } + def withConfiguration(configuration: String): Dep = this match { + case dep : Dep.Java => dep.copy(dep = dep.dep.copy(configuration = configuration)) + case dep : Dep.Scala => dep.copy(dep = dep.dep.copy(configuration = configuration)) + case dep : Dep.Point => dep.copy(dep = dep.dep.copy(configuration = configuration)) + } } object Dep{ + val DefaultConfiguration = "default(compile)" + implicit def parse(signature: String) = { val parts = signature.split(';') val module = parts.head @@ -31,45 +37,45 @@ object Dep{ } } (module.split(':') match { - case Array(a, b, c) => Dep.Java(a, b, c, cross = false) - case Array(a, b, "", c) => Dep.Java(a, b, c, cross = true) - case Array(a, "", b, c) => Dep.Scala(a, b, c, cross = false) - case Array(a, "", b, "", c) => Dep.Scala(a, b, c, cross = true) - case Array(a, "", "", b, c) => Dep.Point(a, b, c, cross = false) - case Array(a, "", "", b, "", c) => Dep.Point(a, b, c, cross = true) + case Array(a, b, c) => Dep.Java(a, b, c, cross = false, force = false) + case Array(a, b, "", c) => Dep.Java(a, b, c, cross = true, force = false) + case Array(a, "", b, c) => Dep.Scala(a, b, c, cross = false, force = false) + case Array(a, "", b, "", c) => Dep.Scala(a, b, c, cross = true, force = false) + case Array(a, "", "", b, c) => Dep.Point(a, b, c, cross = false, force = false) + case Array(a, "", "", b, "", c) => Dep.Point(a, b, c, cross = true, force = false) case _ => throw new Exception(s"Unable to parse signature: [$signature]") }).configure(attributes = attributes) } def apply(org: String, name: String, version: String, cross: Boolean): Dep = { - this(coursier.Dependency(coursier.Module(org, name), version), cross) + this(coursier.Dependency(coursier.Module(org, name), version, DefaultConfiguration), cross) } - case class Java(dep: coursier.Dependency, cross: Boolean) extends Dep { + case class Java(dep: coursier.Dependency, cross: Boolean, force: Boolean) extends Dep { def configure(attributes: coursier.Attributes): Dep = copy(dep = dep.copy(attributes = attributes)) } object Java{ implicit def rw: RW[Java] = macroRW - def apply(org: String, name: String, version: String, cross: Boolean): Dep = { - Java(coursier.Dependency(coursier.Module(org, name), version), cross) + def apply(org: String, name: String, version: String, cross: Boolean, force: Boolean): Dep = { + Java(coursier.Dependency(coursier.Module(org, name), version, DefaultConfiguration), cross, force) } } - implicit def default(dep: coursier.Dependency): Dep = new Java(dep, false) - def apply(dep: coursier.Dependency, cross: Boolean) = Scala(dep, cross) - case class Scala(dep: coursier.Dependency, cross: Boolean) extends Dep { + implicit def default(dep: coursier.Dependency): Dep = new Java(dep, false, false) + def apply(dep: coursier.Dependency, cross: Boolean) = Scala(dep, cross, false) + case class Scala(dep: coursier.Dependency, cross: Boolean, force: Boolean) extends Dep { def configure(attributes: coursier.Attributes): Dep = copy(dep = dep.copy(attributes = attributes)) } object Scala{ implicit def rw: RW[Scala] = macroRW - def apply(org: String, name: String, version: String, cross: Boolean): Dep = { - Scala(coursier.Dependency(coursier.Module(org, name), version), cross) + def apply(org: String, name: String, version: String, cross: Boolean, force: Boolean): Dep = { + Scala(coursier.Dependency(coursier.Module(org, name), version, DefaultConfiguration), cross, force) } } - case class Point(dep: coursier.Dependency, cross: Boolean) extends Dep { + case class Point(dep: coursier.Dependency, cross: Boolean, force: Boolean) extends Dep { def configure(attributes: coursier.Attributes): Dep = copy(dep = dep.copy(attributes = attributes)) } object Point{ implicit def rw: RW[Point] = macroRW - def apply(org: String, name: String, version: String, cross: Boolean): Dep = { - Point(coursier.Dependency(coursier.Module(org, name), version), cross) + def apply(org: String, name: String, version: String, cross: Boolean, force: Boolean): Dep = { + Point(coursier.Dependency(coursier.Module(org, name), version, DefaultConfiguration), cross, force) } } implicit def rw = RW.merge[Dep]( diff --git a/scalalib/src/mill/scalalib/GenIdea.scala b/scalalib/src/mill/scalalib/GenIdeaImpl.scala index b118f29b..3e90f269 100644 --- a/scalalib/src/mill/scalalib/GenIdea.scala +++ b/scalalib/src/mill/scalalib/GenIdeaImpl.scala @@ -12,10 +12,10 @@ import mill.util.Strict.Agg import scala.util.Try -object GenIdeaModule extends ExternalModule { +object GenIdea extends ExternalModule { def idea(ev: Evaluator[Any]) = T.command{ - mill.scalalib.GenIdea( + mill.scalalib.GenIdeaImpl( implicitly, ev.rootModule, ev.rootModule.millDiscover @@ -25,7 +25,7 @@ object GenIdeaModule extends ExternalModule { implicit def millScoptEvaluatorReads[T] = new mill.main.EvaluatorScopt[T]() lazy val millDiscover = Discover[this.type] } -object GenIdea { +object GenIdeaImpl { def apply(ctx: Log with Home, rootModule: BaseModule, @@ -62,27 +62,31 @@ object GenIdea { fetchMillModules: Boolean = true): Seq[(RelPath, scala.xml.Node)] = { val modules = rootModule.millInternal.segmentsToModules.values - .collect{ case x: scalalib.ScalaModule => (x.millModuleSegments, x)} + .collect{ case x: scalalib.JavaModule => (x.millModuleSegments, x)} .toSeq val buildLibraryPaths = if (!fetchMillModules) Nil else sys.props.get("MILL_BUILD_LIBRARIES") match { - case Some(found) => Agg.from(found.split(',').map(Path(_)).distinct) + case Some(found) => found.split(',').map(Path(_)).distinct.toList case None => - val repos = modules.foldLeft(Set.empty[Repository]) { _ ++ _._2.scalaWorker.repositories } + val repos = modules.foldLeft(Set.empty[Repository]) { _ ++ _._2.repositories } val artifactNames = Seq("moduledefs", "core", "scalalib", "scalajslib") val Result.Success(res) = scalalib.Lib.resolveDependencies( repos.toList, - "2.12.4", + Lib.depToDependency(_, "2.12.4", ""), for(name <- artifactNames) yield ivy"com.lihaoyi::mill-$name:${sys.props("MILL_VERSION")}" ) - res.items.toSeq.map(_.path) + res.items.toList.map(_.path) } val resolved = for((path, mod) <- modules) yield { - val allIvyDeps = T.task{mod.transitiveIvyDeps() ++ mod.scalaLibraryIvyDeps() ++ mod.compileIvyDeps()} + val scalaLibraryIvyDeps = mod match{ + case x: ScalaModule => x.scalaLibraryIvyDeps + case _ => T.task{Nil} + } + val allIvyDeps = T.task{mod.transitiveIvyDeps() ++ scalaLibraryIvyDeps() ++ mod.compileIvyDeps()} val externalDependencies = T.task{ mod.resolveDeps(allIvyDeps)() ++ Task.traverse(mod.transitiveModuleDeps)(_.unmanagedClasspath)().flatten @@ -92,8 +96,10 @@ object GenIdea { mod.resolveDeps(allIvyDeps, sources = true)() } - val scalacPluginsIvyDeps = T.task{mod.scalacPluginIvyDeps()} - val scalacOptions = T.task{mod.scalacOptions()} + val (scalacPluginsIvyDeps, scalacOptions) = mod match{ + case mod: ScalaModule => T.task{mod.scalacPluginIvyDeps()} -> T.task{mod.scalacOptions()} + case _ => T.task(Loose.Agg[Dep]()) -> T.task(Seq()) + } val scalacPluginDependencies = T.task{ mod.resolveDeps(scalacPluginsIvyDeps)() } @@ -103,18 +109,27 @@ object GenIdea { val resolvedSp: Loose.Agg[PathRef] = evalOrElse(evaluator, scalacPluginDependencies, Loose.Agg.empty) val scalacOpts: Seq[String] = evalOrElse(evaluator, scalacOptions, Seq()) - (path, resolvedCp.map(_.path).filter(_.ext == "jar") ++ resolvedSrcs.map(_.path), mod, - resolvedSp.map(_.path).filter(_.ext == "jar"), scalacOpts) + ( + path, + resolvedCp.map(_.path).filter(_.ext == "jar") ++ resolvedSrcs.map(_.path), + mod, + resolvedSp.map(_.path).filter(_.ext == "jar"), + scalacOpts + ) } val moduleLabels = modules.map(_.swap).toMap val allResolved = resolved.flatMap(_._2) ++ buildLibraryPaths - val minResolvedLength = allResolved.map(_.segments.length).min - val commonPrefix = allResolved.map(_.segments.take(minResolvedLength)) - .transpose - .takeWhile(_.distinct.length == 1) - .length + val commonPrefix = + if (allResolved.isEmpty) 0 + else { + val minResolvedLength = allResolved.map(_.segments.length).min + allResolved.map(_.segments.take(minResolvedLength)) + .transpose + .takeWhile(_.distinct.length == 1) + .length + } // only resort to full long path names if the jar name is a duplicate val pathShortLibNameDuplicate = allResolved @@ -134,7 +149,7 @@ object GenIdea { .toMap val compilerSettings = resolved - .foldLeft(Map[(Loose.Agg[Path], Seq[String]), Vector[ScalaModule]]()) { + .foldLeft(Map[(Loose.Agg[Path], Seq[String]), Vector[JavaModule]]()) { (r, q) => val key = (q._4, q._5) r + (key -> (r.getOrElse(key, Vector()) :+ q._3)) @@ -190,7 +205,10 @@ object GenIdea { evaluator.outPath, mod.compile.ctx.segments ) - val Seq(scalaVersion: String) = evaluator.evaluate(Agg(mod.scalaVersion)).values + val scalaVersionOpt = mod match { + case x: ScalaModule => Some(evaluator.evaluate(Agg(x.scalaVersion)).values.head.asInstanceOf[String]) + case _ => None + } val generatedSourceOutPath = Evaluator.resolveDestPaths( evaluator.outPath, mod.generatedSources.ctx.segments @@ -198,7 +216,7 @@ object GenIdea { val elem = moduleXmlTemplate( mod.millModuleBasePath.value, - scalaVersion, + scalaVersionOpt, Strict.Agg.from(resourcesPathRefs.map(_.path)), Strict.Agg.from(normalSourcePaths), Strict.Agg.from(generatedSourcePaths), @@ -293,7 +311,7 @@ object GenIdea { </component> } def moduleXmlTemplate(basePath: Path, - scalaVersion: String, + scalaVersionOpt: Option[String], resourcePaths: Strict.Agg[Path], normalSourcePaths: Strict.Agg[Path], generatedSourcePaths: Strict.Agg[Path], @@ -326,7 +344,10 @@ object GenIdea { </content> <orderEntry type="inheritedJdk" /> <orderEntry type="sourceFolder" forTests="false" /> - <orderEntry type="library" name={s"scala-sdk-$scalaVersion"} level="application" /> + { + for(scalaVersion <- scalaVersionOpt.toSeq) + yield <orderEntry type="library" name={s"scala-sdk-$scalaVersion"} level="application" /> + } { for(name <- libNames.toSeq.sorted) @@ -340,7 +361,7 @@ object GenIdea { </component> </module> } - def scalaCompilerTemplate(settings: Map[(Loose.Agg[Path], Seq[String]), Seq[ScalaModule]]) = { + def scalaCompilerTemplate(settings: Map[(Loose.Agg[Path], Seq[String]), Seq[JavaModule]]) = { <project version="4"> <component name="ScalaCompilerConfiguration"> diff --git a/scalalib/src/mill/scalalib/JavaModule.scala b/scalalib/src/mill/scalalib/JavaModule.scala new file mode 100644 index 00000000..b8ae0fd4 --- /dev/null +++ b/scalalib/src/mill/scalalib/JavaModule.scala @@ -0,0 +1,284 @@ +package mill +package scalalib + + +import ammonite.ops._ +import coursier.Repository +import mill.define.Task +import mill.define.TaskModule +import mill.eval.{PathRef, Result} +import mill.modules.Jvm +import mill.modules.Jvm.{createAssembly, createJar} +import Lib._ +import mill.scalalib.publish.{Artifact, Scope} +import mill.util.Loose.Agg + +/** + * Core configuration required to compile a single Scala compilation target + */ +trait JavaModule extends mill.Module with TaskModule { outer => + + def defaultCommandName() = "run" + + def resolvePublishDependency: Task[Dep => publish.Dependency] = T.task{ + Artifact.fromDepJava(_: Dep) + } + def resolveCoursierDependency: Task[Dep => coursier.Dependency] = T.task{ + Lib.depToDependencyJava(_: Dep) + } + + def mainClass: T[Option[String]] = None + + def finalMainClassOpt: T[Either[String, String]] = T{ + mainClass() match{ + case Some(m) => Right(m) + case None => Left("No main class specified or found") + } + } + + def finalMainClass: T[String] = T{ + finalMainClassOpt() match { + case Right(main) => Result.Success(main) + case Left(msg) => Result.Failure(msg) + } + } + + def ivyDeps = T{ Agg.empty[Dep] } + def compileIvyDeps = T{ Agg.empty[Dep] } + def runIvyDeps = T{ Agg.empty[Dep] } + + def javacOptions = T{ Seq.empty[String] } + + def moduleDeps = Seq.empty[JavaModule] + + + def transitiveModuleDeps: Seq[JavaModule] = { + Seq(this) ++ moduleDeps.flatMap(_.transitiveModuleDeps).distinct + } + def unmanagedClasspath = T{ Agg.empty[PathRef] } + + + def transitiveIvyDeps: T[Agg[Dep]] = T{ + ivyDeps() ++ Task.traverse(moduleDeps)(_.transitiveIvyDeps)().flatten + } + + def upstreamCompileOutput = T{ + Task.traverse(moduleDeps)(_.compile) + } + + def transitiveLocalClasspath: T[Agg[PathRef]] = T{ + Task.traverse(moduleDeps)(m => + T.task{m.localClasspath() ++ m.transitiveLocalClasspath()} + )().flatten + } + + def mapDependencies(d: coursier.Dependency) = d + + def resolveDeps(deps: Task[Agg[Dep]], sources: Boolean = false) = T.task{ + resolveDependencies( + repositories, + resolveCoursierDependency().apply(_), + deps(), + sources, + mapDependencies = Some(mapDependencies) + ) + } + + + def repositories: Seq[Repository] = ScalaWorkerModule.repositories + + def platformSuffix = T{ "" } + + private val Milestone213 = raw"""2.13.(\d+)-M(\d+)""".r + + def prependShellScript: T[String] = T{ + mainClass() match{ + case None => "" + case Some(cls) => + val isWin = scala.util.Properties.isWin + mill.modules.Jvm.launcherUniversalScript( + cls, + Agg("$0"), Agg("%~dpnx0"), + forkArgs() + ) + } + } + + def sources = T.sources{ millSourcePath / 'src } + def resources = T.sources{ millSourcePath / 'resources } + def generatedSources = T{ Seq.empty[PathRef] } + def allSources = T{ sources() ++ generatedSources() } + + def allSourceFiles = T{ + for { + root <- allSources() + if exists(root.path) + path <- ls.rec(root.path) + if path.isFile && (path.ext == "scala" || path.ext == "java") + } yield PathRef(path) + } + + def compile: T[CompilationResult] = T{ + Lib.compileJava( + allSourceFiles().map(_.path.toIO).toArray, + compileClasspath().map(_.path.toIO).toArray, + javacOptions(), + upstreamCompileOutput() + ) + } + def localClasspath = T{ + resources() ++ Agg(compile().classes) + } + def compileClasspath = T{ + transitiveLocalClasspath() ++ + resources() ++ + unmanagedClasspath() ++ + resolveDeps(T.task{compileIvyDeps() ++ transitiveIvyDeps()})() + } + + def upstreamAssemblyClasspath = T{ + transitiveLocalClasspath() ++ + unmanagedClasspath() ++ + resolveDeps(T.task{runIvyDeps() ++ transitiveIvyDeps()})() + } + + def runClasspath = T{ + localClasspath() ++ + upstreamAssemblyClasspath() + } + + /** + * Build the assembly for upstream dependencies separate from the current classpath + * + * This should allow much faster assembly creation in the common case where + * upstream dependencies do not change + */ + def upstreamAssembly = T{ + createAssembly(upstreamAssemblyClasspath().map(_.path), mainClass()) + } + + def assembly = T{ + createAssembly( + Agg.from(localClasspath().map(_.path)), + mainClass(), + prependShellScript(), + Some(upstreamAssembly().path) + ) + } + + + def jar = T{ + createJar( + localClasspath().map(_.path).filter(exists), + mainClass() + ) + } + + def docJar = T[PathRef] { + val outDir = T.ctx().dest + + val javadocDir = outDir / 'javadoc + mkdir(javadocDir) + + val files = for{ + ref <- allSources() + if exists(ref.path) + p <- ls.rec(ref.path) + if p.isFile + } yield p.toNIO.toString + + val options = Seq("-d", javadocDir.toNIO.toString) + + if (files.nonEmpty) Jvm.baseInteractiveSubprocess( + commandArgs = Seq( + "javadoc" + ) ++ options ++ + Seq( + "-classpath", + compileClasspath() + .map(_.path) + .filter(_.ext != "pom") + .mkString(java.io.File.pathSeparator) + ) ++ + files.map(_.toString), + envArgs = Map(), + workingDir = T.ctx().dest + ) + + createJar(Agg(javadocDir))(outDir) + } + + def sourceJar = T { + createJar((allSources() ++ resources()).map(_.path).filter(exists)) + } + + def forkArgs = T{ Seq.empty[String] } + + def forkEnv = T{ sys.env.toMap } + + def launcher = T{ + Result.Success( + Jvm.createLauncher( + finalMainClass(), + runClasspath().map(_.path), + forkArgs() + ) + ) + } + + def ivyDepsTree(inverse: Boolean = false) = T.command { + val (flattened, resolution) = Lib.resolveDependenciesMetadata( + repositories, resolveCoursierDependency().apply(_), ivyDeps(), Some(mapDependencies) + ) + + println(coursier.util.Print.dependencyTree(flattened, resolution, + printExclusions = false, reverse = inverse)) + + Result.Success() + } + + def runLocal(args: String*) = T.command { + Jvm.runLocal( + finalMainClass(), + runClasspath().map(_.path), + args + ) + } + + def run(args: String*) = T.command{ + Jvm.interactiveSubprocess( + finalMainClass(), + runClasspath().map(_.path), + forkArgs(), + forkEnv(), + args, + workingDir = ammonite.ops.pwd + ) + } + + + def runMainLocal(mainClass: String, args: String*) = T.command { + Jvm.runLocal( + mainClass, + runClasspath().map(_.path), + args + ) + } + + def runMain(mainClass: String, args: String*) = T.command{ + Jvm.interactiveSubprocess( + mainClass, + runClasspath().map(_.path), + forkArgs(), + forkEnv(), + args, + workingDir = ammonite.ops.pwd + ) + } + + // publish artifact with name "mill_2.12.4" instead of "mill_2.12" + + def artifactName: T[String] = millModuleSegments.parts.mkString("-") + + def artifactId: T[String] = artifactName() +}
\ No newline at end of file diff --git a/scalalib/src/mill/scalalib/Lib.scala b/scalalib/src/mill/scalalib/Lib.scala index e6a7a255..7b4b5bdb 100644 --- a/scalalib/src/mill/scalalib/Lib.scala +++ b/scalalib/src/mill/scalalib/Lib.scala @@ -1,6 +1,9 @@ package mill package scalalib +import java.io.File +import javax.tools.ToolProvider + import ammonite.ops._ import ammonite.util.Util import coursier.{Cache, Dependency, Fetch, Repository, Resolution} @@ -15,6 +18,28 @@ object CompilationResult { case class CompilationResult(analysisFile: Path, classes: PathRef) object Lib{ + def compileJava(sources: Array[java.io.File], + classpath: Array[java.io.File], + javaOpts: Seq[String], + upstreamCompileOutput: Seq[CompilationResult]) + (implicit ctx: mill.util.Ctx) = { + val javac = ToolProvider.getSystemJavaCompiler() + + rm(ctx.dest / 'classes) + mkdir(ctx.dest / 'classes) + val cpArgs = + if(classpath.isEmpty) Seq() + else Seq("-cp", classpath.mkString(File.pathSeparator)) + + val args = Seq("-d", ctx.dest / 'classes) ++ cpArgs ++ javaOpts ++ sources + + javac.run( + ctx.log.inStream, ctx.log.outputStream, ctx.log.errorStream, + args.map(_.toString):_* + ) + if (ls(ctx.dest / 'classes).isEmpty) mill.eval.Result.Failure("Compilation Failed") + else mill.eval.Result.Success(CompilationResult(ctx.dest / 'zinc, PathRef(ctx.dest / 'classes))) + } private val ReleaseVersion = raw"""(\d+)\.(\d+)\.(\d+)""".r private val MinorSnapshotVersion = raw"""(\d+)\.(\d+)\.([1-9]\d*)-SNAPSHOT""".r @@ -34,17 +59,23 @@ object Lib{ .toIO } - def depToDependency(dep: Dep, scalaVersion: String, platformSuffix: String = ""): Dependency = + + def depToDependencyJava(dep: Dep, platformSuffix: String = ""): Dependency = { dep match { - case Dep.Java(dep, cross) => + case Dep.Java(dep, cross, force) => dep.copy( module = dep.module.copy( name = dep.module.name + - (if (!cross) "" else platformSuffix) + (if (!cross) "" else platformSuffix) ) ) - case Dep.Scala(dep, cross) => + } + } + def depToDependency(dep: Dep, scalaVersion: String, platformSuffix: String = ""): Dependency = + dep match { + case d: Dep.Java => depToDependencyJava(dep) + case Dep.Scala(dep, cross, force) => dep.copy( module = dep.module.copy( name = @@ -53,7 +84,7 @@ object Lib{ "_" + scalaBinaryVersion(scalaVersion) ) ) - case Dep.Point(dep, cross) => + case Dep.Point(dep, cross, force) => dep.copy( module = dep.module.copy( name = @@ -65,7 +96,29 @@ object Lib{ } + def resolveDependenciesMetadata(repositories: Seq[Repository], + depToDependency: Dep => coursier.Dependency, + deps: TraversableOnce[Dep], + mapDependencies: Option[Dependency => Dependency] = None) = { + val depSeq = deps.toSeq + val flattened = depSeq.map(depToDependency) + + val forceVersions = depSeq.filter(_.force) + .map(depToDependency) + .map(mapDependencies.getOrElse(identity[Dependency](_))) + .map{d => d.module -> d.version} + .toMap + val start = Resolution( + flattened.map(mapDependencies.getOrElse(identity[Dependency](_))).toSet, + forceVersions = forceVersions, + mapDependencies = mapDependencies + ) + + val fetch = Fetch.from(repositories, Cache.fetch()) + val resolution = start.process.run(fetch).unsafePerformSync + (flattened, resolution) + } /** * Resolve dependencies using Coursier. * @@ -74,16 +127,14 @@ object Lib{ * `import $ivy` syntax. */ def resolveDependencies(repositories: Seq[Repository], - scalaVersion: String, + depToDependency: Dep => coursier.Dependency, deps: TraversableOnce[Dep], - platformSuffix: String = "", - sources: Boolean = false): Result[Agg[PathRef]] = { + sources: Boolean = false, + mapDependencies: Option[Dependency => Dependency] = None): Result[Agg[PathRef]] = { - val flattened = deps.map(depToDependency(_, scalaVersion, platformSuffix)).toSet - val start = Resolution(flattened) - - val fetch = Fetch.from(repositories, Cache.fetch()) - val resolution = start.process.run(fetch).unsafePerformSync + val (_, resolution) = resolveDependenciesMetadata( + repositories, depToDependency, deps, mapDependencies + ) val errs = resolution.metadataErrors if(errs.nonEmpty) { val header = @@ -130,16 +181,17 @@ object Lib{ } } def scalaCompilerIvyDeps(scalaVersion: String) = Agg[Dep]( - ivy"org.scala-lang:scala-compiler:$scalaVersion", - ivy"org.scala-lang:scala-reflect:$scalaVersion" + ivy"org.scala-lang:scala-compiler:$scalaVersion".forceVersion(), + ivy"org.scala-lang:scala-reflect:$scalaVersion".forceVersion() ) def scalaRuntimeIvyDeps(scalaVersion: String) = Agg[Dep]( - ivy"org.scala-lang:scala-library:$scalaVersion" + ivy"org.scala-lang:scala-library:$scalaVersion".forceVersion() ) def compilerBridgeIvyDep(scalaVersion: String) = Dep.Point( coursier.Dependency(coursier.Module("com.lihaoyi", "mill-bridge"), "0.1", transitive = false), - cross = false + cross = false, + force = false ) } diff --git a/scalalib/src/mill/scalalib/PublishModule.scala b/scalalib/src/mill/scalalib/PublishModule.scala index 3cc9fd30..2ab81269 100644 --- a/scalalib/src/mill/scalalib/PublishModule.scala +++ b/scalalib/src/mill/scalalib/PublishModule.scala @@ -9,22 +9,20 @@ import mill.util.Loose.Agg /** * Configuration necessary for publishing a Scala module to Maven Central or similar */ -trait PublishModule extends ScalaModule { outer => +trait PublishModule extends JavaModule { outer => import mill.scalalib.publish._ override def moduleDeps = Seq.empty[PublishModule] def pomSettings: T[PomSettings] def publishVersion: T[String] - def artifactId: T[String] = T { s"${artifactName()}${artifactSuffix()}" } + def publishSelfDependency = T{ Artifact(pomSettings().organization, artifactId(), publishVersion()), } def publishXmlDeps = T.task{ - val ivyPomDeps = ivyDeps().map( - Artifact.fromDep(_, scalaVersion(), Lib.scalaBinaryVersion(scalaVersion())) - ) + val ivyPomDeps = ivyDeps().map(resolvePublishDependency().apply(_)) val modulePomDeps = Task.sequence(moduleDeps.map(_.publishSelfDependency))() ivyPomDeps ++ modulePomDeps.map(Dependency(_, Scope.Compile)) } diff --git a/scalalib/src/mill/scalalib/ScalaModule.scala b/scalalib/src/mill/scalalib/ScalaModule.scala index dc39a4a8..b98f248e 100644 --- a/scalalib/src/mill/scalalib/ScalaModule.scala +++ b/scalalib/src/mill/scalalib/ScalaModule.scala @@ -7,7 +7,7 @@ import mill.define.Task import mill.define.TaskModule import mill.eval.{PathRef, Result} import mill.modules.Jvm -import mill.modules.Jvm.{createAssembly, createJar, subprocess} +import mill.modules.Jvm.{createJar, subprocess} import Lib._ import mill.util.Loose.Agg import mill.util.DummyInputStream @@ -15,23 +15,29 @@ import mill.util.DummyInputStream /** * Core configuration required to compile a single Scala compilation target */ -trait ScalaModule extends mill.Module with TaskModule { outer => - def defaultCommandName() = "run" +trait ScalaModule extends JavaModule { outer => + def scalaWorker: ScalaWorkerModule = mill.scalalib.ScalaWorkerModule + trait Tests extends TestModule{ def scalaVersion = outer.scalaVersion() override def repositories = outer.repositories override def scalacPluginIvyDeps = outer.scalacPluginIvyDeps override def scalacOptions = outer.scalacOptions + override def javacOptions = outer.javacOptions override def scalaWorker = outer.scalaWorker override def moduleDeps = Seq(outer) } def scalaVersion: T[String] - def mainClass: T[Option[String]] = None + override def resolveCoursierDependency: Task[Dep => coursier.Dependency] = T.task{ + Lib.depToDependency(_: Dep, scalaVersion(), platformSuffix()) + } - def scalaWorker: ScalaWorkerModule = mill.scalalib.ScalaWorkerModule + override def resolvePublishDependency: Task[Dep => publish.Dependency] = T.task{ + publish.Artifact.fromDep(_: Dep, scalaVersion(), Lib.scalaBinaryVersion(scalaVersion())) + } - def finalMainClassOpt: T[Either[String, String]] = T{ + override def finalMainClassOpt: T[Either[String, String]] = T{ mainClass() match{ case Some(m) => Right(m) case None => @@ -47,58 +53,12 @@ trait ScalaModule extends mill.Module with TaskModule { outer => } } - def finalMainClass: T[String] = T{ - finalMainClassOpt() match { - case Right(main) => Result.Success(main) - case Left(msg) => Result.Failure(msg) - } - } - def ivyDeps = T{ Agg.empty[Dep] } - def compileIvyDeps = T{ Agg.empty[Dep] } def scalacPluginIvyDeps = T{ Agg.empty[Dep] } - def runIvyDeps = T{ Agg.empty[Dep] } def scalacOptions = T{ Seq.empty[String] } - def javacOptions = T{ Seq.empty[String] } - - def moduleDeps = Seq.empty[ScalaModule] - - - def transitiveModuleDeps: Seq[ScalaModule] = { - Seq(this) ++ moduleDeps.flatMap(_.transitiveModuleDeps).distinct - } - def unmanagedClasspath = T{ Agg.empty[PathRef] } - - - def transitiveIvyDeps: T[Agg[Dep]] = T{ - ivyDeps() ++ Task.traverse(moduleDeps)(_.transitiveIvyDeps)().flatten - } - - def upstreamCompileOutput = T{ - Task.traverse(moduleDeps)(_.compile) - } - - def transitiveLocalClasspath: T[Agg[PathRef]] = T{ - Task.traverse(moduleDeps)(m => - T.task{m.localClasspath() ++ m.transitiveLocalClasspath()} - )().flatten - } - - def resolveDeps(deps: Task[Agg[Dep]], sources: Boolean = false) = T.task{ - resolveDependencies( - repositories, - scalaVersion(), - deps(), - platformSuffix(), - sources - ) - } - - def repositories: Seq[Repository] = scalaWorker.repositories - - def platformSuffix = T{ "" } + override def repositories: Seq[Repository] = scalaWorker.repositories private val Milestone213 = raw"""2.13.(\d+)-M(\d+)""".r @@ -110,7 +70,7 @@ trait ScalaModule extends mill.Module with TaskModule { outer => resolveDependencies( repositories, - scalaVersion0, + Lib.depToDependency(_, scalaVersion0, platformSuffix()), Seq(ivy"org.scala-sbt::compiler-bridge:1.1.0"), sources = true ).map(_.find(_.path.last == s"compiler-bridge_${scalaBinaryVersion0}-1.1.0-sources.jar").map(_.path).get) @@ -129,37 +89,20 @@ trait ScalaModule extends mill.Module with TaskModule { outer => T.task{scalaCompilerIvyDeps(scalaVersion()) ++ scalaRuntimeIvyDeps(scalaVersion())} )() } - - - def prependShellScript: T[String] = T{ - mainClass() match{ - case None => "" - case Some(cls) => - val isWin = scala.util.Properties.isWin - mill.modules.Jvm.launcherShellScript( - isWin, - cls, - Agg(if (isWin) "%~dp0%~nx0" else "$0"), - forkArgs() - ) - } + override def compileClasspath = T{ + transitiveLocalClasspath() ++ + resources() ++ + unmanagedClasspath() ++ + resolveDeps(T.task{compileIvyDeps() ++ scalaLibraryIvyDeps() ++ transitiveIvyDeps()})() } - def sources = T.sources{ millSourcePath / 'src } - def resources = T.sources{ millSourcePath / 'resources } - def generatedSources = T{ Seq.empty[PathRef] } - def allSources = T{ sources() ++ generatedSources() } - - def allSourceFiles = T{ - for { - root <- allSources() - if exists(root.path) - path <- ls.rec(root.path) - if path.isFile && (path.ext == "scala" || path.ext == "java") - } yield PathRef(path) + override def upstreamAssemblyClasspath = T{ + transitiveLocalClasspath() ++ + unmanagedClasspath() ++ + resolveDeps(T.task{runIvyDeps() ++ scalaLibraryIvyDeps() ++ transitiveIvyDeps()})() } - def compile: T[CompilationResult] = T.persistent{ + override def compile: T[CompilationResult] = T.persistent{ scalaWorker.worker().compileScala( scalaVersion(), allSourceFiles().map(_.path), @@ -172,55 +115,8 @@ trait ScalaModule extends mill.Module with TaskModule { outer => upstreamCompileOutput() ) } - def localClasspath = T{ - resources() ++ Agg(compile().classes) - } - def compileClasspath = T{ - transitiveLocalClasspath() ++ - resources() ++ - unmanagedClasspath() ++ - resolveDeps(T.task{compileIvyDeps() ++ scalaLibraryIvyDeps() ++ transitiveIvyDeps()})() - } - - def upstreamAssemblyClasspath = T{ - transitiveLocalClasspath() ++ - unmanagedClasspath() ++ - resolveDeps(T.task{runIvyDeps() ++ scalaLibraryIvyDeps() ++ transitiveIvyDeps()})() - } - - def runClasspath = T{ - localClasspath() ++ - upstreamAssemblyClasspath() - } - - /** - * Build the assembly for upstream dependencies separate from the current classpath - * - * This should allow much faster assembly creation in the common case where - * upstream dependencies do not change - */ - def upstreamAssembly = T{ - createAssembly(upstreamAssemblyClasspath().map(_.path), mainClass()) - } - - def assembly = T{ - createAssembly( - Agg.from(localClasspath().map(_.path)), - mainClass(), - prependShellScript(), - Some(upstreamAssembly().path) - ) - } - - - def jar = T{ - createJar( - localClasspath().map(_.path).filter(exists), - mainClass() - ) - } - def docJar = T { + override def docJar = T { val outDir = T.ctx().dest val javadocDir = outDir / 'javadoc @@ -233,7 +129,8 @@ trait ScalaModule extends mill.Module with TaskModule { outer => if p.isFile } yield p.toNIO.toString - val options = Seq("-d", javadocDir.toNIO.toString, "-usejavacp") + val pluginOptions = scalacPluginClasspath().map(pluginPathRef => s"-Xplugin:${pluginPathRef.path}") + val options = Seq("-d", javadocDir.toNIO.toString, "-usejavacp") ++ pluginOptions if (files.nonEmpty) subprocess( "scala.tools.nsc.ScalaDoc", @@ -244,77 +141,6 @@ trait ScalaModule extends mill.Module with TaskModule { outer => createJar(Agg(javadocDir))(outDir) } - def sourceJar = T { - createJar((allSources() ++ resources()).map(_.path).filter(exists)) - } - - def forkArgs = T{ Seq.empty[String] } - - def forkEnv = T{ sys.env.toMap } - - def launcher = T{ - Result.Success( - Jvm.createLauncher( - finalMainClass(), - runClasspath().map(_.path), - forkArgs() - ) - ) - } - - def ivyDepsTree(inverse: Boolean = false) = T.command { - import coursier.{Cache, Fetch, Resolution} - - val flattened = ivyDeps().map(depToDependency(_, scalaVersion(), platformSuffix())).toSeq - val start = Resolution(flattened.toSet) - val fetch = Fetch.from(repositories, Cache.fetch()) - val resolution = start.process.run(fetch).unsafePerformSync - - println(coursier.util.Print.dependencyTree(flattened, resolution, - printExclusions = false, reverse = inverse)) - - Result.Success() - } - - def runLocal(args: String*) = T.command { - Jvm.runLocal( - finalMainClass(), - runClasspath().map(_.path), - args - ) - } - - def run(args: String*) = T.command{ - Jvm.interactiveSubprocess( - finalMainClass(), - runClasspath().map(_.path), - forkArgs(), - forkEnv(), - args, - workingDir = ammonite.ops.pwd - ) - } - - - def runMainLocal(mainClass: String, args: String*) = T.command { - Jvm.runLocal( - mainClass, - runClasspath().map(_.path), - args - ) - } - - def runMain(mainClass: String, args: String*) = T.command{ - Jvm.interactiveSubprocess( - mainClass, - runClasspath().map(_.path), - forkArgs(), - forkEnv(), - args, - workingDir = ammonite.ops.pwd - ) - } - def console() = T.command{ if (T.ctx().log.inStream == DummyInputStream){ Result.Failure("repl needs to be run with the -i/--interactive flag") @@ -330,15 +156,22 @@ trait ScalaModule extends mill.Module with TaskModule { outer => } def ammoniteReplClasspath = T{ - resolveDeps(T.task{Agg(ivy"com.lihaoyi:::ammonite:1.1.0-7-33b728c")})() + localClasspath() ++ + transitiveLocalClasspath() ++ + unmanagedClasspath() ++ + resolveDeps(T.task{ + runIvyDeps() ++ scalaLibraryIvyDeps() ++ transitiveIvyDeps() ++ + Agg(ivy"com.lihaoyi:::ammonite:1.1.0-12-f07633d") + })() } + def repl() = T.command{ if (T.ctx().log.inStream == DummyInputStream){ Result.Failure("repl needs to be run with the -i/--interactive flag") }else{ Jvm.interactiveSubprocess( mainClass = "ammonite.Main", - classPath = runClasspath().map(_.path) ++ ammoniteReplClasspath().map(_.path), + classPath = ammoniteReplClasspath().map(_.path), mainArgs = Nil, workingDir = pwd ) @@ -354,9 +187,11 @@ trait ScalaModule extends mill.Module with TaskModule { outer => if (crossFullScalaVersion()) scalaVersion() else Lib.scalaBinaryVersion(scalaVersion()) } - def artifactName: T[String] = millModuleSegments.parts.mkString("-") - def artifactSuffix: T[String] = T { s"_${artifactScalaVersion()}" } + def artifactSuffix: T[String] = s"_${artifactScalaVersion()}" + + override def artifactId: T[String] = artifactName() + artifactSuffix() + } diff --git a/scalalib/src/mill/scalalib/ScalaWorkerApi.scala b/scalalib/src/mill/scalalib/ScalaWorkerApi.scala index 9739089a..f6500ae8 100644 --- a/scalalib/src/mill/scalalib/ScalaWorkerApi.scala +++ b/scalalib/src/mill/scalalib/ScalaWorkerApi.scala @@ -29,7 +29,7 @@ trait ScalaWorkerModule extends mill.Module{ } else { resolveDependencies( repositories, - "2.12.4", + Lib.depToDependency(_, "2.12.4", ""), Seq(ivy"com.lihaoyi::mill-scalaworker:${sys.props("MILL_VERSION")}") ).map(_.map(_.path)) } @@ -48,7 +48,7 @@ trait ScalaWorkerModule extends mill.Module{ def compilerInterfaceClasspath = T{ resolveDependencies( repositories, - "2.12.4", + Lib.depToDependency(_, "2.12.4", ""), Seq(ivy"org.scala-sbt:compiler-interface:1.1.0") ) } @@ -56,6 +56,7 @@ trait ScalaWorkerModule extends mill.Module{ } trait ScalaWorkerApi { + def compileScala(scalaVersion: String, sources: Agg[Path], compilerBridgeSources: Path, diff --git a/scalalib/src/mill/scalalib/publish/Ivy.scala b/scalalib/src/mill/scalalib/publish/Ivy.scala index 3b271fa8..22e26ff6 100644 --- a/scalalib/src/mill/scalalib/publish/Ivy.scala +++ b/scalalib/src/mill/scalalib/publish/Ivy.scala @@ -42,10 +42,10 @@ object Ivy { private def renderDependency(dep: Dependency) = { if (dep.exclusions.isEmpty) - <dependency org={dep.artifact.group} name={dep.artifact.id} rev={dep.artifact.version} conf={s"${dep.scope}->default(${dep.configuration.getOrElse("compile")})"} /> + <dependency org={dep.artifact.group} name={dep.artifact.id} rev={dep.artifact.version} conf={s"${scopeToConf(dep.scope)}->${dep.configuration.getOrElse("default(compile)")}"} /> else - <dependency org={dep.artifact.group} name={dep.artifact.id} rev={dep.artifact.version} conf={s"${dep.scope}->default(${dep.configuration.getOrElse("compile")})"}> - {dep.exclusions.map(ex => <exclude org={ex._1} name={ex._2} matcher="exact"/>).toSeq} + <dependency org={dep.artifact.group} name={dep.artifact.id} rev={dep.artifact.version} conf={s"${scopeToConf(dep.scope)}->${dep.configuration.getOrElse("default(compile)")}"}> + {dep.exclusions.map(ex => <exclude org={ex._1} name={ex._2} matcher="exact"/>)} </dependency> } diff --git a/scalalib/src/mill/scalalib/publish/Pom.scala b/scalalib/src/mill/scalalib/publish/Pom.scala index 3c8ba4dc..84cf0632 100644 --- a/scalalib/src/mill/scalalib/publish/Pom.scala +++ b/scalalib/src/mill/scalalib/publish/Pom.scala @@ -108,7 +108,7 @@ object Pom { <groupId>{ex._1}</groupId> <artifactId>{ex._2}</artifactId> </exclude> - )}.toSeq + )} </exclusions> {scope} </dependency> diff --git a/scalalib/src/mill/scalalib/publish/settings.scala b/scalalib/src/mill/scalalib/publish/settings.scala index 34f7e7ad..2cd92eb2 100644 --- a/scalalib/src/mill/scalalib/publish/settings.scala +++ b/scalalib/src/mill/scalalib/publish/settings.scala @@ -7,19 +7,23 @@ case class Artifact(group: String, id: String, version: String) { } object Artifact { - - def fromDep(dep: Dep, - scalaFull: String, - scalaBin: String): Dependency = { + def fromDepJava(dep: Dep) = { dep match { - case Dep.Java(dep, cross) => + case Dep.Java(dep, cross, force) => Dependency( Artifact(dep.module.organization, dep.module.name, dep.version), Scope.Compile, - if (dep.configuration == "" ) None else Some(dep.configuration), + if (dep.configuration == "") None else Some(dep.configuration), dep.exclusions.toList ) - case Dep.Scala(dep, cross) => + } + } + def fromDep(dep: Dep, + scalaFull: String, + scalaBin: String): Dependency = { + dep match { + case d: Dep.Java => fromDepJava(d) + case Dep.Scala(dep, cross, force) => Dependency( Artifact( dep.module.organization, @@ -30,7 +34,7 @@ object Artifact { if (dep.configuration == "") None else Some(dep.configuration), dep.exclusions.toList ) - case Dep.Point(dep, cross) => + case Dep.Point(dep, cross, force) => Dependency( Artifact( dep.module.organization, diff --git a/scalalib/test/resources/hello-java/core/src/hello/Core.java b/scalalib/test/resources/hello-java/core/src/hello/Core.java new file mode 100644 index 00000000..bef1a5a0 --- /dev/null +++ b/scalalib/test/resources/hello-java/core/src/hello/Core.java @@ -0,0 +1,8 @@ +package hello; + +public class Core{ + public static String msg(){ + return "Hello World"; + } + +}
\ No newline at end of file diff --git a/scalalib/test/resources/hello-java/main/src/hello/Main.java b/scalalib/test/resources/hello-java/main/src/hello/Main.java new file mode 100644 index 00000000..44b927bd --- /dev/null +++ b/scalalib/test/resources/hello-java/main/src/hello/Main.java @@ -0,0 +1,7 @@ +package hello; + +public class Main{ + public static void main(String[] args){ + System.out.println(Core.msg() + " " + args[0]); + } +}
\ No newline at end of file diff --git a/scalalib/test/resources/hello-world-macros/core/src/Main.scala b/scalalib/test/resources/hello-world-macros/core/src/Main.scala new file mode 100644 index 00000000..20924a60 --- /dev/null +++ b/scalalib/test/resources/hello-world-macros/core/src/Main.scala @@ -0,0 +1,5 @@ +import monocle.macros._ +@Lenses case class Foo(bar: String) +object Main extends App { + println(Foo.bar.get(Foo("bar"))) +} diff --git a/scalalib/test/src/mill/scalalib/GenIdeaTests.scala b/scalalib/test/src/mill/scalalib/GenIdeaTests.scala index 0f776802..51665867 100644 --- a/scalalib/test/src/mill/scalalib/GenIdeaTests.scala +++ b/scalalib/test/src/mill/scalalib/GenIdeaTests.scala @@ -23,7 +23,7 @@ object GenIdeaTests extends TestSuite { 'genIdeaTests - { val pp = new scala.xml.PrettyPrinter(999, 4) - val layout = GenIdea.xmlFileLayout( + val layout = GenIdeaImpl.xmlFileLayout( helloWorldEvaluator.evaluator, HelloWorld, ("JDK_1_8", "1.8 (1)"), fetchMillModules = false) diff --git a/scalalib/test/src/mill/scalalib/HelloJavaTests.scala b/scalalib/test/src/mill/scalalib/HelloJavaTests.scala new file mode 100644 index 00000000..7794d5a5 --- /dev/null +++ b/scalalib/test/src/mill/scalalib/HelloJavaTests.scala @@ -0,0 +1,60 @@ +package mill.scalalib + + +import ammonite.ops.{%, %%, cp, ls, mkdir, pwd, rm, up} +import ammonite.ops.ImplicitWd._ +import mill.util.{TestEvaluator, TestUtil} +import utest._ +import utest.framework.TestPath + + +object HelloJavaTests extends TestSuite { + + object HelloJava extends TestUtil.BaseModule{ + def millSourcePath = TestUtil.getSrcPathBase() / millOuterCtx.enclosing.split('.') + object core extends JavaModule + object main extends JavaModule{ + def moduleDeps = Seq(core) + } + } + val resourcePath = pwd / 'scalalib / 'test / 'resources / "hello-java" + + def init()(implicit tp: TestPath) = { + val eval = new TestEvaluator(HelloJava) + rm(HelloJava.millSourcePath) + rm(eval.outPath) + mkdir(HelloJava.millSourcePath / up) + cp(resourcePath, HelloJava.millSourcePath) + eval + } + def tests: Tests = Tests { + 'scalaVersion - { + val eval = init() + + val Right((res1, n1)) = eval.apply(HelloJava.core.compile) + val Right((res2, 0)) = eval.apply(HelloJava.core.compile) + val Right((res3, n2)) = eval.apply(HelloJava.main.compile) + + assert( + res1 == res2, + n1 != 0, + n2 != 0, + ls.rec(res1.classes.path).exists(_.last == "Core.class"), + !ls.rec(res1.classes.path).exists(_.last == "Main.class"), + ls.rec(res3.classes.path).exists(_.last == "Main.class"), + !ls.rec(res3.classes.path).exists(_.last == "Core.class") + ) + } + 'docJar - { + val eval = init() + + val Right((ref1, _)) = eval.apply(HelloJava.core.docJar) + val Right((ref2, _)) = eval.apply(HelloJava.main.docJar) + + assert( + %%("jar", "tf", ref1.path).out.lines.contains("hello/Core.html"), + %%("jar", "tf", ref2.path).out.lines.contains("hello/Main.html") + ) + } + } +} diff --git a/scalalib/test/src/mill/scalalib/HelloWorldTests.scala b/scalalib/test/src/mill/scalalib/HelloWorldTests.scala index 74078221..10f936f0 100644 --- a/scalalib/test/src/mill/scalalib/HelloWorldTests.scala +++ b/scalalib/test/src/mill/scalalib/HelloWorldTests.scala @@ -98,6 +98,38 @@ object HelloWorldTests extends TestSuite { override def ivyDeps = Agg(ivy"com.lihaoyi::sourcecode:0.1.4") } } + + object HelloWorldTypeLevel extends HelloBase{ + object foo extends ScalaModule { + def scalaVersion = "2.11.8" + override def mapDependencies(d: coursier.Dependency) = { + val artifacts = Set("scala-library", "scala-compiler", "scala-reflect") + if (d.module.organization != "org.scala-lang" || !artifacts(d.module.name)) d + else d.copy(module = d.module.copy(organization = "org.typelevel")) + } + + def ivyDeps = Agg( + ivy"com.github.julien-truffaut::monocle-macro::1.4.0" + ) + def scalacPluginIvyDeps = super.scalacPluginIvyDeps() ++ Agg( + ivy"org.scalamacros:::paradise:2.1.0" + ) + } + } + + object HelloWorldMacros extends HelloBase{ + object core extends ScalaModule { + def scalaVersion = "2.12.4" + + def ivyDeps = Agg( + ivy"com.github.julien-truffaut::monocle-macro::1.4.0" + ) + def scalacPluginIvyDeps = super.scalacPluginIvyDeps() ++ Agg( + ivy"org.scalamacros:::paradise:2.1.0" + ) + } + } + val resourcePath = pwd / 'scalalib / 'test / 'resources / "hello-world" def jarMainClass(jar: JarFile): Option[String] = { @@ -438,7 +470,42 @@ object HelloWorldTests extends TestSuite { !result2.exists(_.path.last == "sourcecode_2.12-0.1.3.jar") ) } - } - + 'typeLevel - workspaceTest(HelloWorldTypeLevel){ eval => + val classPathsToCheck = Seq( + HelloWorldTypeLevel.foo.runClasspath, + HelloWorldTypeLevel.foo.ammoniteReplClasspath, + HelloWorldTypeLevel.foo.compileClasspath + ) + for(cp <- classPathsToCheck){ + val Right((result, _)) = eval.apply(cp) + assert( + // Make sure every relevant piece org.scala-lang has been substituted for org.typelevel + !result.map(_.toString).exists(x => + x.contains("scala-lang") && + (x.contains("scala-library") || x.contains("scala-compiler") || x.contains("scala-reflect")) + ), + result.map(_.toString).exists(x => x.contains("typelevel") && x.contains("scala-library")) + ) + } + } + 'macros - { + // make sure macros are applied when compiling/running + 'runMain - workspaceTest( + HelloWorldMacros, + resourcePath = pwd / 'scalalib / 'test / 'resources / "hello-world-macros" + ){ eval => + val Right((_, evalCount)) = eval.apply(HelloWorldMacros.core.runMain("Main")) + assert(evalCount > 0) + } + // make sure macros are applied when compiling during scaladoc generation + 'docJar - workspaceTest( + HelloWorldMacros, + resourcePath = pwd / 'scalalib / 'test / 'resources / "hello-world-macros" + ){ eval => + val Right((_, evalCount)) = eval.apply(HelloWorldMacros.core.docJar) + assert(evalCount > 0) + } + } + } } diff --git a/scalalib/test/src/mill/scalalib/ResolveDepsTests.scala b/scalalib/test/src/mill/scalalib/ResolveDepsTests.scala index b1fcec3e..4240478c 100644 --- a/scalalib/test/src/mill/scalalib/ResolveDepsTests.scala +++ b/scalalib/test/src/mill/scalalib/ResolveDepsTests.scala @@ -10,7 +10,11 @@ import utest._ object ResolveDepsTests extends TestSuite { val repos = Seq(Cache.ivy2Local, MavenRepository("https://repo1.maven.org/maven2")) - def evalDeps(deps: Agg[Dep]): Result[Agg[PathRef]] = Lib.resolveDependencies(repos, "2.12.4", deps) + def evalDeps(deps: Agg[Dep]): Result[Agg[PathRef]] = Lib.resolveDependencies( + repos, + Lib.depToDependency(_, "2.12.4", ""), + deps + ) val tests = Tests { 'resolveValidDeps - { @@ -26,6 +30,14 @@ object ResolveDepsTests extends TestSuite { assert(paths.items.next.path.toString.contains("natives-macos")) } + 'resolveTransitiveRuntimeDeps - { + val deps = Agg(ivy"org.mockito:mockito-core:2.7.22") + val Success(paths) = evalDeps(deps) + assert(paths.nonEmpty) + assert(paths.exists(_.path.toString.contains("objenesis"))) + assert(paths.exists(_.path.toString.contains("byte-buddy"))) + } + 'excludeTransitiveDeps - { val deps = Agg(ivy"com.lihaoyi::pprint:0.5.3".exclude("com.lihaoyi" -> "fansi_2.12")) val Success(paths) = evalDeps(deps) diff --git a/scalalib/test/src/mill/scalalib/publish/IvyTests.scala b/scalalib/test/src/mill/scalalib/publish/IvyTests.scala index 0f275dd9..d187f969 100644 --- a/scalalib/test/src/mill/scalalib/publish/IvyTests.scala +++ b/scalalib/test/src/mill/scalalib/publish/IvyTests.scala @@ -26,9 +26,9 @@ object IvyTests extends TestSuite { 'topLevel - { val info = singleNode(fullIvy \ "info") assert( - singleAttr(info, "organisation") == artifact.group - , singleAttr(info, "module") == artifact.id - , singleAttr(info, "revision") == artifact.version + singleAttr(info, "organisation") == artifact.group, + singleAttr(info, "module") == artifact.id, + singleAttr(info, "revision") == artifact.version ) } @@ -40,10 +40,10 @@ object IvyTests extends TestSuite { dependencies.zipWithIndex.foreach { case (dep, index) => assert( - singleAttr(dep, "org") == ivyDeps(index).artifact.group - , singleAttr(dep, "name") == ivyDeps(index).artifact.id - , singleAttr(dep, "rev") == ivyDeps(index).artifact.version - , (dep \ "exclude").zipWithIndex forall { case (exclude, j) => + singleAttr(dep, "org") == ivyDeps(index).artifact.group, + singleAttr(dep, "name") == ivyDeps(index).artifact.id, + singleAttr(dep, "rev") == ivyDeps(index).artifact.version, + (dep \ "exclude").zipWithIndex forall { case (exclude, j) => singleAttr(exclude, "org") == ivyDeps(index).exclusions(j)._1 && singleAttr(exclude, "name") == ivyDeps(index).exclusions(j)._2 } |