diff options
author | Li Haoyi <haoyi.sg@gmail.com> | 2018-01-20 03:49:17 -0800 |
---|---|---|
committer | Li Haoyi <haoyi.sg@gmail.com> | 2018-01-20 03:49:17 -0800 |
commit | d14f56a3fd881f809e58783c49866d1491a5f3fe (patch) | |
tree | 4a9f93d3d7f69211aa444ce15837fe6e79b9db7f /scalalib/src/main/scala | |
parent | aebd7a144fab5bdb95f6ee4f4bc170be65cd0549 (diff) | |
download | mill-d14f56a3fd881f809e58783c49866d1491a5f3fe.tar.gz mill-d14f56a3fd881f809e58783c49866d1491a5f3fe.tar.bz2 mill-d14f56a3fd881f809e58783c49866d1491a5f3fe.zip |
Swap over to simplified Mill module/source layout from SBT's
Removes a lot of useless folders and gives us a chance to exercise this simplified layout. Support for the SBT layout is still verified by our integration tests
Diffstat (limited to 'scalalib/src/main/scala')
17 files changed, 0 insertions, 1672 deletions
diff --git a/scalalib/src/main/scala/mill/scalalib/Dep.scala b/scalalib/src/main/scala/mill/scalalib/Dep.scala deleted file mode 100644 index aa301fcb..00000000 --- a/scalalib/src/main/scala/mill/scalalib/Dep.scala +++ /dev/null @@ -1,44 +0,0 @@ -package mill.scalalib -import mill.util.JsonFormatters._ -import upickle.default.{macroRW, ReadWriter => RW} -sealed trait Dep -object Dep{ - - implicit def parse(signature: String) = { - signature.split(':') match{ - case Array(a, b, c) => Dep.Java(a, b, c) - case Array(a, "", b, c) => Dep.Scala(a, b, c) - case Array(a, "", "", b, c) => Dep.Point(a, b, c) - case _ => throw new Exception(s"Unable to parse signature: [$signature]") - } - } - def apply(org: String, name: String, version: String): Dep = { - this(coursier.Dependency(coursier.Module(org, name), version)) - } - case class Java(dep: coursier.Dependency) extends Dep - object Java{ - implicit def rw: RW[Java] = macroRW - def apply(org: String, name: String, version: String): Dep = { - Java(coursier.Dependency(coursier.Module(org, name), version)) - } - } - implicit def default(dep: coursier.Dependency): Dep = new Java(dep) - def apply(dep: coursier.Dependency) = Scala(dep) - case class Scala(dep: coursier.Dependency) extends Dep - object Scala{ - implicit def rw: RW[Scala] = macroRW - def apply(org: String, name: String, version: String): Dep = { - Scala(coursier.Dependency(coursier.Module(org, name), version)) - } - } - case class Point(dep: coursier.Dependency) extends Dep - object Point{ - implicit def rw: RW[Point] = macroRW - def apply(org: String, name: String, version: String): Dep = { - Point(coursier.Dependency(coursier.Module(org, name), version)) - } - } - implicit def rw = RW.merge[Dep]( - Java.rw, Scala.rw, Point.rw - ) -}
\ No newline at end of file diff --git a/scalalib/src/main/scala/mill/scalalib/GenIdea.scala b/scalalib/src/main/scala/mill/scalalib/GenIdea.scala deleted file mode 100644 index 4496c8c6..00000000 --- a/scalalib/src/main/scala/mill/scalalib/GenIdea.scala +++ /dev/null @@ -1,203 +0,0 @@ -package mill.scalalib - -import ammonite.ops._ -import mill.define.{Segment, Segments, Target} -import mill.eval.{Evaluator, PathRef, RootModuleLoader} -import mill.scalalib -import mill.util.Ctx.{LoaderCtx, LogCtx} -import mill.util.{Loose, PrintLogger, Strict} -import mill.util.Strict.Agg - -object GenIdea { - - def apply()(implicit ctx: LoaderCtx with LogCtx): Unit = { - val rootModule = ctx.load(RootModuleLoader) - val pp = new scala.xml.PrettyPrinter(999, 4) - rm! pwd/".idea" - rm! pwd/".idea_modules" - - - val evaluator = new Evaluator(pwd / 'out, pwd, rootModule , ctx.log) - - for((relPath, xml) <- xmlFileLayout(evaluator, rootModule)){ - write.over(pwd/relPath, pp.format(xml)) - } - } - - def xmlFileLayout[T](evaluator: Evaluator[T], rootModule: mill.Module): Seq[(RelPath, scala.xml.Node)] = { - - - val modules = rootModule.millInternal.segmentsToModules.values.collect{case x: scalalib.ScalaModule => (x.millModuleSegments, x)}.toSeq - - val resolved = for((path, mod) <- modules) yield { - val Seq(resolvedCp: Loose.Agg[PathRef], resolvedSrcs: Loose.Agg[PathRef]) = - evaluator.evaluate(Agg(mod.externalCompileDepClasspath, mod.externalCompileDepSources)) - .values - - (path, resolvedCp.map(_.path).filter(_.ext == "jar") ++ resolvedSrcs.map(_.path), mod) - } - val moduleLabels = modules.map(_.swap).toMap - - val fixedFiles = Seq( - Tuple2(".idea"/"misc.xml", miscXmlTemplate()), - Tuple2( - ".idea"/"modules.xml", - allModulesXmlTemplate( - for((path, mod) <- modules) - yield moduleName(path) - ) - ), - Tuple2(".idea_modules"/"root.iml", rootXmlTemplate()) - ) - - val allResolved = resolved.flatMap(_._2).distinct - val minResolvedLength = allResolved.map(_.segments.length).min - val commonPrefix = allResolved.map(_.segments.take(minResolvedLength)) - .transpose - .takeWhile(_.distinct.length == 1) - .length - - val pathToLibName = allResolved - .map{p => (p, p.segments.drop(commonPrefix).mkString("_"))} - .toMap - - val libraries = allResolved.map{path => - val url = "jar://" + path + "!/" - val name = pathToLibName(path) - Tuple2(".idea"/'libraries/s"$name.xml", libraryXmlTemplate(name, url)) - } - - val moduleFiles = resolved.map{ case (path, resolvedDeps, mod) => - val Seq( - resoucesPathRefs: Loose.Agg[PathRef], - sourcesPathRef: Loose.Agg[PathRef], - generatedSourcePathRefs: Loose.Agg[PathRef], - allSourcesPathRefs: Loose.Agg[PathRef] - ) = evaluator.evaluate(Agg(mod.resources, mod.sources, mod.generatedSources, mod.allSources)).values - - val generatedSourcePaths = generatedSourcePathRefs.map(_.path) - val normalSourcePaths = (allSourcesPathRefs.map(_.path).toSet -- generatedSourcePaths.toSet).toSeq - - val paths = Evaluator.resolveDestPaths( - evaluator.workspacePath, - mod.compile.ctx.segments - ) - - val elem = moduleXmlTemplate( - Strict.Agg.from(resoucesPathRefs.map(_.path)), - Strict.Agg.from(normalSourcePaths), - Strict.Agg.from(generatedSourcePaths), - paths.out, - Strict.Agg.from(resolvedDeps.map(pathToLibName)), - Strict.Agg.from(mod.moduleDeps.map{ m => moduleName(moduleLabels(m))}.distinct) - ) - Tuple2(".idea_modules"/s"${moduleName(path)}.iml", elem) - } - fixedFiles ++ libraries ++ moduleFiles - } - - - def relify(p: Path) = { - val r = p.relativeTo(pwd/".idea_modules") - (Seq.fill(r.ups)("..") ++ r.segments).mkString("/") - } - - def moduleName(p: Segments) = p.value.foldLeft(StringBuilder.newBuilder) { - case (sb, Segment.Label(s)) if sb.isEmpty => sb.append(s) - case (sb, Segment.Cross(s)) if sb.isEmpty => sb.append(s.mkString("-")) - case (sb, Segment.Label(s)) => sb.append(".").append(s) - case (sb, Segment.Cross(s)) => sb.append("-").append(s.mkString("-")) - }.mkString.toLowerCase() - - def miscXmlTemplate() = { - <project version="4"> - <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8 (1)" project-jdk-type="JavaSDK"> - <output url="file://$PROJECT_DIR$/target/idea_output"/> - </component> - </project> - } - - def allModulesXmlTemplate(selectors: Seq[String]) = { - <project version="4"> - <component name="ProjectModuleManager"> - <modules> - <module fileurl="file://$PROJECT_DIR$/.idea_modules/root.iml" filepath="$PROJECT_DIR$/.idea_modules/root.iml" /> - { - for(selector <- selectors) - yield { - val filepath = "$PROJECT_DIR$/.idea_modules/" + selector + ".iml" - val fileurl = "file://" + filepath - <module fileurl={fileurl} filepath={filepath} /> - } - } - </modules> - </component> - </project> - } - def rootXmlTemplate() = { - <module type="JAVA_MODULE" version="4"> - <component name="NewModuleRootManager"> - <output url="file://$MODULE_DIR$/../out"/> - <content url="file://$MODULE_DIR$/.." /> - <exclude-output/> - <orderEntry type="inheritedJdk" /> - <orderEntry type="sourceFolder" forTests="false" /> - </component> - </module> - } - def libraryXmlTemplate(name: String, url: String) = { - <component name="libraryTable"> - <library name={name} type={if(name.contains("org_scala-lang_scala-library_")) "Scala" else null}> - <CLASSES> - <root url={url}/> - </CLASSES> - </library> - </component> - } - def moduleXmlTemplate(resourcePaths: Strict.Agg[Path], - normalSourcePaths: Strict.Agg[Path], - generatedSourcePaths: Strict.Agg[Path], - outputPath: Path, - libNames: Strict.Agg[String], - depNames: Strict.Agg[String]) = { - <module type="JAVA_MODULE" version="4"> - <component name="NewModuleRootManager"> - <output url={"file://$MODULE_DIR$/" + relify(outputPath) + "/dest/classes"} /> - <exclude-output /> - { - for (normalSourcePath <- normalSourcePaths.toSeq.sorted) - yield - <content url={"file://$MODULE_DIR$/" + relify(normalSourcePath)}> - <sourceFolder url={"file://$MODULE_DIR$/" + relify(normalSourcePath)} isTestSource="false" /> - </content> - } - { - for (generatedSourcePath <- generatedSourcePaths.toSeq.sorted) - yield - <content url={"file://$MODULE_DIR$/" + relify(generatedSourcePath)}> - <sourceFolder url={"file://$MODULE_DIR$/" + relify(generatedSourcePath)} isTestSource="false" generated="true" /> - </content> - } - { - for (resourcePath <- resourcePaths.toSeq.sorted) - yield - <content url={"file://$MODULE_DIR$/" + relify(resourcePath)}> - <sourceFolder url={"file://$MODULE_DIR$/" + relify(resourcePath)} isTestSource="false" type="java-resource" /> - </content> - } - <orderEntry type="inheritedJdk" /> - <orderEntry type="sourceFolder" forTests="false" /> - - { - for(name <- libNames.toSeq.sorted) - yield <orderEntry type="library" name={name} level="project" /> - - } - { - for(depName <- depNames.toSeq.sorted) - yield <orderEntry type="module" module-name={depName} exported="" /> - } - </component> - </module> - } -} diff --git a/scalalib/src/main/scala/mill/scalalib/Lib.scala b/scalalib/src/main/scala/mill/scalalib/Lib.scala deleted file mode 100644 index a038a59b..00000000 --- a/scalalib/src/main/scala/mill/scalalib/Lib.scala +++ /dev/null @@ -1,234 +0,0 @@ -package mill -package scalalib - -import java.io.File -import java.net.URLClassLoader -import java.util.Optional - -import ammonite.ops._ -import coursier.{Cache, Fetch, MavenRepository, Repository, Resolution, Module => CoursierModule} -import mill.define.Worker -import mill.eval.{PathRef, Result} -import mill.util.{Ctx} -import mill.util.Loose.Agg -import sbt.internal.inc._ -import sbt.internal.util.{ConsoleOut, MainAppender} -import sbt.util.LogExchange -import xsbti.compile.{CompilerCache => _, FileAnalysisStore => _, ScalaInstance => _, _} - -object CompilationResult { - implicit val jsonFormatter: upickle.default.ReadWriter[CompilationResult] = upickle.default.macroRW -} - -// analysisFile is represented by Path, so we won't break caches after file changes -case class CompilationResult(analysisFile: Path, classes: PathRef) - -object ZincWorker extends Worker[ZincWorker]{ - def make() = new ZincWorker -} -class ZincWorker{ - @volatile var scalaClassloaderCache = Option.empty[(Long, ClassLoader)] - @volatile var scalaInstanceCache = Option.empty[(Long, ScalaInstance)] -} -object Lib{ - case class MockedLookup(am: File => Optional[CompileAnalysis]) extends PerClasspathEntryLookup { - override def analysis(classpathEntry: File): Optional[CompileAnalysis] = - am(classpathEntry) - - override def definesClass(classpathEntry: File): DefinesClass = - Locate.definesClass(classpathEntry) - } - - def grepJar(classPath: Agg[Path], s: String) = { - classPath - .find(_.toString.endsWith(s)) - .getOrElse(throw new Exception("Cannot find " + s)) - .toIO - } - - def compileScala(zincWorker: ZincWorker, - scalaVersion: String, - sources: Agg[Path], - compileClasspath: Agg[Path], - compilerClasspath: Agg[Path], - pluginClasspath: Agg[Path], - compilerBridge: Path, - scalacOptions: Seq[String], - scalacPluginClasspath: Agg[Path], - javacOptions: Seq[String], - upstreamCompileOutput: Seq[CompilationResult]) - (implicit ctx: Ctx): CompilationResult = { - val compileClasspathFiles = compileClasspath.map(_.toIO).toArray - - val compilerJars = compilerClasspath.toArray.map(_.toIO) - val pluginJars = pluginClasspath.toArray.map(_.toIO) - - val compilerClassloaderSig = compilerClasspath.map(p => p.toString().hashCode + p.mtime.toMillis).sum - val scalaInstanceSig = - compilerClassloaderSig + pluginClasspath.map(p => p.toString().hashCode + p.mtime.toMillis).sum - - val compilerClassLoader = zincWorker.scalaClassloaderCache match{ - case Some((k, v)) if k == compilerClassloaderSig => v - case _ => - val classloader = new URLClassLoader(compilerJars.map(_.toURI.toURL), null) - zincWorker.scalaClassloaderCache = Some((compilerClassloaderSig, classloader)) - classloader - } - - val scalaInstance = zincWorker.scalaInstanceCache match{ - case Some((k, v)) if k == scalaInstanceSig => v - case _ => - val scalaInstance = new ScalaInstance( - version = scalaVersion, - loader = new URLClassLoader(pluginJars.map(_.toURI.toURL), compilerClassLoader), - libraryJar = grepJar(compilerClasspath, s"scala-library-$scalaVersion.jar"), - compilerJar = grepJar(compilerClasspath, s"scala-compiler-$scalaVersion.jar"), - allJars = compilerJars ++ pluginJars, - explicitActual = None - ) - zincWorker.scalaInstanceCache = Some((scalaInstanceSig, scalaInstance)) - scalaInstance - } - - mkdir(ctx.dest) - - val ic = new sbt.internal.inc.IncrementalCompilerImpl() - - val logger = { - val consoleAppender = MainAppender.defaultScreen(ConsoleOut.printStreamOut( - ctx.log.outputStream - )) - val l = LogExchange.logger("Hello") - LogExchange.unbindLoggerAppenders("Hello") - LogExchange.bindLoggerAppenders("Hello", (consoleAppender -> sbt.util.Level.Info) :: Nil) - l - } - - def analysisMap(f: File): Optional[CompileAnalysis] = { - if (f.isFile) { - Optional.empty[CompileAnalysis] - } else { - upstreamCompileOutput.collectFirst { - case CompilationResult(zincPath, classFiles) if classFiles.path.toNIO == f.toPath => - FileAnalysisStore.binary(zincPath.toIO).get().map[CompileAnalysis](_.getAnalysis) - }.getOrElse(Optional.empty[CompileAnalysis]) - } - } - - val lookup = MockedLookup(analysisMap) - - val zincFile = ctx.dest / 'zinc - val classesDir = ctx.dest / 'classes - - val zincIOFile = zincFile.toIO - val classesIODir = classesDir.toIO - - val store = FileAnalysisStore.binary(zincIOFile) - - val newResult = ic.compile( - ic.inputs( - classpath = classesIODir +: compileClasspathFiles, - sources = for{ - root <- sources.toArray - if exists(root) - path <- ls.rec(root) - if path.isFile && (path.ext == "scala" || path.ext == "java") - } yield path.toIO, - classesDirectory = classesIODir, - scalacOptions = (scalacPluginClasspath.map(jar => s"-Xplugin:${jar}") ++ scalacOptions).toArray, - javacOptions = javacOptions.toArray, - maxErrors = 10, - sourcePositionMappers = Array(), - order = CompileOrder.Mixed, - compilers = ic.compilers( - scalaInstance, - ClasspathOptionsUtil.boot, - None, - ZincUtil.scalaCompiler(scalaInstance, compilerBridge.toIO) - ), - setup = ic.setup( - lookup, - skip = false, - zincIOFile, - new FreshCompilerCache, - IncOptions.of(), - new ManagedLoggedReporter(10, logger), - None, - Array() - ), - pr = { - val prev = store.get() - PreviousResult.of(prev.map(_.getAnalysis), prev.map(_.getMiniSetup)) - } - ), - logger = logger - ) - - store.set( - AnalysisContents.create( - newResult.analysis(), - newResult.setup() - ) - ) - - CompilationResult(zincFile, PathRef(classesDir)) - } - - def resolveDependencies(repositories: Seq[Repository], - scalaVersion: String, - scalaBinaryVersion: String, - deps: TraversableOnce[Dep], - sources: Boolean = false): Result[Agg[PathRef]] = { - val flattened = deps.map{ - case Dep.Java(dep) => dep - case Dep.Scala(dep) => - dep.copy(module = dep.module.copy(name = dep.module.name + "_" + scalaBinaryVersion)) - case Dep.Point(dep) => - dep.copy(module = dep.module.copy(name = dep.module.name + "_" + scalaVersion)) - }.toSet - val start = Resolution(flattened) - - val fetch = Fetch.from(repositories, Cache.fetch()) - val resolution = start.process.run(fetch).unsafePerformSync - val errs = resolution.metadataErrors - if(errs.nonEmpty) { - val header = - s"""| - |Resolution failed for ${errs.length} modules: - |-------------------------------------------- - |""".stripMargin - - val errLines = errs.map { - case ((module, vsn), errMsgs) => s" ${module.trim}:$vsn \n\t" + errMsgs.mkString("\n\t") - }.mkString("\n") - val msg = header + errLines + "\n" - Result.Failure(msg) - } else { - val sourceOrJar = - if (sources) resolution.classifiersArtifacts(Seq("sources")) - else resolution.artifacts - val localArtifacts: Seq[File] = scalaz.concurrent.Task - .gatherUnordered(sourceOrJar.map(Cache.file(_).run)) - .unsafePerformSync - .flatMap(_.toOption) - - Agg.from( - localArtifacts.map(p => PathRef(Path(p), quick = true)).filter(_.path.ext == "jar") - ) - } - } - def scalaCompilerIvyDeps(scalaVersion: String) = Agg[Dep]( - Dep.Java("org.scala-lang", "scala-compiler", scalaVersion), - Dep.Java("org.scala-lang", "scala-reflect", scalaVersion) - ) - def scalaRuntimeIvyDeps(scalaVersion: String) = Agg[Dep]( - Dep.Java("org.scala-lang", "scala-library", scalaVersion) - ) - def compilerBridgeIvyDep(scalaVersion: String) = - Dep.Point(coursier.Dependency(coursier.Module("com.lihaoyi", "mill-bridge"), "0.1", transitive = false)) - - val DefaultShellScript: Seq[String] = Seq( - "#!/usr/bin/env sh", - "exec java -jar \"$0\" \"$@\"" - ) -} diff --git a/scalalib/src/main/scala/mill/scalalib/Main.scala b/scalalib/src/main/scala/mill/scalalib/Main.scala deleted file mode 100644 index 584fe9d1..00000000 --- a/scalalib/src/main/scala/mill/scalalib/Main.scala +++ /dev/null @@ -1,7 +0,0 @@ -package mill.scalalib - -object Main { - def main(args: Array[String]): Unit = { - mill.Main.main(args) - } -} diff --git a/scalalib/src/main/scala/mill/scalalib/MiscModule.scala b/scalalib/src/main/scala/mill/scalalib/MiscModule.scala deleted file mode 100644 index 502ba461..00000000 --- a/scalalib/src/main/scala/mill/scalalib/MiscModule.scala +++ /dev/null @@ -1,72 +0,0 @@ -package mill -package scalalib - -import mill.define.Cross.Resolver -import mill.define.{Cross, Task} -import mill.eval.{PathRef, Result} -import mill.util.Loose.Agg - - -trait SbtModule extends ScalaModule { outer => - override def sources = T.input{ - Agg( - PathRef(basePath / 'src / 'main / 'scala), - PathRef(basePath / 'src / 'main / 'java) - ) - } - override def resources = T.input{ Agg(PathRef(basePath / 'src / 'main / 'resources)) } - trait Tests extends super.Tests { - override def basePath = outer.basePath - override def sources = T.input{ - Agg( - PathRef(basePath / 'src / 'test / 'scala), - PathRef(basePath / 'src / 'test / 'java) - ) - } - override def resources = T.input{ Agg(PathRef(basePath / 'src / 'test / 'resources)) } - } -} - -trait CrossSbtModule extends SbtModule { outer => - override def basePath = super.basePath / ammonite.ops.up - implicit def crossSbtModuleResolver: Resolver[CrossSbtModule] = new Resolver[CrossSbtModule]{ - def resolve[V <: CrossSbtModule](c: Cross[V]): V = { - crossScalaVersion.split('.') - .inits - .takeWhile(_.length > 1) - .flatMap( prefix => - c.items.map(_._2).find(_.crossScalaVersion.split('.').startsWith(prefix)) - ) - .collectFirst{case x => x} - .getOrElse( - throw new Exception( - s"Unable to find compatible cross version between $crossScalaVersion and "+ - c.items.map(_._2.crossScalaVersion).mkString(",") - ) - ) - } - } - - def crossScalaVersion: String - def scalaVersion = crossScalaVersion - override def sources = T.input{ - super.sources() ++ - crossScalaVersion.split('.').inits.filter(_.nonEmpty).map(_.mkString(".")).map{ - s => PathRef{ basePath / 'src / 'main / s"scala-$s" } - } - - } - override def resources = T.input{ Agg(PathRef(basePath / 'src / 'main / 'resources)) } - trait Tests extends super.Tests { - override def basePath = outer.basePath - override def sources = T.input{ - super.sources() ++ - crossScalaVersion.split('.').inits.filter(_.nonEmpty).map(_.mkString(".")).map{ - s => PathRef{ basePath / 'src / 'test / s"scala-$s" } - } - } - override def resources = T.input{ Agg(PathRef(basePath / 'src / 'test / 'resources)) } - } -} - - diff --git a/scalalib/src/main/scala/mill/scalalib/PublishModule.scala b/scalalib/src/main/scala/mill/scalalib/PublishModule.scala deleted file mode 100644 index 64efce77..00000000 --- a/scalalib/src/main/scala/mill/scalalib/PublishModule.scala +++ /dev/null @@ -1,70 +0,0 @@ -package mill -package scalalib - -import ammonite.ops._ -import mill.eval.{PathRef, Result} -import mill.util.Loose.Agg -/** - * Configuration necessary for publishing a Scala module to Maven Central or similar - */ -trait PublishModule extends ScalaModule { outer => - import mill.scalalib.publish._ - - def pomSettings: T[PomSettings] - def publishVersion: T[String] = "0.0.1-SNAPSHOT" - - def pom = T { - val dependencies = - ivyDeps().map(Artifact.fromDep(_, scalaVersion(), scalaBinaryVersion())) - val pom = Pom(artifact(), dependencies, artifactName(), pomSettings()) - - val pomPath = T.ctx().dest / s"${artifactId()}-${publishVersion()}.pom" - write.over(pomPath, pom) - PathRef(pomPath) - } - - def ivy = T { - val dependencies = - ivyDeps().map(Artifact.fromDep(_, scalaVersion(), scalaBinaryVersion())) - val ivy = Ivy(artifact(), dependencies) - val ivyPath = T.ctx().dest / "ivy.xml" - write.over(ivyPath, ivy) - PathRef(ivyPath) - } - - def artifact: T[Artifact] = T { - Artifact(pomSettings().organization, artifactId(), publishVersion()) - } - - def publishLocal(): define.Command[Unit] = T.command { - LocalPublisher.publish( - jar = jar().path, - sourcesJar = sourcesJar().path, - docsJar = docsJar().path, - pom = pom().path, - ivy = ivy().path, - artifact = artifact() - ) - } - - def sonatypeUri: String = "https://oss.sonatype.org/service/local" - - def sonatypeSnapshotUri: String = "https://oss.sonatype.org/content/repositories/snapshots" - - def publish(credentials: String, gpgPassphrase: String): define.Command[Unit] = T.command { - val baseName = s"${artifactId()}-${publishVersion()}" - val artifacts = Seq( - jar().path -> s"${baseName}.jar", - sourcesJar().path -> s"${baseName}-sources.jar", - docsJar().path -> s"${baseName}-javadoc.jar", - pom().path -> s"${baseName}.pom" - ) - new SonatypePublisher( - sonatypeUri, - sonatypeSnapshotUri, - credentials, - gpgPassphrase, - T.ctx().log - ).publish(artifacts, artifact()) - } -} diff --git a/scalalib/src/main/scala/mill/scalalib/ScalaModule.scala b/scalalib/src/main/scala/mill/scalalib/ScalaModule.scala deleted file mode 100644 index 1b2bd28d..00000000 --- a/scalalib/src/main/scala/mill/scalalib/ScalaModule.scala +++ /dev/null @@ -1,318 +0,0 @@ -package mill -package scalalib - -import ammonite.ops._ -import coursier.{Cache, MavenRepository, Repository} -import mill.define.{Cross, Task} -import mill.define.TaskModule -import mill.eval.{PathRef, Result} -import mill.modules.Jvm -import mill.modules.Jvm.{createAssembly, createJar, interactiveSubprocess, subprocess, inprocess} -import Lib._ -import mill.define.Cross.Resolver -import mill.util.Loose.Agg -import sbt.testing.Status - -/** - * Core configuration required to compile a single Scala compilation target - */ -trait ScalaModule extends mill.Module with TaskModule { outer => - def defaultCommandName() = "run" - trait Tests extends TestModule{ - def scalaVersion = outer.scalaVersion() - override def moduleDeps = Seq(outer) - } - def scalaVersion: T[String] - def mainClass: T[Option[String]] = None - - def scalaBinaryVersion = T{ scalaVersion().split('.').dropRight(1).mkString(".") } - 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 repositories: Seq[Repository] = Seq( - Cache.ivy2Local, - MavenRepository("https://repo1.maven.org/maven2") - ) - - def moduleDeps = Seq.empty[ScalaModule] - def depClasspath = T{ Agg.empty[PathRef] } - - - def upstreamRunClasspath = T{ - Task.traverse(moduleDeps)(p => - T.task(p.runDepClasspath() ++ p.runClasspath()) - ) - } - - def upstreamCompileOutput = T{ - Task.traverse(moduleDeps)(_.compile) - } - def upstreamCompileClasspath = T{ - externalCompileDepClasspath() ++ - upstreamCompileOutput().map(_.classes) ++ - Task.traverse(moduleDeps)(_.compileDepClasspath)().flatten - } - - def resolveDeps(deps: Task[Agg[Dep]], sources: Boolean = false) = T.task{ - resolveDependencies( - repositories, - scalaVersion(), - scalaBinaryVersion(), - deps(), - sources - ) - } - - def externalCompileDepClasspath: T[Agg[PathRef]] = T{ - Agg.from(Task.traverse(moduleDeps)(_.externalCompileDepClasspath)().flatten) ++ - resolveDeps( - T.task{ivyDeps() ++ compileIvyDeps() ++ scalaCompilerIvyDeps(scalaVersion())} - )() - } - - def externalCompileDepSources: T[Agg[PathRef]] = T{ - Agg.from(Task.traverse(moduleDeps)(_.externalCompileDepSources)().flatten) ++ - resolveDeps( - T.task{ivyDeps() ++ compileIvyDeps() ++ scalaCompilerIvyDeps(scalaVersion())}, - sources = true - )() - } - - /** - * Things that need to be on the classpath in order for this code to compile; - * might be less than the runtime classpath - */ - def compileDepClasspath: T[Agg[PathRef]] = T{ - upstreamCompileClasspath() ++ - depClasspath() - } - - /** - * Strange compiler-bridge jar that the Zinc incremental compile needs - */ - def compilerBridge: T[PathRef] = T{ - val compilerBridgeKey = "MILL_COMPILER_BRIDGE_" + scalaVersion().replace('.', '_') - val compilerBridgePath = sys.props(compilerBridgeKey) - if (compilerBridgePath != null) PathRef(Path(compilerBridgePath), quick = true) - else { - val dep = compilerBridgeIvyDep(scalaVersion()) - val classpath = resolveDependencies( - repositories, - scalaVersion(), - scalaBinaryVersion(), - Seq(dep) - ) - classpath match { - case Result.Success(resolved) => - resolved.filter(_.path.ext != "pom").toSeq match { - case Seq(single) => PathRef(single.path, quick = true) - case Seq() => throw new Exception(dep + " resolution failed") // TODO: find out, is it possible? - case _ => throw new Exception(dep + " resolution resulted in more than one file") - } - case f: Result.Failure => throw new Exception(dep + s" resolution failed.\n + ${f.msg}") // TODO: remove, resolveDependencies will take care of this. - } - } - } - - def scalacPluginClasspath: T[Agg[PathRef]] = - resolveDeps( - T.task{scalacPluginIvyDeps()} - )() - - /** - * Classpath of the Scala Compiler & any compiler plugins - */ - def scalaCompilerClasspath: T[Agg[PathRef]] = T{ - resolveDeps( - T.task{scalaCompilerIvyDeps(scalaVersion()) ++ scalaRuntimeIvyDeps(scalaVersion())} - )() - } - - /** - * Things that need to be on the classpath in order for this code to run - */ - def runDepClasspath: T[Agg[PathRef]] = T{ - Agg.from(upstreamRunClasspath().flatten) ++ - depClasspath() ++ - resolveDeps( - T.task{ivyDeps() ++ runIvyDeps() ++ scalaRuntimeIvyDeps(scalaVersion())} - )() - } - - def prependShellScript: T[String] = T{ "" } - - def sources = T.input{ Agg(PathRef(basePath / 'src)) } - def resources = T.input{ Agg(PathRef(basePath / 'resources)) } - def generatedSources = T { Agg.empty[PathRef] } - def allSources = T{ sources() ++ generatedSources() } - def compile: T[CompilationResult] = T.persistent{ - compileScala( - ZincWorker(), - scalaVersion(), - allSources().map(_.path), - compileDepClasspath().map(_.path), - scalaCompilerClasspath().map(_.path), - scalacPluginClasspath().map(_.path), - compilerBridge().path, - scalacOptions(), - scalacPluginClasspath().map(_.path), - javacOptions(), - upstreamCompileOutput() - ) - } - def runClasspath = T{ - runDepClasspath() ++ resources() ++ Seq(compile().classes) - } - - def assembly = T{ - createAssembly( - runClasspath().map(_.path).filter(exists), - mainClass(), - prependShellScript = prependShellScript() - ) - } - - def localClasspath = T{ resources() ++ Seq(compile().classes) } - - def jar = T{ - createJar( - localClasspath().map(_.path).filter(exists), - mainClass() - ) - } - - def docsJar = T { - val outDir = T.ctx().dest - - val javadocDir = outDir / 'javadoc - mkdir(javadocDir) - - val options = { - - val files = for{ - ref <- sources() - p <- ls.rec(ref.path) - if p.isFile - } yield p.toNIO.toString - files ++ Seq("-d", javadocDir.toNIO.toString, "-usejavacp") - } - - subprocess( - "scala.tools.nsc.ScalaDoc", - compileDepClasspath().filter(_.path.ext != "pom").map(_.path), - options = options.toSeq - ) - - createJar(Agg(javadocDir))(outDir / "javadoc.jar") - } - - def sourcesJar = T { - createJar((sources() ++ resources()).map(_.path).filter(exists))(T.ctx().dest / "sources.jar") - } - - def forkArgs = T{ Seq.empty[String] } - - - def run(args: String*) = T.command { - inprocess( - mainClass().getOrElse(throw new RuntimeException("No mainClass provided!")), - runClasspath().map(_.path), - args) - } - - def forkRun(args: String*) = T.command{ - subprocess( - mainClass().getOrElse(throw new RuntimeException("No mainClass provided!")), - runClasspath().map(_.path), - forkArgs(), - args, - workingDir = ammonite.ops.pwd) - } - - def runMain(mainClass: String, args: String*) = T.command{ - subprocess( - mainClass, - runClasspath().map(_.path), - forkArgs(), - args, - workingDir = ammonite.ops.pwd - ) - } - - def console() = T.command{ - interactiveSubprocess( - mainClass = "scala.tools.nsc.MainGenericRunner", - classPath = runClasspath().map(_.path), - options = Seq("-usejavacp") - ) - } - - // publish artifact with name "mill_2.12.4" instead of "mill_2.12" - def crossFullScalaVersion: T[Boolean] = false - - def artifactName: T[String] = basePath.last.toString - def artifactScalaVersion: T[String] = T { - if (crossFullScalaVersion()) scalaVersion() - else scalaBinaryVersion() - } - - def artifactId: T[String] = T { s"${artifactName()}_${artifactScalaVersion()}" } - -} - - -object TestModule{ - def handleResults(doneMsg: String, results: Seq[TestRunner.Result]) = { - if (results.count(Set(Status.Error, Status.Failure)) == 0) Result.Success((doneMsg, results)) - else { - val grouped = results.map(_.status).groupBy(x => x).mapValues(_.length).filter(_._2 != 0).toList.sorted - - Result.Failure(grouped.map{case (k, v) => k + ": " + v}.mkString(",")) - } - } -} -trait TestModule extends ScalaModule with TaskModule { - override def defaultCommandName() = "test" - def testFramework: T[String] - - def forkWorkingDir = ammonite.ops.pwd - - def forkTest(args: String*) = T.command{ - mkdir(T.ctx().dest) - val outputPath = T.ctx().dest/"out.json" - - Jvm.subprocess( - mainClass = "mill.scalalib.TestRunner", - classPath = Jvm.gatherClassloaderJars(), - jvmOptions = forkArgs(), - options = Seq( - testFramework(), - runClasspath().map(_.path).mkString(" "), - Seq(compile().classes.path).mkString(" "), - args.mkString(" "), - outputPath.toString, - T.ctx().log.colored.toString - ), - workingDir = forkWorkingDir - ) - - val jsonOutput = upickle.json.read(outputPath.toIO) - val (doneMsg, results) = upickle.default.readJs[(String, Seq[TestRunner.Result])](jsonOutput) - TestModule.handleResults(doneMsg, results) - - } - def test(args: String*) = T.command{ - val (doneMsg, results) = TestRunner( - testFramework(), - runClasspath().map(_.path), - Agg(compile().classes.path), - args - ) - TestModule.handleResults(doneMsg, results) - } -}
\ No newline at end of file diff --git a/scalalib/src/main/scala/mill/scalalib/TestRunner.scala b/scalalib/src/main/scala/mill/scalalib/TestRunner.scala deleted file mode 100644 index 01726022..00000000 --- a/scalalib/src/main/scala/mill/scalalib/TestRunner.scala +++ /dev/null @@ -1,172 +0,0 @@ -package mill.scalalib - -import java.io.FileInputStream -import java.lang.annotation.Annotation -import java.net.URLClassLoader -import java.util.zip.ZipInputStream - -import ammonite.ops.{Path, ls, pwd} -import ammonite.util.Colors -import mill.modules.Jvm -import mill.util.Ctx.LogCtx -import mill.util.{PrintLogger} -import mill.util.Loose.Agg -import sbt.testing._ -import upickle.Js -import mill.util.JsonFormatters._ - -import scala.collection.mutable - -object TestRunner { - def listClassFiles(base: Path): Iterator[String] = { - if (base.isDir) ls.rec(base).toIterator.filter(_.ext == "class").map(_.relativeTo(base).toString) - else { - val zip = new ZipInputStream(new FileInputStream(base.toIO)) - Iterator.continually(zip.getNextEntry).takeWhile(_ != null).map(_.getName).filter(_.endsWith(".class")) - } - } - def runTests(cl: ClassLoader, framework: Framework, classpath: Agg[Path]) = { - - - val fingerprints = framework.fingerprints() - val testClasses = classpath.flatMap { base => - listClassFiles(base).flatMap { path => - val cls = cl.loadClass(path.stripSuffix(".class").replace('/', '.')) - fingerprints.find { - case f: SubclassFingerprint => - - (f.isModule == cls.getName.endsWith("$")) && - cl.loadClass(f.superclassName()).isAssignableFrom(cls) - case f: AnnotatedFingerprint => - (f.isModule == cls.getName.endsWith("$")) && - cls.isAnnotationPresent( - cl.loadClass(f.annotationName()).asInstanceOf[Class[Annotation]] - ) - }.map { f => (cls, f) } - } - } - testClasses - } - def main(args: Array[String]): Unit = { - try{ - val result = apply( - frameworkName = args(0), - entireClasspath = Agg.from(args(1).split(" ").map(Path(_))), - testClassfilePath = Agg.from(args(2).split(" ").map(Path(_))), - args = args(3) match{ case "" => Nil case x => x.split(" ").toList } - )(new PrintLogger( - args(5) == "true", - if(args(5) == "true") Colors.Default - else Colors.BlackWhite, - System.out, - System.err, - System.err - )) - val outputPath = args(4) - - ammonite.ops.write(Path(outputPath), upickle.default.write(result)) - }catch{case e: Throwable => - println(e) - e.printStackTrace() - } - // Tests are over, kill the JVM whether or not anyone's threads are still running - // Always return 0, even if tests fail. The caller can pick up the detailed test - // results from the outputPath - System.exit(0) - } - def apply(frameworkName: String, - entireClasspath: Agg[Path], - testClassfilePath: Agg[Path], - args: Seq[String]) - (implicit ctx: LogCtx): (String, Seq[Result]) = { - Jvm.inprocess(entireClasspath, classLoaderOverrideSbtTesting = true, cl => { - val framework = cl.loadClass(frameworkName) - .newInstance() - .asInstanceOf[sbt.testing.Framework] - - val testClasses = runTests(cl, framework, testClassfilePath) - - val runner = framework.runner(args.toArray, args.toArray, cl) - - val tasks = runner.tasks( - for ((cls, fingerprint) <- testClasses.toArray) - yield new TaskDef(cls.getName.stripSuffix("$"), fingerprint, true, Array(new SuiteSelector)) - ) - val events = mutable.Buffer.empty[Event] - for (t <- tasks) { - t.execute( - new EventHandler { - def handle(event: Event) = events.append(event) - }, - Array( - new Logger { - def debug(msg: String) = ctx.log.info(msg) - - def error(msg: String) = ctx.log.error(msg) - - def ansiCodesSupported() = true - - def warn(msg: String) = ctx.log.info(msg) - - def trace(t: Throwable) = t.printStackTrace(ctx.log.outputStream) - - def info(msg: String) = ctx.log.info(msg) - }) - ) - } - val doneMsg = runner.done() - val results = for(e <- events) yield { - val ex = if (e.throwable().isDefined) Some(e.throwable().get) else None - Result( - e.fullyQualifiedName(), - e.selector() match{ - case s: NestedSuiteSelector => s.suiteId() - case s: NestedTestSelector => s.suiteId() + "." + s.testName() - case s: SuiteSelector => s.toString - case s: TestSelector => s.testName() - case s: TestWildcardSelector => s.testWildcard() - }, - e.duration(), - e.status(), - ex.map(_.getClass.getName), - ex.map(_.getMessage), - ex.map(_.getStackTrace) - ) - } - (doneMsg, results) - }) - } - - case class Result(fullyQualifiedName: String, - selector: String, - duration: Long, - status: Status, - exceptionName: Option[String], - exceptionMsg: Option[String], - exceptionTrace: Option[Seq[StackTraceElement]]) - - object Result{ - implicit def resultRW: upickle.default.ReadWriter[Result] = upickle.default.macroRW[Result] - implicit def statusRW: upickle.default.ReadWriter[Status] = upickle.default.ReadWriter[Status]( - { - case Status.Success => Js.Str("Success") - case Status.Error => Js.Str("Error") - case Status.Failure => Js.Str("Failure") - case Status.Skipped => Js.Str("Skipped") - case Status.Ignored => Js.Str("Ignored") - case Status.Canceled => Js.Str("Canceled") - case Status.Pending => Js.Str("Pending") - }, - { - case Js.Str("Success") => Status.Success - case Js.Str("Error") => Status.Error - case Js.Str("Failure") => Status.Failure - case Js.Str("Skipped") => Status.Skipped - case Js.Str("Ignored") => Status.Ignored - case Js.Str("Canceled") => Status.Canceled - case Js.Str("Pending") => Status.Pending - } - ) - } - -} diff --git a/scalalib/src/main/scala/mill/scalalib/package.scala b/scalalib/src/main/scala/mill/scalalib/package.scala deleted file mode 100644 index 5a282e82..00000000 --- a/scalalib/src/main/scala/mill/scalalib/package.scala +++ /dev/null @@ -1,12 +0,0 @@ -package mill - -package object scalalib { - implicit class DepSyntax(ctx: StringContext){ - def ivy(args: Any*) = Dep.parse{ - ( - ctx.parts.take(args.length).zip(args).flatMap{case (p, a) => Seq(p, a)} ++ - ctx.parts.drop(args.length) - ).mkString - } - } -} diff --git a/scalalib/src/main/scala/mill/scalalib/publish/Ivy.scala b/scalalib/src/main/scala/mill/scalalib/publish/Ivy.scala deleted file mode 100644 index b0b6443e..00000000 --- a/scalalib/src/main/scala/mill/scalalib/publish/Ivy.scala +++ /dev/null @@ -1,55 +0,0 @@ -package mill.scalalib.publish - -import mill.util.Loose.Agg - -import scala.xml.PrettyPrinter - -object Ivy { - - val head = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" - - def apply( - artifact: Artifact, - dependencies: Agg[Dependency] - ): String = { - val xml = - <ivy-module version="2.0" xmlns:e="http://ant.apache.org/ivy/extra"> - <info - organisation={artifact.group} module={artifact.id} revision={artifact.version} status="release"> - <description/> - </info> - <configurations> - <conf name="pom" visibility="public" description=""/> - <conf extends="runtime" name="test" visibility="public" description=""/> - <conf name="provided" visibility="public" description=""/> - <conf name="optional" visibility="public" description=""/> - <conf name="compile" visibility="public" description=""/> - <conf extends="compile" name="runtime" visibility="public" description=""/> - </configurations> - - <publications> - <artifact name={artifact.id} type="pom" ext="pom" conf="pom"/> - <artifact name={artifact.id} type="jar" ext="jar" conf="compile"/> - <artifact name={artifact.id} type="src" ext="jar" conf="compile" e:classifier="sources"/> - <artifact name={artifact.id} type="doc" ext="jar" conf="compile" e:classifier="javadoc"/> - </publications> - <dependencies>{dependencies.map(renderDependency)}</dependencies> - </ivy-module> - - val pp = new PrettyPrinter(120, 4) - head + pp.format(xml).replaceAll(">", ">") - } - - private def renderDependency(dep: Dependency) = { - val scope = scopeToConf(dep.scope) - <dependency org={dep.artifact.group} name={dep.artifact.id} rev={dep.artifact.version} conf={s"$scope->default(compile)"}></dependency> - } - - private def scopeToConf(s: Scope): String = s match { - case Scope.Compile => "compile" - case Scope.Provided => "provided" - case Scope.Test => "test" - case Scope.Runtime => "runtime" - } - -} diff --git a/scalalib/src/main/scala/mill/scalalib/publish/JsonFormatters.scala b/scalalib/src/main/scala/mill/scalalib/publish/JsonFormatters.scala deleted file mode 100644 index cf1af557..00000000 --- a/scalalib/src/main/scala/mill/scalalib/publish/JsonFormatters.scala +++ /dev/null @@ -1,11 +0,0 @@ -package mill.scalalib.publish - -import upickle.default.{ReadWriter => RW} - -trait JsonFormatters { - implicit lazy val artifactFormat: RW[Artifact] = upickle.default.macroRW - implicit lazy val developerFormat: RW[Developer] = upickle.default.macroRW - implicit lazy val licenseFormat: RW[License] = upickle.default.macroRW - implicit lazy val scmFormat: RW[SCM] = upickle.default.macroRW - implicit lazy val pomSettingsFormat: RW[PomSettings] = upickle.default.macroRW -} diff --git a/scalalib/src/main/scala/mill/scalalib/publish/LocalPublisher.scala b/scalalib/src/main/scala/mill/scalalib/publish/LocalPublisher.scala deleted file mode 100644 index a9957e5c..00000000 --- a/scalalib/src/main/scala/mill/scalalib/publish/LocalPublisher.scala +++ /dev/null @@ -1,33 +0,0 @@ -package mill.scalalib.publish - -import ammonite.ops._ - -object LocalPublisher { - - private val root: Path = home / ".ivy2" / "local" - - def publish(jar: Path, - sourcesJar: Path, - docsJar: Path, - pom: Path, - ivy: Path, - artifact: Artifact): Unit = { - val releaseDir = root / artifact.group / artifact.id / artifact.version - writeFiles( - jar -> releaseDir / "jars" / s"${artifact.id}.jar", - sourcesJar -> releaseDir / "srcs" / s"${artifact.id}-sources.jar", - docsJar -> releaseDir / "docs" / s"${artifact.id}-javadoc.jar", - pom -> releaseDir / "poms" / s"${artifact.id}.pom", - ivy -> releaseDir / "ivys" / "ivy.xml" - ) - } - - private def writeFiles(fromTo: (Path, Path)*): Unit = { - fromTo.foreach { - case (from, to) => - mkdir(to / up) - cp.over(from, to) - } - } - -} diff --git a/scalalib/src/main/scala/mill/scalalib/publish/Pom.scala b/scalalib/src/main/scala/mill/scalalib/publish/Pom.scala deleted file mode 100644 index 74dc6e8f..00000000 --- a/scalalib/src/main/scala/mill/scalalib/publish/Pom.scala +++ /dev/null @@ -1,90 +0,0 @@ -package mill.scalalib.publish - -import mill.util.Loose.Agg - -import scala.xml.{Elem, NodeSeq, PrettyPrinter} - -object Pom { - - val head = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" - - //TODO - not only jar packaging support? - def apply(artifact: Artifact, - dependencies: Agg[Dependency], - name: String, - pomSettings: PomSettings): String = { - val xml = - <project - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" - xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" - xmlns ="http://maven.apache.org/POM/4.0.0"> - - <modelVersion>4.0.0</modelVersion> - <name>{name}</name> - <groupId>{artifact.group}</groupId> - <artifactId>{artifact.id}</artifactId> - <packaging>jar</packaging> - <description>{pomSettings.description}</description> - - <version>{artifact.version}</version> - <url>{pomSettings.url}</url> - <licenses> - {pomSettings.licenses.map(renderLicense)} - </licenses> - <scm> - <url>{pomSettings.scm.url}</url> - <connection>{pomSettings.scm.connection}</connection> - </scm> - <developers> - {pomSettings.developers.map(renderDeveloper)} - </developers> - <dependencies> - {dependencies.map(renderDependency)} - </dependencies> - </project> - - val pp = new PrettyPrinter(120, 4) - head + pp.format(xml) - } - - private def renderLicense(l: License): Elem = { - <license> - <name>{l.name}</name> - <url>{l.url}</url> - <distribution>{l.distribution}</distribution> - </license> - } - - private def renderDeveloper(d: Developer): Elem = { - <developer> - <id>{d.id}</id> - <name>{d.name}</name> - { - d.organization.map { org => - <organization>{org}</organization> - }.getOrElse(NodeSeq.Empty) - } - { - d.organizationUrl.map { orgUrl => - <organizationUrl>{orgUrl}</organizationUrl> - }.getOrElse(NodeSeq.Empty) - } - </developer> - } - - private def renderDependency(d: Dependency): Elem = { - val scope = d.scope match { - case Scope.Compile => NodeSeq.Empty - case Scope.Provided => <scope>provided</scope> - case Scope.Test => <scope>test</scope> - case Scope.Runtime => <scope>runtime</scope> - } - <dependency> - <groupId>{d.artifact.group}</groupId> - <artifactId>{d.artifact.id}</artifactId> - <version>{d.artifact.version}</version> - {scope} - </dependency> - } - -} diff --git a/scalalib/src/main/scala/mill/scalalib/publish/SonatypeHttpApi.scala b/scalalib/src/main/scala/mill/scalalib/publish/SonatypeHttpApi.scala deleted file mode 100644 index 8ccdf3ea..00000000 --- a/scalalib/src/main/scala/mill/scalalib/publish/SonatypeHttpApi.scala +++ /dev/null @@ -1,130 +0,0 @@ -package mill.scalalib.publish - -import java.util.Base64 - -import upickle.json - -import scala.concurrent.duration._ -import scalaj.http.{BaseHttp, HttpOptions, HttpRequest, HttpResponse} - -object PatientHttp - extends BaseHttp( - options = Seq( - HttpOptions.connTimeout(5.seconds.toMillis.toInt), - HttpOptions.readTimeout(1.minute.toMillis.toInt), - HttpOptions.followRedirects(false) - ) - ) - -class SonatypeHttpApi(uri: String, credentials: String) { - - private val base64Creds = base64(credentials) - - private val commonHeaders = Seq( - "Authorization" -> s"Basic ${base64Creds}", - "Accept" -> "application/json", - "Content-Type" -> "application/json" - ) - - // https://oss.sonatype.org/nexus-staging-plugin/default/docs/path__staging_profiles.html - def getStagingProfileUri(groupId: String): String = { - val response = withRetry( - PatientHttp(s"${uri}/staging/profiles").headers(commonHeaders)) - - val resourceUri = - json - .read(response.body)("data") - .arr - .find(profile => profile("name").str == groupId) - .map(_("resourceURI").str.toString) - - resourceUri.getOrElse( - throw new RuntimeException( - s"Could not find staging profile for groupId: ${groupId}") - ) - } - - def getStagingRepoState(stagingRepoId: String): String = { - val response = PatientHttp(s"${uri}/staging/repository/${stagingRepoId}") - .option(HttpOptions.readTimeout(60000)) - .headers(commonHeaders) - .asString - - json.read(response.body)("type").str.toString - } - - // https://oss.sonatype.org/nexus-staging-plugin/default/docs/path__staging_profiles_-profileIdKey-_start.html - def createStagingRepo(profileUri: String, groupId: String): String = { - val response = withRetry(PatientHttp(s"${profileUri}/start") - .headers(commonHeaders) - .postData( - s"""{"data": {"description": "fresh staging profile for ${groupId}"}}""")) - - json.read(response.body)("data")("stagedRepositoryId").str.toString - } - - // https://oss.sonatype.org/nexus-staging-plugin/default/docs/path__staging_profiles_-profileIdKey-_finish.html - def closeStagingRepo(profileUri: String, repositoryId: String): Boolean = { - val response = withRetry( - PatientHttp(s"${profileUri}/finish") - .headers(commonHeaders) - .postData( - s"""{"data": {"stagedRepositoryId": "${repositoryId}", "description": "closing staging repository"}}""" - )) - - response.code == 201 - } - - // https://oss.sonatype.org/nexus-staging-plugin/default/docs/path__staging_profiles_-profileIdKey-_promote.html - def promoteStagingRepo(profileUri: String, repositoryId: String): Boolean = { - val response = withRetry( - PatientHttp(s"${profileUri}/promote") - .headers(commonHeaders) - .postData( - s"""{"data": {"stagedRepositoryId": "${repositoryId}", "description": "promote staging repository"}}""" - )) - - response.code == 201 - } - - // https://oss.sonatype.org/nexus-staging-plugin/default/docs/path__staging_profiles_-profileIdKey-_drop.html - def dropStagingRepo(profileUri: String, repositoryId: String): Boolean = { - val response = withRetry( - PatientHttp(s"${profileUri}/drop") - .headers(commonHeaders) - .postData( - s"""{"data": {"stagedRepositoryId": "${repositoryId}", "description": "drop staging repository"}}""" - )) - - response.code == 201 - } - - private val uploadTimeout = 5.minutes.toMillis.toInt - - def upload(uri: String, data: Array[Byte]): HttpResponse[String] = { - PatientHttp(uri) - .option(HttpOptions.readTimeout(uploadTimeout)) - .method("PUT") - .headers( - "Content-Type" -> "application/binary", - "Authorization" -> s"Basic ${base64Creds}" - ) - .put(data) - .asString - } - - private def withRetry(request: HttpRequest, - retries: Int = 10): HttpResponse[String] = { - val resp = request.asString - if (resp.is5xx && retries > 0) { - Thread.sleep(500) - withRetry(request, retries - 1) - } else { - resp - } - } - - private def base64(s: String) = - new String(Base64.getEncoder.encode(s.getBytes)) - -} diff --git a/scalalib/src/main/scala/mill/scalalib/publish/SonatypePublisher.scala b/scalalib/src/main/scala/mill/scalalib/publish/SonatypePublisher.scala deleted file mode 100644 index 0749b0c5..00000000 --- a/scalalib/src/main/scala/mill/scalalib/publish/SonatypePublisher.scala +++ /dev/null @@ -1,148 +0,0 @@ -package mill.scalalib.publish - -import java.math.BigInteger -import java.security.MessageDigest - -import ammonite.ops._ -import mill.util.Logger - -import scalaj.http.HttpResponse - -class SonatypePublisher(uri: String, - snapshotUri: String, - credentials: String, - gpgPassphrase: String, - log: Logger) { - - private val api = new SonatypeHttpApi(uri, credentials) - - def publish(artifacts: Seq[(Path, String)], artifact: Artifact): Unit = { - val signedArtifacts = artifacts ++ artifacts.map { - case (file, name) => - poorMansSign(file, gpgPassphrase) -> s"${name}.asc" - } - - val signedArtifactsWithDigest = signedArtifacts.flatMap { - case (file, name) => - val content = read.bytes(file) - - Seq( - name -> content, - (name + ".md5") -> md5hex(content), - (name + ".sha1") -> sha1hex(content) - ) - } - - val publishPath = Seq( - artifact.group.replace(".", "/"), - artifact.id, - artifact.version - ).mkString("/") - - if (artifact.isSnapshot) - publishSnapshot(publishPath, signedArtifactsWithDigest, artifact) - else - publishRelease(publishPath, signedArtifactsWithDigest, artifact) - } - - private def publishSnapshot(publishPath: String, - payloads: Seq[(String, Array[Byte])], - artifact: Artifact): Unit = { - val baseUri: String = snapshotUri + "/" + publishPath - - val publishResults = payloads.map { - case (fileName, data) => - log.info(s"Uploading ${fileName}") - val resp = api.upload(s"${baseUri}/${fileName}", data) - resp - } - reportPublishResults(publishResults, artifact) - } - - private def publishRelease(publishPath: String, - payloads: Seq[(String, Array[Byte])], - artifact: Artifact): Unit = { - val profileUri = api.getStagingProfileUri(artifact.group) - val stagingRepoId = - api.createStagingRepo(profileUri, artifact.group) - val baseUri = - s"${uri}/staging/deployByRepositoryId/${stagingRepoId}/${publishPath}" - - val publishResults = payloads.map { - case (fileName, data) => - log.info(s"Uploading ${fileName}") - api.upload(s"${baseUri}/${fileName}", data) - } - reportPublishResults(publishResults, artifact) - - log.info("Closing staging repository") - api.closeStagingRepo(profileUri, stagingRepoId) - - log.info("Waiting for staging repository to close") - awaitRepoStatus("closed", stagingRepoId) - - log.info("Promoting staging repository") - api.promoteStagingRepo(profileUri, stagingRepoId) - - log.info("Waiting for staging repository to release") - awaitRepoStatus("released", stagingRepoId) - - log.info("Dropping staging repository") - api.dropStagingRepo(profileUri, stagingRepoId) - - log.info(s"Published ${artifact.id} successfully") - } - - private def reportPublishResults(publishResults: Seq[HttpResponse[String]], - artifact: Artifact) = { - if (publishResults.forall(_.is2xx)) { - log.info(s"Published ${artifact.id} to Sonatype") - } else { - val errors = publishResults.filterNot(_.is2xx).map { response => - s"Code: ${response.code}, message: ${response.body}" - } - throw new RuntimeException( - s"Failed to publish ${artifact.id} to Sonatype. Errors: \n${errors.mkString("\n")}" - ) - } - } - - private def awaitRepoStatus(status: String, - stagingRepoId: String, - attempts: Int = 20): Unit = { - def isRightStatus = - api.getStagingRepoState(stagingRepoId).equalsIgnoreCase(status) - var attemptsLeft = attempts - - while (attemptsLeft > 0 && !isRightStatus) { - Thread.sleep(3000) - attemptsLeft -= 1 - if (attemptsLeft == 0) { - throw new RuntimeException( - s"Couldn't wait for staging repository to be ${status}. Failing") - } - } - } - - // http://central.sonatype.org/pages/working-with-pgp-signatures.html#signing-a-file - private def poorMansSign(file: Path, passphrase: String): Path = { - val fileName = file.toString - import ammonite.ops.ImplicitWd._ - %("gpg", "--yes", "-a", "-b", "--passphrase", passphrase, fileName) - Path(fileName + ".asc") - } - - private def md5hex(bytes: Array[Byte]): Array[Byte] = - hexArray(md5.digest(bytes)).getBytes - - private def sha1hex(bytes: Array[Byte]): Array[Byte] = - hexArray(sha1.digest(bytes)).getBytes - - private def md5 = MessageDigest.getInstance("md5") - - private def sha1 = MessageDigest.getInstance("sha1") - - private def hexArray(arr: Array[Byte]) = - String.format("%0" + (arr.length << 1) + "x", new BigInteger(1, arr)) - -} diff --git a/scalalib/src/main/scala/mill/scalalib/publish/package.scala b/scalalib/src/main/scala/mill/scalalib/publish/package.scala deleted file mode 100644 index 99eeec14..00000000 --- a/scalalib/src/main/scala/mill/scalalib/publish/package.scala +++ /dev/null @@ -1,3 +0,0 @@ -package mill.scalalib - -package object publish extends JsonFormatters diff --git a/scalalib/src/main/scala/mill/scalalib/publish/settings.scala b/scalalib/src/main/scala/mill/scalalib/publish/settings.scala deleted file mode 100644 index eb0a44b6..00000000 --- a/scalalib/src/main/scala/mill/scalalib/publish/settings.scala +++ /dev/null @@ -1,70 +0,0 @@ -package mill.scalalib.publish - -import mill.scalalib.Dep - -case class Artifact(group: String, id: String, version: String) { - def isSnapshot: Boolean = version.endsWith("-SNAPSHOT") -} - -object Artifact { - - def fromDep(dep: Dep, scalaFull: String, scalaBin: String): Dependency = { - dep match { - case Dep.Java(dep) => - Dependency( - Artifact(dep.module.organization, dep.module.name, dep.version), - Scope.Compile) - case Dep.Scala(dep) => - Dependency(Artifact(dep.module.organization, - s"${dep.module.name}_${scalaBin}", - dep.version), - Scope.Compile) - case Dep.Point(dep) => - Dependency(Artifact(dep.module.organization, - s"${dep.module.name}_${scalaFull}", - dep.version), - Scope.Compile) - } - } -} - -sealed trait Scope -object Scope { - case object Compile extends Scope - case object Provided extends Scope - case object Runtime extends Scope - case object Test extends Scope -} - -case class Dependency( - artifact: Artifact, - scope: Scope -) - -case class License( - name: String, - url: String, - distribution: String = "repo" -) - -case class SCM( - url: String, - connection: String -) - -case class Developer( - id: String, - name: String, - url: String, - organization: Option[String] = None, - organizationUrl: Option[String] = None -) - -case class PomSettings( - description: String, - organization: String, - url: String, - licenses: Seq[License], - scm: SCM, - developers: Seq[Developer] -) |