From 9ece419bc83c04b3633ee043dec60948474b6044 Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Tue, 21 Feb 2017 15:57:38 +0100 Subject: [PATCH 01/16] Take into account COURSIER_PROGRESS the right way COURSIER_PROGRESS=true was disabling progress bars before that --- cache/src/main/scala/coursier/TermDisplay.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cache/src/main/scala/coursier/TermDisplay.scala b/cache/src/main/scala/coursier/TermDisplay.scala index f611a912e..bdf6edbba 100644 --- a/cache/src/main/scala/coursier/TermDisplay.scala +++ b/cache/src/main/scala/coursier/TermDisplay.scala @@ -68,7 +68,7 @@ object TermDisplay { def insideEmacs = sys.env.contains("INSIDE_EMACS") def ci = sys.env.contains("CI") - val env = env0.getOrElse(compatibilityEnv) + val env = env0.fold(compatibilityEnv)(!_) env || nonInteractive || insideEmacs || ci } From 21ba9888a57a599a74260ebe57727f00e6bf69ce Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Tue, 21 Feb 2017 15:57:39 +0100 Subject: [PATCH 02/16] Flush output after progress bars Should fix some progress info not being cleared --- cache/src/main/scala/coursier/TermDisplay.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cache/src/main/scala/coursier/TermDisplay.scala b/cache/src/main/scala/coursier/TermDisplay.scala index bdf6edbba..cc8647957 100644 --- a/cache/src/main/scala/coursier/TermDisplay.scala +++ b/cache/src/main/scala/coursier/TermDisplay.scala @@ -348,6 +348,8 @@ object TermDisplay { } for (_ <- 0 until currentHeight) out.up(2) + + out.flush() } private var previous = Set.empty[String] From a84057f7921ad001159786eacecae6b6703d3338 Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Tue, 21 Feb 2017 15:57:39 +0100 Subject: [PATCH 03/16] Ensure no progress stuff if printed... after progress report is stopped just in case --- build.sbt | 1 + cache/src/main/scala/coursier/TermDisplay.scala | 10 +++++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/build.sbt b/build.sbt index 32f31df24..537114a13 100644 --- a/build.sbt +++ b/build.sbt @@ -162,6 +162,7 @@ lazy val cache = project import com.typesafe.tools.mima.core._ Seq( + ProblemFilters.exclude[DirectMissingMethodProblem]("coursier.TermDisplay#UpdateDisplayRunnable.cleanDisplay"), ProblemFilters.exclude[FinalClassProblem]("coursier.TermDisplay$DownloadInfo"), ProblemFilters.exclude[FinalClassProblem]("coursier.TermDisplay$CheckUpdateInfo"), ProblemFilters.exclude[FinalClassProblem]("coursier.util.Base64$B64Scheme"), diff --git a/cache/src/main/scala/coursier/TermDisplay.scala b/cache/src/main/scala/coursier/TermDisplay.scala index cc8647957..d1bc009fe 100644 --- a/cache/src/main/scala/coursier/TermDisplay.scala +++ b/cache/src/main/scala/coursier/TermDisplay.scala @@ -173,6 +173,8 @@ object TermDisplay { private var currentHeight = 0 private var printedAnything0 = false + private var stopped = false + def printedAnything() = printedAnything0 private val needsUpdate = new AtomicBoolean(false) @@ -286,7 +288,7 @@ object TermDisplay { } private def updateDisplay(): Unit = - if (needsUpdate.getAndSet(false)) { + if (!stopped && needsUpdate.getAndSet(false)) { val (done0, downloads0) = downloads.synchronized { val q = doneQueue .toVector @@ -341,7 +343,7 @@ object TermDisplay { currentHeight = downloads0.length } - def cleanDisplay(): Unit = { + def stop(): Unit = { for (_ <- 1 to 2; _ <- 0 until currentHeight) { out.clearLine(2) out.down(1) @@ -350,6 +352,8 @@ object TermDisplay { out.up(2) out.flush() + + stopped = true } private var previous = Set.empty[String] @@ -456,7 +460,7 @@ class TermDisplay( def stopDidPrintSomething(): Boolean = { scheduler.shutdown() scheduler.awaitTermination(2 * refreshInterval, TimeUnit.MILLISECONDS) - updateRunnable.cleanDisplay() + updateRunnable.stop() updateRunnable.printedAnything() } From 0e9109c3f7343aeba9970b12c8dd6de5c0bc6313 Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Tue, 21 Feb 2017 15:57:39 +0100 Subject: [PATCH 04/16] Don't assume directory listing to be changing... for non changing dependencies --- core/shared/src/main/scala/coursier/ivy/IvyRepository.scala | 2 +- core/shared/src/main/scala/coursier/maven/MavenRepository.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/shared/src/main/scala/coursier/ivy/IvyRepository.scala b/core/shared/src/main/scala/coursier/ivy/IvyRepository.scala index f1bf448b2..6b0324a89 100644 --- a/core/shared/src/main/scala/coursier/ivy/IvyRepository.scala +++ b/core/shared/src/main/scala/coursier/ivy/IvyRepository.scala @@ -168,7 +168,7 @@ final case class IvyRepository( Map.empty, Map.empty, Attributes("", ""), - changing = true, + changing = changing.getOrElse(version.contains("-SNAPSHOT")), authentication ) diff --git a/core/shared/src/main/scala/coursier/maven/MavenRepository.scala b/core/shared/src/main/scala/coursier/maven/MavenRepository.scala index 0be04d393..3bacc9939 100644 --- a/core/shared/src/main/scala/coursier/maven/MavenRepository.scala +++ b/core/shared/src/main/scala/coursier/maven/MavenRepository.scala @@ -258,7 +258,7 @@ final case class MavenRepository( Map.empty, Map.empty, Attributes("", ""), - changing = true, + changing = changing.getOrElse(version.contains("-SNAPSHOT")), authentication ) From 7914603f400e77c12911cd0193ab376853ec5ab9 Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Tue, 21 Feb 2017 15:57:40 +0100 Subject: [PATCH 05/16] Remove deprecated comment --- sbt-coursier/src/main/scala-2.10/coursier/FromSbt.scala | 4 ---- 1 file changed, 4 deletions(-) diff --git a/sbt-coursier/src/main/scala-2.10/coursier/FromSbt.scala b/sbt-coursier/src/main/scala-2.10/coursier/FromSbt.scala index 262bb3c52..b63bbbdcf 100644 --- a/sbt-coursier/src/main/scala-2.10/coursier/FromSbt.scala +++ b/sbt-coursier/src/main/scala-2.10/coursier/FromSbt.scala @@ -109,10 +109,6 @@ object FromSbt { scalaBinaryVersion: String ): Project = { - // FIXME Ignored for now - easy to support though - // val sbtDepOverrides = dependencyOverrides.value - // val sbtExclusions = excludeDependencies.value - val deps = allDependencies.flatMap(dependencies(_, scalaVersion, scalaBinaryVersion)) Project( From c131d6c8561c9180c9f2b991c253aacac9b19f46 Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Tue, 21 Feb 2017 15:57:40 +0100 Subject: [PATCH 06/16] No more "Unrecognized repository inter-project" from the sbt plugin --- sbt-coursier/src/main/scala-2.10/coursier/FromSbt.scala | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sbt-coursier/src/main/scala-2.10/coursier/FromSbt.scala b/sbt-coursier/src/main/scala-2.10/coursier/FromSbt.scala index b63bbbdcf..725ab6eae 100644 --- a/sbt-coursier/src/main/scala-2.10/coursier/FromSbt.scala +++ b/sbt-coursier/src/main/scala-2.10/coursier/FromSbt.scala @@ -222,6 +222,9 @@ object FromSbt { mavenRepositoryOpt(mavenCompatibleBase, log, authentication) } + case raw: sbt.RawRepository if raw.name == "inter-project" => // sbt.RawRepository.equals just compares names anyway + None + case other => log.warn(s"Unrecognized repository ${other.name}, ignoring it") None From ac00abd152f36b3d9cc0d17617f929c64301ae03 Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Tue, 21 Feb 2017 15:57:40 +0100 Subject: [PATCH 07/16] Tweak artifact listing Can't remember when / why this one was necessary... --- core/shared/src/main/scala/coursier/maven/MavenSource.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/shared/src/main/scala/coursier/maven/MavenSource.scala b/core/shared/src/main/scala/coursier/maven/MavenSource.scala index 931fb9a62..23712893b 100644 --- a/core/shared/src/main/scala/coursier/maven/MavenSource.scala +++ b/core/shared/src/main/scala/coursier/maven/MavenSource.scala @@ -82,7 +82,7 @@ final case class MavenSource( project.publications.collect { case (_, p) if p.`type` == dependency.attributes.`type` || - p.ext == dependency.attributes.`type` // wow + (p.ext == dependency.attributes.`type` && project.packagingOpt.toSeq.contains(p.`type`)) // wow => p } From 36ba4b6252236667ffcc1ff8f967e95eafd5a4c5 Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Tue, 21 Feb 2017 15:57:40 +0100 Subject: [PATCH 08/16] Remove unnecessary --default-artifact-type option, fix javadoc / sources --- .../scala-2.11/coursier/cli/Bootstrap.scala | 11 ++++------ .../main/scala-2.11/coursier/cli/Fetch.scala | 5 ++++- .../main/scala-2.11/coursier/cli/Helper.scala | 14 ++++++------- .../scala-2.11/coursier/cli/Options.scala | 21 +++++++++---------- .../scala-2.11/coursier/cli/SparkSubmit.scala | 4 ++-- 5 files changed, 27 insertions(+), 28 deletions(-) diff --git a/cli/src/main/scala-2.11/coursier/cli/Bootstrap.scala b/cli/src/main/scala-2.11/coursier/cli/Bootstrap.scala index 071de1c5e..1174dd156 100644 --- a/cli/src/main/scala-2.11/coursier/cli/Bootstrap.scala +++ b/cli/src/main/scala-2.11/coursier/cli/Bootstrap.scala @@ -65,10 +65,7 @@ final case class Bootstrap( sys.exit(1) } - val isolatedDeps = options.isolated.isolatedDeps( - options.common.defaultArtifactType, - options.common.scalaVersion - ) + val isolatedDeps = options.isolated.isolatedDeps(options.common.scalaVersion) val (_, isolatedArtifactFiles) = options.isolated.targets.foldLeft((Vector.empty[String], Map.empty[String, (Seq[String], Seq[File])])) { @@ -81,7 +78,7 @@ final case class Bootstrap( def subFiles0 = helper.fetch( sources = false, javadoc = false, - artifactTypes = artifactOptions.artifactTypes, + artifactTypes = artifactOptions.artifactTypes(sources = false, javadoc = false), subset = isolatedDeps.getOrElse(target, Seq.empty).toSet ) @@ -103,7 +100,7 @@ final case class Bootstrap( helper.fetch( sources = false, javadoc = false, - artifactTypes = artifactOptions.artifactTypes + artifactTypes = artifactOptions.artifactTypes(sources = false, javadoc = false) ) ) else @@ -111,7 +108,7 @@ final case class Bootstrap( helper.artifacts( sources = false, javadoc = false, - artifactTypes = artifactOptions.artifactTypes + artifactTypes = artifactOptions.artifactTypes(sources = false, javadoc = false) ).map(_.url), Seq.empty[File] ) diff --git a/cli/src/main/scala-2.11/coursier/cli/Fetch.scala b/cli/src/main/scala-2.11/coursier/cli/Fetch.scala index a541e02f0..916c0e546 100644 --- a/cli/src/main/scala-2.11/coursier/cli/Fetch.scala +++ b/cli/src/main/scala-2.11/coursier/cli/Fetch.scala @@ -17,7 +17,10 @@ final case class Fetch( val files0 = helper.fetch( sources = options.sources, javadoc = options.javadoc, - artifactTypes = options.artifactOptions.artifactTypes + artifactTypes = options.artifactOptions.artifactTypes( + options.sources || options.common.classifier0("sources"), + options.javadoc || options.common.classifier0("javadoc") + ) ) val out = diff --git a/cli/src/main/scala-2.11/coursier/cli/Helper.scala b/cli/src/main/scala-2.11/coursier/cli/Helper.scala index ee3df053a..6b2c2ee7e 100644 --- a/cli/src/main/scala-2.11/coursier/cli/Helper.scala +++ b/cli/src/main/scala-2.11/coursier/cli/Helper.scala @@ -249,7 +249,7 @@ class Helper( Dependency( module, version, - attributes = Attributes(defaultArtifactType, ""), + attributes = Attributes("", ""), configuration = configOpt.getOrElse(defaultConfiguration), exclusions = excludes ) @@ -260,7 +260,7 @@ class Helper( Dependency( module, version, - attributes = Attributes(defaultArtifactType, ""), + attributes = Attributes("", ""), configuration = configOpt.getOrElse(defaultConfiguration), exclusions = excludes, transitive = false @@ -523,11 +523,11 @@ class Helper( if (classifier0.nonEmpty || sources || javadoc) { var classifiers = classifier0 if (sources) - classifiers = classifiers :+ "sources" + classifiers = classifiers + "sources" if (javadoc) - classifiers = classifiers :+ "javadoc" + classifiers = classifiers + "javadoc" - res0.dependencyClassifiersArtifacts(classifiers.distinct).map(_._2) + res0.dependencyClassifiersArtifacts(classifiers.toVector.sorted).map(_._2) } else res0.dependencyArtifacts.map(_._2) @@ -616,7 +616,7 @@ class Helper( // FIXME That shouldn't be hard-coded this way... // This whole class ought to be rewritten more cleanly. - val artifactTypes = Set("jar") + val artifactTypes = Set("jar", "bundle") val files0 = fetch( sources = false, @@ -628,7 +628,7 @@ class Helper( (baseLoader, files0) else { - val isolatedDeps = isolated.isolatedDeps(common.defaultArtifactType, common.scalaVersion) + val isolatedDeps = isolated.isolatedDeps(common.scalaVersion) val (isolatedLoader, filteredFiles0) = isolated.targets.foldLeft((baseLoader, files0)) { case ((parent, files0), target) => diff --git a/cli/src/main/scala-2.11/coursier/cli/Options.scala b/cli/src/main/scala-2.11/coursier/cli/Options.scala index deb6df077..b2a183a54 100644 --- a/cli/src/main/scala-2.11/coursier/cli/Options.scala +++ b/cli/src/main/scala-2.11/coursier/cli/Options.scala @@ -61,10 +61,6 @@ final case class CommonOptions( @Value("configuration") @Short("c") defaultConfiguration: String = "default(compile)", - @Help("Default artifact type (default: follow packaging infos, else default to jar)") - @Value("type") - @Short("a") - defaultArtifactType: String = "", @Help("Maximum number of parallel downloads (default: 6)") @Short("n") parallel: Int = 6, @@ -91,7 +87,7 @@ final case class CommonOptions( cacheOptions: CacheOptions = CacheOptions() ) { val verbosityLevel = Tag.unwrap(verbose) - (if (quiet) 1 else 0) - lazy val classifier0 = classifier.flatMap(_.split(',')).filter(_.nonEmpty) + lazy val classifier0 = classifier.flatMap(_.split(',')).filter(_.nonEmpty).toSet } final case class CacheOptions( @@ -154,7 +150,7 @@ final case class IsolatedLoaderOptions( t -> modVers } - def isolatedDeps(defaultArtifactType: String, defaultScalaVersion: String) = + def isolatedDeps(defaultScalaVersion: String) = isolatedModuleVersions(defaultScalaVersion).map { case (t, l) => t -> l.map { @@ -163,7 +159,7 @@ final case class IsolatedLoaderOptions( mod, ver, configuration = "runtime", - attributes = Attributes(defaultArtifactType, "") + attributes = Attributes("", "") ) } } @@ -182,15 +178,18 @@ final case class ArtifactOptions( @Help("Fetch artifacts even if the resolution is errored") force: Boolean = false ) { - lazy val artifactTypes = { + def artifactTypes(sources: Boolean, javadoc: Boolean) = { val types0 = artifactType .flatMap(_.split(',')) .filter(_.nonEmpty) .toSet - if (types0.isEmpty) - ArtifactOptions.defaultArtifactTypes - else if (types0("*")) + if (types0.isEmpty) { + if (sources || javadoc) + Some("src").filter(_ => sources).toSet ++ Some("doc").filter(_ => javadoc) + else + ArtifactOptions.defaultArtifactTypes + } else if (types0("*")) Set("*") else types0 diff --git a/cli/src/main/scala-2.11/coursier/cli/SparkSubmit.scala b/cli/src/main/scala-2.11/coursier/cli/SparkSubmit.scala index 72f48b777..9b5abdfa5 100644 --- a/cli/src/main/scala-2.11/coursier/cli/SparkSubmit.scala +++ b/cli/src/main/scala-2.11/coursier/cli/SparkSubmit.scala @@ -73,7 +73,7 @@ final case class SparkSubmit( helper.fetch( sources = false, javadoc = false, - artifactTypes = options.artifactOptions.artifactTypes + artifactTypes = options.artifactOptions.artifactTypes(sources = false, javadoc = false) ) ++ options.extraJars.map(new File(_)) val (scalaVersion, sparkVersion) = @@ -195,7 +195,7 @@ final case class SparkSubmit( sparkVersion, options.noDefaultSubmitDependencies, options.submitDependencies.flatMap(_.split(",")).filter(_.nonEmpty), - options.artifactOptions.artifactTypes, + options.artifactOptions.artifactTypes(sources = false, javadoc = false), options.common ) From e5a6a609e3ac12fee311732da2034d0d83434f3f Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Tue, 21 Feb 2017 15:57:41 +0100 Subject: [PATCH 09/16] Exclude pom from pack distributions Upcoming sbt-coursier includes this in the update report, and these aren't ignored by sbt-pack by default (yet?) --- build.sbt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 537114a13..5ede4bd5a 100644 --- a/build.sbt +++ b/build.sbt @@ -206,6 +206,7 @@ lazy val cli = project else Seq() }, + packExcludeArtifactTypes += "pom", resourceGenerators in Compile += packageBin.in(bootstrap).in(Compile).map { jar => Seq(jar) }.taskValue, @@ -399,7 +400,8 @@ lazy val `http-server` = project ) else Seq() - } + }, + packExcludeArtifactTypes += "pom" ) lazy val okhttp = project From 88f54cc356cf6b59e2cbe8381ddcb01667f72ccb Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Tue, 21 Feb 2017 15:57:41 +0100 Subject: [PATCH 10/16] Add sbt-launcher --- build.sbt | 14 + project/generate-sbt-launcher.sh | 11 + .../sbtlauncher/AppConfiguration.scala | 9 + .../coursier/sbtlauncher/AppProvider.scala | 18 + .../coursier/sbtlauncher/ApplicationID.scala | 54 ++ .../sbtlauncher/ComponentProvider.scala | 70 ++ .../sbtlauncher/DummyGlobalLock.scala | 21 + .../scala/coursier/sbtlauncher/Launcher.scala | 638 ++++++++++++++++++ .../scala/coursier/sbtlauncher/MainApp.scala | 110 +++ .../coursier/sbtlauncher/Repository.scala | 19 + .../coursier/sbtlauncher/SbtConfig.scala | 112 +++ .../coursier/sbtlauncher/ScalaProvider.scala | 16 + sbt.properties | 16 + 13 files changed, 1108 insertions(+) create mode 100755 project/generate-sbt-launcher.sh create mode 100644 sbt-launcher/src/main/scala/coursier/sbtlauncher/AppConfiguration.scala create mode 100644 sbt-launcher/src/main/scala/coursier/sbtlauncher/AppProvider.scala create mode 100644 sbt-launcher/src/main/scala/coursier/sbtlauncher/ApplicationID.scala create mode 100644 sbt-launcher/src/main/scala/coursier/sbtlauncher/ComponentProvider.scala create mode 100644 sbt-launcher/src/main/scala/coursier/sbtlauncher/DummyGlobalLock.scala create mode 100644 sbt-launcher/src/main/scala/coursier/sbtlauncher/Launcher.scala create mode 100644 sbt-launcher/src/main/scala/coursier/sbtlauncher/MainApp.scala create mode 100644 sbt-launcher/src/main/scala/coursier/sbtlauncher/Repository.scala create mode 100644 sbt-launcher/src/main/scala/coursier/sbtlauncher/SbtConfig.scala create mode 100644 sbt-launcher/src/main/scala/coursier/sbtlauncher/ScalaProvider.scala create mode 100644 sbt.properties diff --git a/build.sbt b/build.sbt index 5ede4bd5a..129738960 100644 --- a/build.sbt +++ b/build.sbt @@ -382,6 +382,19 @@ lazy val `sbt-shading` = project libraryDependencies += "org.anarres.jarjar" % "jarjar-core" % "1.0.0" ) +lazy val `sbt-launcher` = project + .dependsOn(cache) + .settings(commonSettings) + .settings(packAutoSettings) + .settings( + libraryDependencies ++= Seq( + "com.github.alexarchambault" %% "case-app" % "1.1.3", + "org.scala-sbt" % "launcher-interface" % "1.0.0", + "com.typesafe" % "config" % "1.3.1" + ), + packExcludeArtifactTypes += "pom" + ) + val http4sVersion = "0.8.6" lazy val `http-server` = project @@ -426,6 +439,7 @@ lazy val `coursier` = project.in(file(".")) cli, `sbt-coursier`, `sbt-shading`, + `sbt-launcher`, web, doc, `http-server`, diff --git a/project/generate-sbt-launcher.sh b/project/generate-sbt-launcher.sh new file mode 100755 index 000000000..c376262d9 --- /dev/null +++ b/project/generate-sbt-launcher.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +set -e + +COURSIER_VERSION=1.0.0-SNAPSHOT + +"$(dirname "$0")/../coursier" bootstrap \ + "io.get-coursier:sbt-launcher_2.12:$COURSIER_VERSION" \ + -i launcher \ + -I launcher:org.scala-sbt:launcher-interface:1.0.0 \ + -o csbt \ + -J -Djline.shutdownhook=false diff --git a/sbt-launcher/src/main/scala/coursier/sbtlauncher/AppConfiguration.scala b/sbt-launcher/src/main/scala/coursier/sbtlauncher/AppConfiguration.scala new file mode 100644 index 000000000..7fadd73f4 --- /dev/null +++ b/sbt-launcher/src/main/scala/coursier/sbtlauncher/AppConfiguration.scala @@ -0,0 +1,9 @@ +package coursier.sbtlauncher + +import java.io.File + +final case class AppConfiguration( + arguments: Array[String], + baseDirectory: File, + provider: xsbti.AppProvider +) extends xsbti.AppConfiguration diff --git a/sbt-launcher/src/main/scala/coursier/sbtlauncher/AppProvider.scala b/sbt-launcher/src/main/scala/coursier/sbtlauncher/AppProvider.scala new file mode 100644 index 000000000..ac671f138 --- /dev/null +++ b/sbt-launcher/src/main/scala/coursier/sbtlauncher/AppProvider.scala @@ -0,0 +1,18 @@ +package coursier.sbtlauncher + +import java.io.File + +final case class AppProvider( + scalaProvider: xsbti.ScalaProvider, + id: xsbti.ApplicationID, + loader: ClassLoader, + mainClass: Class[_ <: xsbti.AppMain], + createMain: () => xsbti.AppMain, + mainClasspath: Array[File], + components: xsbti.ComponentProvider +) extends xsbti.AppProvider { + def entryPoint: Class[_] = + mainClass + def newMain(): xsbti.AppMain = + createMain() +} diff --git a/sbt-launcher/src/main/scala/coursier/sbtlauncher/ApplicationID.scala b/sbt-launcher/src/main/scala/coursier/sbtlauncher/ApplicationID.scala new file mode 100644 index 000000000..423eb35ea --- /dev/null +++ b/sbt-launcher/src/main/scala/coursier/sbtlauncher/ApplicationID.scala @@ -0,0 +1,54 @@ +package coursier.sbtlauncher + +import java.io.File + +final case class ApplicationID( + groupID: String, + name: String, + version: String, + mainClass: String, + mainComponents: Array[String], + crossVersioned: Boolean, + crossVersionedValue: xsbti.CrossValue, + classpathExtra: Array[File] +) extends xsbti.ApplicationID { + + assert(crossVersioned == (crossVersionedValue != xsbti.CrossValue.Disabled)) + + def disableCrossVersion(scalaVersion: String): ApplicationID = + crossVersionedValue match { + case xsbti.CrossValue.Disabled => + this + case xsbti.CrossValue.Binary => + val scalaBinaryVersion = scalaVersion.split('.').take(2).mkString(".") + copy( + crossVersioned = false, + crossVersionedValue = xsbti.CrossValue.Disabled, + version = s"${version}_$scalaBinaryVersion" + ) + case xsbti.CrossValue.Full => + copy( + crossVersioned = false, + crossVersionedValue = xsbti.CrossValue.Disabled, + version = s"${version}_$scalaVersion" + ) + } +} + +object ApplicationID { + def apply(id: xsbti.ApplicationID): ApplicationID = + id match { + case id0: ApplicationID => id0 + case _ => + ApplicationID( + id.groupID(), + id.name(), + id.version(), + id.mainClass(), + id.mainComponents(), + id.crossVersionedValue() != xsbti.CrossValue.Disabled, + id.crossVersionedValue(), + id.classpathExtra() + ) + } +} diff --git a/sbt-launcher/src/main/scala/coursier/sbtlauncher/ComponentProvider.scala b/sbt-launcher/src/main/scala/coursier/sbtlauncher/ComponentProvider.scala new file mode 100644 index 000000000..923de660d --- /dev/null +++ b/sbt-launcher/src/main/scala/coursier/sbtlauncher/ComponentProvider.scala @@ -0,0 +1,70 @@ +package coursier.sbtlauncher + +import java.io.File +import java.nio.file.{Files, StandardCopyOption} + +import scala.collection.mutable + +final class ComponentProvider(cacheDir: File) extends xsbti.ComponentProvider { + + private val components0 = new mutable.HashMap[String, Array[File]] + + def componentLocation(id: String): File = + new File(cacheDir, id) + + def component(componentId: String): Array[File] = { + val res = components0.getOrElse[Array[File]]( + componentId, + { + val dir = componentLocation(componentId) + if (dir.exists()) + Option(dir.listFiles()).getOrElse(Array()) + else + Array() + } + ) + res + } + + private def clear(componentId: String): Unit = { + + def deleteRecursively(f: File): Unit = + if (f.isFile) + f.delete() + else + Option(f.listFiles()) + .getOrElse(Array()) + .foreach(deleteRecursively) + + val dir = componentLocation(componentId) + deleteRecursively(dir) + } + + private def copying(componentId: String, f: File): File = { + + // TODO Use some locking mechanisms here + + val dir = componentLocation(componentId) + dir.mkdirs() + val dest = new File(dir, f.getName) + Files.copy(f.toPath, dest.toPath, StandardCopyOption.REPLACE_EXISTING) + dest + } + + def defineComponentNoCopy(componentId: String, components: Array[File]): Unit = { + components0 += componentId -> components.distinct + } + def defineComponent(componentId: String, components: Array[File]): Unit = { + clear(componentId) + components0 += componentId -> components.distinct.map(copying(componentId, _)) + } + def addToComponent(componentId: String, components: Array[File]): Boolean = { + val previousFiles = components0.getOrElse(componentId, Array.empty[File]) + val newFiles = (previousFiles ++ components.distinct.map(copying(componentId, _))).distinct + components0 += componentId -> newFiles + newFiles.length != previousFiles.length + } + + def lockFile: File = new File("/component-lock") + +} diff --git a/sbt-launcher/src/main/scala/coursier/sbtlauncher/DummyGlobalLock.scala b/sbt-launcher/src/main/scala/coursier/sbtlauncher/DummyGlobalLock.scala new file mode 100644 index 000000000..21584e1d7 --- /dev/null +++ b/sbt-launcher/src/main/scala/coursier/sbtlauncher/DummyGlobalLock.scala @@ -0,0 +1,21 @@ +package coursier.sbtlauncher + +import java.io.File +import java.util.concurrent.{Callable, ConcurrentHashMap} + +case object DummyGlobalLock extends xsbti.GlobalLock { + + private val locks = new ConcurrentHashMap[File, AnyRef] + + def apply[T](lockFile: File, run: Callable[T]): T = + Option(lockFile) match { + case None => run.call() + case Some(lockFile0) => + val lock0 = new AnyRef + val lock = Option(locks.putIfAbsent(lockFile0, lock0)).getOrElse(lock0) + + lock.synchronized { + run.call() + } + } +} diff --git a/sbt-launcher/src/main/scala/coursier/sbtlauncher/Launcher.scala b/sbt-launcher/src/main/scala/coursier/sbtlauncher/Launcher.scala new file mode 100644 index 000000000..d591a9dd8 --- /dev/null +++ b/sbt-launcher/src/main/scala/coursier/sbtlauncher/Launcher.scala @@ -0,0 +1,638 @@ +package coursier.sbtlauncher + +import java.io.{File, OutputStreamWriter} +import java.net.{URL, URLClassLoader} +import java.util.concurrent.ConcurrentHashMap + +import coursier.Cache.Logger +import coursier._ +import coursier.ivy.IvyRepository +import coursier.maven.MavenSource + +import scala.annotation.tailrec +import scalaz.{-\/, \/-} +import scalaz.concurrent.Task + +class Launcher( + scalaVersion: String, + componentsCache: File, + val ivyHome: File +) extends xsbti.Launcher { + + val componentProvider = new ComponentProvider(componentsCache) + + lazy val baseLoader = { + + @tailrec + def rootLoader(cl: ClassLoader): ClassLoader = + if (cl == null) + sys.error("Cannot find base loader") + else { + val isLauncherLoader = + try { + cl + .asInstanceOf[AnyRef { def getIsolationTargets: Array[String] }] + .getIsolationTargets + .contains("launcher") + } catch { + case _: Throwable => false + } + + if (isLauncherLoader) + cl + else + rootLoader(cl.getParent) + } + + rootLoader(Thread.currentThread().getContextClassLoader) + } + + val repositoryIdPrefix = "coursier-launcher-" + + val repositories = Seq( + // mmh, ID "local" seems to be required for publishLocal to be fine if we're launching sbt + "local" -> Cache.ivy2Local, + s"${repositoryIdPrefix}central" -> MavenRepository("https://repo1.maven.org/maven2", sbtAttrStub = true), + s"${repositoryIdPrefix}typesafe-ivy-releases" -> IvyRepository.parse( + "https://repo.typesafe.com/typesafe/ivy-releases/[organization]/[module]/[revision]/[type]s/[artifact](-[classifier]).[ext]" + ).leftMap(sys.error).merge, + s"${repositoryIdPrefix}sbt-plugin-releases" -> IvyRepository.parse( + "https://repo.scala-sbt.org/scalasbt/sbt-plugin-releases/[organization]/[module](/scala_[scalaVersion])(/sbt_[sbtVersion])/[revision]/[type]s/[artifact](-[classifier]).[ext]" + ).leftMap(sys.error).merge + ) + + assert(!repositories.groupBy(_._1).exists(_._2.lengthCompare(1) > 0)) + + val cachePolicies = CachePolicy.default + + def fetch(logger: Option[Logger]) = { + def helper(policy: CachePolicy) = + Cache.fetch(cachePolicy = policy, logger = logger) + + val f = cachePolicies.map(helper) + + Fetch.from(repositories.map(_._2), f.head, f.tail: _*) + } + + val keepArtifactTypes = Set("jar", "bundle") + + def tasks(res: Resolution, logger: Option[Logger], classifiersOpt: Option[Seq[String]] = None) = { + val a = classifiersOpt + .fold(res.dependencyArtifacts.map(_._2))(res.dependencyClassifiersArtifacts(_).map(_._2)) + + val keepArtifactTypes = classifiersOpt.fold(Set("jar", "bundle"))(c => c.map(c => MavenSource.classifierExtensionDefaultTypes.getOrElse((c, "jar"), ???)).toSet) + + a.collect { + case artifact if keepArtifactTypes(artifact.`type`) => + def file(policy: CachePolicy) = Cache.file( + artifact, + cachePolicy = policy, + logger = logger + ) + + (file(cachePolicies.head) /: cachePolicies.tail)(_ orElse file(_)) + .run + .map(artifact.->) + } + } + + + def isOverrideRepositories = false // ??? + + def bootDirectory: File = ??? + + def getScala(version: String): xsbti.ScalaProvider = + getScala(version, "") + + def getScala(version: String, reason: String): xsbti.ScalaProvider = + getScala(version, reason, "org.scala-lang") + + def getScala(version: String, reason: String, scalaOrg: String): xsbti.ScalaProvider = { + + val key = (version, scalaOrg) + + Option(scalaProviderCache.get(key)).getOrElse { + val prov = getScala0(version, reason, scalaOrg) + val previous = Option(scalaProviderCache.putIfAbsent(key, prov)) + previous.getOrElse(prov) + } + } + + private val scalaProviderCache = new ConcurrentHashMap[(String, String), xsbti.ScalaProvider] + + private def getScala0(version: String, reason: String, scalaOrg: String): xsbti.ScalaProvider = { + + val files = getScalaFiles(version, reason, scalaOrg) + + val libraryJar = files.find(_.getName.startsWith("scala-library")).getOrElse { + throw new NoSuchElementException("scala-library JAR") + } + val compilerJar = files.find(_.getName.startsWith("scala-compiler")).getOrElse { + throw new NoSuchElementException("scala-compiler JAR") + } + + val scalaLoader = new URLClassLoader(files.map(_.toURI.toURL).toArray, baseLoader) + + ScalaProvider( + this, + version, + scalaLoader, + files.toArray, + libraryJar, + compilerJar, + id => app(id, id.version()) + ) + } + + private def getScalaFiles(version: String, reason: String, scalaOrg: String): Seq[File] = { + + val initialRes = Resolution( + Set( + Dependency(Module(scalaOrg, "scala-library"), version), + Dependency(Module(scalaOrg, "scala-compiler"), version) + ), + forceVersions = Map( + Module(scalaOrg, "scala-library") -> version, + Module(scalaOrg, "scala-compiler") -> version, + Module(scalaOrg, "scala-reflect") -> version + ) + ) + + val logger = + Some(new TermDisplay( + new OutputStreamWriter(System.err) + )) + + logger.foreach(_.init { + System.err.println(s"Resolving Scala $version (organization $scalaOrg)") + }) + + val res = initialRes.process.run(fetch(logger)).unsafePerformSync + + logger.foreach { l => + if (l.stopDidPrintSomething()) + System.err.println(s"Resolved Scala $version (organization $scalaOrg)") + } + + if (res.errors.nonEmpty) { + Console.err.println(s"Errors:\n${res.errors.map(" " + _).mkString("\n")}") + sys.exit(1) + } + + if (res.conflicts.nonEmpty) { + Console.err.println(s"Conflicts:\n${res.conflicts.map(" " + _).mkString("\n")}") + sys.exit(1) + } + + if (!res.isDone) { + Console.err.println("Did not converge") + sys.exit(1) + } + + val artifactLogger = + Some(new TermDisplay( + new OutputStreamWriter(System.err) + )) + + artifactLogger.foreach(_.init { + System.err.println(s"Fetching Scala $version artifacts (organization $scalaOrg)") + }) + + val results = Task.gatherUnordered(tasks(res, artifactLogger)).unsafePerformSync + + artifactLogger.foreach { l => + if (l.stopDidPrintSomething()) + System.err.println(s"Fetched Scala $version artifacts (organization $scalaOrg)") + } + + val errors = results.collect { case (a, -\/(err)) => (a, err) } + val files = results.collect { case (_, \/-(f)) => f } + + if (errors.nonEmpty) { + Console.err.println(s"Error downloading artifacts:\n${errors.map(" " + _).mkString("\n")}") + sys.exit(1) + } + + files + } + + def topLoader: ClassLoader = baseLoader + + def appRepositories: Array[xsbti.Repository] = + repositories.map { + case (id, m: MavenRepository) => + Repository.Maven(id, new URL(m.root)) + case (id, i: IvyRepository) => + + assert(i.metadataPatternOpt.forall(_ == i.pattern)) + + val (base, pat) = i.pattern.string.span(c => c != '[' && c != '$' && c != '(') + + assert(base.nonEmpty, i.pattern.string) + + Repository.Ivy( + id, + new URL(base), + pat, + pat, + mavenCompatible = false, + skipConsistencyCheck = true, // ??? + descriptorOptional = true // ??? + ) + }.toArray + + def ivyRepositories: Array[xsbti.Repository] = + appRepositories // ??? + + def globalLock = DummyGlobalLock + + // See https://github.com/sbt/ivy/blob/2cf13e211b2cb31f0d3b317289dca70eca3362f6/src/java/org/apache/ivy/util/ChecksumHelper.java + def checksums: Array[String] = Array("sha1", "md5") + + def app(id: xsbti.ApplicationID, version: String): xsbti.AppProvider = + app(ApplicationID(id).copy(version = version)) + + def app(id: xsbti.ApplicationID, extra: Dependency*): xsbti.AppProvider = { + + val (scalaFiles, files) = appFiles(id, extra: _*) + + val scalaLoader = new URLClassLoader(scalaFiles.map(_.toURI.toURL).toArray, baseLoader) + + val libraryJar = scalaFiles.find(_.getName.startsWith("scala-library")).getOrElse { + throw new NoSuchElementException("scala-library JAR") + } + val compilerJar = scalaFiles.find(_.getName.startsWith("scala-compiler")).getOrElse { + throw new NoSuchElementException("scala-compiler JAR") + } + + val scalaProvider = ScalaProvider( + this, + scalaVersion, + scalaLoader, + scalaFiles.toArray, + libraryJar, + compilerJar, + id => app(id, id.version()) + ) + + val loader = new URLClassLoader(files.filterNot(scalaFiles.toSet).map(_.toURI.toURL).toArray, scalaLoader) + val mainClass0 = loader.loadClass(id.mainClass).asSubclass(classOf[xsbti.AppMain]) + + AppProvider( + scalaProvider, + id, + loader, + mainClass0, + () => mainClass0.newInstance(), + files.toArray, + componentProvider + ) + } + + private def appFiles(id: xsbti.ApplicationID, extra: Dependency*): (Seq[File], Seq[File]) = { + + val id0 = ApplicationID(id).disableCrossVersion(scalaVersion) + + val initialRes = Resolution( + Set( + Dependency(Module("org.scala-lang", "scala-library"), scalaVersion), + Dependency(Module("org.scala-lang", "scala-compiler"), scalaVersion), + Dependency(Module(id0.groupID, id0.name), id0.version) + ) ++ extra, + forceVersions = Map( + Module("org.scala-lang", "scala-library") -> scalaVersion, + Module("org.scala-lang", "scala-compiler") -> scalaVersion, + Module("org.scala-lang", "scala-reflect") -> scalaVersion + ) + ) + + val logger = + Some(new TermDisplay( + new OutputStreamWriter(System.err) + )) + + val extraMsg = + if (extra.isEmpty) + "" + else + s" (plus ${extra.length} dependencies)" + + logger.foreach(_.init { + System.err.println(s"Resolving ${id0.groupID}:${id0.name}:${id0.version}$extraMsg") + }) + + val res = initialRes.process.run(fetch(logger)).unsafePerformSync + + logger.foreach { l => + if (l.stopDidPrintSomething()) + System.err.println(s"Resolved ${id0.groupID}:${id0.name}:${id0.version}$extraMsg") + } + + if (res.errors.nonEmpty) { + Console.err.println(s"Errors:\n${res.errors.map(" " + _).mkString("\n")}") + sys.exit(1) + } + + if (res.conflicts.nonEmpty) { + Console.err.println(s"Conflicts:\n${res.conflicts.map(" " + _).mkString("\n")}") + sys.exit(1) + } + + if (!res.isDone) { + Console.err.println("Did not converge") + sys.exit(1) + } + + val artifactLogger = + Some(new TermDisplay( + new OutputStreamWriter(System.err) + )) + + artifactLogger.foreach(_.init { + System.err.println(s"Fetching ${id0.groupID}:${id0.name}:${id0.version} artifacts") + }) + + val results = Task.gatherUnordered(tasks(res, artifactLogger)).unsafePerformSync + + artifactLogger.foreach { l => + if (l.stopDidPrintSomething()) + System.err.println(s"Fetched ${id0.groupID}:${id0.name}:${id0.version} artifacts") + } + + val errors = results.collect { case (a, -\/(err)) => (a, err) } + val files = results.collect { case (_, \/-(f)) => f } + + if (errors.nonEmpty) { + Console.err.println(s"Error downloading artifacts:\n${errors.map(" " + _).mkString("\n")}") + sys.exit(1) + } + + val scalaSubRes = res.subset( + Set( + Dependency(Module("org.scala-lang", "scala-library"), scalaVersion), + Dependency(Module("org.scala-lang", "scala-compiler"), scalaVersion) + ) + ) + + val scalaArtifactLogger = + Some(new TermDisplay( + new OutputStreamWriter(System.err) + )) + + scalaArtifactLogger.foreach(_.init { + System.err.println(s"Fetching ${id0.groupID}:${id0.name}:${id0.version} Scala artifacts") + }) + + val scalaResults = Task.gatherUnordered(tasks(scalaSubRes, scalaArtifactLogger)).unsafePerformSync + + scalaArtifactLogger.foreach { l => + if (l.stopDidPrintSomething()) + System.err.println(s"Fetched ${id0.groupID}:${id0.name}:${id0.version} Scala artifacts") + } + + val scalaErrors = scalaResults.collect { case (a, -\/(err)) => (a, err) } + val scalaFiles = scalaResults.collect { case (_, \/-(f)) => f } + + if (scalaErrors.nonEmpty) { + Console.err.println(s"Error downloading artifacts:\n${scalaErrors.map(" " + _).mkString("\n")}") + sys.exit(1) + } + + (scalaFiles, files) + } + + def registerScalaComponents(scalaVersion: String = scalaVersion): Unit = { + + lazy val prov = getScala(scalaVersion) + lazy val jars = prov.jars() + + lazy val libraryJar = jars.find(_.getName.startsWith("scala-library")).getOrElse { + throw new NoSuchElementException("scala-library JAR") + } + lazy val compilerJar = jars.find(_.getName.startsWith("scala-compiler")).getOrElse { + throw new NoSuchElementException("scala-compiler JAR") + } + lazy val reflectJar = jars.find(_.getName.startsWith("scala-reflect")).getOrElse { + throw new NoSuchElementException("scala-reflect JAR") + } + + if (componentProvider.component("library").isEmpty) + componentProvider.defineComponentNoCopy("library", Array(libraryJar)) + if (componentProvider.component("compiler").isEmpty) + componentProvider.defineComponentNoCopy("compiler", Array(compilerJar)) + if (componentProvider.component("reflect").isEmpty) + componentProvider.defineComponentNoCopy("reflect", Array(reflectJar)) + } + + def registerSbtInterfaceComponents(sbtVersion: String): Unit = { + + lazy val (interfaceJar, _) = sbtInterfaceComponentFiles(sbtVersion) + lazy val compilerInterfaceSourceJar = sbtCompilerInterfaceSrcComponentFile(sbtVersion) + + if (componentProvider.component("xsbti").isEmpty) + componentProvider.defineComponentNoCopy("xsbti", Array(interfaceJar)) + if (componentProvider.component("compiler-interface").isEmpty) + componentProvider.defineComponentNoCopy("compiler-interface", Array(compilerInterfaceSourceJar)) + if (componentProvider.component("compiler-interface-src").isEmpty) + componentProvider.defineComponentNoCopy("compiler-interface-src", Array(compilerInterfaceSourceJar)) + } + + private def sbtInterfaceComponentFiles(sbtVersion: String): (File, File) = { + + lazy val res = { + + val initialRes = Resolution( + Set( + Dependency(Module("org.scala-sbt", "interface"), sbtVersion, transitive = false) + ) + ) + + val logger = + Some(new TermDisplay( + new OutputStreamWriter(System.err) + )) + + logger.foreach(_.init { + System.err.println(s"Resolving org.scala-sbt:interface:$sbtVersion") + }) + + val res = initialRes.process.run(fetch(logger)).unsafePerformSync + + logger.foreach { l => + if (l.stopDidPrintSomething()) + System.err.println(s"Resolved org.scala-sbt:interface:$sbtVersion") + } + + if (res.errors.nonEmpty) { + Console.err.println(s"Errors:\n${res.errors.map(" " + _).mkString("\n")}") + sys.exit(1) + } + + if (res.conflicts.nonEmpty) { + Console.err.println(s"Conflicts:\n${res.conflicts.map(" " + _).mkString("\n")}") + sys.exit(1) + } + + if (!res.isDone) { + Console.err.println("Did not converge") + sys.exit(1) + } + + res + } + + lazy val interfaceJar = { + + val artifactLogger = + Some(new TermDisplay( + new OutputStreamWriter(System.err) + )) + + artifactLogger.foreach(_.init { + System.err.println(s"Fetching org.scala-sbt:interface:$sbtVersion artifacts") + }) + + val results = Task.gatherUnordered(tasks(res, artifactLogger)).unsafePerformSync + + artifactLogger.foreach { l => + if (l.stopDidPrintSomething()) + System.err.println(s"Fetched org.scala-sbt:interface:$sbtVersion artifacts") + } + + val errors = results.collect { case (a, -\/(err)) => (a, err) } + val files = results.collect { case (_, \/-(f)) => f } + + if (errors.nonEmpty) { + Console.err.println(s"Error downloading artifacts:\n${errors.map(" " + _).mkString("\n")}") + sys.exit(1) + } + + files match { + case Nil => + throw new NoSuchElementException(s"interface JAR for sbt $sbtVersion") + case List(jar) => + jar + case _ => + sys.error(s"Too many interface JAR for sbt $sbtVersion: ${files.mkString(", ")}") + } + } + + lazy val compilerInterfaceSourcesJar = { + + val artifactLogger = + Some(new TermDisplay( + new OutputStreamWriter(System.err) + )) + + artifactLogger.foreach(_.init { + System.err.println(s"Fetching org.scala-sbt:interface:$sbtVersion source artifacts") + }) + + val results = Task.gatherUnordered(tasks(res, artifactLogger, Some(Seq("sources")))).unsafePerformSync + + artifactLogger.foreach { l => + if (l.stopDidPrintSomething()) + System.err.println(s"Fetched org.scala-sbt:interface:$sbtVersion source artifacts") + } + + val errors = results.collect { case (a, -\/(err)) => (a, err) } + val files = results.collect { case (_, \/-(f)) => f } + + if (errors.nonEmpty) { + Console.err.println(s"Error downloading artifacts:\n${errors.map(" " + _).mkString("\n")}") + sys.exit(1) + } + + files match { + case Nil => + throw new NoSuchElementException(s"compiler-interface source JAR for sbt $sbtVersion") + case List(jar) => + jar + case _ => + sys.error(s"Too many compiler-interface source JAR for sbt $sbtVersion: ${files.mkString(", ")}") + } + } + + (interfaceJar, compilerInterfaceSourcesJar) + } + + private def sbtCompilerInterfaceSrcComponentFile(sbtVersion: String): File = { + + val res = { + + val initialRes = Resolution( + Set( + Dependency(Module("org.scala-sbt", "compiler-interface"), sbtVersion, transitive = false) + ) + ) + + val logger = + Some(new TermDisplay( + new OutputStreamWriter(System.err) + )) + + logger.foreach(_.init { + System.err.println(s"Resolving org.scala-sbt:compiler-interface:$sbtVersion") + }) + + val res = initialRes.process.run(fetch(logger)).unsafePerformSync + + logger.foreach { l => + if (l.stopDidPrintSomething()) + System.err.println(s"Resolved org.scala-sbt:compiler-interface:$sbtVersion") + } + + if (res.errors.nonEmpty) { + Console.err.println(s"Errors:\n${res.errors.map(" " + _).mkString("\n")}") + sys.exit(1) + } + + if (res.conflicts.nonEmpty) { + Console.err.println(s"Conflicts:\n${res.conflicts.map(" " + _).mkString("\n")}") + sys.exit(1) + } + + if (!res.isDone) { + Console.err.println("Did not converge") + sys.exit(1) + } + + res + } + + val files = { + + val artifactLogger = + Some(new TermDisplay( + new OutputStreamWriter(System.err) + )) + + artifactLogger.foreach(_.init { + System.err.println(s"Fetching org.scala-sbt:compiler-interface:$sbtVersion source artifacts") + }) + + val results = Task.gatherUnordered( + tasks(res, artifactLogger, None) ++ + tasks(res, artifactLogger, Some(Seq("sources"))) + ).unsafePerformSync + + artifactLogger.foreach { l => + if (l.stopDidPrintSomething()) + System.err.println(s"Fetched org.scala-sbt:compiler-interface:$sbtVersion source artifacts") + } + + val errors = results.collect { case (a, -\/(err)) => (a, err) } + + if (errors.nonEmpty) { + Console.err.println(s"Error downloading artifacts:\n${errors.map(" " + _).mkString("\n")}") + sys.exit(1) + } + + results.collect { case (_, \/-(f)) => f } + } + + files.find(f => f.getName == "compiler-interface-src.jar" || f.getName == "compiler-interface-sources.jar").getOrElse { + sys.error("compiler-interface-src not found") + } + } +} diff --git a/sbt-launcher/src/main/scala/coursier/sbtlauncher/MainApp.scala b/sbt-launcher/src/main/scala/coursier/sbtlauncher/MainApp.scala new file mode 100644 index 000000000..c83b7b1d6 --- /dev/null +++ b/sbt-launcher/src/main/scala/coursier/sbtlauncher/MainApp.scala @@ -0,0 +1,110 @@ +package coursier.sbtlauncher + +import java.io.File +import java.nio.charset.StandardCharsets +import java.nio.file.Files + +import caseapp._ +import com.typesafe.config.ConfigFactory +import coursier.Dependency + +final case class MainApp( + @ExtraName("org") + organization: String, + name: String, + version: String, + scalaVersion: String, + sbtVersion: String, + mainClass: String, + mainComponents: List[String], + classpathExtra: List[String], + extra: List[String] +) extends App { + + val sbtPropFile = new File(sys.props("user.dir") + "/sbt.properties") + val buildPropFile = new File(sys.props("user.dir") + "/project/build.properties") + + val propFileOpt = Some(sbtPropFile).filter(_.exists()) + .orElse(Some(buildPropFile).filter(_.exists())) + + val (org0, name0, ver0, scalaVer0, extraDeps0, mainClass0, sbtVersion0) = + propFileOpt match { + case Some(propFile) => + // can't get ConfigFactory.parseFile to work fine here + val conf = ConfigFactory.parseString(new String(Files.readAllBytes(propFile.toPath), StandardCharsets.UTF_8)) + .withFallback(ConfigFactory.defaultReference(Thread.currentThread().getContextClassLoader)) + .resolve() + val sbtConfig = SbtConfig.fromConfig(conf) + + (sbtConfig.organization, sbtConfig.moduleName, sbtConfig.version, sbtConfig.scalaVersion, sbtConfig.dependencies, sbtConfig.mainClass, sbtConfig.version) + case None => + require(scalaVersion.nonEmpty, "No scala version specified") + (organization, name, version, scalaVersion, Nil, mainClass, sbtVersion) + } + + val (extraParseErrors, extraModuleVersions) = coursier.util.Parse.moduleVersions(extra, scalaVersion) + + if (extraParseErrors.nonEmpty) { + ??? + } + + val extraDeps = extraModuleVersions.map { + case (mod, ver) => + Dependency(mod, ver) + } + + val launcher = new Launcher( + scalaVer0, + // FIXME Add org & moduleName in this path + new File(s"${sys.props("user.dir")}/target/sbt-components/components_scala$scalaVer0${if (sbtVersion0.isEmpty) "" else "_sbt" + sbtVersion0}"), + new File(s"${sys.props("user.dir")}/target/ivy2") + ) + + launcher.registerScalaComponents() + + if (sbtVersion0.nonEmpty) + launcher.registerSbtInterfaceComponents(sbtVersion0) + + val appId = ApplicationID( + org0, + name0, + ver0, + mainClass0, + mainComponents.toArray, + crossVersioned = false, + xsbti.CrossValue.Disabled, + classpathExtra.map(new File(_)).toArray + ) + + val appProvider = launcher.app(appId, extraDeps0 ++ extraDeps: _*) + + val appMain = appProvider.newMain() + + val appConfig = AppConfiguration( + remainingArgs.toArray, + new File(sys.props("user.dir")), + appProvider + ) + + val thread = Thread.currentThread() + val previousLoader = thread.getContextClassLoader + + val result = + try { + thread.setContextClassLoader(appProvider.loader()) + appMain.run(appConfig) + } finally { + thread.setContextClassLoader(previousLoader) + } + + result match { + case _: xsbti.Continue => + case e: xsbti.Exit => + sys.exit(e.code()) + case _: xsbti.Reboot => + sys.error("Not able to reboot yet") + } + +} + +object Main extends AppOf[MainApp] diff --git a/sbt-launcher/src/main/scala/coursier/sbtlauncher/Repository.scala b/sbt-launcher/src/main/scala/coursier/sbtlauncher/Repository.scala new file mode 100644 index 000000000..c77e0acee --- /dev/null +++ b/sbt-launcher/src/main/scala/coursier/sbtlauncher/Repository.scala @@ -0,0 +1,19 @@ +package coursier.sbtlauncher + +import java.net.URL + +object Repository { + + final case class Maven(id: String, url: URL) extends xsbti.MavenRepository + + final case class Ivy( + id: String, + url: URL, + ivyPattern: String, + artifactPattern: String, + mavenCompatible: Boolean, + skipConsistencyCheck: Boolean, + descriptorOptional: Boolean + ) extends xsbti.IvyRepository + +} diff --git a/sbt-launcher/src/main/scala/coursier/sbtlauncher/SbtConfig.scala b/sbt-launcher/src/main/scala/coursier/sbtlauncher/SbtConfig.scala new file mode 100644 index 000000000..1806ecf19 --- /dev/null +++ b/sbt-launcher/src/main/scala/coursier/sbtlauncher/SbtConfig.scala @@ -0,0 +1,112 @@ +package coursier.sbtlauncher + +import com.typesafe.config.Config +import coursier.Dependency +import coursier.util.Parse + +import scala.collection.JavaConverters._ + +final case class SbtConfig( + organization: String, + moduleName: String, + version: String, + scalaVersion: String, + mainClass: String, + dependencies: Seq[Dependency] +) + +object SbtConfig { + + def defaultOrganization = "org.scala-sbt" + def defaultModuleName = "sbt" + def defaultMainClass = "sbt.xMain" + + def fromConfig(config: Config): SbtConfig = { + + val version = config.getString("sbt.version") + + val scalaVersion = + if (config.hasPath("scala.version")) + config.getString("scala.version") + else if (version.startsWith("0.13.")) + "2.10.6" + else if (version.startsWith("1.0.")) + "2.12.1" + else + throw new Exception(s"Don't know what Scala version should be used for sbt version '$version'") + + val org = + if (config.hasPath("sbt.organization")) + config.getString("sbt.organization") + else + defaultOrganization + + val name = + if (config.hasPath("sbt.module-name")) + config.getString("sbt.module-name") + else + defaultModuleName + + val mainClass = + if (config.hasPath("sbt.main-class")) + config.getString("sbt.main-class") + else + defaultMainClass + + val scalaBinaryVersion = scalaVersion.split('.').take(2).mkString(".") + val sbtBinaryVersion = version.split('.').take(2).mkString(".") + + val rawPlugins = + if (config.hasPath("plugins")) + config.getStringList("plugins").asScala + else + Nil + + val (pluginErrors, pluginsModuleVersions) = Parse.moduleVersions(rawPlugins, scalaVersion) + + if (pluginErrors.nonEmpty) { + ??? + } + + val pluginDependencies = + pluginsModuleVersions.map { + case (mod, ver) => + Dependency( + mod.copy( + attributes = mod.attributes ++ Seq( + "scalaVersion" -> scalaBinaryVersion, + "sbtVersion" -> sbtBinaryVersion + ) + ), + ver + ) + } + + val rawDeps = + if (config.hasPath("dependencies")) + config.getStringList("dependencies").asScala + else + Nil + + val (depsErrors, depsModuleVersions) = Parse.moduleVersions(rawDeps, scalaVersion) + + if (depsErrors.nonEmpty) { + ??? + } + + val dependencies = + depsModuleVersions.map { + case (mod, ver) => + Dependency(mod, ver) + } + + SbtConfig( + org, + name, + version, + scalaVersion, + mainClass, + pluginDependencies ++ dependencies + ) + } +} diff --git a/sbt-launcher/src/main/scala/coursier/sbtlauncher/ScalaProvider.scala b/sbt-launcher/src/main/scala/coursier/sbtlauncher/ScalaProvider.scala new file mode 100644 index 000000000..d29ae0e2a --- /dev/null +++ b/sbt-launcher/src/main/scala/coursier/sbtlauncher/ScalaProvider.scala @@ -0,0 +1,16 @@ +package coursier.sbtlauncher + +import java.io.File + +final case class ScalaProvider( + launcher: xsbti.Launcher, + version: String, + loader: ClassLoader, + jars: Array[File], + libraryJar: File, + compilerJar: File, + createApp: xsbti.ApplicationID => xsbti.AppProvider +) extends xsbti.ScalaProvider { + def app(id: xsbti.ApplicationID): xsbti.AppProvider = + createApp(id) +} diff --git a/sbt.properties b/sbt.properties new file mode 100644 index 000000000..d42ee8c7a --- /dev/null +++ b/sbt.properties @@ -0,0 +1,16 @@ +sbt.version=0.13.8 + +plugins = [ + "io.get-coursier:sbt-coursier:1.0.0-M15-1" + "org.xerial.sbt:sbt-pack:0.8.2" + "org.scala-js:sbt-scalajs:0.6.14" + "com.jsuereth:sbt-pgp:1.0.0" + "org.scoverage:sbt-scoverage:1.4.0" + "org.tpolecat:tut-plugin:0.4.8" + "com.typesafe.sbt:sbt-proguard:0.2.2" + "com.typesafe:sbt-mima-plugin:0.1.13" +] + +dependencies = [ + "org.scala-sbt:scripted-plugin:"${sbt.version} +] From 17faeb7686cc12846f3c75cd6b46b6d205458dbd Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Thu, 23 Feb 2017 00:12:41 +0100 Subject: [PATCH 11/16] Hitting sbt issue 1673? --- build.sbt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build.sbt b/build.sbt index 129738960..5262d66c9 100644 --- a/build.sbt +++ b/build.sbt @@ -3,6 +3,8 @@ import java.io.FileOutputStream val binaryCompatibilityVersion = "1.0.0-M14" val binaryCompatibility212Version = "1.0.0-M15" +parallelExecution in Global := false + lazy val IntegrationTest = config("it") extend Test lazy val scalazVersion = "7.2.8" From 7de033a68f1e9cb171f1a349c7421bd384692647 Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Tue, 21 Feb 2017 15:57:42 +0100 Subject: [PATCH 12/16] Move all scripts under scripts/ --- .travis.yml | 2 +- {project => scripts}/generate-launcher.sh | 0 {project => scripts}/generate-sbt-launcher.sh | 0 {project => scripts}/generate-standalone-launcher.sh | 0 {.ci => scripts}/java-6-test.sh | 0 {.ci => scripts}/push-gh-pages-helper.sh | 0 {.ci => scripts}/push-gh-pages.sh | 0 {.ci => scripts}/travis.sh | 2 +- 8 files changed, 2 insertions(+), 2 deletions(-) rename {project => scripts}/generate-launcher.sh (100%) rename {project => scripts}/generate-sbt-launcher.sh (100%) rename {project => scripts}/generate-standalone-launcher.sh (100%) rename {.ci => scripts}/java-6-test.sh (100%) rename {.ci => scripts}/push-gh-pages-helper.sh (100%) rename {.ci => scripts}/push-gh-pages.sh (100%) rename {.ci => scripts}/travis.sh (98%) diff --git a/.travis.yml b/.travis.yml index b9d531540..726eee302 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,7 @@ install: os: - osx script: - - .ci/travis.sh "${TRAVIS_SCALA_VERSION:-2.11.8}" "$TRAVIS_PULL_REQUEST" "$TRAVIS_BRANCH" "$PUBLISH" + - scripts/travis.sh "${TRAVIS_SCALA_VERSION:-2.11.8}" "$TRAVIS_PULL_REQUEST" "$TRAVIS_BRANCH" "$PUBLISH" # Uncomment once https://github.com/scoverage/sbt-scoverage/issues/111 is fixed # after_success: # - bash <(curl -s https://codecov.io/bash) diff --git a/project/generate-launcher.sh b/scripts/generate-launcher.sh similarity index 100% rename from project/generate-launcher.sh rename to scripts/generate-launcher.sh diff --git a/project/generate-sbt-launcher.sh b/scripts/generate-sbt-launcher.sh similarity index 100% rename from project/generate-sbt-launcher.sh rename to scripts/generate-sbt-launcher.sh diff --git a/project/generate-standalone-launcher.sh b/scripts/generate-standalone-launcher.sh similarity index 100% rename from project/generate-standalone-launcher.sh rename to scripts/generate-standalone-launcher.sh diff --git a/.ci/java-6-test.sh b/scripts/java-6-test.sh similarity index 100% rename from .ci/java-6-test.sh rename to scripts/java-6-test.sh diff --git a/.ci/push-gh-pages-helper.sh b/scripts/push-gh-pages-helper.sh similarity index 100% rename from .ci/push-gh-pages-helper.sh rename to scripts/push-gh-pages-helper.sh diff --git a/.ci/push-gh-pages.sh b/scripts/push-gh-pages.sh similarity index 100% rename from .ci/push-gh-pages.sh rename to scripts/push-gh-pages.sh diff --git a/.ci/travis.sh b/scripts/travis.sh similarity index 98% rename from .ci/travis.sh rename to scripts/travis.sh index f28ce63b3..6d6093f07 100755 --- a/.ci/travis.sh +++ b/scripts/travis.sh @@ -77,7 +77,7 @@ SBT_COMMANDS="$SBT_COMMANDS tut coreJVM/mimaReportBinaryIssues cache/mimaReportB ~/sbt ++${TRAVIS_SCALA_VERSION} $SBT_COMMANDS -.ci/java-6-test.sh +scripts/java-6-test.sh if isNotPr && publish && isMaster; then ~/sbt ++${TRAVIS_SCALA_VERSION} publish From 164e044977aab5e551694724ea4060f736e2ffb3 Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Tue, 21 Feb 2017 15:58:12 +0100 Subject: [PATCH 13/16] Remove deprecated env var on CI --- .travis.yml | 1 - appveyor.yml | 1 - 2 files changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 726eee302..62620e61c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,7 +29,6 @@ matrix: - docker env: global: - - COURSIER_NO_TERM=1 - secure: miHFMwVRD/yjOLy794nOwc2lJTMyL5O0MXABT9ksg5ejQy1FrFVc2YH86Agp80W02/lGLGl0qWCiK1TBcs9q4Apt01nkD1a/0/iuTRm//bdhnu8BbRxFITf+2cyYJVytKPsF585aHldMv1rwZs3TDaTzEEecAEki5r50yyTVo7ycG0lVj9aVWXerKRMIT54Wb8M6nqbyRB1jGWT0ETNU13vOvQznPTUXQG5hsiKnGYRf8T3umOMdOHpV0rvdwYqAIMsikaAFcYCS5P/pLXMtmRHICH9KUG8TV/ST07p1BXtbBg9y1Q+lpnXotXh4ZNoWOp8B6v7fxJ/WlLYTDROWCiHJ4s2V4Di00db/nW4OWrEEBlrh7vJ/npZqyt9V9YeNv6alxi+DCESwusgvD4Cx5c3zh+2X6RB6BYwWHlFnd80rmsLe4R4fFUcc8E/ZR9vUFjP1CsQKqfJ5yfKR6V+n8jK8FjLpoaU9PHPo2H4V3FZM/fCLcxhE37vfaYI7/O7MqE/cdGpZIuz7g3c4toWCgNZJDn8iJCPmrgcbW5zbfDxvWU2K816ycgnUwSQ5dufrJpAbLNrjR1O8EPRkMDDp9bB7/4RVQvfDfP9GGoiHPHHgxGzY0Lf5bm+Bj1mRfB5/SXHd3IjhUCD9q7eD1/ANifEYALC5BJ4TB8RhQUPU8uM= - secure: 2/SSqa7A+aIzTJrMuqfK53QoHqes8HZPpIXUC9BH+bP2V2n7LqlFCnLZ9OSFfiJYfgeYMQDILpt8GTXHYc7JgM/N9xXpywrpYNDCYo7GMhqRyUPQOuK9044IRnZmme289Ut6ozHHptZUeZp/9DEUNZcPOxTN+KbzbHrUL+9l5BxnAxJ3e0HihxhmaINrla3T36EetdfINigarB9muyvuCRdRhZjwxsSF1fo5P+ZgWvAIDhPgNJH8eyjxHVbTabk7efPtWNWu0HjyOqJaIVk+TNjuQhvQPHKpYel0gVlCAfUjq7ZP8hZurfC6NjCFcnfTZ3d4R8GDcWJ47pgBWND8saIQOigNd7KHBPntD4fEJqgBSq3ZWakNBYzOtm8CxMGmiJHDCVqAEGzUG+lowN+SnPS2UluL3QtZ7oL/7MeJqCscH7sPwHtmZY+o0Muqo0ZJ2T2TzekQNYOAE7jeSzG1xOa/NNghny5fT+w6asPxfeolkMgyzuRFp1SLaLUf/XRV4fux0meGY9NIXso47xMSfAYVAAXT1FA2OOwmM1O4yvm3Ur95oEGDNw6z7MnWOSKS663WFwuw2cCaheCfAwvoa5jZUMWMbyUM/cBTgCaQdmETpvCzZzUr5Ls/nBXjyiTdJaQLZATr7HSGZHgYVmEAhVwBvuhTar/6VUZUMKGc2P4= - secure: NmXh4uxqvvqxYvOBOiXE131HajCYhJyd9+7kc1YjllRZVYG11YLah9Np7qnRUyugNOdcBnWVQGlfDHOFe8GHQsZKt5PvsIzxszTor0GeDQOePX3L4YXPkZRJatmoJJ0COxdI6weCAWkI6Zr934RsOndT0mO55gk9c6eeXCcLdNjAJ3izGQHy5Wb2KTzwMhBfwjdTQ0s65c1rzz5dZ/JODilWfTHiHsz+4sKwWVmAvXDTjePd0X3svX775ot23QesJgtaC/p0AKSLcHg3zEjKkJJvvLooQyNn/zU/bio/UatDZWXnNMsTBfEr3qUedjoOY65g3EX/vYlbNRkF3Itk0dpuPooTFmezJASI4ZpewBS9OvPZheMmU/dy5Bx//622x7p4MHyao9IvYmSX0C92VWEd3gwkSzKCJtBEz4Csd5BaGhzeL41di6NSVx4IEiehC9191G1wk4Yj7S2t69N6OdAJEq+znQlYISF5ogCqip7PuesBMYTW4FaIgpnfW/OYP6VpWW87ohw/dz/CcTzP9MzuoM249EHNZKTfnJrmPJBRYSn+W4y9sTgGElPhY1U/NVQ+C/9Fov1kHFD25WeTDPdZe6yCczaUrcvfTDitfo6qnWf8ZW5dJMXN744idaZ25AT/SGoCzkPXMe+us5XLTAOtrbBMP8NXLMv5OtU999E= diff --git a/appveyor.yml b/appveyor.yml index 35c89eb68..97b0b044a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -13,7 +13,6 @@ install: } - cmd: SET PATH=C:\sbt\sbt\bin;%JAVA_HOME%\bin;%PATH% - cmd: SET SBT_OPTS=-XX:MaxPermSize=2g -Xmx4g - - cmd: SET COURSIER_NO_TERM=1 - ps: | if (!(Test-Path 'C:\Users\appveyor\.m2\repository\org\anarres\jarjar\jarjar-core\1.0.1-coursier-SNAPSHOT')) { iex 'git clone https://github.com/alexarchambault/jarjar' From bf92539b53a0ee869f0bf0614f069dcf61b3cd38 Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Tue, 21 Feb 2017 16:06:28 +0100 Subject: [PATCH 14/16] Actually, let's cache ~/.coursier on CI --- .travis.yml | 2 +- appveyor.yml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 62620e61c..7db4202f3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -40,4 +40,4 @@ cache: - $HOME/.m2 - $HOME/.ivy2/cache - $HOME/.sbt -# Not adding $HOME/.coursier, we check that sbt-coursier works fine with an initially empty cache + - $HOME/.coursier diff --git a/appveyor.yml b/appveyor.yml index 97b0b044a..e8e3336d7 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -34,3 +34,4 @@ cache: - C:\Users\appveyor\.ivy2\cache - C:\Users\appveyor\.m2 - C:\Users\appveyor\.sbt + - C:\Users\appveyor\.coursier From c59c7d45cabf357b5e177ea5c6d101be8af497ae Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Mon, 27 Feb 2017 16:15:15 +0100 Subject: [PATCH 15/16] Refactor travis.sh --- .travis.yml | 13 ++- build.sbt | 35 +++++++ scripts/java-6-test.sh | 48 --------- scripts/travis.sh | 221 +++++++++++++++++++++++++++++------------ 4 files changed, 203 insertions(+), 114 deletions(-) delete mode 100755 scripts/java-6-test.sh diff --git a/.travis.yml b/.travis.yml index 7db4202f3..3defc0c09 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,12 +1,10 @@ language: java install: - npm install - - curl -L -o ~/sbt https://github.com/paulp/sbt-extras/raw/478a364a2d43d6e1ac4451f8b84cdafe43a2e43f/sbt - - chmod +x ~/sbt os: - osx script: - - scripts/travis.sh "${TRAVIS_SCALA_VERSION:-2.11.8}" "$TRAVIS_PULL_REQUEST" "$TRAVIS_BRANCH" "$PUBLISH" + - scripts/travis.sh # Uncomment once https://github.com/scoverage/sbt-scoverage/issues/111 is fixed # after_success: # - bash <(curl -s https://codecov.io/bash) @@ -27,6 +25,15 @@ matrix: sudo: required services: - docker + - env: TRAVIS_SCALA_VERSION=2.12.1 PUBLISH=1 SCALA_JS=1 + os: linux + jdk: oraclejdk8 + - env: TRAVIS_SCALA_VERSION=2.11.8 PUBLISH=1 SCALA_JS=1 + os: linux + jdk: oraclejdk8 + - env: TRAVIS_SCALA_VERSION=2.10.6 PUBLISH=1 SCALA_JS=1 + os: linux + jdk: oraclejdk8 env: global: - secure: miHFMwVRD/yjOLy794nOwc2lJTMyL5O0MXABT9ksg5ejQy1FrFVc2YH86Agp80W02/lGLGl0qWCiK1TBcs9q4Apt01nkD1a/0/iuTRm//bdhnu8BbRxFITf+2cyYJVytKPsF585aHldMv1rwZs3TDaTzEEecAEki5r50yyTVo7ycG0lVj9aVWXerKRMIT54Wb8M6nqbyRB1jGWT0ETNU13vOvQznPTUXQG5hsiKnGYRf8T3umOMdOHpV0rvdwYqAIMsikaAFcYCS5P/pLXMtmRHICH9KUG8TV/ST07p1BXtbBg9y1Q+lpnXotXh4ZNoWOp8B6v7fxJ/WlLYTDROWCiHJ4s2V4Di00db/nW4OWrEEBlrh7vJ/npZqyt9V9YeNv6alxi+DCESwusgvD4Cx5c3zh+2X6RB6BYwWHlFnd80rmsLe4R4fFUcc8E/ZR9vUFjP1CsQKqfJ5yfKR6V+n8jK8FjLpoaU9PHPo2H4V3FZM/fCLcxhE37vfaYI7/O7MqE/cdGpZIuz7g3c4toWCgNZJDn8iJCPmrgcbW5zbfDxvWU2K816ycgnUwSQ5dufrJpAbLNrjR1O8EPRkMDDp9bB7/4RVQvfDfP9GGoiHPHHgxGzY0Lf5bm+Bj1mRfB5/SXHd3IjhUCD9q7eD1/ANifEYALC5BJ4TB8RhQUPU8uM= diff --git a/build.sbt b/build.sbt index 5262d66c9..d104041ec 100644 --- a/build.sbt +++ b/build.sbt @@ -429,6 +429,41 @@ lazy val okhttp = project ) ) +lazy val jvm = project + .aggregate( + coreJvm, + testsJvm, + cache, + bootstrap, + cli, + `sbt-coursier`, + `sbt-shading`, + `sbt-launcher`, + doc, + `http-server`, + okhttp + ) + .settings(commonSettings) + .settings(noPublishSettings) + .settings(releaseSettings) + .settings( + moduleName := "coursier-jvm" + ) + +lazy val js = project + .aggregate( + coreJs, + `fetch-js`, + testsJs, + web + ) + .settings(commonSettings) + .settings(noPublishSettings) + .settings(releaseSettings) + .settings( + moduleName := "coursier-js" + ) + lazy val `coursier` = project.in(file(".")) .aggregate( coreJvm, diff --git a/scripts/java-6-test.sh b/scripts/java-6-test.sh deleted file mode 100755 index a97d3df5d..000000000 --- a/scripts/java-6-test.sh +++ /dev/null @@ -1,48 +0,0 @@ -#!/bin/bash -set -ev - -# We're not using a jdk6 matrix entry with Travis here as some sources of coursier require Java 7 to compile -# (even though it won't try to call Java 7 specific methods if it detects it runs under Java 6). -# The tests here check that coursier is nonetheless fine when run under Java 6. - -if echo "$TRAVIS_SCALA_VERSION" | grep -q "^2\.11"; then - ~/sbt ++${TRAVIS_SCALA_VERSION} cli/pack - docker run -it --rm \ - -v $(pwd)/cli/target/pack:/opt/coursier \ - -e CI=true \ - openjdk:6-jre \ - /opt/coursier/bin/coursier fetch org.scalacheck::scalacheck:1.13.4 - - docker run -it --rm \ - -v $(pwd)/cli/target/pack:/opt/coursier \ - -e CI=true \ - openjdk:6-jre \ - /opt/coursier/bin/coursier launch --help -fi - -function clean_plugin_sbt() { - mv plugins.sbt plugins.sbt0 - grep -v coursier plugins.sbt0 > plugins.sbt || true - echo ' -addSbtPlugin("io.get-coursier" % "sbt-coursier" % "1.0.0-SNAPSHOT") - ' >> plugins.sbt -} - -if echo "$TRAVIS_SCALA_VERSION" | grep -q "^2\.10"; then - ~/sbt ++${TRAVIS_SCALA_VERSION} publishLocal - git clone https://github.com/alexarchambault/scalacheck-shapeless.git - cd scalacheck-shapeless - cd project - clean_plugin_sbt - cd project - clean_plugin_sbt - cd ../.. - docker run -it --rm \ - -v $HOME/.ivy2/local:/root/.ivy2/local \ - -v $HOME/sbt:/root/sbt \ - -v $(pwd):/root/project \ - -e CI=true \ - openjdk:6-jre \ - /bin/bash -c "cd /root/project && /root/sbt update" - cd .. -fi diff --git a/scripts/travis.sh b/scripts/travis.sh index 6d6093f07..e5c083259 100755 --- a/scripts/travis.sh +++ b/scripts/travis.sh @@ -1,93 +1,188 @@ -#!/bin/bash +#!/usr/bin/env bash set -ev -TRAVIS_SCALA_VERSION="$1" -shift -TRAVIS_PULL_REQUEST="$1" -shift -TRAVIS_BRANCH="$1" -shift -PUBLISH="$1" -shift +SCALA_VERSION="${SCALA_VERSION:-${TRAVIS_SCALA_VERSION:-2.12.1}}" +PULL_REQUEST="${PULL_REQUEST:-${TRAVIS_PULL_REQUEST:-false}}" +BRANCH="${BRANCH:-${TRAVIS_BRANCH:-$(git rev-parse --abbrev-ref HEAD)}}" +PUBLISH="${PUBLISH:-0}" +SCALA_JS="${SCALA_JS:-0}" +JARJAR_VERSION="${JARJAR_VERSION:-1.0.1-coursier-SNAPSHOT}" -function isNotPr() { - [ "$TRAVIS_PULL_REQUEST" = "false" ] +setupCoursierBinDir() { + mkdir -p bin + cp coursier bin/ + export PATH="$(pwd)/bin:$PATH" } -function publish() { - [ "$PUBLISH" = 1 ] +downloadInstallSbtExtras() { + curl -L -o bin/sbt https://github.com/paulp/sbt-extras/raw/9ade5fa54914ca8aded44105bf4b9a60966f3ccd/sbt + chmod +x bin/sbt } -function isMaster() { - [ "$TRAVIS_BRANCH" = "master" ] +integrationTestsRequirements() { + # Required for ~/.ivy2/local repo tests + sbt ++2.11.8 coreJVM/publishLocal http-server/publishLocal + + # Required for HTTP authentication tests + coursier launch \ + io.get-coursier:http-server-java7_2.11:1.0.0-SNAPSHOT \ + -r http://dl.bintray.com/scalaz/releases \ + -- \ + -d tests/jvm/src/test/resources/test-repo/http/abc.com \ + -u user -P pass -r realm \ + --list-pages \ + -v & } -function isMasterOrDevelop() { - [ "$TRAVIS_BRANCH" = "master" -o "$TRAVIS_BRANCH" = "develop" ] +setupCustomJarjar() { + if [ ! -d "$HOME/.m2/repository/org/anarres/jarjar/jarjar-core/$JARJAR_VERSION" ]; then + git clone https://github.com/alexarchambault/jarjar.git + cd jarjar + if ! grep -q "^version=$JARJAR_VERSION\$" gradle.properties; then + echo "Expected jarjar version not found" 1>&2 + exit 1 + fi + git checkout 249c8dbb970f8 + ./gradlew :jarjar-core:install + cd .. + rm -rf jarjar + fi } -# Required for ~/.ivy2/local repo tests -~/sbt ++2.11.8 coreJVM/publishLocal http-server/publishLocal +isScalaJs() { + [ "$SCALA_JS" = 1 ] +} + +is210() { + echo "$SCALA_VERSION" | grep -q "^2\.10" +} + +is211() { + echo "$SCALA_VERSION" | grep -q "^2\.11" +} + +runSbtCoursierTests() { + sbt ++$SCALA_VERSION coreJVM/publishLocal cache/publishLocal sbt-coursier/scripted +} + +runSbtShadingTests() { + setupCustomJarjar + sbt ++$SCALA_VERSION sbt-coursier/publishLocal sbt-shading/scripted +} + +jsCompile() { + sbt ++$SCALA_VERSION js/compile js/test:compile coreJS/fastOptJS fetch-js/fastOptJS testsJS/test:fastOptJS js/test:fastOptJS +} + +jvmCompile() { + sbt ++$SCALA_VERSION jvm/compile jvm/test:compile +} + +runJsTests() { + sbt ++$SCALA_VERSION js/test +} + +runJvmTests() { + sbt ++$SCALA_VERSION jvm/test jvm/it:test +} + +validateReadme() { + sbt ++${SCALA_VERSION} tut +} + +checkBinaryCompatibility() { + sbt ++${SCALA_VERSION} coreJVM/mimaReportBinaryIssues cache/mimaReportBinaryIssues +} + +testLauncherJava6() { + sbt ++${SCALA_VERSION} cli/pack + docker run -it --rm \ + -v $(pwd)/cli/target/pack:/opt/coursier \ + -e CI=true \ + openjdk:6-jre \ + /opt/coursier/bin/coursier fetch org.scalacheck::scalacheck:1.13.4 + + docker run -it --rm \ + -v $(pwd)/cli/target/pack:/opt/coursier \ + -e CI=true \ + openjdk:6-jre \ + /opt/coursier/bin/coursier launch --help +} + +testSbtCoursierJava6() { + sbt ++${SCALA_VERSION} publishLocal + git clone https://github.com/alexarchambault/scalacheck-shapeless.git + cd scalacheck-shapeless + cd project + clean_plugin_sbt + cd project + clean_plugin_sbt + cd ../.. + docker run -it --rm \ + -v $HOME/.ivy2/local:/root/.ivy2/local \ + -v $(pwd):/root/project \ + -v $(pwd)/../bin:/root/bin \ + -e CI=true \ + openjdk:6-jre \ + /bin/bash -c "cd /root/project && /root/bin/sbt update" + cd .. +} + +clean_plugin_sbt() { + mv plugins.sbt plugins.sbt0 + grep -v coursier plugins.sbt0 > plugins.sbt || true + echo ' +addSbtPlugin("io.get-coursier" % "sbt-coursier" % "1.0.0-SNAPSHOT") + ' >> plugins.sbt +} + +publish() { + sbt ++${SCALA_VERSION} publish +} -# Required for HTTP authentication tests -./coursier launch \ - io.get-coursier:http-server-java7_2.11:1.0.0-SNAPSHOT \ - -r http://dl.bintray.com/scalaz/releases \ - -- \ - -d tests/jvm/src/test/resources/test-repo/http/abc.com \ - -u user -P pass -r realm \ - --list-pages \ - -v & # TODO Add coverage once https://github.com/scoverage/sbt-scoverage/issues/111 is fixed -SBT_COMMANDS="compile test it:test" +setupCoursierBinDir +downloadInstallSbtExtras -RUN_SHADING_TESTS=1 +if isScalaJs; then + jsCompile + runJsTests +else + integrationTestsRequirements + jvmCompile + runJvmTests -if echo "$TRAVIS_SCALA_VERSION" | grep -q "^2\.10"; then - SBT_COMMANDS="$SBT_COMMANDS publishLocal" # to make the scripted tests happy - SBT_COMMANDS="$SBT_COMMANDS sbt-coursier/scripted" + if is210; then + runSbtCoursierTests + runSbtShadingTests + fi - if [ "$RUN_SHADING_TESTS" = 1 ]; then - # for the shading scripted test - sudo cp coursier /usr/local/bin/ + validateReadme + checkBinaryCompatibility - JARJAR_VERSION=1.0.1-coursier-SNAPSHOT + # We're not using a jdk6 matrix entry with Travis here as some sources of coursier require Java 7 to compile + # (even though it won't try to call Java 7 specific methods if it detects it runs under Java 6). + # The tests here check that coursier is nonetheless fine when run under Java 6. - if [ ! -d "$HOME/.m2/repository/org/anarres/jarjar/jarjar-core/$JARJAR_VERSION" ]; then - git clone https://github.com/alexarchambault/jarjar.git - cd jarjar - if ! grep -q "^version=$JARJAR_VERSION\$" gradle.properties; then - echo "Expected jarjar version not found" 1>&2 - exit 1 - fi - git checkout 249c8dbb970f8 - ./gradlew :jarjar-core:install - cd .. - rm -rf jarjar - fi + if is211; then + testLauncherJava6 + fi - SBT_COMMANDS="$SBT_COMMANDS sbt-coursier/publishLocal sbt-shading/scripted" + if is210; then + testSbtCoursierJava6 fi fi -SBT_COMMANDS="$SBT_COMMANDS tut coreJVM/mimaReportBinaryIssues cache/mimaReportBinaryIssues" -~/sbt ++${TRAVIS_SCALA_VERSION} $SBT_COMMANDS +if [ "$PUBLISH" = 1 -a "$PULL_REQUEST" = false -a "$BRANCH" = master ]; then + publish -scripts/java-6-test.sh - -if isNotPr && publish && isMaster; then - ~/sbt ++${TRAVIS_SCALA_VERSION} publish -fi - -PUSH_GHPAGES=0 -if isNotPr && publish && isMasterOrDevelop; then - if echo "$TRAVIS_SCALA_VERSION" | grep -q "^2\.11"; then - PUSH_GHPAGES=1 + if is211 && isScalaJs; then + #"$(dirname "$0")/push-gh-pages.sh" "$SCALA_VERSION" + : fi fi -# [ "$PUSH_GHPAGES" = 0 ] || "$(dirname "$0")/push-gh-pages.sh" "$TRAVIS_SCALA_VERSION" From 4dd9f7954b28e1bae7df29860dbfe8a95ddcfe3d Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Wed, 22 Feb 2017 22:18:30 +0100 Subject: [PATCH 16/16] Tweak some type class derivations Hopefully will address more-or-less random failures, on the CI in particular --- .../scala-2.11/coursier/cli/Coursier.scala | 99 ++++++++++++++++++- 1 file changed, 94 insertions(+), 5 deletions(-) diff --git a/cli/src/main/scala-2.11/coursier/cli/Coursier.scala b/cli/src/main/scala-2.11/coursier/cli/Coursier.scala index 1cb732438..b15dcd461 100644 --- a/cli/src/main/scala-2.11/coursier/cli/Coursier.scala +++ b/cli/src/main/scala-2.11/coursier/cli/Coursier.scala @@ -2,8 +2,12 @@ package coursier package cli import caseapp._ -import caseapp.core.{ ArgsApp, CommandsMessages } +import caseapp.core.{ ArgsApp, CommandMessages, CommandsMessages } +import caseapp.core.util.pascalCaseSplit +import caseapp.util.AnnotationOption +import shapeless._ +import shapeless.labelled.FieldType import shapeless.union.Union // Temporary, see comment in Coursier below @@ -21,10 +25,95 @@ final case class CoursierCommandHelper( object CoursierCommandHelper { type U = Union.`'bootstrap -> Bootstrap, 'fetch -> Fetch, 'launch -> Launch, 'resolve -> Resolve, 'sparksubmit -> SparkSubmit`.T - implicit val commandParser: CommandParser[CoursierCommandHelper] = - CommandParser[U].map(CoursierCommandHelper(_)) - implicit val commandsMessages: CommandsMessages[CoursierCommandHelper] = - CommandsMessages(CommandsMessages[U].messages) + // Partially deriving these ones manually, to circumvent more-or-less random failures during auto derivation + // Only running into those with the new custom sbt launcher though :-| + + implicit def commandParser: CommandParser[CoursierCommandHelper] = + CommandParser.ccons( + Witness('bootstrap), + AnnotationOption[CommandName, Bootstrap], + Parser[Bootstrap], + CommandParser.ccons( + Witness('fetch), + AnnotationOption[CommandName, Fetch], + Parser[Fetch], + CommandParser.ccons( + Witness('launch), + AnnotationOption[CommandName, Launch], + Parser[Launch], + CommandParser.ccons( + Witness('resolve), + AnnotationOption[CommandName, Resolve], + Parser[Resolve], + CommandParser.ccons( + Witness('sparksubmit), + AnnotationOption[CommandName, SparkSubmit], + Parser[SparkSubmit], + CommandParser.cnil + ) + ) + ) + ) + ).map(CoursierCommandHelper(_)) + + + // Cut-n-pasted from caseapp.core.CommandsMessages.ccons, fixing the type of argsName + private def commandsMessagesCCons[K <: Symbol, H, T <: Coproduct] + (implicit + key: Witness.Aux[K], + commandName: AnnotationOption[CommandName, H], + parser: Strict[Parser[H]], + argsName: AnnotationOption[ArgsName, H], + tail: CommandsMessages[T] + ): CommandsMessages[FieldType[K, H] :+: T] = { + // FIXME Duplicated in CommandParser.ccons + val name = commandName().map(_.commandName).getOrElse { + pascalCaseSplit(key.value.name.toList.takeWhile(_ != '$')) + .map(_.toLowerCase) + .mkString("-") + } + + CommandsMessages((name -> CommandMessages( + parser.value.args, + argsName().map(_.argsName) + )) +: tail.messages) + } + + + implicit def commandsMessages: CommandsMessages[CoursierCommandHelper] = + CommandsMessages( + commandsMessagesCCons( + Witness('bootstrap), + AnnotationOption[CommandName, Bootstrap], + Parser[Bootstrap], + AnnotationOption[ArgsName, Bootstrap], + commandsMessagesCCons( + Witness('fetch), + AnnotationOption[CommandName, Fetch], + Parser[Fetch], + AnnotationOption[ArgsName, Fetch], + commandsMessagesCCons( + Witness('launch), + AnnotationOption[CommandName, Launch], + Parser[Launch], + AnnotationOption[ArgsName, Launch], + commandsMessagesCCons( + Witness('resolve), + AnnotationOption[CommandName, Resolve], + Parser[Resolve], + AnnotationOption[ArgsName, Resolve], + commandsMessagesCCons( + Witness('sparksubmit), + AnnotationOption[CommandName, SparkSubmit], + Parser[SparkSubmit], + AnnotationOption[ArgsName, SparkSubmit], + CommandsMessages.cnil + ) + ) + ) + ) + ).messages + ) } object Coursier extends CommandAppOf[