diff options
author | Li Haoyi <haoyi.sg@gmail.com> | 2018-12-12 16:56:02 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-12-12 16:56:02 -0800 |
commit | 9ba4cb69331386dfde9bac69dc2d5b22401face3 (patch) | |
tree | 120349e8015ae5717d36bd44209cde6ff9543518 /main/src/mill/modules/Jvm.scala | |
parent | ea7fceb6e56f53bde3517586dfc57e10a605a524 (diff) | |
download | mill-9ba4cb69331386dfde9bac69dc2d5b22401face3.tar.gz mill-9ba4cb69331386dfde9bac69dc2d5b22401face3.tar.bz2 mill-9ba4cb69331386dfde9bac69dc2d5b22401face3.zip |
collapse boilerplate folder structure within src/ folders (#505)
* collapse boilerplate folder structure within src/ folders
* .
Diffstat (limited to 'main/src/mill/modules/Jvm.scala')
-rw-r--r-- | main/src/mill/modules/Jvm.scala | 481 |
1 files changed, 0 insertions, 481 deletions
diff --git a/main/src/mill/modules/Jvm.scala b/main/src/mill/modules/Jvm.scala deleted file mode 100644 index 1a51ed8b..00000000 --- a/main/src/mill/modules/Jvm.scala +++ /dev/null @@ -1,481 +0,0 @@ -package mill.modules - -import java.io._ -import java.lang.reflect.Modifier -import java.net.URI -import java.nio.file.{FileSystems, Files, StandardOpenOption} -import java.nio.file.attribute.PosixFilePermission -import java.util.Collections -import java.util.jar.{JarEntry, JarFile, JarOutputStream} - -import coursier.{Cache, Dependency, Fetch, Repository, Resolution} -import coursier.util.{Gather, Task} -import geny.Generator -import mill.main.client.InputPumper -import mill.eval.{PathRef, Result} -import mill.util.Ctx -import mill.api.IO -import mill.util.Loose.Agg - -import scala.collection.mutable -import scala.collection.JavaConverters._ - -object Jvm { - /** - * Runs a JVM subprocess with the given configuration and returns a - * [[os.CommandResult]] with it's aggregated output and error streams - */ - def callSubprocess(mainClass: String, - classPath: Agg[os.Path], - jvmArgs: Seq[String] = Seq.empty, - envArgs: Map[String, String] = Map.empty, - mainArgs: Seq[String] = Seq.empty, - workingDir: os.Path = null, - streamOut: Boolean = true) - (implicit ctx: Ctx) = { - - val commandArgs = - Vector("java") ++ - jvmArgs ++ - Vector("-cp", classPath.mkString(File.pathSeparator), mainClass) ++ - mainArgs - - val workingDir1 = Option(workingDir).getOrElse(ctx.dest) - os.makeDir.all(workingDir1) - - os.proc(commandArgs).call(cwd = workingDir1, env = envArgs) - } - - /** - * Runs a JVM subprocess with the given configuration and streams - * it's stdout and stderr to the console. - */ - def runSubprocess(mainClass: String, - classPath: Agg[os.Path], - jvmArgs: Seq[String] = Seq.empty, - envArgs: Map[String, String] = Map.empty, - mainArgs: Seq[String] = Seq.empty, - workingDir: os.Path = null, - background: Boolean = false): Unit = { - val args = - Vector("java") ++ - jvmArgs ++ - Vector("-cp", classPath.mkString(File.pathSeparator), mainClass) ++ - mainArgs - - if (background) spawnSubprocess(args, envArgs, workingDir) - else runSubprocess(args, envArgs, workingDir) - } - - @deprecated("Use runSubprocess instead") - def baseInteractiveSubprocess(commandArgs: Seq[String], - envArgs: Map[String, String], - workingDir: os.Path) = { - runSubprocess(commandArgs, envArgs, workingDir) - } - - /** - * Runs a generic subprocess and waits for it to terminate. - */ - def runSubprocess(commandArgs: Seq[String], - envArgs: Map[String, String], - workingDir: os.Path) = { - val process = spawnSubprocess(commandArgs, envArgs, workingDir) - - process.waitFor() - if (process.exitCode() == 0) () - else throw new Exception("Interactive Subprocess Failed") - } - - /** - * Spawns a generic subprocess, streaming the stdout and stderr to the - * console. If the System.out/System.err have been substituted, makes sure - * that the subprocess's stdout and stderr streams go to the subtituted - * streams - */ - def spawnSubprocess(commandArgs: Seq[String], - envArgs: Map[String, String], - workingDir: os.Path) = { - // If System.in is fake, then we pump output manually rather than relying - // on `os.Inherit`. That is because `os.Inherit` does not follow changes - // to System.in/System.out/System.err, so the subprocess's streams get sent - // to the parent process's origin outputs even if we want to direct them - // elsewhere - if (System.in.isInstanceOf[ByteArrayInputStream]){ - val process = os.proc(commandArgs).spawn( - cwd = workingDir, - env = envArgs, - stdin = os.Pipe, - stdout = os.Pipe, - stderr = os.Pipe - ) - - val sources = Seq( - process.stdout -> System.out, - process.stderr -> System.err, - System.in -> process.stdin - ) - - for((std, dest) <- sources){ - new Thread(new InputPumper(std, dest, false)).start() - } - - process - }else{ - os.proc(commandArgs).spawn( - cwd = workingDir, - env = envArgs, - stdin = os.Inherit, - stdout = os.Inherit, - stderr = os.Inherit - ) - } - } - - - def runLocal(mainClass: String, - classPath: Agg[os.Path], - mainArgs: Seq[String] = Seq.empty) - (implicit ctx: Ctx): Unit = { - inprocess(classPath, classLoaderOverrideSbtTesting = false, isolated = true, closeContextClassLoaderWhenDone = true, cl => { - getMainMethod(mainClass, cl).invoke(null, mainArgs.toArray) - }) - } - - private def getMainMethod(mainClassName: String, cl: ClassLoader) = { - val mainClass = cl.loadClass(mainClassName) - val method = mainClass.getMethod("main", classOf[Array[String]]) - // jvm allows the actual main class to be non-public and to run a method in the non-public class, - // we need to make it accessible - method.setAccessible(true) - val modifiers = method.getModifiers - if (!Modifier.isPublic(modifiers)) - throw new NoSuchMethodException(mainClassName + ".main is not public") - if (!Modifier.isStatic(modifiers)) - throw new NoSuchMethodException(mainClassName + ".main is not static") - method - } - - - def inprocess[T](classPath: Agg[os.Path], - classLoaderOverrideSbtTesting: Boolean, - isolated: Boolean, - closeContextClassLoaderWhenDone: Boolean, - body: ClassLoader => T) - (implicit ctx: Ctx.Home): T = { - val urls = classPath.map(_.toIO.toURI.toURL) - val cl = if (classLoaderOverrideSbtTesting) { - val outerClassLoader = getClass.getClassLoader - mill.api.ClassLoader.create(urls.toVector, null, customFindClass = { name => - if (name.startsWith("sbt.testing.")) - Some(outerClassLoader.loadClass(name)) - else None - }) - } else if (isolated) { - mill.api.ClassLoader.create(urls.toVector, null) - } else { - mill.api.ClassLoader.create(urls.toVector, getClass.getClassLoader) - } - - val oldCl = Thread.currentThread().getContextClassLoader - Thread.currentThread().setContextClassLoader(cl) - try { - body(cl) - } finally { - if (closeContextClassLoaderWhenDone) { - Thread.currentThread().setContextClassLoader(oldCl) - cl.close() - } - } - } - - - private def createManifest(mainClass: Option[String]) = { - val m = new java.util.jar.Manifest() - m.getMainAttributes.put(java.util.jar.Attributes.Name.MANIFEST_VERSION, "1.0") - m.getMainAttributes.putValue( "Created-By", "Scala mill" ) - mainClass.foreach( - m.getMainAttributes.put(java.util.jar.Attributes.Name.MAIN_CLASS, _) - ) - m - } - - /** - * Create a jar file containing all files from the specified input Paths, - * called out.jar in the implicit ctx.dest folder. An optional main class may - * be provided for the jar. An optional filter function may also be provided to - * selectively include/exclude specific files. - * @param inputPaths - `Agg` of `os.Path`s containing files to be included in the jar - * @param mainClass - optional main class for the jar - * @param fileFilter - optional file filter to select files to be included. - * Given a `os.Path` (from inputPaths) and a `os.RelPath` for the individual file, - * return true if the file is to be included in the jar. - * @param ctx - implicit `Ctx.Dest` used to determine the output directory for the jar. - * @return - a `PathRef` for the created jar. - */ - def createJar(inputPaths: Agg[os.Path], - mainClass: Option[String] = None, - fileFilter: (os.Path, os.RelPath) => Boolean = (p: os.Path, r: os.RelPath) => true) - (implicit ctx: Ctx.Dest): PathRef = { - val outputPath = ctx.dest / "out.jar" - os.remove.all(outputPath) - - val seen = mutable.Set.empty[os.RelPath] - seen.add(os.rel / "META-INF" / "MANIFEST.MF") - val jar = new JarOutputStream( - new FileOutputStream(outputPath.toIO), - createManifest(mainClass) - ) - - try{ - assert(inputPaths.forall(os.exists(_))) - for{ - p <- inputPaths - (file, mapping) <- - if (os.isFile(p)) Iterator(p -> os.rel / p.last) - else os.walk(p).filter(os.isFile).map(sub => sub -> sub.relativeTo(p)).sorted - if !seen(mapping) && fileFilter(p, mapping) - } { - seen.add(mapping) - val entry = new JarEntry(mapping.toString) - entry.setTime(os.mtime(file)) - jar.putNextEntry(entry) - jar.write(os.read.bytes(file)) - jar.closeEntry() - } - } finally { - jar.close() - } - - PathRef(outputPath) - } - - def createAssembly(inputPaths: Agg[os.Path], - mainClass: Option[String] = None, - prependShellScript: String = "", - base: Option[os.Path] = None, - assemblyRules: Seq[Assembly.Rule] = Assembly.defaultRules) - (implicit ctx: Ctx.Dest with Ctx.Log): PathRef = { - - val tmp = ctx.dest / "out-tmp.jar" - - val baseUri = "jar:" + tmp.toIO.getCanonicalFile.toURI.toASCIIString - val hm = new java.util.HashMap[String, String]() - - base match{ - case Some(b) => os.copy(b, tmp) - case None => hm.put("create", "true") - } - - val zipFs = FileSystems.newFileSystem(URI.create(baseUri), hm) - - val manifest = createManifest(mainClass) - val manifestPath = zipFs.getPath(JarFile.MANIFEST_NAME) - Files.createDirectories(manifestPath.getParent) - val manifestOut = Files.newOutputStream( - manifestPath, - StandardOpenOption.TRUNCATE_EXISTING, - StandardOpenOption.CREATE - ) - manifest.write(manifestOut) - manifestOut.close() - - Assembly.groupAssemblyEntries(inputPaths, assemblyRules).view - .foreach { - case (mapping, AppendEntry(entries)) => - val path = zipFs.getPath(mapping).toAbsolutePath - val concatenated = new SequenceInputStream( - Collections.enumeration(entries.map(_.inputStream).asJava)) - writeEntry(path, concatenated, append = true) - case (mapping, WriteOnceEntry(entry)) => - val path = zipFs.getPath(mapping).toAbsolutePath - if (Files.notExists(path)) { - writeEntry(path, entry.inputStream, append = false) - } - } - - zipFs.close() - val output = ctx.dest / "out.jar" - - // Prepend shell script and make it executable - if (prependShellScript.isEmpty) os.move(tmp, output) - else{ - val lineSep = if (!prependShellScript.endsWith("\n")) "\n\r\n" else "" - os.write( - output, - Seq[os.Source]( - prependShellScript + lineSep, - os.read.inputStream(tmp) - ) - ) - - if (!scala.util.Properties.isWin) { - os.perms.set( - output, - os.perms(output) - + PosixFilePermission.GROUP_EXECUTE - + PosixFilePermission.OWNER_EXECUTE - + PosixFilePermission.OTHERS_EXECUTE - ) - } - } - - PathRef(output) - } - - private def writeEntry(p: java.nio.file.Path, is: InputStream, append: Boolean): Unit = { - if (p.getParent != null) Files.createDirectories(p.getParent) - val options = - if(append) Seq(StandardOpenOption.APPEND, StandardOpenOption.CREATE) - else Seq(StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE) - - val outputStream = java.nio.file.Files.newOutputStream(p, options:_*) - IO.stream(is, outputStream) - outputStream.close() - is.close() - } - def universalScript(shellCommands: String, - cmdCommands: String, - shebang: Boolean = false): String = { - Seq( - if (shebang) "#!/usr/bin/env sh" else "", - "@ 2>/dev/null # 2>nul & echo off & goto BOF\r", - ":", - shellCommands.replaceAll("\r\n|\n", "\n"), - "exit", - Seq( - "", - ":BOF", - "@echo off", - cmdCommands.replaceAll("\r\n|\n", "\r\n"), - "exit /B %errorlevel%", - "" - ).mkString("\r\n") - ).filterNot(_.isEmpty).mkString("\n") - } - - def launcherUniversalScript(mainClass: String, - shellClassPath: Agg[String], - cmdClassPath: Agg[String], - jvmArgs: Seq[String], - shebang: Boolean = false) = { - universalScript( - shellCommands = - s"""exec java ${jvmArgs.mkString(" ")} $$JAVA_OPTS -cp "${shellClassPath.mkString(":")}" $mainClass "$$@"""", - cmdCommands = - s"""java ${jvmArgs.mkString(" ")} %JAVA_OPTS% -cp "${cmdClassPath.mkString(";")}" $mainClass %*""", - shebang = shebang - ) - } - def createLauncher(mainClass: String, - classPath: Agg[os.Path], - jvmArgs: Seq[String]) - (implicit ctx: Ctx.Dest)= { - val isWin = scala.util.Properties.isWin - val isBatch = isWin && - !(org.jline.utils.OSUtils.IS_CYGWIN - || org.jline.utils.OSUtils.IS_MINGW - || "MSYS" == System.getProperty("MSYSTEM")) - val outputPath = ctx.dest / (if (isBatch) "run.bat" else "run") - val classPathStrs = classPath.map(_.toString) - - os.write(outputPath, launcherUniversalScript(mainClass, classPathStrs, classPathStrs, jvmArgs)) - - if (!isWin) { - val perms = Files.getPosixFilePermissions(outputPath.toNIO) - perms.add(PosixFilePermission.GROUP_EXECUTE) - perms.add(PosixFilePermission.OWNER_EXECUTE) - perms.add(PosixFilePermission.OTHERS_EXECUTE) - Files.setPosixFilePermissions(outputPath.toNIO, perms) - } - PathRef(outputPath) - } - - /** - * Resolve dependencies using Coursier. - * - * We do not bother breaking this out into the separate ZincWorkerApi classpath, - * because Coursier is already bundled with mill/Ammonite to support the - * `import $ivy` syntax. - */ - def resolveDependencies(repositories: Seq[Repository], - deps: TraversableOnce[coursier.Dependency], - force: TraversableOnce[coursier.Dependency], - sources: Boolean = false, - mapDependencies: Option[Dependency => Dependency] = None): Result[Agg[PathRef]] = { - - val (_, resolution) = resolveDependenciesMetadata( - repositories, deps, force, mapDependencies - ) - 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 { - - def load(artifacts: Seq[coursier.Artifact]) = { - val logger = None - - import scala.concurrent.ExecutionContext.Implicits.global - val loadedArtifacts = Gather[Task].gather( - for (a <- artifacts) - yield coursier.Cache.file[Task](a, logger = logger).run - .map(a.isOptional -> _) - ).unsafeRun - - val errors = loadedArtifacts.collect { - case (false, Left(x)) => x - case (true, Left(x)) if !x.notFound => x - } - val successes = loadedArtifacts.collect { case (_, Right(x)) => x } - (errors, successes) - } - - val sourceOrJar = - if (sources) resolution.classifiersArtifacts(Seq("sources")) - else resolution.artifacts(true) - val (errors, successes) = load(sourceOrJar) - if(errors.isEmpty){ - mill.Agg.from( - successes.map(p => PathRef(os.Path(p), quick = true)).filter(_.path.ext == "jar") - ) - }else{ - val errorDetails = errors.map(e => s"${ammonite.util.Util.newLine} ${e.describe}").mkString - Result.Failure("Failed to load source dependencies" + errorDetails) - } - } - } - - - def resolveDependenciesMetadata(repositories: Seq[Repository], - deps: TraversableOnce[coursier.Dependency], - force: TraversableOnce[coursier.Dependency], - mapDependencies: Option[Dependency => Dependency] = None) = { - - val forceVersions = force - .map(mapDependencies.getOrElse(identity[Dependency](_))) - .map{d => d.module -> d.version} - .toMap - - val start = Resolution( - deps.map(mapDependencies.getOrElse(identity[Dependency](_))).toSet, - forceVersions = forceVersions, - mapDependencies = mapDependencies - ) - - val fetch = Fetch.from(repositories, Cache.fetch[Task]()) - - import scala.concurrent.ExecutionContext.Implicits.global - val resolution = start.process.run(fetch).unsafeRun() - (deps.toSeq, resolution) - } -} |