All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.alexarchambault.artifact.app.ArtifactApp.scala Maven / Gradle / Ivy

The newest version!
package com.github.alexarchambault.artifact.app

import java.io.File
import java.net.{ URLClassLoader, URI }
import org.slf4j.{Logger, LoggerFactory}
import scalaz._, Scalaz._
import jove.sbt.{ ModuleID, DefaultMavenRepository, Resolver, CrossVersion, Path }
import jove.jvmfork.Fork

object Parsers {
  // TODO Use the resolver parser from conscript?
  def parseResolver(s: String): String \/ Resolver =
    if (s startsWith "sonatype:")
      Resolver.sonatypeRepo(s drop "sonatype:".length).right
    else if (s startsWith "file:")
      Resolver.file(s drop "file:".length).right
    else if (s == "local")
      Resolver.defaultLocal.right
    else if (s == "default-maven")
      DefaultMavenRepository.right
    else
      try {
        val uri = new URI(s)

        if (uri.getScheme == "ssh" || uri.getScheme == "sftp") {
          val params = uri.getQuery.split("&").map(_.split("=", 2)).collect{case Array(k, v) => k -> v }.toMap
          val r =
            if (uri.getScheme == "ssh")
              Resolver.ssh(params.getOrElse("name", s), uri.getHost, uri.getPath)
            else
              Resolver.sftp(params.getOrElse("name", s), uri.getHost, uri.getPath)

          val u = uri.getUserInfo

          {
            if (u contains ":") {
              val t = u.split(":", 2)
              r.as(t(0), t(1))
            } else if (params contains "pkey")
              r.as(u, new File(params("pkey")))
            else
              r
          } .right
        } else
          s"Unrecognized resolver: $s".left
      } catch {
        case _: Exception =>
          s"Unrecognized resolver: $s".left
      }

  def parseResolvers(l: List[String]): String \/ List[Resolver] =
    l.map(_.trim).filter(_.nonEmpty).flatMap(_ split ',') traverseU parseResolver

  def parseModule(s: String): String \/ ModuleID =
    s.split('%').map(_.trim) match {
      case Array(organization, "", name, revision) =>
        ModuleID(organization, name, revision, Some("compile")).cross(CrossVersion.binary).right
      case Array(organization, name, revision) =>
        ModuleID(organization, name, revision, Some("compile")).right
      case _ =>
        s"Unrecognized module: $s".left
    }

  def parseModules(l: List[String]): String \/ List[ModuleID] =
    l.map(_.trim).filter(_.nonEmpty).flatMap(_ split ',') traverseU parseModule
}

object ArtifactApp {

  def apply(
    resolvers: List[String],
    noDefaultResolvers: Boolean,
    snapshotResolvers: Boolean,
    modules: List[String],
    mainClass: String,
    scalaVersion: String,
    forceScalaVersion: Boolean,
    forkJavaOption: List[String],
    fork: Boolean,
    printClassPath: Boolean,
    quiet: Boolean
  ): String \/ (Seq[String] => Unit) =
    temporary(
      resolvers = resolvers,
      noDefaultResolvers = noDefaultResolvers,
      snapshotResolvers = snapshotResolvers,
      modules = modules,
      mainClass = mainClass,
      scalaVersion = scalaVersion,
      forceScalaVersion = forceScalaVersion,
      forkJavaOption = forkJavaOption,
      fork = fork,
      printClassPath = printClassPath,
      quiet = quiet,
      extraResolvers = Nil
    )

  @deprecated("Temporary method, apply should be used instead if possible")
  def temporary(
    resolvers: List[String],
    noDefaultResolvers: Boolean,
    snapshotResolvers: Boolean,
    modules: List[String],
    mainClass: String,
    scalaVersion: String,
    forceScalaVersion: Boolean,
    forkJavaOption: List[String],
    fork: Boolean,
    printClassPath: Boolean,
    quiet: Boolean,
    extraResolvers: List[Resolver]
  ): String \/ (Seq[String] => Unit) = {
    val defaultResolvers = List(
      Resolver.defaultLocal,
      DefaultMavenRepository,
      Resolver sonatypeRepo "releases"
    )

    val defaultSnapshotResolvers = List(
      Resolver sonatypeRepo "snapshots"
    )

    def forcedCp(_cp: Seq[File], _scalaVersion: Option[String], resolvers: List[Resolver], logger: Logger): String \/ Seq[File] = {
      val hasLibrary = _cp.exists(_.getName startsWith "scala-library-")
      val hasReflect = _cp.exists(_.getName startsWith "scala-reflect-")
      val hasCompiler = _cp.exists(_.getName startsWith "scala-compiler-")

      if (hasLibrary || hasReflect || hasCompiler) {
        var scalaModules = List.empty[ModuleID]

        if (hasLibrary)
          scalaModules = ModuleID("org.scala-lang", "scala-library", _scalaVersion.get, Some("compile")) :: scalaModules

        if (hasReflect)
          scalaModules = ModuleID("org.scala-lang", "scala-reflect", _scalaVersion.get, Some("compile")) :: scalaModules

        if (hasCompiler)
          scalaModules = ModuleID("org.scala-lang", "scala-compiler", _scalaVersion.get, Some("compile")) :: scalaModules

        if (!quiet)
          Console.err println s"(Forcing Scala version) Fetching / updating ${scalaModules.length} artifacts"

        val cp =
          IvyUtil.classPath(_scalaVersion, resolvers, scalaModules, logger) .map { forced =>
            val filter = { f: File =>
              val name = f.getName
              name.startsWith("scala-library-") || name.startsWith("scala-compiler-") || name.startsWith("scala-reflect-")
            }

            _cp.filterNot(filter) ++ forced.filter(filter)
          }

        if (!quiet)
          Console.err println s"(Forcing Scala version) Done fetching / updating ${scalaModules.length} artifacts"

        cp
      } else {
        if (!quiet)
          Console.err println s"Warning: no scala JAR found in calculated class path, cannot force scala version"
        \/-(_cp)
      }
    }

    for {
      mainClass0 <- Some(mainClass.trim).filter(_.nonEmpty) toRightDisjunction "No main class specified"
      userResolvers <- Parsers parseResolvers resolvers
      modules <- Parsers parseModules modules
      logger = LoggerFactory getLogger "ArtifactApp"
      resolvers = extraResolvers ++ userResolvers ++ (if (noDefaultResolvers) Nil else defaultResolvers) ++ (if (snapshotResolvers || fork) defaultSnapshotResolvers else Nil)
      _scalaVersion = Some(scalaVersion).filter(_.nonEmpty)
      _cp <- {
        val _modules = modules ++ (if (fork) Seq(ModuleID("com.github.alexarchambault.jove", "jvm-fork", "0.1.0-SNAPSHOT", Some("compile")).cross(CrossVersion.binary)) else Seq())

        if (!quiet)
          Console.err println s"Fetching / updating ${_modules.length} artifacts"

        val cp = IvyUtil.classPath(
          _scalaVersion,
          resolvers,
          _modules,
          logger
        )

        if (!quiet)
          Console.err println s"Done fetching / updating ${_modules.length} artifacts"

        cp
      }
      forcedOpt <- {
        if (_scalaVersion.nonEmpty && forceScalaVersion)
          forcedCp(_cp, _scalaVersion, resolvers, logger).map(Some(_))
        else
          \/-(None)
      }
    } yield {
      if (forceScalaVersion && _scalaVersion.isEmpty && !quiet)
        Console.err println s"Warning: no scala version specified, cannot force scala version"

      val cp = forcedOpt getOrElse _cp

      if (printClassPath) {
        Console.out println "Class path:"
        cp.map(_.getAbsolutePath).sorted.distinct foreach Console.out.println
        Console.out println ""
      }

      if (fork) {
        import concurrent.ExecutionContext.Implicits.global

        args: Seq[String] =>
          val process = Fork(new File("."), forkJavaOption, cp.map(_.getAbsolutePath), mainClass0, args)
          process.waitFor()
          val ret = process.exitValue()
          if (ret != 0)
            sys exit ret
          ()
      } else {
        val classLoader = new URLClassLoader(Path.toURLs(cp), null)
        val clazz = classLoader.loadClass(mainClass0)
        val mainMethod = clazz.getMethod("main", classOf[Array[String]])

        args: Seq[String] =>
          Thread.currentThread setContextClassLoader classLoader
          mainMethod.invoke(null, args.toArray)
          ()
      }
    }
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy