Merge pull request #439 from alexarchambault/topic/sbt-launcher

Add sbt launcher
This commit is contained in:
Alexandre Archambault 2017-02-27 21:50:22 +01:00 committed by GitHub
commit b6295b2719
33 changed files with 1486 additions and 192 deletions

View File

@ -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

View File

@ -1,93 +0,0 @@
#!/bin/bash
set -ev
TRAVIS_SCALA_VERSION="$1"
shift
TRAVIS_PULL_REQUEST="$1"
shift
TRAVIS_BRANCH="$1"
shift
PUBLISH="$1"
shift
function isNotPr() {
[ "$TRAVIS_PULL_REQUEST" = "false" ]
}
function publish() {
[ "$PUBLISH" = 1 ]
}
function isMaster() {
[ "$TRAVIS_BRANCH" = "master" ]
}
function isMasterOrDevelop() {
[ "$TRAVIS_BRANCH" = "master" -o "$TRAVIS_BRANCH" = "develop" ]
}
# 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 &
# TODO Add coverage once https://github.com/scoverage/sbt-scoverage/issues/111 is fixed
SBT_COMMANDS="compile test it:test"
RUN_SHADING_TESTS=1
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 [ "$RUN_SHADING_TESTS" = 1 ]; then
# for the shading scripted test
sudo cp coursier /usr/local/bin/
JARJAR_VERSION=1.0.1-coursier-SNAPSHOT
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
SBT_COMMANDS="$SBT_COMMANDS sbt-coursier/publishLocal sbt-shading/scripted"
fi
fi
SBT_COMMANDS="$SBT_COMMANDS tut coreJVM/mimaReportBinaryIssues cache/mimaReportBinaryIssues"
~/sbt ++${TRAVIS_SCALA_VERSION} $SBT_COMMANDS
.ci/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
fi
fi
# [ "$PUSH_GHPAGES" = 0 ] || "$(dirname "$0")/push-gh-pages.sh" "$TRAVIS_SCALA_VERSION"

View File

@ -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:
- .ci/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,9 +25,17 @@ 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:
- 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=
@ -41,4 +47,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

View File

@ -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'
@ -35,3 +34,4 @@ cache:
- C:\Users\appveyor\.ivy2\cache
- C:\Users\appveyor\.m2
- C:\Users\appveyor\.sbt
- C:\Users\appveyor\.coursier

View File

@ -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"
@ -162,6 +164,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"),
@ -205,6 +208,7 @@ lazy val cli = project
else
Seq()
},
packExcludeArtifactTypes += "pom",
resourceGenerators in Compile += packageBin.in(bootstrap).in(Compile).map { jar =>
Seq(jar)
}.taskValue,
@ -380,6 +384,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
@ -398,7 +415,8 @@ lazy val `http-server` = project
)
else
Seq()
}
},
packExcludeArtifactTypes += "pom"
)
lazy val okhttp = project
@ -411,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,
@ -423,6 +476,7 @@ lazy val `coursier` = project.in(file("."))
cli,
`sbt-coursier`,
`sbt-shading`,
`sbt-launcher`,
web,
doc,
`http-server`,

View File

@ -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
}
@ -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,13 +343,17 @@ object TermDisplay {
currentHeight = downloads0.length
}
def cleanDisplay(): Unit = {
def stop(): Unit = {
for (_ <- 1 to 2; _ <- 0 until currentHeight) {
out.clearLine(2)
out.down(1)
}
for (_ <- 0 until currentHeight)
out.up(2)
out.flush()
stopped = true
}
private var previous = Set.empty[String]
@ -454,7 +460,7 @@ class TermDisplay(
def stopDidPrintSomething(): Boolean = {
scheduler.shutdown()
scheduler.awaitTermination(2 * refreshInterval, TimeUnit.MILLISECONDS)
updateRunnable.cleanDisplay()
updateRunnable.stop()
updateRunnable.printedAnything()
}

View File

@ -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]
)

View File

@ -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[

View File

@ -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 =

View File

@ -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) =>

View File

@ -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

View File

@ -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
)

View File

@ -168,7 +168,7 @@ final case class IvyRepository(
Map.empty,
Map.empty,
Attributes("", ""),
changing = true,
changing = changing.getOrElse(version.contains("-SNAPSHOT")),
authentication
)

View File

@ -258,7 +258,7 @@ final case class MavenRepository(
Map.empty,
Map.empty,
Attributes("", ""),
changing = true,
changing = changing.getOrElse(version.contains("-SNAPSHOT")),
authentication
)

View File

@ -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
}

View File

@ -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(
@ -226,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

View File

@ -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

View File

@ -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()
}

View File

@ -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()
)
}
}

View File

@ -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")
}

View File

@ -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()
}
}
}

View File

@ -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")
}
}
}

View File

@ -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]

View File

@ -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
}

View File

@ -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
)
}
}

View File

@ -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)
}

16
sbt.properties Normal file
View File

@ -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}
]

View File

@ -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

188
scripts/travis.sh Executable file
View File

@ -0,0 +1,188 @@
#!/usr/bin/env bash
set -ev
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}"
setupCoursierBinDir() {
mkdir -p bin
cp coursier bin/
export PATH="$(pwd)/bin:$PATH"
}
downloadInstallSbtExtras() {
curl -L -o bin/sbt https://github.com/paulp/sbt-extras/raw/9ade5fa54914ca8aded44105bf4b9a60966f3ccd/sbt
chmod +x bin/sbt
}
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 &
}
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
}
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
}
# TODO Add coverage once https://github.com/scoverage/sbt-scoverage/issues/111 is fixed
setupCoursierBinDir
downloadInstallSbtExtras
if isScalaJs; then
jsCompile
runJsTests
else
integrationTestsRequirements
jvmCompile
runJvmTests
if is210; then
runSbtCoursierTests
runSbtShadingTests
fi
validateReadme
checkBinaryCompatibility
# 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 is211; then
testLauncherJava6
fi
if is210; then
testSbtCoursierJava6
fi
fi
if [ "$PUBLISH" = 1 -a "$PULL_REQUEST" = false -a "$BRANCH" = master ]; then
publish
if is211 && isScalaJs; then
#"$(dirname "$0")/push-gh-pages.sh" "$SCALA_VERSION"
:
fi
fi