diff --git a/.travis.yml b/.travis.yml
index 17d74437d..a2c012190 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,7 +1,7 @@
language: scala
scala:
-- 2.11.6
-- 2.10.5
+- 2.11.7
+- 2.10.6
jdk:
- oraclejdk7
- oraclejdk8
diff --git a/appveyor.yml b/appveyor.yml
index 102fc5cc6..a5ef6d67e 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -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
diff --git a/bootstrap/src/main/scala/coursier/Bootstrap.scala b/bootstrap/src/main/scala/coursier/Bootstrap.scala
new file mode 100644
index 000000000..787d3d12f
--- /dev/null
+++ b/bootstrap/src/main/scala/coursier/Bootstrap.scala
@@ -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...")
+ }
+
+}
\ No newline at end of file
diff --git a/build.sbt b/build.sbt
new file mode 100644
index 000000000..8ac735cbb
--- /dev/null
+++ b/build.sbt
@@ -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:git:github.com/alexarchambault/coursier.git
+ scm:git:git@github.com:alexarchambault/coursier.git
+ github.com/alexarchambault/coursier.git
+
+ },
+ 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
+ )
diff --git a/cli/src/main/scala/coursier/cli/Coursier.scala b/cli/src/main/scala/coursier/cli/Coursier.scala
index 1e7b8620e..d67a99f98 100644
--- a/cli/src/main/scala/coursier/cli/Coursier.scala
+++ b/cli/src/main/scala/coursier/cli/Coursier.scala
@@ -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]
diff --git a/cli/src/main/scala/coursier/cli/Helper.scala b/cli/src/main/scala/coursier/cli/Helper.scala
new file mode 100644
index 000000000..84593d4bf
--- /dev/null
+++ b/cli/src/main/scala/coursier/cli/Helper.scala
@@ -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
+ }
+}
diff --git a/cli/src/main/scala/coursier/cli/Repositories.scala b/cli/src/main/scala/coursier/cli/Repositories.scala
deleted file mode 100644
index 4e717fa03..000000000
--- a/cli/src/main/scala/coursier/cli/Repositories.scala
+++ /dev/null
@@ -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
-}
diff --git a/core-js/src/main/scala/coursier/core/MavenRepository.scala b/core/js/src/main/scala/coursier/core/MavenRepository.scala
similarity index 100%
rename from core-js/src/main/scala/coursier/core/MavenRepository.scala
rename to core/js/src/main/scala/coursier/core/MavenRepository.scala
diff --git a/core-js/src/main/scala/coursier/core/compatibility/package.scala b/core/js/src/main/scala/coursier/core/compatibility/package.scala
similarity index 100%
rename from core-js/src/main/scala/coursier/core/compatibility/package.scala
rename to core/js/src/main/scala/coursier/core/compatibility/package.scala
diff --git a/core-js/src/main/scala/scalaz/concurrent/package.scala b/core/js/src/main/scala/scalaz/concurrent/package.scala
similarity index 100%
rename from core-js/src/main/scala/scalaz/concurrent/package.scala
rename to core/js/src/main/scala/scalaz/concurrent/package.scala
diff --git a/core-js/src/test/scala/coursier/test/JsTests.scala b/core/js/src/test/scala/coursier/test/JsTests.scala
similarity index 100%
rename from core-js/src/test/scala/coursier/test/JsTests.scala
rename to core/js/src/test/scala/coursier/test/JsTests.scala
diff --git a/core-js/src/test/scala/coursier/test/compatibility/package.scala b/core/js/src/test/scala/coursier/test/compatibility/package.scala
similarity index 90%
rename from core-js/src/test/scala/coursier/test/compatibility/package.scala
rename to core/js/src/test/scala/coursier/test/compatibility/package.scala
index 87ef5860a..38099453c 100644
--- a/core-js/src/test/scala/coursier/test/compatibility/package.scala
+++ b/core/js/src/test/scala/coursier/test/compatibility/package.scala
@@ -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))
diff --git a/core-jvm/src/main/scala/coursier/core/MavenRepository.scala b/core/jvm/src/main/scala/coursier/core/MavenRepository.scala
similarity index 100%
rename from core-jvm/src/main/scala/coursier/core/MavenRepository.scala
rename to core/jvm/src/main/scala/coursier/core/MavenRepository.scala
diff --git a/core-jvm/src/main/scala/coursier/core/compatibility/package.scala b/core/jvm/src/main/scala/coursier/core/compatibility/package.scala
similarity index 100%
rename from core-jvm/src/main/scala/coursier/core/compatibility/package.scala
rename to core/jvm/src/main/scala/coursier/core/compatibility/package.scala
diff --git a/core-jvm/src/test/scala/coursier/test/IvyLocalTests.scala b/core/jvm/src/test/scala/coursier/test/IvyLocalTests.scala
similarity index 100%
rename from core-jvm/src/test/scala/coursier/test/IvyLocalTests.scala
rename to core/jvm/src/test/scala/coursier/test/IvyLocalTests.scala
diff --git a/core-jvm/src/test/scala/coursier/test/compatibility/package.scala b/core/jvm/src/test/scala/coursier/test/compatibility/package.scala
similarity index 100%
rename from core-jvm/src/test/scala/coursier/test/compatibility/package.scala
rename to core/jvm/src/test/scala/coursier/test/compatibility/package.scala
diff --git a/core/src/main/scala/coursier/core/Definitions.scala b/core/shared/src/main/scala/coursier/core/Definitions.scala
similarity index 100%
rename from core/src/main/scala/coursier/core/Definitions.scala
rename to core/shared/src/main/scala/coursier/core/Definitions.scala
diff --git a/core/src/main/scala/coursier/core/Exclusions.scala b/core/shared/src/main/scala/coursier/core/Exclusions.scala
similarity index 100%
rename from core/src/main/scala/coursier/core/Exclusions.scala
rename to core/shared/src/main/scala/coursier/core/Exclusions.scala
diff --git a/core/src/main/scala/coursier/core/Orders.scala b/core/shared/src/main/scala/coursier/core/Orders.scala
similarity index 100%
rename from core/src/main/scala/coursier/core/Orders.scala
rename to core/shared/src/main/scala/coursier/core/Orders.scala
diff --git a/core/src/main/scala/coursier/core/Parse.scala b/core/shared/src/main/scala/coursier/core/Parse.scala
similarity index 100%
rename from core/src/main/scala/coursier/core/Parse.scala
rename to core/shared/src/main/scala/coursier/core/Parse.scala
diff --git a/core/src/main/scala/coursier/core/Repository.scala b/core/shared/src/main/scala/coursier/core/Repository.scala
similarity index 100%
rename from core/src/main/scala/coursier/core/Repository.scala
rename to core/shared/src/main/scala/coursier/core/Repository.scala
diff --git a/core/src/main/scala/coursier/core/Resolution.scala b/core/shared/src/main/scala/coursier/core/Resolution.scala
similarity index 100%
rename from core/src/main/scala/coursier/core/Resolution.scala
rename to core/shared/src/main/scala/coursier/core/Resolution.scala
diff --git a/core/src/main/scala/coursier/core/ResolutionProcess.scala b/core/shared/src/main/scala/coursier/core/ResolutionProcess.scala
similarity index 100%
rename from core/src/main/scala/coursier/core/ResolutionProcess.scala
rename to core/shared/src/main/scala/coursier/core/ResolutionProcess.scala
diff --git a/core/src/main/scala/coursier/core/Version.scala b/core/shared/src/main/scala/coursier/core/Version.scala
similarity index 100%
rename from core/src/main/scala/coursier/core/Version.scala
rename to core/shared/src/main/scala/coursier/core/Version.scala
diff --git a/core/src/main/scala/coursier/core/Versions.scala b/core/shared/src/main/scala/coursier/core/Versions.scala
similarity index 100%
rename from core/src/main/scala/coursier/core/Versions.scala
rename to core/shared/src/main/scala/coursier/core/Versions.scala
diff --git a/core/src/main/scala/coursier/core/Xml.scala b/core/shared/src/main/scala/coursier/core/Xml.scala
similarity index 100%
rename from core/src/main/scala/coursier/core/Xml.scala
rename to core/shared/src/main/scala/coursier/core/Xml.scala
diff --git a/core/src/main/scala/coursier/package.scala b/core/shared/src/main/scala/coursier/package.scala
similarity index 100%
rename from core/src/main/scala/coursier/package.scala
rename to core/shared/src/main/scala/coursier/package.scala
diff --git a/core/src/test/resources/resolutions/com.github.alexarchambault/argonaut-shapeless_6.1_2.11/0.2.0 b/core/shared/src/test/resources/resolutions/com.github.alexarchambault/argonaut-shapeless_6.1_2.11/0.2.0
similarity index 100%
rename from core/src/test/resources/resolutions/com.github.alexarchambault/argonaut-shapeless_6.1_2.11/0.2.0
rename to core/shared/src/test/resources/resolutions/com.github.alexarchambault/argonaut-shapeless_6.1_2.11/0.2.0
diff --git a/core/src/test/resources/resolutions/com.github.alexarchambault/argonaut-shapeless_6.1_2.11/0.2.0.jcabi b/core/shared/src/test/resources/resolutions/com.github.alexarchambault/argonaut-shapeless_6.1_2.11/0.2.0.jcabi
similarity index 100%
rename from core/src/test/resources/resolutions/com.github.alexarchambault/argonaut-shapeless_6.1_2.11/0.2.0.jcabi
rename to core/shared/src/test/resources/resolutions/com.github.alexarchambault/argonaut-shapeless_6.1_2.11/0.2.0.jcabi
diff --git a/core/src/test/resources/resolutions/com.github.alexarchambault/coursier_2.11/0.1.0-SNAPSHOT b/core/shared/src/test/resources/resolutions/com.github.alexarchambault/coursier_2.11/0.1.0-SNAPSHOT
similarity index 100%
rename from core/src/test/resources/resolutions/com.github.alexarchambault/coursier_2.11/0.1.0-SNAPSHOT
rename to core/shared/src/test/resources/resolutions/com.github.alexarchambault/coursier_2.11/0.1.0-SNAPSHOT
diff --git a/core/src/test/resources/resolutions/com.github.fommil/java-logging/1.2-SNAPSHOT b/core/shared/src/test/resources/resolutions/com.github.fommil/java-logging/1.2-SNAPSHOT
similarity index 100%
rename from core/src/test/resources/resolutions/com.github.fommil/java-logging/1.2-SNAPSHOT
rename to core/shared/src/test/resources/resolutions/com.github.fommil/java-logging/1.2-SNAPSHOT
diff --git a/core/src/test/resources/resolutions/org.apache.spark/spark-core_2.11/1.3.1 b/core/shared/src/test/resources/resolutions/org.apache.spark/spark-core_2.11/1.3.1
similarity index 100%
rename from core/src/test/resources/resolutions/org.apache.spark/spark-core_2.11/1.3.1
rename to core/shared/src/test/resources/resolutions/org.apache.spark/spark-core_2.11/1.3.1
diff --git a/core/src/test/resources/resolutions/org.apache.spark/spark-core_2.11/1.3.1.jcabi b/core/shared/src/test/resources/resolutions/org.apache.spark/spark-core_2.11/1.3.1.jcabi
similarity index 100%
rename from core/src/test/resources/resolutions/org.apache.spark/spark-core_2.11/1.3.1.jcabi
rename to core/shared/src/test/resources/resolutions/org.apache.spark/spark-core_2.11/1.3.1.jcabi
diff --git a/core/src/test/scala/coursier/test/CentralTests.scala b/core/shared/src/test/scala/coursier/test/CentralTests.scala
similarity index 100%
rename from core/src/test/scala/coursier/test/CentralTests.scala
rename to core/shared/src/test/scala/coursier/test/CentralTests.scala
diff --git a/core/src/test/scala/coursier/test/ExclusionsTests.scala b/core/shared/src/test/scala/coursier/test/ExclusionsTests.scala
similarity index 100%
rename from core/src/test/scala/coursier/test/ExclusionsTests.scala
rename to core/shared/src/test/scala/coursier/test/ExclusionsTests.scala
diff --git a/core/src/test/scala/coursier/test/PomParsingTests.scala b/core/shared/src/test/scala/coursier/test/PomParsingTests.scala
similarity index 100%
rename from core/src/test/scala/coursier/test/PomParsingTests.scala
rename to core/shared/src/test/scala/coursier/test/PomParsingTests.scala
diff --git a/core/src/test/scala/coursier/test/ResolutionTests.scala b/core/shared/src/test/scala/coursier/test/ResolutionTests.scala
similarity index 100%
rename from core/src/test/scala/coursier/test/ResolutionTests.scala
rename to core/shared/src/test/scala/coursier/test/ResolutionTests.scala
diff --git a/core/src/test/scala/coursier/test/TestRepository.scala b/core/shared/src/test/scala/coursier/test/TestRepository.scala
similarity index 100%
rename from core/src/test/scala/coursier/test/TestRepository.scala
rename to core/shared/src/test/scala/coursier/test/TestRepository.scala
diff --git a/core/src/test/scala/coursier/test/VersionConstraintTests.scala b/core/shared/src/test/scala/coursier/test/VersionConstraintTests.scala
similarity index 100%
rename from core/src/test/scala/coursier/test/VersionConstraintTests.scala
rename to core/shared/src/test/scala/coursier/test/VersionConstraintTests.scala
diff --git a/core/src/test/scala/coursier/test/VersionIntervalTests.scala b/core/shared/src/test/scala/coursier/test/VersionIntervalTests.scala
similarity index 100%
rename from core/src/test/scala/coursier/test/VersionIntervalTests.scala
rename to core/shared/src/test/scala/coursier/test/VersionIntervalTests.scala
diff --git a/core/src/test/scala/coursier/test/VersionTests.scala b/core/shared/src/test/scala/coursier/test/VersionTests.scala
similarity index 100%
rename from core/src/test/scala/coursier/test/VersionTests.scala
rename to core/shared/src/test/scala/coursier/test/VersionTests.scala
diff --git a/core/src/test/scala/coursier/test/package.scala b/core/shared/src/test/scala/coursier/test/package.scala
similarity index 100%
rename from core/src/test/scala/coursier/test/package.scala
rename to core/shared/src/test/scala/coursier/test/package.scala
diff --git a/project/Coursier.scala b/project/Coursier.scala
deleted file mode 100644
index 80a4f8dd6..000000000
--- a/project/Coursier.scala
+++ /dev/null
@@ -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 := {
- https://github.com/alexarchambault/coursier
-
-
- Apache 2.0
- http://opensource.org/licenses/Apache-2.0
-
-
-
- scm:git:github.com/alexarchambault/coursier.git
- scm:git:git@github.com:alexarchambault/coursier.git
- github.com/alexarchambault/coursier.git
-
-
-
- alexarchambault
- Alexandre Archambault
- https://github.com/alexarchambault
-
-
- },
- 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 := ()
- )
-
-}
diff --git a/project/plugins.sbt b/project/plugins.sbt
index dcd3228a0..591cf84ea 100644
--- a/project/plugins.sbt
+++ b/project/plugins.sbt
@@ -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")
diff --git a/project/travis.sh b/project/travis.sh
index 2424df814..ae2049176 100755
--- a/project/travis.sh
+++ b/project/travis.sh
@@ -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