mirror of https://github.com/sbt/sbt.git
Static launcher, get bridge sources from resources
This commit introduces a new "static" launcher that does not use Ivy to gather all the artifacts that it requires, but rather expect them to be immediately available. To be able to use sbt without Internet access, we add a new `ComponentCompiler` that is able to retrieve the bridge sources from the resources on classpath and compile it.
This commit is contained in:
parent
cb4500f085
commit
b8472668ff
45
build.sbt
45
build.sbt
|
|
@ -28,7 +28,24 @@ def commonSettings: Seq[Setting[_]] = Seq(
|
|||
incOptions := incOptions.value.withNameHashing(true),
|
||||
crossScalaVersions := Seq(scala210),
|
||||
bintrayPackage := (bintrayPackage in ThisBuild).value,
|
||||
bintrayRepository := (bintrayRepository in ThisBuild).value
|
||||
bintrayRepository := (bintrayRepository in ThisBuild).value,
|
||||
test in assembly := {},
|
||||
assemblyOption in assembly := (assemblyOption in assembly).value.copy(includeScala = true),
|
||||
assemblyMergeStrategy in assembly := {
|
||||
case PathList(ps @ _*) if ps.last == "javax.inject.Named" => MergeStrategy.first
|
||||
case PathList(ps @ _*) if ps.last endsWith ".class" => MergeStrategy.first
|
||||
case PathList(ps @ _*) if ps.last endsWith "module.properties" => MergeStrategy.first
|
||||
case PathList(ps @ _*) if ps.last == "MANIFEST.MF" => MergeStrategy.rename
|
||||
case "LICENSE" => MergeStrategy.first
|
||||
case "NOTICE" => MergeStrategy.first
|
||||
// excluded from fat jar because otherwise we may pick it up when determining the `actualVersion`
|
||||
// of other scala instances.
|
||||
case "compiler.properties" => MergeStrategy.discard
|
||||
|
||||
case x =>
|
||||
val oldStrategy = (assemblyMergeStrategy in assembly).value
|
||||
oldStrategy(x)
|
||||
}
|
||||
)
|
||||
|
||||
def minimalSettings: Seq[Setting[_]] =
|
||||
|
|
@ -346,11 +363,24 @@ lazy val compilerIntegrationProj = (project in (compilePath / "integration")).
|
|||
name := "Compiler Integration"
|
||||
)
|
||||
|
||||
lazy val packageBridgeSource = settingKey[Boolean]("Whether to package the compiler bridge sources in compiler ivy project's resources.")
|
||||
lazy val compilerIvyProj = (project in compilePath / "ivy").
|
||||
dependsOn (ivyProj, compilerProj).
|
||||
settings(
|
||||
baseSettings,
|
||||
name := "Compiler Ivy Integration"
|
||||
name := "Compiler Ivy Integration",
|
||||
packageBridgeSource := false,
|
||||
resourceGenerators in Compile <+= Def.task {
|
||||
if (packageBridgeSource.value) {
|
||||
val compilerBridgeSrc = (Keys.packageSrc in (compileInterfaceProj, Compile)).value
|
||||
val xsbtiJAR = (Keys.packageBin in (interfaceProj, Compile)).value
|
||||
// They are immediately used by the static launcher.
|
||||
val included = Set("scala-compiler.jar", "scala-library.jar")
|
||||
val scalaJars = (externalDependencyClasspath in Compile).value.map(_.data).filter(j => included contains j.getName)
|
||||
Seq(compilerBridgeSrc, xsbtiJAR) ++ scalaJars
|
||||
}
|
||||
else Nil
|
||||
}
|
||||
)
|
||||
|
||||
lazy val scriptedBaseProj = (project in scriptedPath / "base").
|
||||
|
|
@ -594,5 +624,16 @@ def customCommands: Seq[Setting[_]] = Seq(
|
|||
"publish" ::
|
||||
"bintrayRelease" ::
|
||||
state
|
||||
},
|
||||
// Produces a fat runnable JAR that contains everything needed to use sbt.
|
||||
commands += Command.command("install") { state =>
|
||||
val packageBridgeSourceKey = packageBridgeSource.key.label
|
||||
val compilerIvy = compilerIvyProj.id
|
||||
val sbt = sbtProj.id
|
||||
s"$compilerIvy/clean" ::
|
||||
s"set $packageBridgeSourceKey in $compilerIvy := true" ::
|
||||
s"$sbt/assembly" ::
|
||||
s"set $packageBridgeSourceKey in $compilerIvy := false" ::
|
||||
state
|
||||
}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
package sbt
|
||||
package compiler
|
||||
|
||||
/**
|
||||
* Base trait for the different means of retrieving the compiler bridge sources
|
||||
*/
|
||||
sealed trait CompilerBridgeProvider
|
||||
|
||||
/**
|
||||
* Indicates that the compiler bridge should be retrieved using Ivy.
|
||||
* @param ivyConfiguration The `sbt.IvyConfiguration` to use to retrieve the sources.
|
||||
* @param module The module that contains the sources of the compiler bridge.
|
||||
*/
|
||||
final case class IvyBridgeProvider(ivyConfiguration: IvyConfiguration, module: ModuleID) extends CompilerBridgeProvider
|
||||
|
||||
/**
|
||||
* Indicates that the compiler bridge sould be retrieved from the resources on classpath.
|
||||
* @param sourceJarName The name of the JAR containing the bridge sources, to find in the resources.
|
||||
* @param reflectJarName The name of the JAR corresponding to `scala-reflect.jar` in the standard scala distribution.
|
||||
*/
|
||||
final case class ResourceBridgeProvider(sourceJarName: String, reflectJarName: String) extends CompilerBridgeProvider
|
||||
|
|
@ -15,7 +15,7 @@ object ComponentCompiler {
|
|||
val compilerInterfaceSrcID = compilerInterfaceID + srcExtension
|
||||
val javaVersion = System.getProperty("java.class.version")
|
||||
|
||||
@deprecated("Use `interfaceProvider(ComponentManager, IvyConfiguration, ModuleID)`.", "0.13.10")
|
||||
@deprecated("Use `interfaceProvider(ComponentManager, CompilerBridgeProvider)`.", "0.13.12")
|
||||
def interfaceProvider(manager: ComponentManager): CompilerInterfaceProvider = new CompilerInterfaceProvider {
|
||||
def apply(scalaInstance: xsbti.compile.ScalaInstance, log: Logger): File =
|
||||
{
|
||||
|
|
@ -26,13 +26,21 @@ object ComponentCompiler {
|
|||
}
|
||||
}
|
||||
|
||||
def interfaceProvider(manager: ComponentManager, ivyConfiguration: IvyConfiguration, sourcesModule: ModuleID): CompilerInterfaceProvider = new CompilerInterfaceProvider {
|
||||
@deprecated("Use `interfaceProvider(ComponentManager, CompilerBridgeProvider)`", "0.13.12")
|
||||
def interfaceProvider(manager: ComponentManager, ivyConfiguration: IvyConfiguration, sourcesModule: ModuleID): CompilerInterfaceProvider =
|
||||
interfaceProvider(manager, IvyBridgeProvider(ivyConfiguration, sourcesModule))
|
||||
|
||||
def interfaceProvider(manager: ComponentManager, compilerBridgeProvider: CompilerBridgeProvider): CompilerInterfaceProvider = new CompilerInterfaceProvider {
|
||||
def apply(scalaInstance: xsbti.compile.ScalaInstance, log: Logger): File =
|
||||
{
|
||||
// this is the instance used to compile the interface component
|
||||
val componentCompiler = new IvyComponentCompiler(new RawCompiler(scalaInstance, ClasspathOptions.auto, log), manager, ivyConfiguration, sourcesModule, log)
|
||||
log.debug("Getting " + sourcesModule + " from component compiler for Scala " + scalaInstance.version)
|
||||
componentCompiler()
|
||||
compilerBridgeProvider match {
|
||||
case IvyBridgeProvider(ivyConfiguration, sourcesModule) =>
|
||||
val componentCompiler = new IvyComponentCompiler(new RawCompiler(scalaInstance, ClasspathOptions.auto, log), manager, ivyConfiguration, sourcesModule, log)
|
||||
log.debug("Getting " + sourcesModule + " from component compiler for Scala " + scalaInstance.version)
|
||||
componentCompiler()
|
||||
case ResourceBridgeProvider(sourceJarName, reflectJarName) =>
|
||||
val componentCompiler = new ResourceComponentCompiler(new RawCompiler(scalaInstance, ClasspathOptions.auto, log), manager, sourceJarName, reflectJarName, log)
|
||||
log.debug("Compiling bridge source from resources for Scala " + scalaInstance.version)
|
||||
componentCompiler()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -41,7 +49,7 @@ object ComponentCompiler {
|
|||
* The compiled classes are cached using the provided component manager according
|
||||
* to the actualVersion field of the RawCompiler.
|
||||
*/
|
||||
@deprecated("Replaced by IvyComponentCompiler.", "0.13.10")
|
||||
@deprecated("Replaced by IvyComponentCompiler and ResourceComponentCompiler.", "0.13.12")
|
||||
class ComponentCompiler(compiler: RawCompiler, manager: ComponentManager) {
|
||||
import ComponentCompiler._
|
||||
def apply(id: String): File =
|
||||
|
|
@ -79,6 +87,59 @@ class ComponentCompiler(compiler: RawCompiler, manager: ComponentManager) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the compiler bridge using the source extracted from the resources on classpath.
|
||||
*/
|
||||
private[compiler] class ResourceComponentCompiler(compiler: RawCompiler, manager: ComponentManager, sourceJarName: String, reflectJarName: String, log: Logger) {
|
||||
import ComponentCompiler._
|
||||
|
||||
private val reflectID = "reflect"
|
||||
|
||||
def apply(): File = {
|
||||
val binID = "bridge-from-resource" + binSeparator + compiler.scalaInstance.actualVersion + "__" + javaVersion
|
||||
manager.file(binID)(new IfMissing.Define(true, compileAndInstall(binID)))
|
||||
}
|
||||
|
||||
private def copyFromResources(destinationDirectory: File, fileName: String): File = {
|
||||
Option(getClass.getClassLoader.getResourceAsStream(sourceJarName)) match {
|
||||
case Some(stream) =>
|
||||
val copiedFile = new File(destinationDirectory, fileName)
|
||||
val out = new java.io.FileOutputStream(copiedFile)
|
||||
|
||||
var read = 0
|
||||
val content = new Array[Byte](1024)
|
||||
while ({ read = stream.read(content); read != -1 }) {
|
||||
out.write(content, 0, read)
|
||||
}
|
||||
|
||||
copiedFile
|
||||
|
||||
case None =>
|
||||
throw new InvalidComponent(s"Could not find '$fileName' on resources path.")
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private def compileAndInstall(binID: String): Unit =
|
||||
IO.withTemporaryDirectory { binaryDirectory =>
|
||||
val targetJar = new File(binaryDirectory, s"$binID.jar")
|
||||
val xsbtiJars = manager.files(xsbtiID)(IfMissing.Fail)
|
||||
|
||||
IO.withTemporaryDirectory { tempDirectory =>
|
||||
|
||||
val sourceJar = copyFromResources(tempDirectory, sourceJarName)
|
||||
val reflectJar = copyFromResources(tempDirectory, reflectJarName)
|
||||
|
||||
// We need to have `scala-reflect.jar` on the classpath when compiling the compiler bridge.
|
||||
// In `IvyComponentCompiler`, `scala-reflect.jar` is automatically pulled in as a dependency.
|
||||
AnalyzingCompiler.compileSources(Seq(sourceJar), targetJar, xsbtiJars ++ Seq(reflectJar), "bridge-from-resources", compiler, log)
|
||||
manager.define(binID, Seq(targetJar))
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Component compiler which is able to to retrieve the compiler bridge sources
|
||||
* `sourceModule` using Ivy.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,192 @@
|
|||
package sbt
|
||||
|
||||
import java.io.File
|
||||
import java.net.URL
|
||||
|
||||
import org.apache.ivy.core.cache.ArtifactOrigin
|
||||
import org.apache.ivy.core.cache.{ DefaultRepositoryCacheManager, RepositoryCacheManager }
|
||||
import org.apache.ivy.core.module.descriptor.{ Artifact => IvyArtifact, DefaultArtifact, DefaultDependencyArtifactDescriptor, DefaultModuleDescriptor, DependencyArtifactDescriptor, DependencyDescriptor }
|
||||
import org.apache.ivy.core.module.id.ModuleRevisionId
|
||||
import org.apache.ivy.core.report.ArtifactDownloadReport
|
||||
import org.apache.ivy.core.report.{ DownloadReport, DownloadStatus }
|
||||
import org.apache.ivy.core.report.MetadataArtifactDownloadReport
|
||||
import org.apache.ivy.core.resolve.{ DownloadOptions, ResolveData, ResolvedModuleRevision }
|
||||
import org.apache.ivy.core.search.{ ModuleEntry, OrganisationEntry, RevisionEntry }
|
||||
import org.apache.ivy.core.settings.IvySettings
|
||||
import org.apache.ivy.plugins.namespace.Namespace
|
||||
import org.apache.ivy.plugins.repository.url.URLResource
|
||||
import org.apache.ivy.plugins.resolver.{ DependencyResolver, ResolverSettings }
|
||||
import org.apache.ivy.plugins.resolver.util.ResolvedResource
|
||||
|
||||
import FakeResolver._
|
||||
|
||||
/**
|
||||
* A fake `DependencyResolver` that statically serves predefined artifacts.
|
||||
*/
|
||||
private[sbt] class FakeResolver(private var name: String, cacheDir: File, modules: ModulesMap) extends DependencyResolver {
|
||||
|
||||
private object Artifact {
|
||||
def unapply(art: IvyArtifact): Some[(String, String, String)] = {
|
||||
val revisionID = art.getModuleRevisionId()
|
||||
val organisation = revisionID.getOrganisation
|
||||
val name = revisionID.getName
|
||||
val revision = revisionID.getRevision
|
||||
Some((organisation, name, revision))
|
||||
}
|
||||
|
||||
def unapply(dd: DependencyDescriptor): Some[(String, String, String)] = {
|
||||
val module = dd.getDependencyId()
|
||||
val organisation = module.getOrganisation
|
||||
val name = module.getName
|
||||
val mrid = dd.getDependencyRevisionId()
|
||||
val revision = mrid.getRevision()
|
||||
Some((organisation, name, revision))
|
||||
}
|
||||
}
|
||||
|
||||
override def publish(artifact: IvyArtifact, src: File, overwrite: Boolean): Unit =
|
||||
throw new UnsupportedOperationException("This resolver doesn't support publishing.")
|
||||
|
||||
override def abortPublishTransaction(): Unit =
|
||||
throw new UnsupportedOperationException("This resolver doesn't support publishing.")
|
||||
|
||||
override def beginPublishTransaction(module: ModuleRevisionId, overwrite: Boolean): Unit =
|
||||
throw new UnsupportedOperationException("This resolver doesn't support publishing.")
|
||||
|
||||
override def commitPublishTransaction(): Unit =
|
||||
throw new UnsupportedOperationException("This resolver doesn't support publishing.")
|
||||
|
||||
override def download(artifact: ArtifactOrigin, options: DownloadOptions): ArtifactDownloadReport = {
|
||||
val report = new ArtifactDownloadReport(artifact.getArtifact)
|
||||
val path = new URL(artifact.getLocation).toURI.getPath
|
||||
assert(path.nonEmpty, "Path to local artifact is empty.")
|
||||
|
||||
val localFile = new File(path)
|
||||
assert(localFile.exists, "Local file doesn't exist.")
|
||||
|
||||
report.setLocalFile(localFile)
|
||||
report.setDownloadStatus(DownloadStatus.SUCCESSFUL)
|
||||
report.setSize(localFile.length)
|
||||
|
||||
report
|
||||
}
|
||||
|
||||
override def download(artifacts: Array[IvyArtifact], options: DownloadOptions): DownloadReport = {
|
||||
val report = new DownloadReport
|
||||
|
||||
artifacts foreach { art =>
|
||||
val artifactOrigin = locate(art)
|
||||
report.addArtifactReport(download(artifactOrigin, options))
|
||||
}
|
||||
|
||||
report
|
||||
}
|
||||
|
||||
override def dumpSettings(): Unit = ()
|
||||
|
||||
override def exists(artifact: IvyArtifact): Boolean = {
|
||||
val Artifact(organisation, name, revision) = artifact
|
||||
modules.get((organisation, name, revision)).isDefined
|
||||
}
|
||||
|
||||
// This is a fake resolver and we don't have Ivy files. Ivy's spec says we can return `null` if
|
||||
// we can't find the module descriptor.
|
||||
override def findIvyFileRef(dd: DependencyDescriptor, data: ResolveData): ResolvedResource = null
|
||||
|
||||
override def getDependency(dd: DependencyDescriptor, data: ResolveData): ResolvedModuleRevision = {
|
||||
|
||||
val Artifact(organisation, name, revision) = dd
|
||||
val mrid = dd.getDependencyRevisionId()
|
||||
|
||||
val artifact = modules get ((organisation, name, revision)) map { arts =>
|
||||
|
||||
val artifacts: Array[DependencyArtifactDescriptor] = arts.toArray map (_ artifactOf dd)
|
||||
val moduleDescriptor = DefaultModuleDescriptor.newDefaultInstance(mrid, artifacts)
|
||||
val defaultArtifact = arts.headOption match {
|
||||
case Some(FakeArtifact(name, tpe, ext, _)) => new DefaultArtifact(mrid, new java.util.Date, name, tpe, ext)
|
||||
case None => null
|
||||
}
|
||||
val metadataReport = new MetadataArtifactDownloadReport(defaultArtifact)
|
||||
metadataReport.setDownloadStatus(DownloadStatus.SUCCESSFUL)
|
||||
|
||||
new ResolvedModuleRevision(this, this, moduleDescriptor, metadataReport)
|
||||
}
|
||||
|
||||
artifact getOrElse (throw new Exception(s"Could not find module $organisation % $name % $revision"))
|
||||
|
||||
}
|
||||
|
||||
override def getName(): String = name
|
||||
|
||||
override val getNamespace: Namespace = {
|
||||
val ns = new Namespace()
|
||||
ns.setName(name)
|
||||
ns
|
||||
}
|
||||
|
||||
override val getRepositoryCacheManager: RepositoryCacheManager = {
|
||||
val cacheName = name + "-cache"
|
||||
val ivySettings = new IvySettings()
|
||||
val baseDir = cacheDir
|
||||
new DefaultRepositoryCacheManager(cacheName, ivySettings, baseDir)
|
||||
}
|
||||
|
||||
override def listModules(organisation: OrganisationEntry): Array[ModuleEntry] =
|
||||
modules.keys.collect {
|
||||
case (o, m, _) if o == organisation.getOrganisation =>
|
||||
val organisationEntry = new OrganisationEntry(this, o)
|
||||
new ModuleEntry(organisationEntry, m)
|
||||
}.toArray
|
||||
|
||||
override def listOrganisations(): Array[OrganisationEntry] =
|
||||
modules.keys.map { case (o, _, _) => new OrganisationEntry(this, o) }.toArray
|
||||
|
||||
override def listRevisions(module: ModuleEntry): Array[RevisionEntry] =
|
||||
modules.keys.collect {
|
||||
case (o, m, v) if o == module.getOrganisation && m == module.getModule =>
|
||||
new RevisionEntry(module, v)
|
||||
}.toArray
|
||||
|
||||
override def listTokenValues(tokens: Array[String], criteria: java.util.Map[_, _]): Array[java.util.Map[_, _]] =
|
||||
Array.empty
|
||||
|
||||
override def listTokenValues(token: String, otherTokenValues: java.util.Map[_, _]): Array[String] =
|
||||
Array.empty
|
||||
|
||||
override def locate(art: IvyArtifact): ArtifactOrigin = {
|
||||
val Artifact(moduleOrganisation, moduleName, moduleRevision) = art
|
||||
val artifact =
|
||||
for {
|
||||
artifacts <- modules get ((moduleOrganisation, moduleName, moduleRevision))
|
||||
artifact <- artifacts find (a => a.name == art.getName && a.tpe == art.getType && a.ext == art.getExt)
|
||||
} yield new ArtifactOrigin(art, /* isLocal = */ true, artifact.file.toURI.toURL.toString)
|
||||
|
||||
artifact getOrElse (throw new IllegalStateException(s"Asking for non-existing module: $moduleOrganisation % $moduleName % $moduleRevision"))
|
||||
|
||||
}
|
||||
|
||||
override def reportFailure(art: IvyArtifact): Unit = ()
|
||||
override def reportFailure(): Unit = ()
|
||||
|
||||
override def setName(name: String): Unit = {
|
||||
this.name = name
|
||||
getNamespace.setName(name)
|
||||
}
|
||||
|
||||
override def setSettings(settings: ResolverSettings): Unit = ()
|
||||
|
||||
private class LocalURLResource(jar: File) extends URLResource(jar.toURI.toURL) {
|
||||
override def isLocal(): Boolean = true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private[sbt] object FakeResolver {
|
||||
|
||||
type ModulesMap = Map[(String, String, String), Seq[FakeArtifact]]
|
||||
|
||||
final case class FakeArtifact(name: String, tpe: String, ext: String, file: File) {
|
||||
def artifactOf(dd: DependencyDescriptor): DependencyArtifactDescriptor =
|
||||
new DefaultDependencyArtifactDescriptor(dd, name, tpe, ext, file.toURI.toURL, new java.util.HashMap)
|
||||
}
|
||||
}
|
||||
|
|
@ -163,6 +163,11 @@ final case class SftpRepository(name: String, connection: SshConnection, pattern
|
|||
protected def copy(patterns: Patterns): SftpRepository = SftpRepository(name, connection, patterns)
|
||||
protected def copy(connection: SshConnection): SftpRepository = SftpRepository(name, connection, patterns)
|
||||
}
|
||||
/** A repository that conforms to sbt launcher's interface */
|
||||
private[sbt] class FakeRepository(resolver: DependencyResolver) extends xsbti.Repository {
|
||||
def rawRepository = new RawRepository(resolver)
|
||||
}
|
||||
|
||||
|
||||
import Resolver._
|
||||
|
||||
|
|
|
|||
|
|
@ -69,7 +69,11 @@ object Compiler {
|
|||
}
|
||||
compilers(instance, cpOptions, CheaterJavaTool(javac2, javac))
|
||||
}
|
||||
@deprecated("Use `compilers(ScalaInstance, ClasspathOptions, Option[File], CompilerBridgeProvider)`.", "0.13.12")
|
||||
def compilers(instance: ScalaInstance, cpOptions: ClasspathOptions, javaHome: Option[File], ivyConfiguration: IvyConfiguration, sourcesModule: ModuleID)(implicit app: AppConfiguration, log: Logger): Compilers =
|
||||
compilers(instance, cpOptions, javaHome, sbt.compiler.IvyBridgeProvider(ivyConfiguration, sourcesModule))(app, log)
|
||||
|
||||
def compilers(instance: ScalaInstance, cpOptions: ClasspathOptions, javaHome: Option[File], compilerBridgeProvider: CompilerBridgeProvider)(implicit app: AppConfiguration, log: Logger): Compilers =
|
||||
{
|
||||
val javac =
|
||||
AggressiveCompile.directOrFork(instance, cpOptions, javaHome)
|
||||
|
|
@ -81,7 +85,7 @@ object Compiler {
|
|||
javac.compile(contract, sources, classpath, outputDirectory, options)(log)
|
||||
def onArgs(f: Seq[String] => Unit): JavaTool = CheaterJavaTool(newJavac, delegate.onArgs(f))
|
||||
}
|
||||
val scalac = scalaCompiler(instance, cpOptions, ivyConfiguration, sourcesModule)
|
||||
val scalac = scalaCompiler(instance, cpOptions, compilerBridgeProvider)
|
||||
new Compilers(scalac, CheaterJavaTool(javac2, javac))
|
||||
}
|
||||
@deprecated("Deprecated in favor of new sbt.compiler.javac package.", "0.13.8")
|
||||
|
|
@ -96,7 +100,7 @@ object Compiler {
|
|||
val scalac = scalaCompiler(instance, cpOptions)
|
||||
new Compilers(scalac, javac)
|
||||
}
|
||||
@deprecated("Use `scalaCompiler(ScalaInstance, ClasspathOptions, IvyConfiguration, ModuleID)`.", "0.13.10")
|
||||
@deprecated("Use `scalaCompiler(ScalaInstance, ClasspathOptions, CompilerBridgeProvider)`.", "0.13.12")
|
||||
def scalaCompiler(instance: ScalaInstance, cpOptions: ClasspathOptions)(implicit app: AppConfiguration, log: Logger): AnalyzingCompiler =
|
||||
{
|
||||
val launcher = app.provider.scalaProvider.launcher
|
||||
|
|
@ -104,11 +108,15 @@ object Compiler {
|
|||
val provider = ComponentCompiler.interfaceProvider(componentManager)
|
||||
new AnalyzingCompiler(instance, provider, cpOptions)
|
||||
}
|
||||
@deprecated("Use `scalaCompiler(ScalaInstance, ClasspathOptions, CompilerBridgeProvider)`.", "0.13.12")
|
||||
def scalaCompiler(instance: ScalaInstance, cpOptions: ClasspathOptions, ivyConfiguration: IvyConfiguration, sourcesModule: ModuleID)(implicit app: AppConfiguration, log: Logger): AnalyzingCompiler =
|
||||
scalaCompiler(instance, cpOptions, sbt.compiler.IvyBridgeProvider(ivyConfiguration, sourcesModule))(app, log)
|
||||
|
||||
def scalaCompiler(instance: ScalaInstance, cpOptions: ClasspathOptions, compilerBridgeProvider: CompilerBridgeProvider)(implicit app: AppConfiguration, log: Logger): AnalyzingCompiler =
|
||||
{
|
||||
val launcher = app.provider.scalaProvider.launcher
|
||||
val componentManager = new ComponentManager(launcher.globalLock, app.provider.components, Option(launcher.ivyHome), log)
|
||||
val provider = ComponentCompiler.interfaceProvider(componentManager, ivyConfiguration, sourcesModule)
|
||||
val provider = ComponentCompiler.interfaceProvider(componentManager, compilerBridgeProvider)
|
||||
new AnalyzingCompiler(instance, provider, cpOptions)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@ object ConsoleProject {
|
|||
val scalaProvider = state.configuration.provider.scalaProvider
|
||||
ScalaInstance(scalaProvider.version, scalaProvider.launcher)
|
||||
}
|
||||
val sourcesModule = extracted.get(Keys.scalaCompilerBridgeSource)
|
||||
val compiler = Compiler.scalaCompiler(scalaInstance, ClasspathOptions.repl, ivyConf, sourcesModule)(state.configuration, log)
|
||||
val (_, sourcesModule) = extracted.runTask(Keys.compilerBridgeProvider, state)
|
||||
val compiler = Compiler.scalaCompiler(scalaInstance, ClasspathOptions.repl, sourcesModule)(state.configuration, log)
|
||||
val imports = BuildUtil.getImports(unit.unit) ++ BuildUtil.importAll(bindings.map(_._1))
|
||||
val importString = imports.mkString("", ";\n", ";\n\n")
|
||||
val initCommands = importString + extra
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import CrossVersion.{ binarySbtVersion, binaryScalaVersion, partialVersion }
|
|||
import complete._
|
||||
import std.TaskExtra._
|
||||
import sbt.inc.{ Analysis, FileValueCache, IncOptions, Locate }
|
||||
import sbt.compiler.{ MixedAnalyzingCompiler, AggressiveCompile }
|
||||
import sbt.compiler.{ MixedAnalyzingCompiler, AggressiveCompile, IvyBridgeProvider }
|
||||
import testing.{ Framework, Runner, AnnotatedFingerprint, SubclassFingerprint }
|
||||
|
||||
import sys.error
|
||||
|
|
@ -235,7 +235,8 @@ object Defaults extends BuildCommon {
|
|||
val _ = clean.value
|
||||
IvyActions.cleanCachedResolutionCache(ivyModule.value, streams.value.log)
|
||||
},
|
||||
scalaCompilerBridgeSource := ModuleID(xsbti.ArtifactInfo.SbtOrganization, "compiler-interface", sbtVersion.value, Some("component")).sources()
|
||||
scalaCompilerBridgeSource := ModuleID(xsbti.ArtifactInfo.SbtOrganization, "compiler-interface", sbtVersion.value, Some("component")).sources(),
|
||||
compilerBridgeProvider := IvyBridgeProvider(bootIvyConfiguration.value, scalaCompilerBridgeSource.value)
|
||||
)
|
||||
// must be a val: duplication detected by object identity
|
||||
private[this] lazy val compileBaseGlobal: Seq[Setting[_]] = globalDefaults(Seq(
|
||||
|
|
@ -265,7 +266,7 @@ object Defaults extends BuildCommon {
|
|||
}
|
||||
|
||||
def compilersSetting = compilers := Compiler.compilers(scalaInstance.value, classpathOptions.value, javaHome.value,
|
||||
bootIvyConfiguration.value, scalaCompilerBridgeSource.value)(appConfiguration.value, streams.value.log)
|
||||
compilerBridgeProvider.value)(appConfiguration.value, streams.value.log)
|
||||
|
||||
lazy val configTasks = docTaskSettings(doc) ++ inTask(compile)(compileInputsSettings) ++ configGlobal ++ compileAnalysisSettings ++ Seq(
|
||||
compile <<= compileTask,
|
||||
|
|
@ -1883,6 +1884,7 @@ object Classpaths {
|
|||
{
|
||||
import xsbti.Predefined
|
||||
repo match {
|
||||
case f: FakeRepository => f.rawRepository
|
||||
case m: xsbti.MavenRepository => MavenRepository(m.id, m.url.toString)
|
||||
case i: xsbti.IvyRepository =>
|
||||
val patterns = Patterns(i.ivyPattern :: Nil, i.artifactPattern :: Nil, mavenCompatible(i), descriptorOptional(i), skipConsistencyCheck(i))
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ import Configurations.CompilerPlugin
|
|||
import Types.Id
|
||||
import KeyRanks._
|
||||
|
||||
import sbt.compiler.CompilerBridgeProvider
|
||||
|
||||
object Keys {
|
||||
val TraceValues = "-1 to disable, 0 for up to the first sbt frame, or a positive number to set the maximum number of frames shown."
|
||||
|
||||
|
|
@ -138,6 +140,7 @@ object Keys {
|
|||
val printWarnings = TaskKey[Unit]("print-warnings", "Shows warnings from compilation, including ones that weren't printed initially.", BPlusTask)
|
||||
val fileInputOptions = SettingKey[Seq[String]]("file-input-options", "Options that take file input, which may invalidate the cache.", CSetting)
|
||||
val scalaCompilerBridgeSource = SettingKey[ModuleID]("scala-compiler-bridge-source", "Configures the module ID of the sources of the compiler bridge.", CSetting)
|
||||
val compilerBridgeProvider = TaskKey[CompilerBridgeProvider]("compiler-bridge-provider", "Configures how sbt will retrieve the compiler bridge.", CTask)
|
||||
|
||||
val clean = TaskKey[Unit]("clean", "Deletes files produced by the build, such as generated sources, compiled classes, and task caches.", APlusTask)
|
||||
val console = TaskKey[Unit]("console", "Starts the Scala interpreter with the project classes on the classpath.", APlusTask)
|
||||
|
|
|
|||
|
|
@ -7,3 +7,4 @@ addSbtPlugin("com.typesafe.sbt" % "sbt-javaversioncheck" % "0.1.0")
|
|||
addSbtPlugin("com.typesafe.sbt" % "sbt-scalariform" % "1.3.0") // 1.6.0 is out but is a hard upgrade
|
||||
addSbtPlugin("com.typesafe.sbt" % "sbt-site" % "0.8.2")
|
||||
addSbtPlugin("me.lessis" % "bintray-sbt" % "0.3.0")
|
||||
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.2")
|
||||
|
|
@ -0,0 +1,170 @@
|
|||
package sbt
|
||||
|
||||
import java.net.URLClassLoader
|
||||
import java.util.Properties
|
||||
|
||||
/**
|
||||
* A Main class for running sbt without sbt launcher.
|
||||
*/
|
||||
object Main {
|
||||
def main(args: Array[String]): Unit = {
|
||||
val appConfiguration = new StaticAppConfiguration(args)
|
||||
new xMain().run(appConfiguration)
|
||||
}
|
||||
}
|
||||
|
||||
private object StaticUtils {
|
||||
val MAIN = "sbt.Main"
|
||||
val SCALA_ORG = "org.scala-lang"
|
||||
val COMPILER = "compiler"
|
||||
val COMPILER_JAR = "scala-compiler.jar"
|
||||
val LIBRARY = "library"
|
||||
val LIBRARY_JAR = "scala-library.jar"
|
||||
val XSBTI = "xsbti"
|
||||
val XSBTI_JAR = s"interface-${sbtApplicationID.version}.jar"
|
||||
val thisJAR: File = new File(getClass.getProtectionDomain().getCodeSource().getLocation().toURI().getPath())
|
||||
|
||||
def getProperty(loader: ClassLoader, filename: String, property: String): Option[String] =
|
||||
for {
|
||||
stream <- Option(loader.getResourceAsStream(filename))
|
||||
props = new Properties()
|
||||
_ = props.load(stream)
|
||||
o <- Option(props get property)
|
||||
s = o.asInstanceOf[String]
|
||||
} yield s
|
||||
|
||||
}
|
||||
|
||||
private class StaticComponentProvider(bootDirectory: File) extends xsbti.ComponentProvider {
|
||||
override def addToComponent(componentID: String, components: Array[File]): Boolean = {
|
||||
components foreach { c =>
|
||||
IO.copyFile(c, componentLocation(componentID) / c.getName)
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
override def component(componentID: String): Array[File] =
|
||||
PathFinder(componentLocation(componentID)).***.get.filter(_.isFile).toArray
|
||||
|
||||
override def componentLocation(id: String): File =
|
||||
bootDirectory / s"static-sbt-${sbtApplicationID.version}" / id
|
||||
|
||||
override def defineComponent(componentID: String, components: Array[File]): Unit =
|
||||
addToComponent(componentID, components)
|
||||
|
||||
override def lockFile(): File = null
|
||||
}
|
||||
|
||||
private object sbtApplicationID extends xsbti.ApplicationID {
|
||||
override val groupID: String = xsbti.ArtifactInfo.SbtOrganization
|
||||
override val name: String = "sbt"
|
||||
override def version(): String = StaticUtils.getProperty(getClass.getClassLoader, "xsbt.version.properties", "version") getOrElse "unknown"
|
||||
override val mainClass: String = StaticUtils.MAIN
|
||||
override val mainComponents: Array[String] = Array.empty
|
||||
override val crossVersioned: Boolean = false
|
||||
override val crossVersionedValue: xsbti.CrossValue = xsbti.CrossValue.Disabled
|
||||
override val classpathExtra: Array[File] = Array.empty
|
||||
}
|
||||
|
||||
private class WeakGlobalLock extends xsbti.GlobalLock {
|
||||
override def apply[T](lockFile: File, run: java.util.concurrent.Callable[T]): T = run.call
|
||||
}
|
||||
|
||||
private class StaticLauncher(appProvider: StaticAppProvider, scalaProvider: StaticScalaProvider) extends xsbti.Launcher {
|
||||
override def getScala(version: String): xsbti.ScalaProvider = getScala(version, "")
|
||||
override def getScala(version: String, reason: String): xsbti.ScalaProvider = getScala(version, reason, StaticUtils.SCALA_ORG)
|
||||
override def getScala(version: String, reason: String, scalaOrg: String): xsbti.ScalaProvider = {
|
||||
val myScalaVersion = scalaProvider.version
|
||||
if (myScalaVersion == version) scalaProvider
|
||||
else throw new InvalidComponent(s"This launcher can only provide scala $myScalaVersion, asked for scala $version")
|
||||
}
|
||||
override def app(id: xsbti.ApplicationID, version: String): xsbti.AppProvider = appProvider
|
||||
|
||||
override def topLoader(): ClassLoader = new URLClassLoader(Array.empty)
|
||||
override def globalLock(): xsbti.GlobalLock = new WeakGlobalLock
|
||||
|
||||
override def bootDirectory(): File = new File(sys props "user.home") / ".sbt" / "boot"
|
||||
|
||||
override def ivyRepositories(): Array[xsbti.Repository] = Array.empty
|
||||
override def appRepositories(): Array[xsbti.Repository] = Array(new FakeRepository(new FakeResolver("fakeresolver", bootDirectory / "fakeresolver-cache", modules)))
|
||||
|
||||
override def isOverrideRepositories(): Boolean = false
|
||||
|
||||
override def ivyHome(): File = null
|
||||
override def checksums(): Array[String] = Array.empty
|
||||
|
||||
private val modules = Map(
|
||||
("org.scala-sbt", "sbt", "0.13.12-SNAPSHOT") -> Seq(FakeResolver.FakeArtifact("sbt", "jar", "jar", StaticUtils.thisJAR))
|
||||
)
|
||||
}
|
||||
|
||||
private class StaticScalaProvider(appProvider: StaticAppProvider) extends xsbti.ScalaProvider {
|
||||
|
||||
private def getComponent(componentID: String): File = {
|
||||
val component = appProvider.components.component(componentID)
|
||||
assert(component.length == 1, s"""Component $componentID should have 1 file, ${component.length} files found: ${component.mkString(", ")}.""")
|
||||
component(0)
|
||||
}
|
||||
override def launcher: xsbti.Launcher = new StaticLauncher(appProvider, this)
|
||||
override def app(id: xsbti.ApplicationID): xsbti.AppProvider = appProvider
|
||||
override def compilerJar(): File = getComponent(StaticUtils.COMPILER)
|
||||
override def libraryJar(): File = getComponent(StaticUtils.LIBRARY)
|
||||
override def jars(): Array[File] = Array(compilerJar, libraryJar)
|
||||
override def loader(): ClassLoader = new URLClassLoader(jars map (_.toURI.toURL))
|
||||
override def version(): String = StaticUtils.getProperty(loader, "compiler.properties", "version.number") getOrElse "unknown"
|
||||
}
|
||||
|
||||
private class StaticAppProvider(appConfig: StaticAppConfiguration) extends xsbti.AppProvider {
|
||||
|
||||
if (components.component(StaticUtils.COMPILER).isEmpty) {
|
||||
installFromResources(StaticUtils.COMPILER_JAR, StaticUtils.COMPILER)
|
||||
}
|
||||
|
||||
if (components.component(StaticUtils.LIBRARY).isEmpty) {
|
||||
installFromResources(StaticUtils.LIBRARY_JAR, StaticUtils.LIBRARY)
|
||||
}
|
||||
|
||||
if (components.component(StaticUtils.XSBTI).isEmpty) {
|
||||
installFromResources(StaticUtils.XSBTI_JAR, StaticUtils.XSBTI)
|
||||
}
|
||||
|
||||
override def components(): xsbti.ComponentProvider = new StaticComponentProvider(scalaProvider.launcher.bootDirectory)
|
||||
override def entryPoint(): Class[_] = loader.getClass
|
||||
override def id(): xsbti.ApplicationID = sbtApplicationID
|
||||
override def loader(): ClassLoader = getClass.getClassLoader
|
||||
override def mainClass(): Class[xsbti.AppMain] = loader.loadClass(id.mainClass).asInstanceOf[Class[xsbti.AppMain]]
|
||||
override def mainClasspath(): Array[File] = Array(StaticUtils.thisJAR)
|
||||
override def newMain(): xsbti.AppMain = new xMain
|
||||
override def scalaProvider(): xsbti.ScalaProvider = new StaticScalaProvider(this)
|
||||
|
||||
/**
|
||||
* Retrieves `fileName` from the resources and installs it in `componentID`.
|
||||
* @param filename Name of the file to get from the resources.
|
||||
* @param componentID ID of the component to create.
|
||||
*/
|
||||
private def installFromResources(filename: String, componentID: String): Unit =
|
||||
IO.withTemporaryDirectory { tmp =>
|
||||
Option(getClass.getClassLoader.getResourceAsStream(filename)) match {
|
||||
case Some(stream) =>
|
||||
val target = tmp / filename
|
||||
val out = new java.io.FileOutputStream(target)
|
||||
|
||||
var read = 0
|
||||
val content = new Array[Byte](1024)
|
||||
while ({ read = stream.read(content); read != -1 }) {
|
||||
out.write(content, 0, read)
|
||||
}
|
||||
|
||||
components.defineComponent(componentID, Array(target))
|
||||
|
||||
case None =>
|
||||
sys.error(s"Couldn't install component $componentID: $filename not found on resource path.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class StaticAppConfiguration(override val arguments: Array[String]) extends xsbti.AppConfiguration {
|
||||
override val baseDirectory: File = new File(sys props "user.dir")
|
||||
override val provider: xsbti.AppProvider = new StaticAppProvider(this)
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue