mirror of https://github.com/sbt/sbt.git
Merge pull request #68 from alexarchambault/topic/develop
Latest developments
This commit is contained in:
commit
a89c0d92e3
|
|
@ -1,7 +1,7 @@
|
|||
language: scala
|
||||
scala:
|
||||
- 2.11.6
|
||||
- 2.10.5
|
||||
- 2.11.7
|
||||
- 2.10.6
|
||||
jdk:
|
||||
- oraclejdk7
|
||||
- oraclejdk8
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ install:
|
|||
build_script:
|
||||
- sbt clean compile publish-local
|
||||
test_script:
|
||||
- sbt core-jvm/test # Would node be around for core-js/test?
|
||||
- sbt coreJVM/test # Would node be around for coreJS/test?
|
||||
cache:
|
||||
- C:\sbt\
|
||||
- C:\Users\appveyor\.m2
|
||||
|
|
|
|||
|
|
@ -0,0 +1,143 @@
|
|||
package coursier
|
||||
|
||||
import java.io.{ ByteArrayOutputStream, InputStream, File }
|
||||
import java.net.{ URI, URLClassLoader }
|
||||
import java.nio.file.Files
|
||||
import java.util.concurrent.{ Executors, ThreadFactory }
|
||||
|
||||
import scala.concurrent.duration.Duration
|
||||
import scala.concurrent.{ ExecutionContext, Future, Await }
|
||||
|
||||
import scala.util.{ Try, Success, Failure }
|
||||
|
||||
object Bootstrap extends App {
|
||||
|
||||
val concurrentDownloadCount = 6
|
||||
val threadFactory = new ThreadFactory {
|
||||
// from scalaz Strategy.DefaultDaemonThreadFactory
|
||||
val defaultThreadFactory = Executors.defaultThreadFactory()
|
||||
def newThread(r: Runnable) = {
|
||||
val t = defaultThreadFactory.newThread(r)
|
||||
t.setDaemon(true)
|
||||
t
|
||||
}
|
||||
}
|
||||
val defaultPool = Executors.newFixedThreadPool(concurrentDownloadCount, threadFactory)
|
||||
implicit val ec = ExecutionContext.fromExecutorService(defaultPool)
|
||||
|
||||
private def readFullySync(is: InputStream) = {
|
||||
val buffer = new ByteArrayOutputStream()
|
||||
val data = Array.ofDim[Byte](16384)
|
||||
|
||||
var nRead = is.read(data, 0, data.length)
|
||||
while (nRead != -1) {
|
||||
buffer.write(data, 0, nRead)
|
||||
nRead = is.read(data, 0, data.length)
|
||||
}
|
||||
|
||||
buffer.flush()
|
||||
buffer.toByteArray
|
||||
}
|
||||
|
||||
private def errPrintln(s: String): Unit =
|
||||
Console.err.println(s)
|
||||
|
||||
private def exit(msg: String = ""): Nothing = {
|
||||
if (msg.nonEmpty)
|
||||
errPrintln(msg)
|
||||
sys.exit(255)
|
||||
}
|
||||
|
||||
args match {
|
||||
case Array(mainClass0, jarDir0, remainingArgs @ _*) =>
|
||||
val jarDir = new File(jarDir0)
|
||||
|
||||
if (jarDir.exists()) {
|
||||
if (!jarDir.isDirectory)
|
||||
exit(s"Error: $jarDir0 is not a directory")
|
||||
} else if (!jarDir.mkdirs())
|
||||
errPrintln(s"Warning: cannot create $jarDir0, continuing anyway.")
|
||||
|
||||
val splitIdx = remainingArgs.indexOf("--")
|
||||
val (jarStrUrls, userArgs) =
|
||||
if (splitIdx < 0)
|
||||
(remainingArgs, Nil)
|
||||
else
|
||||
(remainingArgs.take(splitIdx), remainingArgs.drop(splitIdx + 1))
|
||||
|
||||
val tryUrls = jarStrUrls.map(urlStr => urlStr -> Try(URI.create(urlStr).toURL))
|
||||
|
||||
val failedUrls = tryUrls.collect {
|
||||
case (strUrl, Failure(t)) => strUrl -> t
|
||||
}
|
||||
if (failedUrls.nonEmpty)
|
||||
exit(
|
||||
s"Error parsing ${failedUrls.length} URL(s):\n" +
|
||||
failedUrls.map { case (s, t) => s"$s: ${t.getMessage}" }.mkString("\n")
|
||||
)
|
||||
|
||||
val jarUrls = tryUrls.collect {
|
||||
case (_, Success(url)) => url
|
||||
}
|
||||
|
||||
val jarLocalUrlFutures = jarUrls.map { url =>
|
||||
if (url.getProtocol == "file")
|
||||
Future.successful(url)
|
||||
else
|
||||
Future {
|
||||
val path = url.getPath
|
||||
val idx = path.lastIndexOf('/')
|
||||
// FIXME Add other components in path to prevent conflicts?
|
||||
val fileName = path.drop(idx + 1)
|
||||
val dest = new File(jarDir, fileName)
|
||||
|
||||
// FIXME If dest exists, do a HEAD request and check that its size or last modified time is OK?
|
||||
|
||||
if (!dest.exists()) {
|
||||
Console.err.println(s"Downloading $url")
|
||||
try {
|
||||
val conn = url.openConnection()
|
||||
val lastModified = conn.getLastModified
|
||||
val s = conn.getInputStream
|
||||
val b = readFullySync(s)
|
||||
Files.write(dest.toPath, b)
|
||||
dest.setLastModified(lastModified)
|
||||
} catch { case e: Exception =>
|
||||
Console.err.println(s"Error while downloading $url: ${e.getMessage}, ignoring it")
|
||||
}
|
||||
}
|
||||
|
||||
dest.toURI.toURL
|
||||
}
|
||||
}
|
||||
|
||||
val jarLocalUrls = Await.result(Future.sequence(jarLocalUrlFutures), Duration.Inf)
|
||||
|
||||
val thread = Thread.currentThread()
|
||||
val parentClassLoader = thread.getContextClassLoader
|
||||
|
||||
val classLoader = new URLClassLoader(jarLocalUrls.toArray, parentClassLoader)
|
||||
|
||||
val mainClass =
|
||||
try classLoader.loadClass(mainClass0)
|
||||
catch { case e: ClassNotFoundException =>
|
||||
exit(s"Error: class $mainClass0 not found")
|
||||
}
|
||||
|
||||
val mainMethod =
|
||||
try mainClass.getMethod("main", classOf[Array[String]])
|
||||
catch { case e: NoSuchMethodException =>
|
||||
exit(s"Error: main method not found in class $mainClass0")
|
||||
}
|
||||
|
||||
thread.setContextClassLoader(classLoader)
|
||||
try mainMethod.invoke(null, userArgs.toArray)
|
||||
finally {
|
||||
thread.setContextClassLoader(parentClassLoader)
|
||||
}
|
||||
|
||||
case _ =>
|
||||
exit("Usage: bootstrap main-class JAR-directory JAR-URLs...")
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,174 @@
|
|||
import sbtrelease.ReleasePlugin.ReleaseKeys.{ publishArtifactsAction, versionBump }
|
||||
import sbtrelease.Version.Bump
|
||||
|
||||
lazy val publishingSettings = Seq(
|
||||
publishMavenStyle := true,
|
||||
publishTo := {
|
||||
val nexus = "https://oss.sonatype.org/"
|
||||
if (isSnapshot.value)
|
||||
Some("snapshots" at nexus + "content/repositories/snapshots")
|
||||
else
|
||||
Some("releases" at nexus + "service/local/staging/deploy/maven2")
|
||||
},
|
||||
licenses := Seq("Apache 2.0" -> url("http://opensource.org/licenses/Apache-2.0")),
|
||||
homepage := Some(url("https://github.com/alexarchambault/coursier")),
|
||||
developers := List(
|
||||
Developer("alexarchambault", "Alexandre Archambault", "", url("https://github.com/alexarchambault"))
|
||||
),
|
||||
pomExtra := {
|
||||
<scm>
|
||||
<connection>scm:git:github.com/alexarchambault/coursier.git</connection>
|
||||
<developerConnection>scm:git:git@github.com:alexarchambault/coursier.git</developerConnection>
|
||||
<url>github.com/alexarchambault/coursier.git</url>
|
||||
</scm>
|
||||
},
|
||||
credentials += {
|
||||
Seq("SONATYPE_USER", "SONATYPE_PASS").map(sys.env.get) match {
|
||||
case Seq(Some(user), Some(pass)) =>
|
||||
Credentials("Sonatype Nexus Repository Manager", "oss.sonatype.org", user, pass)
|
||||
case _ =>
|
||||
Credentials(Path.userHome / ".ivy2" / ".credentials")
|
||||
}
|
||||
},
|
||||
versionBump := Bump.Bugfix,
|
||||
publishArtifactsAction := PgpKeys.publishSigned.value
|
||||
) ++ releaseSettings
|
||||
|
||||
lazy val noPublishSettings = Seq(
|
||||
publish := (),
|
||||
publishLocal := (),
|
||||
publishArtifact := false
|
||||
)
|
||||
|
||||
lazy val commonSettings = Seq(
|
||||
organization := "com.github.alexarchambault",
|
||||
scalaVersion := "2.11.7",
|
||||
crossScalaVersions := Seq("2.10.6", "2.11.7"),
|
||||
resolvers ++= Seq(
|
||||
"Scalaz Bintray Repo" at "http://dl.bintray.com/scalaz/releases",
|
||||
Resolver.sonatypeRepo("releases"),
|
||||
Resolver.sonatypeRepo("snapshots")
|
||||
),
|
||||
libraryDependencies ++= {
|
||||
if (scalaVersion.value startsWith "2.10.")
|
||||
Seq(compilerPlugin("org.scalamacros" % "paradise" % "2.0.1" cross CrossVersion.full))
|
||||
else
|
||||
Seq()
|
||||
}
|
||||
)
|
||||
|
||||
lazy val core = crossProject
|
||||
.settings(commonSettings: _*)
|
||||
.settings(publishingSettings: _*)
|
||||
.settings(
|
||||
name := "coursier",
|
||||
libraryDependencies += "org.scala-lang.modules" %% "scala-async" % "0.9.1" % "provided",
|
||||
unmanagedResourceDirectories in Compile += (baseDirectory in LocalRootProject).value / "core" / "shared" / "src" / "main" / "resources",
|
||||
unmanagedResourceDirectories in Test += (baseDirectory in LocalRootProject).value / "core" / "shared" / "src" / "test" / "resources",
|
||||
testFrameworks += new TestFramework("utest.runner.Framework")
|
||||
)
|
||||
.jvmSettings(
|
||||
libraryDependencies ++= Seq(
|
||||
"org.scalaz" %% "scalaz-concurrent" % "7.1.2",
|
||||
"com.lihaoyi" %% "utest" % "0.3.0" % "test"
|
||||
) ++ {
|
||||
if (scalaVersion.value.startsWith("2.10.")) Seq()
|
||||
else Seq(
|
||||
"org.scala-lang.modules" %% "scala-xml" % "1.0.3"
|
||||
)
|
||||
}
|
||||
)
|
||||
.jsSettings(
|
||||
libraryDependencies ++= Seq(
|
||||
"org.scala-js" %%% "scalajs-dom" % "0.8.0",
|
||||
"com.github.japgolly.fork.scalaz" %%% "scalaz-core" % (if (scalaVersion.value.startsWith("2.10.")) "7.1.1" else "7.1.2"),
|
||||
"be.doeraene" %%% "scalajs-jquery" % "0.8.0",
|
||||
"com.lihaoyi" %%% "utest" % "0.3.0" % "test"
|
||||
),
|
||||
postLinkJSEnv := NodeJSEnv().value,
|
||||
scalaJSStage in Global := FastOptStage
|
||||
)
|
||||
|
||||
lazy val coreJvm = core.jvm
|
||||
lazy val coreJs = core.js
|
||||
|
||||
lazy val files = project
|
||||
.dependsOn(coreJvm)
|
||||
.settings(commonSettings)
|
||||
.settings(publishingSettings)
|
||||
.settings(
|
||||
name := "coursier-files",
|
||||
libraryDependencies ++= Seq(
|
||||
"com.lihaoyi" %% "utest" % "0.3.0" % "test"
|
||||
),
|
||||
testFrameworks += new TestFramework("utest.runner.Framework")
|
||||
)
|
||||
|
||||
lazy val cli = project
|
||||
.dependsOn(coreJvm, files)
|
||||
.settings(commonSettings)
|
||||
.settings(publishingSettings)
|
||||
.settings(packAutoSettings ++ publishPackTxzArchive ++ publishPackZipArchive)
|
||||
.settings(
|
||||
packArchivePrefix := s"coursier-cli_${scalaBinaryVersion.value}",
|
||||
packArchiveTxzArtifact := Artifact("coursier-cli", "arch", "tar.xz"),
|
||||
packArchiveZipArtifact := Artifact("coursier-cli", "arch", "zip")
|
||||
)
|
||||
.settings(
|
||||
name := "coursier-cli",
|
||||
libraryDependencies ++= Seq(
|
||||
"com.github.alexarchambault" %% "case-app" % "1.0.0-SNAPSHOT",
|
||||
"ch.qos.logback" % "logback-classic" % "1.1.3"
|
||||
),
|
||||
resourceGenerators in Compile += assembly.in(bootstrap).in(assembly).map { jar =>
|
||||
Seq(jar)
|
||||
}.taskValue
|
||||
)
|
||||
|
||||
lazy val web = project
|
||||
.enablePlugins(ScalaJSPlugin)
|
||||
.dependsOn(coreJs)
|
||||
.settings(commonSettings)
|
||||
.settings(noPublishSettings)
|
||||
.settings(
|
||||
libraryDependencies ++= {
|
||||
if (scalaVersion.value startsWith "2.10.")
|
||||
Seq()
|
||||
else
|
||||
Seq("com.github.japgolly.scalajs-react" %%% "core" % "0.9.0")
|
||||
},
|
||||
sourceDirectory := {
|
||||
val dir = sourceDirectory.value
|
||||
|
||||
if (scalaVersion.value startsWith "2.10.")
|
||||
dir / "dummy"
|
||||
else
|
||||
dir
|
||||
},
|
||||
test in Test := (),
|
||||
testOnly in Test := (),
|
||||
resolvers += "Webjars Bintray" at "https://dl.bintray.com/webjars/maven/",
|
||||
jsDependencies ++= Seq(
|
||||
("org.webjars.bower" % "bootstrap" % "3.3.4" intransitive()) / "bootstrap.min.js" commonJSName "Bootstrap",
|
||||
("org.webjars.bower" % "react" % "0.12.2" intransitive()) / "react-with-addons.js" commonJSName "React",
|
||||
("org.webjars.bower" % "bootstrap-treeview" % "1.2.0" intransitive()) / "bootstrap-treeview.min.js" commonJSName "Treeview",
|
||||
("org.webjars.bower" % "raphael" % "2.1.4" intransitive()) / "raphael-min.js" commonJSName "Raphael"
|
||||
)
|
||||
)
|
||||
|
||||
lazy val bootstrap = project
|
||||
.settings(commonSettings)
|
||||
.settings(noPublishSettings)
|
||||
.settings(
|
||||
name := "coursier-bootstrap",
|
||||
assemblyJarName in assembly := s"bootstrap.jar"
|
||||
)
|
||||
|
||||
lazy val `coursier` = project.in(file("."))
|
||||
.aggregate(coreJvm, coreJs, files, cli, web, bootstrap)
|
||||
.settings(commonSettings)
|
||||
.settings(noPublishSettings)
|
||||
.settings(
|
||||
(unmanagedSourceDirectories in Compile) := Nil,
|
||||
(unmanagedSourceDirectories in Test) := Nil
|
||||
)
|
||||
|
|
@ -1,330 +1,321 @@
|
|||
package coursier
|
||||
package cli
|
||||
|
||||
import java.io.File
|
||||
import java.io.{ File, IOException }
|
||||
import java.net.URLClassLoader
|
||||
import java.nio.file.{ Files => NIOFiles }
|
||||
|
||||
import caseapp._
|
||||
import coursier.core.{ MavenRepository, Parse, CachePolicy }
|
||||
|
||||
import scalaz.{ \/-, -\/ }
|
||||
import scalaz.concurrent.Task
|
||||
|
||||
case class Coursier(
|
||||
case class CommonOptions(
|
||||
@HelpMessage("Keep optional dependencies (Maven)")
|
||||
keepOptional: Boolean,
|
||||
@HelpMessage("Fetch main artifacts (default: true if --classpath is specified and sources and javadoc are not fetched, else false)")
|
||||
@ExtraName("J")
|
||||
default: Boolean,
|
||||
@HelpMessage("Fetch source artifacts")
|
||||
@ExtraName("S")
|
||||
sources: Boolean,
|
||||
@HelpMessage("Fetch javadoc artifacts")
|
||||
@ExtraName("D")
|
||||
javadoc: Boolean,
|
||||
@HelpMessage("Print java -cp compatible classpath (use like java -cp $(coursier -P ..dependencies..) )")
|
||||
@ExtraName("P")
|
||||
@ExtraName("cp")
|
||||
classpath: Boolean,
|
||||
@HelpMessage("Off-line mode: only use cache and local repositories")
|
||||
@ExtraName("c")
|
||||
@ExtraName("c")
|
||||
offline: Boolean,
|
||||
@HelpMessage("Force download: for remote repositories only: re-download items, that is, don't use cache directly")
|
||||
@ExtraName("f")
|
||||
@ExtraName("f")
|
||||
force: Boolean,
|
||||
@HelpMessage("Quiet output")
|
||||
@ExtraName("q")
|
||||
@ExtraName("q")
|
||||
quiet: Boolean,
|
||||
@HelpMessage("Increase verbosity (specify several times to increase more)")
|
||||
@ExtraName("v")
|
||||
@ExtraName("v")
|
||||
verbose: List[Unit],
|
||||
@HelpMessage("Maximum number of resolution iterations (specify a negative value for unlimited, default: 100)")
|
||||
@ExtraName("N")
|
||||
@ExtraName("N")
|
||||
maxIterations: Int = 100,
|
||||
@HelpMessage("Repositories - for multiple repositories, separate with comma and/or repeat this option (e.g. -r central,ivy2local -r sonatype-snapshots, or equivalently -r central,ivy2local,sonatype-snapshots)")
|
||||
@ExtraName("r")
|
||||
@ExtraName("r")
|
||||
repository: List[String],
|
||||
@HelpMessage("Maximim number of parallel downloads (default: 6)")
|
||||
@ExtraName("n")
|
||||
@HelpMessage("Maximum number of parallel downloads (default: 6)")
|
||||
@ExtraName("n")
|
||||
parallel: Int = 6
|
||||
) extends App {
|
||||
) {
|
||||
val verbose0 = verbose.length + (if (quiet) 1 else 0)
|
||||
}
|
||||
|
||||
val verbose0 = {
|
||||
verbose.length +
|
||||
(if (quiet) 1 else 0)
|
||||
@AppName("Coursier")
|
||||
@ProgName("coursier")
|
||||
sealed trait CoursierCommand extends Command
|
||||
|
||||
case class Fetch(
|
||||
@HelpMessage("Fetch source artifacts")
|
||||
@ExtraName("S")
|
||||
sources: Boolean,
|
||||
@HelpMessage("Fetch javadoc artifacts")
|
||||
@ExtraName("D")
|
||||
javadoc: Boolean,
|
||||
@Recurse
|
||||
common: CommonOptions
|
||||
) extends CoursierCommand {
|
||||
|
||||
val helper = new Helper(common, remainingArgs)
|
||||
|
||||
val files0 = helper.fetch(main = true, sources = false, javadoc = false)
|
||||
|
||||
Console.out.println(
|
||||
files0
|
||||
.map(_.toString)
|
||||
.mkString("\n")
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
case class Launch(
|
||||
@ExtraName("M")
|
||||
@ExtraName("main")
|
||||
mainClass: String,
|
||||
@Recurse
|
||||
common: CommonOptions
|
||||
) extends CoursierCommand {
|
||||
|
||||
val (rawDependencies, extraArgs) = {
|
||||
val idxOpt = Some(remainingArgs.indexOf("--")).filter(_ >= 0)
|
||||
idxOpt.fold((remainingArgs, Seq.empty[String])) { idx =>
|
||||
val (l, r) = remainingArgs.splitAt(idx)
|
||||
assert(r.nonEmpty)
|
||||
(l, r.tail)
|
||||
}
|
||||
}
|
||||
|
||||
def fileRepr(f: File) = f.toString
|
||||
val helper = new Helper(common, rawDependencies)
|
||||
|
||||
def println(s: String) = Console.err.println(s)
|
||||
val files0 = helper.fetch(main = true, sources = false, javadoc = false)
|
||||
|
||||
|
||||
if (force && offline) {
|
||||
println("Error: --offline (-c) and --force (-f) options can't be specified at the same time.")
|
||||
def printParents(cl: ClassLoader): Unit =
|
||||
Option(cl.getParent) match {
|
||||
case None =>
|
||||
case Some(cl0) =>
|
||||
println(cl0.toString)
|
||||
printParents(cl0)
|
||||
}
|
||||
|
||||
printParents(Thread.currentThread().getContextClassLoader)
|
||||
|
||||
import scala.collection.JavaConverters._
|
||||
val cl = new URLClassLoader(
|
||||
files0.map(_.toURI.toURL).toArray,
|
||||
// setting this to null provokes strange things (wrt terminal, ...)
|
||||
// but this is far from perfect: this puts all our dependencies along with the user's,
|
||||
// and with a higher priority
|
||||
Thread.currentThread().getContextClassLoader
|
||||
)
|
||||
|
||||
val mainClass0 =
|
||||
if (mainClass.nonEmpty)
|
||||
mainClass
|
||||
else {
|
||||
val metaInfs = cl.findResources("META-INF/MANIFEST.MF").asScala.toVector
|
||||
val mainClasses = metaInfs.flatMap { url =>
|
||||
Option(new java.util.jar.Manifest(url.openStream()).getMainAttributes.getValue("Main-Class"))
|
||||
}
|
||||
|
||||
if (mainClasses.isEmpty) {
|
||||
println(s"No main class found. Specify one with -M or --main.")
|
||||
sys.exit(255)
|
||||
}
|
||||
|
||||
if (common.verbose0 >= 0)
|
||||
println(s"Found ${mainClasses.length} main class(es):\n${mainClasses.map(" " + _).mkString("\n")}")
|
||||
|
||||
mainClasses.head
|
||||
}
|
||||
|
||||
val cls =
|
||||
try cl.loadClass(mainClass0)
|
||||
catch { case e: ClassNotFoundException =>
|
||||
println(s"Error: class $mainClass0 not found")
|
||||
sys.exit(255)
|
||||
}
|
||||
val method =
|
||||
try cls.getMethod("main", classOf[Array[String]])
|
||||
catch { case e: NoSuchMethodError =>
|
||||
println(s"Error: method main not found in $mainClass0")
|
||||
sys.exit(255)
|
||||
}
|
||||
|
||||
if (common.verbose0 >= 1)
|
||||
println(s"Calling $mainClass0 ${extraArgs.mkString(" ")}")
|
||||
|
||||
Thread.currentThread().setContextClassLoader(cl)
|
||||
method.invoke(null, extraArgs.toArray)
|
||||
}
|
||||
|
||||
case class Classpath(
|
||||
@Recurse
|
||||
common: CommonOptions
|
||||
) extends CoursierCommand {
|
||||
|
||||
val helper = new Helper(common, remainingArgs)
|
||||
|
||||
val files0 = helper.fetch(main = true, sources = false, javadoc = false)
|
||||
|
||||
Console.out.println(
|
||||
files0
|
||||
.map(_.toString)
|
||||
.mkString(File.pathSeparator)
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
// TODO: allow removing a repository (with confirmations, etc.)
|
||||
case class Repository(
|
||||
@ValueDescription("id:baseUrl")
|
||||
@ExtraName("a")
|
||||
add: List[String],
|
||||
@ExtraName("L")
|
||||
list: Boolean,
|
||||
@ExtraName("l")
|
||||
defaultList: Boolean,
|
||||
ivyLike: Boolean
|
||||
) extends CoursierCommand {
|
||||
|
||||
if (add.exists(!_.contains(":"))) {
|
||||
CaseApp.printUsage[Repository](err = true)
|
||||
sys.exit(255)
|
||||
}
|
||||
|
||||
if (parallel <= 0) {
|
||||
println(s"Error: invalid --parallel (-n) value: $parallel")
|
||||
val add0 = add
|
||||
.map{ s =>
|
||||
val Seq(id, baseUrl) = s.split(":", 2).toSeq
|
||||
id -> baseUrl
|
||||
}
|
||||
|
||||
if (
|
||||
add0.exists(_._1.contains("/")) ||
|
||||
add0.exists(_._1.startsWith(".")) ||
|
||||
add0.exists(_._1.isEmpty)
|
||||
) {
|
||||
CaseApp.printUsage[Repository](err = true)
|
||||
sys.exit(255)
|
||||
}
|
||||
|
||||
|
||||
def defaultLogger: MavenRepository.Logger with Files.Logger =
|
||||
new MavenRepository.Logger with Files.Logger {
|
||||
def downloading(url: String) =
|
||||
println(s"Downloading $url")
|
||||
def downloaded(url: String, success: Boolean) =
|
||||
if (!success)
|
||||
println(s"Failed: $url")
|
||||
def readingFromCache(f: File) = {}
|
||||
def puttingInCache(f: File) = {}
|
||||
|
||||
def foundLocally(f: File) = {}
|
||||
def downloadingArtifact(url: String) =
|
||||
println(s"Downloading $url")
|
||||
def downloadedArtifact(url: String, success: Boolean) =
|
||||
if (!success)
|
||||
println(s"Failed: $url")
|
||||
}
|
||||
|
||||
def verboseLogger: MavenRepository.Logger with Files.Logger =
|
||||
new MavenRepository.Logger with Files.Logger {
|
||||
def downloading(url: String) =
|
||||
println(s"Downloading $url")
|
||||
def downloaded(url: String, success: Boolean) =
|
||||
println(
|
||||
if (success) s"Downloaded $url"
|
||||
else s"Failed: $url"
|
||||
)
|
||||
def readingFromCache(f: File) = {
|
||||
println(s"Reading ${fileRepr(f)} from cache")
|
||||
}
|
||||
def puttingInCache(f: File) =
|
||||
println(s"Writing ${fileRepr(f)} in cache")
|
||||
|
||||
def foundLocally(f: File) =
|
||||
println(s"Found locally ${fileRepr(f)}")
|
||||
def downloadingArtifact(url: String) =
|
||||
println(s"Downloading $url")
|
||||
def downloadedArtifact(url: String, success: Boolean) =
|
||||
println(
|
||||
if (success) s"Downloaded $url"
|
||||
else s"Failed: $url"
|
||||
)
|
||||
}
|
||||
|
||||
val logger =
|
||||
if (verbose0 < 0)
|
||||
None
|
||||
else if (verbose0 == 0)
|
||||
Some(defaultLogger)
|
||||
else
|
||||
Some(verboseLogger)
|
||||
|
||||
implicit val cachePolicy =
|
||||
if (offline)
|
||||
CachePolicy.LocalOnly
|
||||
else if (force)
|
||||
CachePolicy.ForceDownload
|
||||
else
|
||||
CachePolicy.Default
|
||||
|
||||
val cache = Cache.default
|
||||
cache.init(verbose = verbose0 >= 0)
|
||||
|
||||
val repositoryIds = {
|
||||
val repository0 = repository
|
||||
.flatMap(_.split(','))
|
||||
.map(_.trim)
|
||||
.filter(_.nonEmpty)
|
||||
|
||||
if (repository0.isEmpty)
|
||||
cache.default()
|
||||
else
|
||||
repository0
|
||||
}
|
||||
|
||||
val repoMap = cache.map()
|
||||
|
||||
if (repositoryIds.exists(!repoMap.contains(_))) {
|
||||
val notFound = repositoryIds
|
||||
.filter(!repoMap.contains(_))
|
||||
|
||||
Console.err.println(
|
||||
(if (notFound.lengthCompare(1) == 1) "Repository" else "Repositories") +
|
||||
" not found: " +
|
||||
notFound.mkString(", ")
|
||||
)
|
||||
|
||||
if (cache.cache.exists() && !cache.cache.isDirectory) {
|
||||
Console.err.println(s"Error: ${cache.cache} not a directory")
|
||||
sys.exit(1)
|
||||
}
|
||||
|
||||
val (repositories0, fileCaches) = repositoryIds
|
||||
.map(repoMap)
|
||||
.unzip
|
||||
if (!cache.cache.exists())
|
||||
cache.init(verbose = true)
|
||||
|
||||
val repositories = repositories0
|
||||
.map(_.copy(logger = logger))
|
||||
val current = cache.list().map(_._1).toSet
|
||||
|
||||
val alreadyAdded = add0
|
||||
.map(_._1)
|
||||
.filter(current)
|
||||
|
||||
val (splitDependencies, malformed) = remainingArgs.toList
|
||||
.map(_.split(":", 3).toSeq)
|
||||
.partition(_.length == 3)
|
||||
|
||||
if (splitDependencies.isEmpty) {
|
||||
CaseApp.printUsage[Coursier]()
|
||||
sys exit 1
|
||||
if (alreadyAdded.nonEmpty) {
|
||||
Console.err.println(s"Error: already added: ${alreadyAdded.mkString(", ")}")
|
||||
sys.exit(1)
|
||||
}
|
||||
|
||||
if (malformed.nonEmpty) {
|
||||
println(s"Malformed dependencies:\n${malformed.map(_.mkString(":")).mkString("\n")}")
|
||||
sys exit 1
|
||||
for ((id, baseUrl0) <- add0) {
|
||||
val baseUrl =
|
||||
if (baseUrl0.endsWith("/"))
|
||||
baseUrl0
|
||||
else
|
||||
baseUrl0 + "/"
|
||||
|
||||
cache.add(id, baseUrl, ivyLike = ivyLike)
|
||||
}
|
||||
|
||||
val moduleVersions = splitDependencies.map{
|
||||
case Seq(org, name, version) =>
|
||||
(Module(org, name), version)
|
||||
if (defaultList) {
|
||||
val map = cache.repositoryMap()
|
||||
|
||||
for (id <- cache.default(withNotFound = true))
|
||||
map.get(id) match {
|
||||
case Some(repo) =>
|
||||
println(s"$id: ${repo.root}" + (if (repo.ivyLike) " (Ivy-like)" else ""))
|
||||
case None =>
|
||||
println(s"$id (not found)")
|
||||
}
|
||||
}
|
||||
|
||||
val deps = moduleVersions.map{case (mod, ver) =>
|
||||
Dependency(mod, ver, scope = Scope.Runtime)
|
||||
if (list)
|
||||
for ((id, repo, _) <- cache.list().sortBy(_._1)) {
|
||||
println(s"$id: ${repo.root}" + (if (repo.ivyLike) " (Ivy-like)" else ""))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
case class Bootstrap(
|
||||
@ExtraName("M")
|
||||
@ExtraName("main")
|
||||
mainClass: String,
|
||||
@ExtraName("o")
|
||||
output: String,
|
||||
@ExtraName("D")
|
||||
downloadDir: String,
|
||||
@ExtraName("f")
|
||||
force: Boolean,
|
||||
@Recurse
|
||||
common: CommonOptions
|
||||
) extends CoursierCommand {
|
||||
|
||||
if (mainClass.isEmpty) {
|
||||
Console.err.println(s"Error: no main class specified. Specify one with -M or --main")
|
||||
sys.exit(255)
|
||||
}
|
||||
|
||||
val startRes = Resolution(
|
||||
deps.toSet,
|
||||
filter = Some(dep => keepOptional || !dep.optional)
|
||||
if (downloadDir.isEmpty) {
|
||||
Console.err.println(s"Error: no download dir specified. Specify one with -D or --download-dir")
|
||||
Console.err.println("E.g. -D \"\\$HOME/.app-name/jars\"")
|
||||
sys.exit(255)
|
||||
}
|
||||
|
||||
val downloadDir0 =
|
||||
if (downloadDir.isEmpty)
|
||||
"$HOME/"
|
||||
else
|
||||
downloadDir
|
||||
|
||||
val bootstrapJar =
|
||||
Option(Thread.currentThread().getContextClassLoader.getResourceAsStream("bootstrap.jar")) match {
|
||||
case Some(is) => Files.readFullySync(is)
|
||||
case None =>
|
||||
Console.err.println(s"Error: bootstrap JAR not found")
|
||||
sys.exit(1)
|
||||
}
|
||||
|
||||
// scala-library version in the resulting JARs has to match the one in the bootstrap JAR
|
||||
// This should be enforced more strictly (possibly by having one bootstrap JAR per scala version).
|
||||
|
||||
val helper = new Helper(
|
||||
common,
|
||||
remainingArgs :+ s"org.scala-lang:scala-library:${scala.util.Properties.versionNumberString}"
|
||||
)
|
||||
|
||||
val fetchQuiet = coursier.fetch(repositories)
|
||||
val fetch0 =
|
||||
if (verbose0 == 0) fetchQuiet
|
||||
else {
|
||||
modVers: Seq[(Module, String)] =>
|
||||
val print = Task{
|
||||
println(s"Getting ${modVers.length} project definition(s)")
|
||||
}
|
||||
val artifacts = helper.res.artifacts
|
||||
|
||||
print.flatMap(_ => fetchQuiet(modVers))
|
||||
}
|
||||
val urls = artifacts.map(_.url)
|
||||
|
||||
if (verbose0 >= 0)
|
||||
println(s"Resolving\n" + moduleVersions.map{case (mod, ver) => s" $mod:$ver"}.mkString("\n"))
|
||||
val unrecognized = urls.filter(s => !s.startsWith("http://") && !s.startsWith("https://"))
|
||||
if (unrecognized.nonEmpty)
|
||||
Console.err.println(s"Warning: non HTTP URLs:\n${unrecognized.mkString("\n")}")
|
||||
|
||||
val res = startRes
|
||||
.process
|
||||
.run(fetch0, maxIterations)
|
||||
.run
|
||||
|
||||
if (!res.isDone) {
|
||||
println(s"Maximum number of iteration reached!")
|
||||
sys exit 1
|
||||
val output0 = new File(output)
|
||||
if (!force && output0.exists()) {
|
||||
Console.err.println(s"Error: $output already exists, use -f option to force erasing it.")
|
||||
sys.exit(1)
|
||||
}
|
||||
|
||||
def repr(dep: Dependency) = {
|
||||
// dep.version can be an interval, whereas the one from project can't
|
||||
val version = res
|
||||
.projectCache
|
||||
.get(dep.moduleVersion)
|
||||
.map(_._2.version)
|
||||
.getOrElse(dep.version)
|
||||
val extra =
|
||||
if (version == dep.version) ""
|
||||
else s" ($version for ${dep.version})"
|
||||
val shellPreamble = Seq(
|
||||
"#!/usr/bin/env sh",
|
||||
"exec java -jar \"$0\" \"" + mainClass + "\" \"" + downloadDir + "\" " + urls.map("\"" + _ + "\"").mkString(" ") + " -- \"$@\"",
|
||||
""
|
||||
).mkString("\n")
|
||||
|
||||
(
|
||||
Seq(
|
||||
dep.module.organization,
|
||||
dep.module.name,
|
||||
dep.attributes.`type`
|
||||
) ++
|
||||
Some(dep.attributes.classifier)
|
||||
.filter(_.nonEmpty)
|
||||
.toSeq ++
|
||||
Seq(
|
||||
version
|
||||
)
|
||||
).mkString(":") + extra
|
||||
try NIOFiles.write(output0.toPath, shellPreamble.getBytes("UTF-8") ++ bootstrapJar)
|
||||
catch { case e: IOException =>
|
||||
Console.err.println(s"Error while writing $output0: ${e.getMessage}")
|
||||
sys.exit(1)
|
||||
}
|
||||
|
||||
val trDeps = res
|
||||
.minDependencies
|
||||
.toList
|
||||
.sortBy(repr)
|
||||
|
||||
if (verbose0 >= 0) {
|
||||
println("")
|
||||
println(
|
||||
trDeps
|
||||
.map(repr)
|
||||
.distinct
|
||||
.mkString("\n")
|
||||
)
|
||||
}
|
||||
|
||||
if (res.conflicts.nonEmpty) {
|
||||
// Needs test
|
||||
println(s"${res.conflicts.size} conflict(s):\n ${res.conflicts.toList.map(repr).sorted.mkString(" \n")}")
|
||||
}
|
||||
|
||||
val errors = res.errors
|
||||
if (errors.nonEmpty) {
|
||||
println(s"\n${errors.size} error(s):")
|
||||
for ((dep, errs) <- errors) {
|
||||
println(s" ${dep.module}:${dep.version}:\n${errs.map(" " + _.replace("\n", " \n")).mkString("\n")}")
|
||||
}
|
||||
}
|
||||
|
||||
if (classpath || default || sources || javadoc) {
|
||||
println("")
|
||||
|
||||
val artifacts0 = res.artifacts
|
||||
val default0 = default || (!sources && !javadoc)
|
||||
val artifacts = artifacts0
|
||||
.flatMap{ artifact =>
|
||||
var l = List.empty[Artifact]
|
||||
if (sources)
|
||||
l = artifact.extra.get("sources").toList ::: l
|
||||
if (javadoc)
|
||||
l = artifact.extra.get("javadoc").toList ::: l
|
||||
if (default0)
|
||||
l = artifact :: l
|
||||
|
||||
l
|
||||
}
|
||||
|
||||
val files = {
|
||||
var files0 = cache
|
||||
.files()
|
||||
.copy(logger = logger)
|
||||
files0 = files0.copy(concurrentDownloadCount = parallel)
|
||||
files0
|
||||
}
|
||||
|
||||
val tasks = artifacts.map(artifact => files.file(artifact).run.map(artifact.->))
|
||||
def printTask = Task{
|
||||
if (verbose0 >= 0 && artifacts.nonEmpty)
|
||||
println(s"Found ${artifacts.length} artifacts")
|
||||
}
|
||||
val task = printTask.flatMap(_ => Task.gatherUnordered(tasks))
|
||||
|
||||
val results = task.run
|
||||
val errors = results.collect{case (artifact, -\/(err)) => artifact -> err }
|
||||
val files0 = results.collect{case (artifact, \/-(f)) => f }
|
||||
|
||||
if (errors.nonEmpty) {
|
||||
println(s"${errors.size} error(s):")
|
||||
for ((artifact, error) <- errors) {
|
||||
println(s" ${artifact.url}: $error")
|
||||
}
|
||||
}
|
||||
|
||||
Console.out.println(
|
||||
files0
|
||||
.map(_.toString)
|
||||
.mkString(if (classpath) File.pathSeparator else "\n")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
object Coursier extends AppOf[Coursier] {
|
||||
val parser = default
|
||||
}
|
||||
object Coursier extends CommandAppOf[CoursierCommand]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,302 @@
|
|||
package coursier.cli
|
||||
|
||||
import java.io.File
|
||||
|
||||
import caseapp.CaseApp
|
||||
import coursier._
|
||||
import coursier.core.{ CachePolicy, MavenRepository }
|
||||
|
||||
import scalaz.{ \/-, -\/ }
|
||||
import scalaz.concurrent.Task
|
||||
|
||||
object Helper {
|
||||
def validate(common: CommonOptions) = {
|
||||
import common._
|
||||
|
||||
if (force && offline) {
|
||||
Console.err.println("Error: --offline (-c) and --force (-f) options can't be specified at the same time.")
|
||||
sys.exit(255)
|
||||
}
|
||||
|
||||
if (parallel <= 0) {
|
||||
Console.err.println(s"Error: invalid --parallel (-n) value: $parallel")
|
||||
sys.exit(255)
|
||||
}
|
||||
|
||||
???
|
||||
}
|
||||
|
||||
def fileRepr(f: File) = f.toString
|
||||
|
||||
def errPrintln(s: String) = Console.err.println(s)
|
||||
|
||||
def defaultLogger: MavenRepository.Logger with Files.Logger =
|
||||
new MavenRepository.Logger with Files.Logger {
|
||||
def downloading(url: String) =
|
||||
errPrintln(s"Downloading $url")
|
||||
def downloaded(url: String, success: Boolean) =
|
||||
if (!success)
|
||||
errPrintln(s"Failed: $url")
|
||||
def readingFromCache(f: File) = {}
|
||||
def puttingInCache(f: File) = {}
|
||||
|
||||
def foundLocally(f: File) = {}
|
||||
def downloadingArtifact(url: String) =
|
||||
errPrintln(s"Downloading $url")
|
||||
def downloadedArtifact(url: String, success: Boolean) =
|
||||
if (!success)
|
||||
errPrintln(s"Failed: $url")
|
||||
}
|
||||
|
||||
def verboseLogger: MavenRepository.Logger with Files.Logger =
|
||||
new MavenRepository.Logger with Files.Logger {
|
||||
def downloading(url: String) =
|
||||
errPrintln(s"Downloading $url")
|
||||
def downloaded(url: String, success: Boolean) =
|
||||
errPrintln(
|
||||
if (success) s"Downloaded $url"
|
||||
else s"Failed: $url"
|
||||
)
|
||||
def readingFromCache(f: File) = {
|
||||
errPrintln(s"Reading ${fileRepr(f)} from cache")
|
||||
}
|
||||
def puttingInCache(f: File) =
|
||||
errPrintln(s"Writing ${fileRepr(f)} in cache")
|
||||
|
||||
def foundLocally(f: File) =
|
||||
errPrintln(s"Found locally ${fileRepr(f)}")
|
||||
def downloadingArtifact(url: String) =
|
||||
errPrintln(s"Downloading $url")
|
||||
def downloadedArtifact(url: String, success: Boolean) =
|
||||
errPrintln(
|
||||
if (success) s"Downloaded $url"
|
||||
else s"Failed: $url"
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Helper(
|
||||
common: CommonOptions,
|
||||
remainingArgs: Seq[String]
|
||||
) {
|
||||
import common._
|
||||
import Helper.errPrintln
|
||||
|
||||
|
||||
val logger =
|
||||
if (verbose0 < 0)
|
||||
None
|
||||
else if (verbose0 == 0)
|
||||
Some(Helper.defaultLogger)
|
||||
else
|
||||
Some(Helper.verboseLogger)
|
||||
|
||||
implicit val cachePolicy =
|
||||
if (offline)
|
||||
CachePolicy.LocalOnly
|
||||
else if (force)
|
||||
CachePolicy.ForceDownload
|
||||
else
|
||||
CachePolicy.Default
|
||||
|
||||
val cache = Cache.default
|
||||
cache.init(verbose = verbose0 >= 0)
|
||||
|
||||
val repositoryIds = {
|
||||
val repositoryIds0 = repository
|
||||
.flatMap(_.split(','))
|
||||
.map(_.trim)
|
||||
.filter(_.nonEmpty)
|
||||
|
||||
if (repositoryIds0.isEmpty)
|
||||
cache.default()
|
||||
else
|
||||
repositoryIds0
|
||||
}
|
||||
|
||||
val repoMap = cache.map()
|
||||
|
||||
if (repositoryIds.exists(!repoMap.contains(_))) {
|
||||
val notFound = repositoryIds
|
||||
.filter(!repoMap.contains(_))
|
||||
|
||||
errPrintln(
|
||||
(if (notFound.lengthCompare(1) == 1) "Repository" else "Repositories") +
|
||||
" not found: " +
|
||||
notFound.mkString(", ")
|
||||
)
|
||||
|
||||
sys.exit(1)
|
||||
}
|
||||
|
||||
val (repositories0, fileCaches) = repositoryIds
|
||||
.map(repoMap)
|
||||
.unzip
|
||||
|
||||
val repositories = repositories0
|
||||
.map(_.copy(logger = logger))
|
||||
|
||||
val (rawDependencies, extraArgs) = {
|
||||
val idxOpt = Some(remainingArgs.indexOf("--")).filter(_ >= 0)
|
||||
idxOpt.fold((remainingArgs, Seq.empty[String])) { idx =>
|
||||
val (l, r) = remainingArgs.splitAt(idx)
|
||||
assert(r.nonEmpty)
|
||||
(l, r.tail)
|
||||
}
|
||||
}
|
||||
|
||||
val (splitDependencies, malformed) = rawDependencies.toList
|
||||
.map(_.split(":", 3).toSeq)
|
||||
.partition(_.length == 3)
|
||||
|
||||
if (splitDependencies.isEmpty) {
|
||||
???
|
||||
// CaseApp.printUsage[Coursier]()
|
||||
sys exit 1
|
||||
}
|
||||
|
||||
if (malformed.nonEmpty) {
|
||||
errPrintln(s"Malformed dependencies:\n${malformed.map(_.mkString(":")).mkString("\n")}")
|
||||
sys exit 1
|
||||
}
|
||||
|
||||
val moduleVersions = splitDependencies.map{
|
||||
case Seq(org, name, version) =>
|
||||
(Module(org, name), version)
|
||||
}
|
||||
|
||||
val deps = moduleVersions.map{case (mod, ver) =>
|
||||
Dependency(mod, ver, scope = Scope.Runtime)
|
||||
}
|
||||
|
||||
val startRes = Resolution(
|
||||
deps.toSet,
|
||||
filter = Some(dep => keepOptional || !dep.optional)
|
||||
)
|
||||
|
||||
val fetchQuiet = coursier.fetch(repositories)
|
||||
val fetch0 =
|
||||
if (verbose0 == 0) fetchQuiet
|
||||
else {
|
||||
modVers: Seq[(Module, String)] =>
|
||||
val print = Task{
|
||||
errPrintln(s"Getting ${modVers.length} project definition(s)")
|
||||
}
|
||||
|
||||
print.flatMap(_ => fetchQuiet(modVers))
|
||||
}
|
||||
|
||||
if (verbose0 >= 0)
|
||||
errPrintln(s"Resolving\n" + moduleVersions.map{case (mod, ver) => s" $mod:$ver"}.mkString("\n"))
|
||||
|
||||
val res = startRes
|
||||
.process
|
||||
.run(fetch0, maxIterations)
|
||||
.run
|
||||
|
||||
if (!res.isDone) {
|
||||
errPrintln(s"Maximum number of iteration reached!")
|
||||
sys.exit(1)
|
||||
}
|
||||
|
||||
def repr(dep: Dependency) = {
|
||||
// dep.version can be an interval, whereas the one from project can't
|
||||
val version = res
|
||||
.projectCache
|
||||
.get(dep.moduleVersion)
|
||||
.map(_._2.version)
|
||||
.getOrElse(dep.version)
|
||||
val extra =
|
||||
if (version == dep.version) ""
|
||||
else s" ($version for ${dep.version})"
|
||||
|
||||
(
|
||||
Seq(
|
||||
dep.module.organization,
|
||||
dep.module.name,
|
||||
dep.attributes.`type`
|
||||
) ++
|
||||
Some(dep.attributes.classifier)
|
||||
.filter(_.nonEmpty)
|
||||
.toSeq ++
|
||||
Seq(
|
||||
version
|
||||
)
|
||||
).mkString(":") + extra
|
||||
}
|
||||
|
||||
val trDeps = res
|
||||
.minDependencies
|
||||
.toList
|
||||
.sortBy(repr)
|
||||
|
||||
if (verbose0 >= 0) {
|
||||
println("")
|
||||
println(
|
||||
trDeps
|
||||
.map(repr)
|
||||
.distinct
|
||||
.mkString("\n")
|
||||
)
|
||||
}
|
||||
|
||||
if (res.conflicts.nonEmpty) {
|
||||
// Needs test
|
||||
println(s"${res.conflicts.size} conflict(s):\n ${res.conflicts.toList.map(repr).sorted.mkString(" \n")}")
|
||||
}
|
||||
|
||||
val errors = res.errors
|
||||
if (errors.nonEmpty) {
|
||||
println(s"\n${errors.size} error(s):")
|
||||
for ((dep, errs) <- errors) {
|
||||
println(s" ${dep.module}:${dep.version}:\n${errs.map(" " + _.replace("\n", " \n")).mkString("\n")}")
|
||||
}
|
||||
}
|
||||
|
||||
def fetch(main: Boolean, sources: Boolean, javadoc: Boolean): Seq[File] = {
|
||||
println("")
|
||||
|
||||
val artifacts0 = res.artifacts
|
||||
val main0 = main || (!sources && !javadoc)
|
||||
val artifacts = artifacts0.flatMap{ artifact =>
|
||||
var l = List.empty[Artifact]
|
||||
if (sources)
|
||||
l = artifact.extra.get("sources").toList ::: l
|
||||
if (javadoc)
|
||||
l = artifact.extra.get("javadoc").toList ::: l
|
||||
if (main0)
|
||||
l = artifact :: l
|
||||
|
||||
l
|
||||
}
|
||||
|
||||
val files = {
|
||||
var files0 = cache
|
||||
.files()
|
||||
.copy(logger = logger)
|
||||
files0 = files0.copy(concurrentDownloadCount = parallel)
|
||||
files0
|
||||
}
|
||||
|
||||
val tasks = artifacts.map(artifact => files.file(artifact).run.map(artifact.->))
|
||||
def printTask = Task{
|
||||
if (verbose0 >= 0 && artifacts.nonEmpty)
|
||||
println(s"Found ${artifacts.length} artifacts")
|
||||
}
|
||||
val task = printTask.flatMap(_ => Task.gatherUnordered(tasks))
|
||||
|
||||
val results = task.run
|
||||
val errors = results.collect{case (artifact, -\/(err)) => artifact -> err }
|
||||
val files0 = results.collect{case (artifact, \/-(f)) => f }
|
||||
|
||||
if (errors.nonEmpty) {
|
||||
println(s"${errors.size} error(s):")
|
||||
for ((artifact, error) <- errors) {
|
||||
println(s" ${artifact.url}: $error")
|
||||
}
|
||||
}
|
||||
|
||||
files0
|
||||
}
|
||||
}
|
||||
|
|
@ -1,87 +0,0 @@
|
|||
package coursier.cli
|
||||
|
||||
import coursier.Cache
|
||||
import caseapp._
|
||||
|
||||
// TODO: allow removing a repository (with confirmations, etc.)
|
||||
case class Repositories(
|
||||
@ValueDescription("id:baseUrl") @ExtraName("a") add: List[String],
|
||||
@ExtraName("L") list: Boolean,
|
||||
@ExtraName("l") defaultList: Boolean,
|
||||
ivyLike: Boolean
|
||||
) extends App {
|
||||
|
||||
if (add.exists(!_.contains(":"))) {
|
||||
CaseApp.printUsage[Repositories](err = true)
|
||||
sys.exit(255)
|
||||
}
|
||||
|
||||
val add0 = add
|
||||
.map{ s =>
|
||||
val Seq(id, baseUrl) = s.split(":", 2).toSeq
|
||||
id -> baseUrl
|
||||
}
|
||||
|
||||
if (
|
||||
add0.exists(_._1.contains("/")) ||
|
||||
add0.exists(_._1.startsWith(".")) ||
|
||||
add0.exists(_._1.isEmpty)
|
||||
) {
|
||||
CaseApp.printUsage[Repositories](err = true)
|
||||
sys.exit(255)
|
||||
}
|
||||
|
||||
|
||||
val cache = Cache.default
|
||||
|
||||
if (cache.cache.exists() && !cache.cache.isDirectory) {
|
||||
Console.err.println(s"Error: ${cache.cache} not a directory")
|
||||
sys.exit(1)
|
||||
}
|
||||
|
||||
if (!cache.cache.exists())
|
||||
cache.init(verbose = true)
|
||||
|
||||
val current = cache.list().map(_._1).toSet
|
||||
|
||||
val alreadyAdded = add0
|
||||
.map(_._1)
|
||||
.filter(current)
|
||||
|
||||
if (alreadyAdded.nonEmpty) {
|
||||
Console.err.println(s"Error: already added: ${alreadyAdded.mkString(", ")}")
|
||||
sys.exit(1)
|
||||
}
|
||||
|
||||
for ((id, baseUrl0) <- add0) {
|
||||
val baseUrl =
|
||||
if (baseUrl0.endsWith("/"))
|
||||
baseUrl0
|
||||
else
|
||||
baseUrl0 + "/"
|
||||
|
||||
cache.add(id, baseUrl, ivyLike = ivyLike)
|
||||
}
|
||||
|
||||
if (defaultList) {
|
||||
val map = cache.repositoryMap()
|
||||
|
||||
for (id <- cache.default(withNotFound = true))
|
||||
map.get(id) match {
|
||||
case Some(repo) =>
|
||||
println(s"$id: ${repo.root}" + (if (repo.ivyLike) " (Ivy-like)" else ""))
|
||||
case None =>
|
||||
println(s"$id (not found)")
|
||||
}
|
||||
}
|
||||
|
||||
if (list)
|
||||
for ((id, repo, _) <- cache.list().sortBy(_._1)) {
|
||||
println(s"$id: ${repo.root}" + (if (repo.ivyLike) " (Ivy-like)" else ""))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
object Repositories extends AppOf[Repositories] {
|
||||
val parser = default
|
||||
}
|
||||
|
|
@ -13,7 +13,7 @@ package object compatibility {
|
|||
def textResource(path: String)(implicit ec: ExecutionContext): Future[String] = {
|
||||
val p = Promise[String]()
|
||||
|
||||
fs.readFile("core/src/test/resources/" + path, "utf-8", {
|
||||
fs.readFile("core/shared/src/test/resources/" + path, "utf-8", {
|
||||
(err: js.Dynamic, data: js.Dynamic) =>
|
||||
if (err == null) p.success(data.asInstanceOf[String])
|
||||
else p.failure(new Exception(err.toString))
|
||||
|
|
@ -1,183 +0,0 @@
|
|||
import org.scalajs.sbtplugin.ScalaJSPlugin
|
||||
import org.scalajs.sbtplugin.ScalaJSPlugin.autoImport._
|
||||
|
||||
import sbt._, Keys._
|
||||
|
||||
import sbtrelease.ReleasePlugin.releaseSettings
|
||||
import sbtrelease.ReleasePlugin.ReleaseKeys.{ publishArtifactsAction, versionBump }
|
||||
import sbtrelease.Version.Bump
|
||||
import com.typesafe.sbt.pgp.PgpKeys
|
||||
|
||||
import xerial.sbt.Pack._
|
||||
|
||||
|
||||
object CoursierBuild extends Build {
|
||||
|
||||
lazy val publishingSettings = Seq[Setting[_]](
|
||||
publishMavenStyle := true,
|
||||
publishTo := {
|
||||
val nexus = "https://oss.sonatype.org/"
|
||||
if (isSnapshot.value)
|
||||
Some("snapshots" at nexus + "content/repositories/snapshots")
|
||||
else
|
||||
Some("releases" at nexus + "service/local/staging/deploy/maven2")
|
||||
},
|
||||
pomExtra := {
|
||||
<url>https://github.com/alexarchambault/coursier</url>
|
||||
<licenses>
|
||||
<license>
|
||||
<name>Apache 2.0</name>
|
||||
<url>http://opensource.org/licenses/Apache-2.0</url>
|
||||
</license>
|
||||
</licenses>
|
||||
<scm>
|
||||
<connection>scm:git:github.com/alexarchambault/coursier.git</connection>
|
||||
<developerConnection>scm:git:git@github.com:alexarchambault/coursier.git</developerConnection>
|
||||
<url>github.com/alexarchambault/coursier.git</url>
|
||||
</scm>
|
||||
<developers>
|
||||
<developer>
|
||||
<id>alexarchambault</id>
|
||||
<name>Alexandre Archambault</name>
|
||||
<url>https://github.com/alexarchambault</url>
|
||||
</developer>
|
||||
</developers>
|
||||
},
|
||||
credentials += {
|
||||
Seq("SONATYPE_USER", "SONATYPE_PASS").map(sys.env.get) match {
|
||||
case Seq(Some(user), Some(pass)) =>
|
||||
Credentials("Sonatype Nexus Repository Manager", "oss.sonatype.org", user, pass)
|
||||
case _ =>
|
||||
Credentials(Path.userHome / ".ivy2" / ".credentials")
|
||||
}
|
||||
},
|
||||
versionBump := Bump.Bugfix,
|
||||
publishArtifactsAction := PgpKeys.publishSigned.value
|
||||
) ++ releaseSettings
|
||||
|
||||
lazy val commonSettings = Seq[Setting[_]](
|
||||
organization := "com.github.alexarchambault",
|
||||
scalaVersion := "2.11.7",
|
||||
crossScalaVersions := Seq("2.10.5", "2.11.7"),
|
||||
resolvers ++= Seq(
|
||||
"Scalaz Bintray Repo" at "http://dl.bintray.com/scalaz/releases",
|
||||
Resolver.sonatypeRepo("releases")
|
||||
)
|
||||
) ++ publishingSettings
|
||||
|
||||
private lazy val commonCoreSettings = commonSettings ++ Seq[Setting[_]](
|
||||
name := "coursier",
|
||||
libraryDependencies += "org.scala-lang.modules" %% "scala-async" % "0.9.1" % "provided",
|
||||
unmanagedSourceDirectories in Compile += (baseDirectory in LocalRootProject).value / "core" / "src" / "main" / "scala",
|
||||
unmanagedSourceDirectories in Test += (baseDirectory in LocalRootProject).value / "core" / "src" / "test" / "scala",
|
||||
unmanagedResourceDirectories in Compile += (baseDirectory in LocalRootProject).value / "core" / "src" / "main" / "resources",
|
||||
unmanagedResourceDirectories in Test += (baseDirectory in LocalRootProject).value / "core" / "src" / "test" / "resources",
|
||||
testFrameworks += new TestFramework("utest.runner.Framework")
|
||||
)
|
||||
|
||||
lazy val coreJvm = Project(id = "core-jvm", base = file("core-jvm"))
|
||||
.settings(commonCoreSettings: _*)
|
||||
.settings(
|
||||
libraryDependencies ++= Seq(
|
||||
"org.scalaz" %% "scalaz-concurrent" % "7.1.2",
|
||||
"com.lihaoyi" %% "utest" % "0.3.0" % "test"
|
||||
) ++ {
|
||||
if (scalaVersion.value.startsWith("2.10.")) Seq()
|
||||
else Seq(
|
||||
"org.scala-lang.modules" %% "scala-xml" % "1.0.3"
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
lazy val coreJs = Project(id = "core-js", base = file("core-js"))
|
||||
.settings(commonCoreSettings: _*)
|
||||
.settings(
|
||||
libraryDependencies ++= Seq(
|
||||
"org.scala-js" %%% "scalajs-dom" % "0.8.0",
|
||||
"com.github.japgolly.fork.scalaz" %%% "scalaz-core" % (if (scalaVersion.value.startsWith("2.10.")) "7.1.1" else "7.1.2"),
|
||||
"be.doeraene" %%% "scalajs-jquery" % "0.8.0",
|
||||
"com.lihaoyi" %%% "utest" % "0.3.0" % "test"
|
||||
),
|
||||
postLinkJSEnv := NodeJSEnv().value,
|
||||
scalaJSStage in Global := FastOptStage
|
||||
)
|
||||
.enablePlugins(ScalaJSPlugin)
|
||||
|
||||
lazy val files = Project(id = "files", base = file("files"))
|
||||
.dependsOn(coreJvm)
|
||||
.settings(commonSettings: _*)
|
||||
.settings(
|
||||
name := "coursier-files",
|
||||
libraryDependencies ++= Seq(
|
||||
// "org.http4s" %% "http4s-blazeclient" % "0.8.2",
|
||||
"com.lihaoyi" %% "utest" % "0.3.0" % "test"
|
||||
),
|
||||
testFrameworks += new TestFramework("utest.runner.Framework")
|
||||
)
|
||||
|
||||
lazy val cli = Project(id = "cli", base = file("cli"))
|
||||
.dependsOn(coreJvm, files)
|
||||
.settings(commonSettings ++ packAutoSettings ++ publishPackTxzArchive ++ publishPackZipArchive: _*)
|
||||
.settings(
|
||||
packArchivePrefix := s"coursier-cli_${scalaBinaryVersion.value}",
|
||||
packArchiveTxzArtifact := Artifact("coursier-cli", "arch", "tar.xz"),
|
||||
packArchiveZipArtifact := Artifact("coursier-cli", "arch", "zip")
|
||||
)
|
||||
.settings(
|
||||
name := "coursier-cli",
|
||||
libraryDependencies ++= Seq(
|
||||
"com.github.alexarchambault" %% "case-app" % "0.3.0",
|
||||
"ch.qos.logback" % "logback-classic" % "1.1.3"
|
||||
) ++ {
|
||||
if (scalaVersion.value startsWith "2.10.")
|
||||
Seq(compilerPlugin("org.scalamacros" % "paradise" % "2.0.1" cross CrossVersion.full))
|
||||
else
|
||||
Seq()
|
||||
}
|
||||
)
|
||||
|
||||
lazy val web = Project(id = "web", base = file("web"))
|
||||
.dependsOn(coreJs)
|
||||
.settings(commonSettings: _*)
|
||||
.settings(
|
||||
libraryDependencies ++= {
|
||||
if (scalaVersion.value startsWith "2.10.")
|
||||
Seq()
|
||||
else
|
||||
Seq(
|
||||
"com.github.japgolly.scalajs-react" %%% "core" % "0.9.0"
|
||||
)
|
||||
},
|
||||
sourceDirectory := {
|
||||
val dir = sourceDirectory.value
|
||||
|
||||
if (scalaVersion.value startsWith "2.10.")
|
||||
dir / "dummy"
|
||||
else
|
||||
dir
|
||||
},
|
||||
publish := (),
|
||||
publishLocal := (),
|
||||
test in Test := (),
|
||||
testOnly in Test := (),
|
||||
resolvers += "Webjars Bintray" at "https://dl.bintray.com/webjars/maven/",
|
||||
jsDependencies ++= Seq(
|
||||
("org.webjars.bower" % "bootstrap" % "3.3.4" intransitive()) / "bootstrap.min.js" commonJSName "Bootstrap",
|
||||
("org.webjars.bower" % "react" % "0.12.2" intransitive()) / "react-with-addons.js" commonJSName "React",
|
||||
("org.webjars.bower" % "bootstrap-treeview" % "1.2.0" intransitive()) / "bootstrap-treeview.min.js" commonJSName "Treeview",
|
||||
("org.webjars.bower" % "raphael" % "2.1.4" intransitive()) / "raphael-min.js" commonJSName "Raphael"
|
||||
)
|
||||
)
|
||||
.enablePlugins(ScalaJSPlugin)
|
||||
|
||||
lazy val root = Project(id = "root", base = file("."))
|
||||
.aggregate(coreJvm, coreJs, files, cli, web)
|
||||
.settings(commonSettings: _*)
|
||||
.settings(
|
||||
(unmanagedSourceDirectories in Compile) := Nil,
|
||||
(unmanagedSourceDirectories in Test) := Nil,
|
||||
publish := (),
|
||||
publishLocal := ()
|
||||
)
|
||||
|
||||
}
|
||||
|
|
@ -1,9 +1,6 @@
|
|||
addSbtPlugin("org.xerial.sbt" % "sbt-pack" % "0.6.8")
|
||||
|
||||
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.4")
|
||||
|
||||
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.5")
|
||||
addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.0.0")
|
||||
|
||||
addSbtPlugin("com.github.gseitz" % "sbt-release" % "0.8.5")
|
||||
|
||||
addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.1.0")
|
||||
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.0")
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ else
|
|||
fi
|
||||
|
||||
# Required for ~/.ivy2/local repo tests
|
||||
sbt core-jvm/publish-local
|
||||
sbt coreJVM/publish-local
|
||||
|
||||
SBT_COMMANDS="$SBT_COMMANDS test"
|
||||
|
||||
|
|
@ -43,7 +43,7 @@ SBT_COMMANDS="$SBT_COMMANDS test"
|
|||
|
||||
PUSH_GHPAGES=0
|
||||
if isNotPr && isJdk7 && isMaster; then
|
||||
SBT_COMMANDS="$SBT_COMMANDS core-jvm/publish core-js/publish files/publish cli/publish"
|
||||
SBT_COMMANDS="$SBT_COMMANDS coreJVM/publish coreJS/publish files/publish cli/publish"
|
||||
fi
|
||||
|
||||
if isNotPr && isJdk7 && isMasterOrDevelop; then
|
||||
|
|
|
|||
Loading…
Reference in New Issue