From cb4500f085cb9a5358863fa3f4d804c9a4ce37b3 Mon Sep 17 00:00:00 2001 From: Martin Duhem Date: Mon, 4 Apr 2016 13:35:06 +0200 Subject: [PATCH 01/10] Auto format `Tracked.scala` --- .../tracking/src/main/scala/sbt/Tracked.scala | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/cache/tracking/src/main/scala/sbt/Tracked.scala b/cache/tracking/src/main/scala/sbt/Tracked.scala index 66b15546f..e17c4052c 100644 --- a/cache/tracking/src/main/scala/sbt/Tracked.scala +++ b/cache/tracking/src/main/scala/sbt/Tracked.scala @@ -232,23 +232,23 @@ object FileFunction { type UpdateFunction = (ChangeReport[File], ChangeReport[File]) => Set[File] /** - Generic change-detection helper used to help build / artifact generation / - etc. steps detect whether or not they need to run. Returns a function whose - input is a Set of input files, and subsequently executes the action function - (which does the actual work: compiles, generates resources, etc.), returning - a Set of output files that it generated. - - The input file and resulting output file state is cached in - cacheBaseDirectory. On each invocation, the state of the input and output - files from the previous run is compared against the cache, as is the set of - input files. If a change in file state / input files set is detected, the - action function is re-executed. - - @param cacheBaseDirectory The folder in which to store - @param inStyle The strategy by which to detect state change in the input files from the previous run - @param outStyle The strategy by which to detect state change in the output files from the previous run - @param action The work function, which receives a list of input files and returns a list of output files - */ + * Generic change-detection helper used to help build / artifact generation / + * etc. steps detect whether or not they need to run. Returns a function whose + * input is a Set of input files, and subsequently executes the action function + * (which does the actual work: compiles, generates resources, etc.), returning + * a Set of output files that it generated. + * + * The input file and resulting output file state is cached in + * cacheBaseDirectory. On each invocation, the state of the input and output + * files from the previous run is compared against the cache, as is the set of + * input files. If a change in file state / input files set is detected, the + * action function is re-executed. + * + * @param cacheBaseDirectory The folder in which to store + * @param inStyle The strategy by which to detect state change in the input files from the previous run + * @param outStyle The strategy by which to detect state change in the output files from the previous run + * @param action The work function, which receives a list of input files and returns a list of output files + */ def cached(cacheBaseDirectory: File, inStyle: FilesInfo.Style = FilesInfo.lastModified, outStyle: FilesInfo.Style = FilesInfo.exists)(action: Set[File] => Set[File]): Set[File] => Set[File] = cached(cacheBaseDirectory)(inStyle, outStyle)((in, out) => action(in.checked)) From b8472668fff8a8620d236f3d8938ec94bd6cef10 Mon Sep 17 00:00:00 2001 From: Martin Duhem Date: Wed, 6 Apr 2016 11:52:51 +0200 Subject: [PATCH 02/10] 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. --- build.sbt | 45 +++- .../sbt/compiler/CompilerBridgeProvider.scala | 21 ++ .../sbt/compiler/ComponentCompiler.scala | 77 ++++++- ivy/src/main/scala/sbt/FakeResolver.scala | 192 ++++++++++++++++++ ivy/src/main/scala/sbt/Resolver.scala | 5 + .../actions/src/main/scala/sbt/Compiler.scala | 14 +- main/src/main/scala/sbt/ConsoleProject.scala | 4 +- main/src/main/scala/sbt/Defaults.scala | 8 +- main/src/main/scala/sbt/Keys.scala | 3 + project/plugins.sbt | 1 + sbt/src/main/scala/Main.scala | 170 ++++++++++++++++ 11 files changed, 522 insertions(+), 18 deletions(-) create mode 100644 compile/ivy/src/main/scala/sbt/compiler/CompilerBridgeProvider.scala create mode 100644 ivy/src/main/scala/sbt/FakeResolver.scala create mode 100644 sbt/src/main/scala/Main.scala diff --git a/build.sbt b/build.sbt index b1421b366..afe8437c5 100644 --- a/build.sbt +++ b/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 } ) diff --git a/compile/ivy/src/main/scala/sbt/compiler/CompilerBridgeProvider.scala b/compile/ivy/src/main/scala/sbt/compiler/CompilerBridgeProvider.scala new file mode 100644 index 000000000..3e84d4bc0 --- /dev/null +++ b/compile/ivy/src/main/scala/sbt/compiler/CompilerBridgeProvider.scala @@ -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 \ No newline at end of file diff --git a/compile/ivy/src/main/scala/sbt/compiler/ComponentCompiler.scala b/compile/ivy/src/main/scala/sbt/compiler/ComponentCompiler.scala index fa1d57f2d..3b6f3dfcf 100644 --- a/compile/ivy/src/main/scala/sbt/compiler/ComponentCompiler.scala +++ b/compile/ivy/src/main/scala/sbt/compiler/ComponentCompiler.scala @@ -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. diff --git a/ivy/src/main/scala/sbt/FakeResolver.scala b/ivy/src/main/scala/sbt/FakeResolver.scala new file mode 100644 index 000000000..6ef728bdc --- /dev/null +++ b/ivy/src/main/scala/sbt/FakeResolver.scala @@ -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) + } +} diff --git a/ivy/src/main/scala/sbt/Resolver.scala b/ivy/src/main/scala/sbt/Resolver.scala index c0ac1ce31..cce2da583 100644 --- a/ivy/src/main/scala/sbt/Resolver.scala +++ b/ivy/src/main/scala/sbt/Resolver.scala @@ -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._ diff --git a/main/actions/src/main/scala/sbt/Compiler.scala b/main/actions/src/main/scala/sbt/Compiler.scala index 062b11696..20a4ca6ca 100644 --- a/main/actions/src/main/scala/sbt/Compiler.scala +++ b/main/actions/src/main/scala/sbt/Compiler.scala @@ -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) } diff --git a/main/src/main/scala/sbt/ConsoleProject.scala b/main/src/main/scala/sbt/ConsoleProject.scala index 3d66cf552..f444fcb4b 100644 --- a/main/src/main/scala/sbt/ConsoleProject.scala +++ b/main/src/main/scala/sbt/ConsoleProject.scala @@ -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 diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index ae6e93823..2f88004a2 100755 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -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)) diff --git a/main/src/main/scala/sbt/Keys.scala b/main/src/main/scala/sbt/Keys.scala index 566d8ed49..1f92cd219 100644 --- a/main/src/main/scala/sbt/Keys.scala +++ b/main/src/main/scala/sbt/Keys.scala @@ -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) diff --git a/project/plugins.sbt b/project/plugins.sbt index 611885f9a..a440cc1ec 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -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") \ No newline at end of file diff --git a/sbt/src/main/scala/Main.scala b/sbt/src/main/scala/Main.scala new file mode 100644 index 000000000..cf45221ef --- /dev/null +++ b/sbt/src/main/scala/Main.scala @@ -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) +} + From 88dd987d5f6ea8ba011ab690055910c3bc1cc9c2 Mon Sep 17 00:00:00 2001 From: Martin Duhem Date: Wed, 6 Apr 2016 15:10:15 +0200 Subject: [PATCH 03/10] Fix classpath issues in static launcher The launcher defines a top classloader that willbe used by all `ScalaInstance`s. Previously, this top classloader had a parent that contained the scala library 2.10, which prevented the correct compilation of the compiler bridge for scala 2.11. Also, we no longer need the scala-reflect JAR. --- .../sbt/compiler/CompilerBridgeProvider.scala | 5 ++--- .../scala/sbt/compiler/ComponentCompiler.scala | 14 ++++---------- sbt/src/main/scala/Main.scala | 2 +- 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/compile/ivy/src/main/scala/sbt/compiler/CompilerBridgeProvider.scala b/compile/ivy/src/main/scala/sbt/compiler/CompilerBridgeProvider.scala index 3e84d4bc0..993a31ed4 100644 --- a/compile/ivy/src/main/scala/sbt/compiler/CompilerBridgeProvider.scala +++ b/compile/ivy/src/main/scala/sbt/compiler/CompilerBridgeProvider.scala @@ -15,7 +15,6 @@ final case class IvyBridgeProvider(ivyConfiguration: IvyConfiguration, module: M /** * 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. + * @param sourceJarName The name of the JAR containing the bridge sources, to find in the resources. */ -final case class ResourceBridgeProvider(sourceJarName: String, reflectJarName: String) extends CompilerBridgeProvider \ No newline at end of file +final case class ResourceBridgeProvider(sourceJarName: String) extends CompilerBridgeProvider \ No newline at end of file diff --git a/compile/ivy/src/main/scala/sbt/compiler/ComponentCompiler.scala b/compile/ivy/src/main/scala/sbt/compiler/ComponentCompiler.scala index 3b6f3dfcf..541d8f7b3 100644 --- a/compile/ivy/src/main/scala/sbt/compiler/ComponentCompiler.scala +++ b/compile/ivy/src/main/scala/sbt/compiler/ComponentCompiler.scala @@ -37,8 +37,8 @@ object ComponentCompiler { 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) + case ResourceBridgeProvider(sourceJarName) => + val componentCompiler = new ResourceComponentCompiler(new RawCompiler(scalaInstance, ClasspathOptions.auto, log), manager, sourceJarName, log) log.debug("Compiling bridge source from resources for Scala " + scalaInstance.version) componentCompiler() } @@ -90,11 +90,9 @@ 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) { +private[compiler] class ResourceComponentCompiler(compiler: RawCompiler, manager: ComponentManager, sourceJarName: 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))) @@ -128,11 +126,7 @@ private[compiler] class ResourceComponentCompiler(compiler: RawCompiler, manager 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) + AnalyzingCompiler.compileSources(Seq(sourceJar), targetJar, xsbtiJars, "bridge-from-resources", compiler, log) manager.define(binID, Seq(targetJar)) } diff --git a/sbt/src/main/scala/Main.scala b/sbt/src/main/scala/Main.scala index cf45221ef..fcf8e715b 100644 --- a/sbt/src/main/scala/Main.scala +++ b/sbt/src/main/scala/Main.scala @@ -80,7 +80,7 @@ private class StaticLauncher(appProvider: StaticAppProvider, scalaProvider: Stat } override def app(id: xsbti.ApplicationID, version: String): xsbti.AppProvider = appProvider - override def topLoader(): ClassLoader = new URLClassLoader(Array.empty) + override def topLoader(): ClassLoader = new URLClassLoader(Array.empty, null) override def globalLock(): xsbti.GlobalLock = new WeakGlobalLock override def bootDirectory(): File = new File(sys props "user.home") / ".sbt" / "boot" From 6beb8f2c85be3f73a5605f823a608a04331f0248 Mon Sep 17 00:00:00 2001 From: Martin Duhem Date: Fri, 8 Apr 2016 13:29:55 +0200 Subject: [PATCH 04/10] Tests for FakeResolver --- ivy/src/test/resources/artifact1.jar | Bin 0 -> 313 bytes ivy/src/test/resources/artifact2.txt | 0 .../scala/FakeResolverSpecification.scala | 67 ++++++++++++++++++ 3 files changed, 67 insertions(+) create mode 100644 ivy/src/test/resources/artifact1.jar create mode 100644 ivy/src/test/resources/artifact2.txt create mode 100644 ivy/src/test/scala/FakeResolverSpecification.scala diff --git a/ivy/src/test/resources/artifact1.jar b/ivy/src/test/resources/artifact1.jar new file mode 100644 index 0000000000000000000000000000000000000000..be043359eecf5f14458fb95d21a04b9bcd94a4e4 GIT binary patch literal 313 zcmWIWW@Zs#U|`^25O(bFaM{qyG!@9(0L1JJG7J%V$vKI|#i1db49rix*u$RzacKoN z10%~gt7+KnxZ_pE^%o7nR| zQ$pO>>gJ!>6SDu(oh4TmO*pbdW&uZdL0x8G(W+>*j>BRLr%#QTcH;1XLkCWsI3RKG zgaX5(*Uwd+Eq(o4J$0$tbOQ||1ID+Ej0?CXI4PWDdvdbrClAk>sN0ef=1og)HmXQR z2&f!oTryL_f-%6Gkx7mjm%k){zJq`zjUXECOIC<4(R>)-&B_K+#t4MIK>8Yp!vFwy C*jvj0 literal 0 HcmV?d00001 diff --git a/ivy/src/test/resources/artifact2.txt b/ivy/src/test/resources/artifact2.txt new file mode 100644 index 000000000..e69de29bb diff --git a/ivy/src/test/scala/FakeResolverSpecification.scala b/ivy/src/test/scala/FakeResolverSpecification.scala new file mode 100644 index 000000000..baed9cc28 --- /dev/null +++ b/ivy/src/test/scala/FakeResolverSpecification.scala @@ -0,0 +1,67 @@ +package sbt + +import java.io.File + +import org.specs2._ + +class FakeResolverSpecification extends BaseIvySpecification { + import FakeResolver._ + + def is = s2""" + This is a specification for the FakeResolver + + The FakeResolver should + find modules with only one artifact $singleArtifact + find modules with more than one artifact $multipleArtifacts + """ + + val myModule = ModuleID("org.example", "my-module", "0.0.1-SNAPSHOT", Some("compile")) + val example = ModuleID("com.example", "example", "1.0.0", Some("compile")) + + def singleArtifact = { + val m = getModule(myModule) + val report = ivyUpdate(m) + val allFiles = getAllFiles(report) + + report.allModules should haveLength(1) + report.configurations should haveLength(3) + allFiles should haveLength(1) + allFiles(1).getName should beEqualTo("artifact1-0.0.1-SNAPSHOT.jar") + } + + def multipleArtifacts = { + val m = getModule(example) + val report = ivyUpdate(m) + val allFiles = getAllFiles(report).toSet + + report.allModules should haveLength(1) + report.configurations should haveLength(3) + allFiles should haveLength(2) + allFiles map (_.getName) should beEqualTo(Set("artifact1-1.0.0.jar", "artifact2-1.0.0.txt")) + } + + private def artifact1 = new File(getClass.getResource("/artifact1.jar").toURI.getPath) + private def artifact2 = new File(getClass.getResource("/artifact2.txt").toURI.getPath) + + private def modules = Map( + ("org.example", "my-module", "0.0.1-SNAPSHOT") -> List( + FakeArtifact("artifact1", "jar", "jar", artifact1) + ), + + ("com.example", "example", "1.0.0") -> List( + FakeArtifact("artifact1", "jar", "jar", artifact1), + FakeArtifact("artifact2", "txt", "txt", artifact2) + ) + ) + + private def fakeResolver = new FakeResolver("FakeResolver", new File("tmp"), modules) + override def resolvers: Seq[Resolver] = Seq(new RawRepository(fakeResolver)) + private def getModule(myModule: ModuleID): IvySbt#Module = module(defaultModuleId, Seq(myModule), None) + private def getAllFiles(report: UpdateReport) = + for { + conf <- report.configurations + m <- conf.modules + (_, f) <- m.artifacts + } yield f + +} \ No newline at end of file From fab20c73bf083b3d91cbad0921b0dc7367af0759 Mon Sep 17 00:00:00 2001 From: Martin Duhem Date: Fri, 8 Apr 2016 17:55:38 +0200 Subject: [PATCH 05/10] Add `scala-reflect.jar` to JARs of `StaticScalaProvider` It turns out we need to have `scala-reflect.jar` on classpath to compile the compiler bridge for the static scala instance of the launcher. --- build.sbt | 2 +- sbt/src/main/scala/Main.scala | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/build.sbt b/build.sbt index afe8437c5..043984488 100644 --- a/build.sbt +++ b/build.sbt @@ -375,7 +375,7 @@ lazy val compilerIvyProj = (project in compilePath / "ivy"). 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 included = Set("scala-compiler.jar", "scala-library.jar", "scala-reflect.jar") val scalaJars = (externalDependencyClasspath in Compile).value.map(_.data).filter(j => included contains j.getName) Seq(compilerBridgeSrc, xsbtiJAR) ++ scalaJars } diff --git a/sbt/src/main/scala/Main.scala b/sbt/src/main/scala/Main.scala index fcf8e715b..2442f7a5d 100644 --- a/sbt/src/main/scala/Main.scala +++ b/sbt/src/main/scala/Main.scala @@ -20,6 +20,8 @@ private object StaticUtils { val COMPILER_JAR = "scala-compiler.jar" val LIBRARY = "library" val LIBRARY_JAR = "scala-library.jar" + val REFLECT = "reflect" + val REFLECT_JAR = "scala-reflect.jar" val XSBTI = "xsbti" val XSBTI_JAR = s"interface-${sbtApplicationID.version}.jar" val thisJAR: File = new File(getClass.getProtectionDomain().getCodeSource().getLocation().toURI().getPath()) @@ -109,7 +111,7 @@ private class StaticScalaProvider(appProvider: StaticAppProvider) extends xsbti. 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 jars(): Array[File] = Array(compilerJar, libraryJar, getComponent(StaticUtils.REFLECT)) override def loader(): ClassLoader = new URLClassLoader(jars map (_.toURI.toURL)) override def version(): String = StaticUtils.getProperty(loader, "compiler.properties", "version.number") getOrElse "unknown" } @@ -124,6 +126,10 @@ private class StaticAppProvider(appConfig: StaticAppConfiguration) extends xsbti installFromResources(StaticUtils.LIBRARY_JAR, StaticUtils.LIBRARY) } + if (components.component(StaticUtils.REFLECT).isEmpty) { + installFromResources(StaticUtils.REFLECT_JAR, StaticUtils.REFLECT) + } + if (components.component(StaticUtils.XSBTI).isEmpty) { installFromResources(StaticUtils.XSBTI_JAR, StaticUtils.XSBTI) } From 1d44420c914789a863864360e4784a5392ae5efd Mon Sep 17 00:00:00 2001 From: Martin Duhem Date: Mon, 11 Apr 2016 09:48:42 +0200 Subject: [PATCH 06/10] Comply to Ivy's specification in `FakeResolver` --- ivy/src/main/scala/sbt/FakeResolver.scala | 17 +++++++++-------- .../scala/FakeResolverSpecification.scala | 19 +++++++++++++++++++ 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/ivy/src/main/scala/sbt/FakeResolver.scala b/ivy/src/main/scala/sbt/FakeResolver.scala index 6ef728bdc..d9706fd53 100644 --- a/ivy/src/main/scala/sbt/FakeResolver.scala +++ b/ivy/src/main/scala/sbt/FakeResolver.scala @@ -59,14 +59,15 @@ private[sbt] class FakeResolver(private var name: String, cacheDir: File, module 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) + if (path.nonEmpty && localFile.exists) { + report.setLocalFile(localFile) + report.setDownloadStatus(DownloadStatus.SUCCESSFUL) + report.setSize(localFile.length) + } else { + report.setDownloadStatus(DownloadStatus.FAILED) + } report } @@ -112,7 +113,7 @@ private[sbt] class FakeResolver(private var name: String, cacheDir: File, module new ResolvedModuleRevision(this, this, moduleDescriptor, metadataReport) } - artifact getOrElse (throw new Exception(s"Could not find module $organisation % $name % $revision")) + artifact.orNull } @@ -161,7 +162,7 @@ private[sbt] class FakeResolver(private var name: String, cacheDir: File, module 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")) + artifact.orNull } diff --git a/ivy/src/test/scala/FakeResolverSpecification.scala b/ivy/src/test/scala/FakeResolverSpecification.scala index baed9cc28..fc1d9f300 100644 --- a/ivy/src/test/scala/FakeResolverSpecification.scala +++ b/ivy/src/test/scala/FakeResolverSpecification.scala @@ -13,10 +13,14 @@ class FakeResolverSpecification extends BaseIvySpecification { The FakeResolver should find modules with only one artifact $singleArtifact find modules with more than one artifact $multipleArtifacts + fail gracefully when asked for unknown modules $nonExistingModule + fail gracefully when some artifacts cannot be found $existingAndNonExistingArtifacts """ val myModule = ModuleID("org.example", "my-module", "0.0.1-SNAPSHOT", Some("compile")) val example = ModuleID("com.example", "example", "1.0.0", Some("compile")) + val anotherExample = ModuleID("com.example", "another-example", "1.0.0", Some("compile")) + val nonExisting = ModuleID("com.example", "does-not-exist", "1.2.3", Some("compile")) def singleArtifact = { val m = getModule(myModule) @@ -40,6 +44,16 @@ class FakeResolverSpecification extends BaseIvySpecification { allFiles map (_.getName) should beEqualTo(Set("artifact1-1.0.0.jar", "artifact2-1.0.0.txt")) } + def nonExistingModule = { + val m = getModule(nonExisting) + ivyUpdate(m) should throwA[ResolveException] + } + + def existingAndNonExistingArtifacts = { + val m = getModule(anotherExample) + ivyUpdate(m) should throwA[ResolveException]("download failed: com.example#another-example;1.0.0!non-existing.txt") + } + private def artifact1 = new File(getClass.getResource("/artifact1.jar").toURI.getPath) private def artifact2 = new File(getClass.getResource("/artifact2.txt").toURI.getPath) @@ -51,6 +65,11 @@ class FakeResolverSpecification extends BaseIvySpecification { ("com.example", "example", "1.0.0") -> List( FakeArtifact("artifact1", "jar", "jar", artifact1), FakeArtifact("artifact2", "txt", "txt", artifact2) + ), + + ("com.example", "another-example", "1.0.0") -> List( + FakeArtifact("artifact1", "jar", "jar", artifact1), + FakeArtifact("non-existing", "txt", "txt", new File("non-existing-file")) ) ) From 175ece423824d7428c31204e4708b0726d55d1a7 Mon Sep 17 00:00:00 2001 From: Martin Duhem Date: Fri, 15 Apr 2016 08:35:27 +0200 Subject: [PATCH 07/10] Remove `CompilerBridgeProvider` and `ResourceBridgeProvider` It turns out that we can leverage the`FakeResolver` that has been implemented to use with the static launcher, and resolve a "fake compiler bridge" using it, rather than copying it from the resources. This also has the advantage of not requiring to change the build definition. --- .../sbt/compiler/CompilerBridgeProvider.scala | 20 ------ .../sbt/compiler/ComponentCompiler.scala | 71 +++---------------- .../actions/src/main/scala/sbt/Compiler.scala | 14 +--- main/src/main/scala/sbt/ConsoleProject.scala | 4 +- main/src/main/scala/sbt/Defaults.scala | 7 +- main/src/main/scala/sbt/Keys.scala | 3 - 6 files changed, 16 insertions(+), 103 deletions(-) delete mode 100644 compile/ivy/src/main/scala/sbt/compiler/CompilerBridgeProvider.scala diff --git a/compile/ivy/src/main/scala/sbt/compiler/CompilerBridgeProvider.scala b/compile/ivy/src/main/scala/sbt/compiler/CompilerBridgeProvider.scala deleted file mode 100644 index 993a31ed4..000000000 --- a/compile/ivy/src/main/scala/sbt/compiler/CompilerBridgeProvider.scala +++ /dev/null @@ -1,20 +0,0 @@ -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. - */ -final case class ResourceBridgeProvider(sourceJarName: String) extends CompilerBridgeProvider \ No newline at end of file diff --git a/compile/ivy/src/main/scala/sbt/compiler/ComponentCompiler.scala b/compile/ivy/src/main/scala/sbt/compiler/ComponentCompiler.scala index 541d8f7b3..fa1d57f2d 100644 --- a/compile/ivy/src/main/scala/sbt/compiler/ComponentCompiler.scala +++ b/compile/ivy/src/main/scala/sbt/compiler/ComponentCompiler.scala @@ -15,7 +15,7 @@ object ComponentCompiler { val compilerInterfaceSrcID = compilerInterfaceID + srcExtension val javaVersion = System.getProperty("java.class.version") - @deprecated("Use `interfaceProvider(ComponentManager, CompilerBridgeProvider)`.", "0.13.12") + @deprecated("Use `interfaceProvider(ComponentManager, IvyConfiguration, ModuleID)`.", "0.13.10") def interfaceProvider(manager: ComponentManager): CompilerInterfaceProvider = new CompilerInterfaceProvider { def apply(scalaInstance: xsbti.compile.ScalaInstance, log: Logger): File = { @@ -26,21 +26,13 @@ object ComponentCompiler { } } - @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 interfaceProvider(manager: ComponentManager, ivyConfiguration: IvyConfiguration, sourcesModule: ModuleID): CompilerInterfaceProvider = new CompilerInterfaceProvider { def apply(scalaInstance: xsbti.compile.ScalaInstance, log: Logger): File = - 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) => - val componentCompiler = new ResourceComponentCompiler(new RawCompiler(scalaInstance, ClasspathOptions.auto, log), manager, sourceJarName, log) - log.debug("Compiling bridge source from resources for Scala " + scalaInstance.version) - componentCompiler() + { + // 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() } } } @@ -49,7 +41,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 and ResourceComponentCompiler.", "0.13.12") +@deprecated("Replaced by IvyComponentCompiler.", "0.13.10") class ComponentCompiler(compiler: RawCompiler, manager: ComponentManager) { import ComponentCompiler._ def apply(id: String): File = @@ -87,53 +79,6 @@ 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, log: Logger) { - import ComponentCompiler._ - - 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) - AnalyzingCompiler.compileSources(Seq(sourceJar), targetJar, xsbtiJars, "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. diff --git a/main/actions/src/main/scala/sbt/Compiler.scala b/main/actions/src/main/scala/sbt/Compiler.scala index 20a4ca6ca..062b11696 100644 --- a/main/actions/src/main/scala/sbt/Compiler.scala +++ b/main/actions/src/main/scala/sbt/Compiler.scala @@ -69,11 +69,7 @@ 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) @@ -85,7 +81,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, compilerBridgeProvider) + val scalac = scalaCompiler(instance, cpOptions, ivyConfiguration, sourcesModule) new Compilers(scalac, CheaterJavaTool(javac2, javac)) } @deprecated("Deprecated in favor of new sbt.compiler.javac package.", "0.13.8") @@ -100,7 +96,7 @@ object Compiler { val scalac = scalaCompiler(instance, cpOptions) new Compilers(scalac, javac) } - @deprecated("Use `scalaCompiler(ScalaInstance, ClasspathOptions, CompilerBridgeProvider)`.", "0.13.12") + @deprecated("Use `scalaCompiler(ScalaInstance, ClasspathOptions, IvyConfiguration, ModuleID)`.", "0.13.10") def scalaCompiler(instance: ScalaInstance, cpOptions: ClasspathOptions)(implicit app: AppConfiguration, log: Logger): AnalyzingCompiler = { val launcher = app.provider.scalaProvider.launcher @@ -108,15 +104,11 @@ 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, compilerBridgeProvider) + val provider = ComponentCompiler.interfaceProvider(componentManager, ivyConfiguration, sourcesModule) new AnalyzingCompiler(instance, provider, cpOptions) } diff --git a/main/src/main/scala/sbt/ConsoleProject.scala b/main/src/main/scala/sbt/ConsoleProject.scala index f444fcb4b..3d66cf552 100644 --- a/main/src/main/scala/sbt/ConsoleProject.scala +++ b/main/src/main/scala/sbt/ConsoleProject.scala @@ -16,8 +16,8 @@ object ConsoleProject { val scalaProvider = state.configuration.provider.scalaProvider ScalaInstance(scalaProvider.version, scalaProvider.launcher) } - val (_, sourcesModule) = extracted.runTask(Keys.compilerBridgeProvider, state) - val compiler = Compiler.scalaCompiler(scalaInstance, ClasspathOptions.repl, sourcesModule)(state.configuration, log) + val sourcesModule = extracted.get(Keys.scalaCompilerBridgeSource) + val compiler = Compiler.scalaCompiler(scalaInstance, ClasspathOptions.repl, ivyConf, 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 diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index 2f88004a2..8e49c6ebf 100755 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -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, IvyBridgeProvider } +import sbt.compiler.{ MixedAnalyzingCompiler, AggressiveCompile } import testing.{ Framework, Runner, AnnotatedFingerprint, SubclassFingerprint } import sys.error @@ -235,8 +235,7 @@ 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(), - compilerBridgeProvider := IvyBridgeProvider(bootIvyConfiguration.value, scalaCompilerBridgeSource.value) + scalaCompilerBridgeSource := ModuleID(xsbti.ArtifactInfo.SbtOrganization, "compiler-interface", sbtVersion.value, Some("component")).sources() ) // must be a val: duplication detected by object identity private[this] lazy val compileBaseGlobal: Seq[Setting[_]] = globalDefaults(Seq( @@ -266,7 +265,7 @@ object Defaults extends BuildCommon { } def compilersSetting = compilers := Compiler.compilers(scalaInstance.value, classpathOptions.value, javaHome.value, - compilerBridgeProvider.value)(appConfiguration.value, streams.value.log) + bootIvyConfiguration.value, scalaCompilerBridgeSource.value)(appConfiguration.value, streams.value.log) lazy val configTasks = docTaskSettings(doc) ++ inTask(compile)(compileInputsSettings) ++ configGlobal ++ compileAnalysisSettings ++ Seq( compile <<= compileTask, diff --git a/main/src/main/scala/sbt/Keys.scala b/main/src/main/scala/sbt/Keys.scala index 1f92cd219..566d8ed49 100644 --- a/main/src/main/scala/sbt/Keys.scala +++ b/main/src/main/scala/sbt/Keys.scala @@ -21,8 +21,6 @@ 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." @@ -140,7 +138,6 @@ 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) From 81e350325e562414beb2c96082ccd850530aabab Mon Sep 17 00:00:00 2001 From: Martin Duhem Date: Fri, 15 Apr 2016 16:25:52 +0200 Subject: [PATCH 08/10] Fix NPE in FakeResolver --- ivy/src/main/scala/sbt/FakeResolver.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ivy/src/main/scala/sbt/FakeResolver.scala b/ivy/src/main/scala/sbt/FakeResolver.scala index d9706fd53..197922940 100644 --- a/ivy/src/main/scala/sbt/FakeResolver.scala +++ b/ivy/src/main/scala/sbt/FakeResolver.scala @@ -57,6 +57,7 @@ private[sbt] class FakeResolver(private var name: String, cacheDir: File, module 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 val localFile = new File(path) @@ -77,7 +78,7 @@ private[sbt] class FakeResolver(private var name: String, cacheDir: File, module artifacts foreach { art => val artifactOrigin = locate(art) - report.addArtifactReport(download(artifactOrigin, options)) + Option(locate(art)) foreach (o => report.addArtifactReport(download(o, options))) } report From 03c02ec2e09c18230ed6aaf0237bd536eda24794 Mon Sep 17 00:00:00 2001 From: Martin Duhem Date: Fri, 15 Apr 2016 16:26:19 +0200 Subject: [PATCH 09/10] Add compiler bridge sources to fake resolver This allows sbt to resolve the compiler bridge sources when using the static launcher --- sbt/src/main/scala/Main.scala | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/sbt/src/main/scala/Main.scala b/sbt/src/main/scala/Main.scala index 2442f7a5d..6b5bc9442 100644 --- a/sbt/src/main/scala/Main.scala +++ b/sbt/src/main/scala/Main.scala @@ -22,6 +22,8 @@ private object StaticUtils { val LIBRARY_JAR = "scala-library.jar" val REFLECT = "reflect" val REFLECT_JAR = "scala-reflect.jar" + val BRIDGE = "compiler-interface" + val BRIDGE_JAR = s"compiler-interface-${sbtApplicationID.version}-sources.jar" val XSBTI = "xsbti" val XSBTI_JAR = s"interface-${sbtApplicationID.version}.jar" val thisJAR: File = new File(getClass.getProtectionDomain().getCodeSource().getLocation().toURI().getPath()) @@ -87,7 +89,7 @@ private class StaticLauncher(appProvider: StaticAppProvider, scalaProvider: Stat override def bootDirectory(): File = new File(sys props "user.home") / ".sbt" / "boot" - override def ivyRepositories(): Array[xsbti.Repository] = Array.empty + override def ivyRepositories(): Array[xsbti.Repository] = appRepositories override def appRepositories(): Array[xsbti.Repository] = Array(new FakeRepository(new FakeResolver("fakeresolver", bootDirectory / "fakeresolver-cache", modules))) override def isOverrideRepositories(): Boolean = false @@ -95,14 +97,20 @@ private class StaticLauncher(appProvider: StaticAppProvider, scalaProvider: Stat 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 lazy val modules = Map( + ("org.scala-sbt", "sbt", "0.13.12-SNAPSHOT") -> + Seq(FakeResolver.FakeArtifact("sbt", "jar", "jar", StaticUtils.thisJAR)), + + ("org.scala-sbt", "compiler-interface", "0.13.12-SNAPSHOT") -> { + val file = scalaProvider.getComponent(StaticUtils.BRIDGE) + Seq(FakeResolver.FakeArtifact("compiler-interface", "src", "jar", file)) + } ) } private class StaticScalaProvider(appProvider: StaticAppProvider) extends xsbti.ScalaProvider { - private def getComponent(componentID: String): File = { + 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) @@ -134,6 +142,10 @@ private class StaticAppProvider(appConfig: StaticAppConfiguration) extends xsbti installFromResources(StaticUtils.XSBTI_JAR, StaticUtils.XSBTI) } + if (components.component(StaticUtils.BRIDGE).isEmpty) { + installFromResources(StaticUtils.BRIDGE_JAR, StaticUtils.BRIDGE) + } + override def components(): xsbti.ComponentProvider = new StaticComponentProvider(scalaProvider.launcher.bootDirectory) override def entryPoint(): Class[_] = loader.getClass override def id(): xsbti.ApplicationID = sbtApplicationID @@ -173,4 +185,3 @@ private class StaticAppConfiguration(override val arguments: Array[String]) exte override val baseDirectory: File = new File(sys props "user.dir") override val provider: xsbti.AppProvider = new StaticAppProvider(this) } - From 3b5ab0514738ec1a56e665f000e0c82018808fa2 Mon Sep 17 00:00:00 2001 From: Martin Duhem Date: Mon, 18 Apr 2016 09:41:21 +0200 Subject: [PATCH 10/10] Don't hardcode sbt version in static launcher --- sbt/src/main/scala/Main.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sbt/src/main/scala/Main.scala b/sbt/src/main/scala/Main.scala index 6b5bc9442..da641e878 100644 --- a/sbt/src/main/scala/Main.scala +++ b/sbt/src/main/scala/Main.scala @@ -98,10 +98,10 @@ private class StaticLauncher(appProvider: StaticAppProvider, scalaProvider: Stat override def checksums(): Array[String] = Array.empty private lazy val modules = Map( - ("org.scala-sbt", "sbt", "0.13.12-SNAPSHOT") -> + ("org.scala-sbt", "sbt", sbtApplicationID.version) -> Seq(FakeResolver.FakeArtifact("sbt", "jar", "jar", StaticUtils.thisJAR)), - ("org.scala-sbt", "compiler-interface", "0.13.12-SNAPSHOT") -> { + ("org.scala-sbt", "compiler-interface", sbtApplicationID.version) -> { val file = scalaProvider.getComponent(StaticUtils.BRIDGE) Seq(FakeResolver.FakeArtifact("compiler-interface", "src", "jar", file)) }