Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge pull request #112 from dragos/issue/separate-runtime-compiletim…

…e-lib-1000729

Correctly identify the Scala library on the class path, and pass it on t...
  • Loading branch information...
commit 0e901c70668c402a344dc4a31cc4781e1a729de6 2 parents b748d8b + aad11f0
@dragos dragos authored
View
43 org.scala-ide.sdt.core.tests/src/scala/tools/eclipse/sbtbuilder/SbtBuilderTest.scala
@@ -20,6 +20,8 @@ import scala.tools.eclipse.javaelements.ScalaSourceFile
import scala.util.matching.Regex
import testsetup._
import org.eclipse.core.runtime.Path
+import org.eclipse.jdt.core.JavaCore
+import scala.tools.eclipse.buildmanager.sbtintegration._
object SbtBuilderTest extends TestProjectSetup("builder") with CustomAssertion
object depProject extends TestProjectSetup("builder-sub")
@@ -53,7 +55,7 @@ class SbtBuilderTest {
println("building " + depProject)
depProject.project.clean(new NullProgressMonitor())
depProject.project.underlying.build(IncrementalProjectBuilder.FULL_BUILD, new NullProgressMonitor)
-
+
val targetResource = depProject.project.javaProject.getOutputLocation().append(new Path("resource.txt"))
val file = ScalaPlugin.plugin.workspaceRoot.findMember(targetResource)
Assert.assertNotNull("Resource has been copied to the output directory", file ne null)
@@ -109,7 +111,7 @@ class SbtBuilderTest {
/** Where we look for errors. */
val unitsToWatch = compilationUnits("test/ja/JClassA.java", "test/sc/ClassA.scala", "test/dependency/FooClient.scala").toList
-
+
private def getProblemMarkers(): List[IMarker] = {
unitsToWatch.flatMap(SDTTestUtils.findProblemMarkers)
}
@@ -130,6 +132,43 @@ class SbtBuilderTest {
assertNoErrors(fooClientCU)
}
+ @Test def scalaLibrary_shouldBe_on_BootClasspath() {
+ import SDTTestUtils._
+
+ val Seq(prjClient) = createProjects("client")
+
+ Assert.assertTrue("Found Scala library", prjClient.scalaClasspath.scalaLib.isDefined)
+ val basicConf = new BasicConfiguration(prjClient, ScalaCompilerConf.deployedInstance)
+ val args = basicConf.buildArguments(Seq())
+ Assert.assertTrue("BasicConfiguration bootclasspath " + args, args.mkString(" ").contains("-bootclasspath %s".format(prjClient.scalaClasspath.scalaLib.get.toFile.getAbsolutePath)))
+ deleteProjects(prjClient)
+ }
+
+ @Test def scalaLibrary_in_dependent_project_shouldBe_on_BootClasspath() {
+ import SDTTestUtils._
+
+ val Seq(prjClient, prjLib) = createProjects("client", "library")
+ val Seq(packClient, packLib) = Seq(prjClient, prjLib).map(createSourcePackage("scala"))
+ val baseRawClasspath= prjClient.javaProject.getRawClasspath()
+
+ /* The classpath, with the eclipse scala container removed. */
+ def cleanRawClasspath = for (
+ classpathEntry <- baseRawClasspath if classpathEntry.getPath().toPortableString() != "org.scala-ide.sdt.launching.SCALA_CONTAINER"
+ ) yield classpathEntry
+
+ prjClient.javaProject.setRawClasspath(cleanRawClasspath, null)
+
+ packLib.createCompilationUnit("Predef.scala", "object Predef", true, null)
+ addToClasspath(prjClient, JavaCore.newProjectEntry(prjLib.underlying.getFullPath, true))
+
+ Assert.assertTrue("Found Scala library", prjClient.scalaClasspath.scalaLib.isDefined)
+ Assert.assertEquals("Unexpected Scala lib", new Path("/library/src"), prjClient.scalaClasspath.scalaLib.get)
+ val basicConf = new BasicConfiguration(prjClient, ScalaCompilerConf.deployedInstance)
+ val args = basicConf.buildArguments(Seq())
+ Assert.assertTrue("BasicConfiguration bootclasspath " + args, args.mkString(" ").contains("-bootclasspath /library/src"))
+ deleteProjects(prjClient, prjLib)
+ }
+
/** Returns true if the expected regular expression matches the given error message. */
private def similarErrorMessage(msg: String)(expected: String): Boolean = {
msg.matches(expected)
View
30 org.scala-ide.sdt.core/src/scala/tools/eclipse/ClasspathManagement.scala
@@ -32,7 +32,7 @@ import scala.tools.eclipse.logging.HasLogger
*
* The Scala compiler needs these entries to be separated for proper setup.
*/
-class ScalaClasspath(val jdkPaths: Seq[IPath], // JDK classpath
+case class ScalaClasspath(val jdkPaths: Seq[IPath], // JDK classpath
val scalaLib: Option[IPath], // scala library
val userCp: Seq[IPath], // user classpath, excluding the Scala library and JDK
val scalaVersion: Option[String]) {
@@ -137,6 +137,16 @@ trait ClasspathManagement extends HasLogger { self: ScalaProject =>
* this method can't rely on the compiler being present.
*/
def scalaPackageFragments: Seq[(IPackageFragmentRoot, Option[String])] = {
+ val pathToPredef = new Path("scala/Predef.class")
+
+ def isZipFileScalaLib(p: IPath): Boolean = {
+ // catch any JavaModelException and pretend it's not the scala library
+ failAsValue(classOf[JavaModelException], classOf[IOException])(false) {
+ val jarFile = JavaModelManager.getJavaModelManager().getZipFile(p)
+ jarFile.getEntry("scala/Predef.class") ne null
+ }
+ }
+
// look for all package fragment roots containing instances of scala.Predef
val fragmentRoots = new ListBuffer[(IPackageFragmentRoot, Option[String])]
@@ -145,18 +155,18 @@ trait ClasspathManagement extends HasLogger { self: ScalaProject =>
case IPackageFragmentRoot.K_BINARY =>
val resource = fragmentRoot.getUnderlyingResource
- val entry = resource match {
- case folder: IFolder => folder.findMember(new Path("scala/Predef.class"))
- case _ => // it must be a jar file
- // catch any JavaModelException and pretend it's not the scala library
- failAsValue(classOf[JavaModelException])(null) {
- val jarFile = JavaModelManager.getJavaModelManager().getZipFile(fragmentRoot.getPath())
- jarFile.getEntry("scala/Predef.class")
+ val foundIt: Boolean = resource match {
+ case folder: IFolder => folder.findMember(pathToPredef) ne null
+ case file: IFile => isZipFileScalaLib(file.getFullPath)
+ case _ =>
+ val file = fragmentRoot.getPath.toFile
+ file.exists && {
+ if (file.isFile) isZipFileScalaLib(fragmentRoot.getPath)
+ else fragmentRoot.getPath.append(pathToPredef).toFile.exists
}
}
- if (entry ne null)
- fragmentRoots += ((fragmentRoot, getVersionNumber(fragmentRoot)))
+ if (foundIt) fragmentRoots += ((fragmentRoot, getVersionNumber(fragmentRoot)))
case IPackageFragmentRoot.K_SOURCE =>
for {
View
289 ...la-ide.sdt.core/src/scala/tools/eclipse/buildmanager/sbtintegration/AnalysisCompile.scala
@@ -38,169 +38,144 @@ import xsbti.AnalysisCallback
import xsbti.Controller
import xsbti.Reporter
-class AnalysisCompile (conf: BasicConfiguration, bm: EclipseSbtBuildManager, contr: Controller) extends HasLogger {
- private lazy val store = bm.analysisStore
-
- def toAbstractFile(files: Seq[File]): Set[AbstractFile] =
- files.flatMap(f => EclipseResource.fromString(f.getPath)).toSet
-
- def removeSbtOutputDirs(args: List[String]) = {
- val outputOpt = "-d"
- val left = args.takeWhile(_ != outputOpt)
- val right = args.dropWhile(_ != outputOpt)
- right match {
- case d::out::rest =>
- (left:::rest).toSeq
- case _ =>
- // something is wrong
- assert(false, "Incorrect configuration for compiler arguments: " + args)
- args.toSeq
- }
+class AnalysisCompile(conf: BasicConfiguration, bm: EclipseSbtBuildManager, contr: Controller) extends HasLogger {
+ private lazy val store = bm.analysisStore
+
+ def toAbstractFile(files: Seq[File]): Set[AbstractFile] =
+ files.flatMap(f => EclipseResource.fromString(f.getPath)).toSet
+
+ def doCompile(scalac: ScalaSbtCompiler, javac: JavaEclipseCompiler,
+ sources: Seq[File], reporter: Reporter, settings: Settings,
+ compOrder: CompileOrder.Value, compOptions: Seq[String] = Nil,
+ javaSrcBases: Seq[File] = Nil, javacOptions: Seq[String] = Nil,
+ analysisMap: Map[File, Analysis] = Map.empty, maxErrors: Int = 100)(log: sbt.Logger): Analysis = {
+
+ val currentSetup = new CompileSetup(conf.outputDirectory,
+ new CompileOptions(compOptions, javacOptions),
+ scalac.scalaInstance.actualVersion, compOrder)
+
+ val getAnalysis = analysisMap.get _
+
+ val getAPI = (f: File) => {
+ val extApis = getAnalysis(f) match { case Some(a) => a.apis.external; case None => Map.empty[String, Source] }
+ extApis.get _
}
-
- def doCompile(scalac: ScalaSbtCompiler, javac: JavaEclipseCompiler,
- sources: Seq[File], reporter: Reporter, settings: Settings,
- compOrder: CompileOrder.Value, compOptions: Seq[String] = Nil,
- javaSrcBases: Seq[File] = Nil, javacOptions: Seq[String] = Nil,
- analysisMap: Map[File, Analysis] = Map.empty, maxErrors: Int = 100)(log: sbt.Logger): Analysis = {
- val currentSetup = new CompileSetup(conf.outputDirectory, new CompileOptions(compOptions, javacOptions),
- scalac.scalaInstance.actualVersion, compOrder)
- import currentSetup._
-
- val getAnalysis = analysisMap.get _
- val getAPI = (f: File) => {
- val extApis = getAnalysis(f) match { case Some(a) => a.apis.external; case None => Map.empty[String, Source] }
- extApis.get _
+ val apiOption = (api: Either[Boolean, Source]) => api.right.toOption
+
+ val entry = Locate.entry(conf.project.classpath.map(_.toFile), Locate.definesClass) // use default defineClass for now
+
+ val ((previousAnalysis, previousSetup), tm) = util.Utils.timed(extract(store.get))
+
+ logger.debug("API store loaded in %0,3d ms".format(tm))
+ logger.debug("\t" + previousAnalysis)
+
+ val compile0 = (include: Set[File], callback: AnalysisCallback) => {
+ conf.outputDirectories.foreach(IO.createDirectory)
+ val incSrc = sources.filter(include)
+ logger.info("Compiling:\n\t" + incSrc.mkString("\n\t"))
+ bm.buildingFiles(toAbstractFile(incSrc))
+ val (javaSrcs, scalaSrcs) = incSrc partition javaOnly
+
+ var scalaError: Option[xsbti.CompileFailed] = None
+
+ def throwLater() {
+ scalaError match {
+ case Some(err) => throw err
+ case _ => ()
}
- val apiOption = (api: Either[Boolean, Source]) => api.right.toOption
-
- // Resolve classpath correctly
- val compArgs = new CompilerArguments(scalac.scalaInstance,
- // do not include autoBoot becuase then bootclasspath takes
- // whatever is set by the env variable and not necessarily what was given
- // in the project definition
- ClasspathOptions(bootLibrary = true, compiler = false, extra = true, autoBoot = false, filterLibrary = true))
- val jrePath = bm.project.jdkPaths.map(_.toFile)
- val classpathWithoutJVM: Set[File] = conf.classpath.toSet -- jrePath
- val searchClasspath = classpathWithoutJVM ++ jrePath
- val entry = Locate.entry(searchClasspath.toSeq, Locate.definesClass) // use default defineClass for now
-
-
- val ((previousAnalysis, previousSetup), tm) = util.Utils.timed(extract(store.get))
-
- logger.debug("API store loaded in %0,3d ms".format(tm))
- logger.debug("\t" + previousAnalysis)
-
- val compile0 = (include: Set[File], callback: AnalysisCallback) => {
- conf.outputDirectories.foreach(IO.createDirectory)
- val incSrc = sources.filter(include)
- logger.info("Compiling:\n\t" + incSrc.mkString("\n\t"))
- bm.buildingFiles(toAbstractFile(incSrc))
- val (javaSrcs, scalaSrcs) = incSrc partition javaOnly
-
- var scalaError: Option[xsbti.CompileFailed] = None
-
- def throwLater() {
- scalaError match {
- case Some(err) => throw err
- case _ => ()
- }
- }
-
- def compileScala() =
- if(!scalaSrcs.isEmpty) {
- val sources0 = if(order == Mixed) incSrc else scalaSrcs
- val argsWithoutOutput = removeSbtOutputDirs(compArgs(sources0, classpathWithoutJVM.toSeq, conf.outputDirectory, options.options).toList)
-
- val bootClasspathArgs: String = CompilerArguments.absString(jrePath) + File.pathSeparator + scalac.scalaInstance.libraryJar.getAbsolutePath
- val arguments = Seq("-bootclasspath", bootClasspathArgs) ++ argsWithoutOutput
- settings.javabootclasspath.tryToSet(List(bootClasspathArgs)) // otherwise scala compiler ignores JDK settings
- try {
- scalac.compile(arguments, callback, maxErrors, log, contr, settings)
- } catch {
- case err: xsbti.CompileFailed =>
- scalaError = Some(err)
- }
- }
- def compileJava() =
- if(!javaSrcs.isEmpty) {
- import sbt.Path._
- val loader = ClasspathUtilities.toLoader(conf.classpath, scalac.scalaInstance.loader)
- def handleError(e: Throwable) {
- logger.debug("Error running the SBT builder on Java sources:\n " + e)
- logger.debug("Running a full Java build")
- javac.build(org.eclipse.core.resources.IncrementalProjectBuilder.FULL_BUILD)
- }
-
- try {
- def readAPI(source: File, classes: Seq[Class[_]]) { callback.api(source, sbt.ClassToAPI(classes)) }
-
- BuildManagerStore.INSTANCE.setJavaSourceFilesToCompile(javaSrcs.toArray, conf.project.underlying)
-
- sbt.classfile.Analyze(conf.outputDirectories, javaSrcs, log)(callback, loader, readAPI) {
- javac.build(org.eclipse.core.resources.IncrementalProjectBuilder.INCREMENTAL_BUILD)
- }
-
- BuildManagerStore.INSTANCE.setJavaSourceFilesToCompile(null, conf.project.underlying)
- } catch {
- case e: Throwable =>
- handleError(e)
- }
- }
-
- if(order == JavaThenScala) {
- compileJava(); compileScala()
- throwLater()
- } else {
- compileScala(); compileJava()
- // if we reached here, then it might be that compiling scala files failed but java succeded
- // it might be the case that we just want to proceed with compiling java files when scala succeded
- // this is still something that needs to be settled (in the latter case we won't see errors for java files)
- throwLater()
- }
+ }
+
+ def compileScala() =
+ if (!scalaSrcs.isEmpty) {
+ val sources0 = if (currentSetup.order == Mixed) incSrc else scalaSrcs
+ val arguments = conf.buildArguments(sources0)
+
+ try {
+ scalac.compile(arguments, callback, maxErrors, log, contr, settings)
+ } catch {
+ case err: xsbti.CompileFailed =>
+ scalaError = Some(err)
+ }
}
-
- try {
- import CompileSetup._
-
- val analysis = previousSetup match {
- case Some(previous) if equivCompileSetup.equiv(previous, currentSetup) => previousAnalysis
- case _ => Incremental.prune(sources.toSet, previousAnalysis)
+ def compileJava() =
+ if (!javaSrcs.isEmpty) {
+ import sbt.Path._
+ val loader = ClasspathUtilities.toLoader(conf.project.classpath.map(_.toFile), scalac.scalaInstance.loader)
+ def handleError(e: Throwable) {
+ logger.debug("Error running the SBT builder on Java sources:\n " + e)
+ logger.debug("Running a full Java build")
+ javac.build(org.eclipse.core.resources.IncrementalProjectBuilder.FULL_BUILD)
}
-
- // Seems ok to just provide conf.outputDirectory
- val (modified, result) : (Boolean, Analysis) =
- IncrementalCompile(sources.toSet, entry, compile0, analysis, getAnalysis, conf.outputDirectory, log)
-
- // Store if necessary
- logger.info("Compilation was successful")
- //logger.info("Modified: " + modified + " Analysis: " + result + " apis " + result.apis)
- if (modified) {
- store.set(result, currentSetup)
+
+ try {
+ def readAPI(source: File, classes: Seq[Class[_]]) { callback.api(source, sbt.ClassToAPI(classes)) }
+
+ BuildManagerStore.INSTANCE.setJavaSourceFilesToCompile(javaSrcs.toArray, conf.project.underlying)
+
+ sbt.classfile.Analyze(conf.outputDirectories, javaSrcs, log)(callback, loader, readAPI) {
+ javac.build(org.eclipse.core.resources.IncrementalProjectBuilder.INCREMENTAL_BUILD)
+ }
+
+ BuildManagerStore.INSTANCE.setJavaSourceFilesToCompile(null, conf.project.underlying)
+ } catch {
+ case e: Throwable =>
+ handleError(e)
}
- result
- } catch {
- case e: xsbti.CompileFailed =>
- logger.info("Compilation failed")
- null
- case ex @ MissingRequirementError(required) =>
- reporter.log(SbtConverter.convertToSbt(NoPosition), "could not find a required class (incomplete classpath?): " + required, xsbti.Severity.Error)
- null
-
- case ex =>
- eclipseLog.error("Crash in the build compiler.", ex)
- reporter.log(SbtConverter.convertToSbt(NoPosition), "The SBT builder crashed while compiling your project. This is a bug in the Scala compiler or SBT. Check the Erorr Log for details. The error message is: " + ex.getMessage(), xsbti.Severity.Error)
- null
-
}
+
+ if (currentSetup.order == JavaThenScala) {
+ compileJava(); compileScala()
+ throwLater()
+ } else {
+ compileScala(); compileJava()
+ // if we reached here, then it might be that compiling scala files failed but java succeded
+ // it might be the case that we just want to proceed with compiling java files when scala succeded
+ // this is still something that needs to be settled (in the latter case we won't see errors for java files)
+ throwLater()
+ }
}
-
- private def extract(previous: Option[(Analysis, CompileSetup)]): (Analysis, Option[CompileSetup]) =
- previous match {
- case Some((an, setup)) =>
- (an, Some(setup))
- case None =>
- (Analysis.Empty, None)
+
+ try {
+ import CompileSetup._
+
+ val analysis = previousSetup match {
+ case Some(previous) if equivCompileSetup.equiv(previous, currentSetup) => previousAnalysis
+ case _ => Incremental.prune(sources.toSet, previousAnalysis)
}
- def javaOnly(f: File) = f.getName.endsWith(".java")
+
+ // Seems ok to just provide conf.outputDirectory
+ val (modified, result): (Boolean, Analysis) =
+ IncrementalCompile(sources.toSet, entry, compile0, analysis, getAnalysis, conf.outputDirectory, log)
+
+ // Store if necessary
+ logger.info("Compilation was successful")
+ //logger.info("Modified: " + modified + " Analysis: " + result + " apis " + result.apis)
+ if (modified) {
+ store.set(result, currentSetup)
+ }
+ result
+ } catch {
+ case e: xsbti.CompileFailed =>
+ logger.info("Compilation failed")
+ null
+ case ex @ MissingRequirementError(required) =>
+ reporter.log(SbtConverter.convertToSbt(NoPosition), "could not find a required class (incomplete classpath?): " + required, xsbti.Severity.Error)
+ null
+
+ case ex =>
+ eclipseLog.error("Crash in the build compiler.", ex)
+ reporter.log(SbtConverter.convertToSbt(NoPosition), "The SBT builder crashed while compiling your project. This is a bug in the Scala compiler or SBT. Check the Erorr Log for details. The error message is: " + ex.getMessage(), xsbti.Severity.Error)
+ null
+
+ }
+ }
+
+ private def extract(previous: Option[(Analysis, CompileSetup)]): (Analysis, Option[CompileSetup]) =
+ previous match {
+ case Some((an, setup)) =>
+ (an, Some(setup))
+ case None =>
+ (Analysis.Empty, None)
+ }
+ def javaOnly(f: File) = f.getName.endsWith(".java")
}
View
51 ...sdt.core/src/scala/tools/eclipse/buildmanager/sbtintegration/EclipseSbtBuildManager.scala
@@ -33,6 +33,18 @@ object CompileOrderMapper {
}
}
+/** An Eclipse builder using the Sbt engine.
+ *
+ * Unlike the command line Sbt, this builder always instantiates the
+ * compiler that is shipped with Eclipse. This is by-design, since our Sbt engine
+ * should not download any artifacts (other versions of the compiler), therefore
+ * it can only use the one deployed inside Eclipse. This can be improved in the future.
+ *
+ * The classpath is handled by delegating to the underlying project. That means
+ * a valid Scala library has to exist on the classpath, but it's not limited to
+ * being called 'scala-library.jar': @see [[scala.tools.eclipse.ClasspathManagement]] for
+ * how the library is resolved (it can be any jar or even an existing dependent project).
+ */
class EclipseSbtBuildManager(val project: ScalaProject, settings0: Settings)
extends EclipseBuildManager with HasLogger {
@@ -83,18 +95,6 @@ class EclipseSbtBuildManager(val project: ScalaProject, settings0: Settings)
val pendingSources = new mutable.HashSet[IFile]
- /** Filter the classpath. Return the original classpath without the Scala library jar.
- * The second element of the tuple contains the Scala library jar.
- */
- private def filterOutScalaLibrary(l: Seq[IPath]): (Seq[IPath], Option[IPath]) = {
- val jars = l.partition(p => p.lastSegment() == ScalaCompilerConf.LIBRARY_SUFFIX)
-
- // make sure the library file exists on disk. You can have several scala-library.jar entries in
- // the classpath, coming from MANIFEST.MF (ClassPath: entry) expansion of other jars.
- // Such jars may not exist on disk, though.
- (jars._2, jars._1.find(p => p.lastSegment() == ScalaCompilerConf.LIBRARY_SUFFIX && p.toFile().exists()))
- }
-
lazy val scalaVersion = {
// For the moment fixed to 2.9.0
ScalaPlugin.plugin.scalaVer
@@ -144,12 +144,12 @@ class EclipseSbtBuildManager(val project: ScalaProject, settings0: Settings)
private def runCompiler(sources: Seq[File]) {
// setup the settings
- val allJarsAndLibrary = filterOutScalaLibrary(project.classpath)
+ val ScalaClasspath(jdkPaths, scalaLib, userCp, _) = project.scalaClasspath
+
// Fixed 2.9 for now
- val libJar = allJarsAndLibrary match {
- case (_, Some(lib)) =>
- lib.toFile()
- case (_, None) =>
+ val libJar = scalaLib match {
+ case Some(lib) => lib.toFile()
+ case None =>
logger.info("Cannot find Scala library on the classpath. Verify your build path! Using default library corresponding to the compiler")
//ScalaPlugin.plugin.sbtScalaLib.get.toFile
val e = new Exception("Cannot find Scala library on the classpath. Verify your build path!")
@@ -159,17 +159,16 @@ class EclipseSbtBuildManager(val project: ScalaProject, settings0: Settings)
}
//val compJar = ScalaPlugin.plugin.sbtScalaCompiler
val compJar = ScalaPlugin.plugin.compilerClasses
+ val runningLibJar = ScalaPlugin.plugin.libClasses
// TODO pull the actual version from properties and select the correct one
val compInterfaceJar = ScalaPlugin.plugin.sbtCompilerInterface
- val (scalac, javac) = compilers(libJar, compJar.get.toFile, compInterfaceJar.get.toFile)
- // read settings properly
- //val cp = disintegrateClasspath(settings.classpath.value)
- val cp = allJarsAndLibrary._1.map(_.toFile)
- val conf = new BasicConfiguration(project, Seq(scalac.scalaInstance.libraryJar/*, compInterfaceJar.get.toFile*/) ++ cp)
+ // the Scala instance is always using the shipped Scala library/compiler
+ val (scalac, javac) = compilers(runningLibJar.get.toFile, compJar.get.toFile, compInterfaceJar.get.toFile)
+// val conf = new BasicConfiguration(project, scalac.scalaInstance, Seq(scalac.scalaInstance.libraryJar/*, compInterfaceJar.get.toFile*/) ++ cp)
+ val conf = new BasicConfiguration(project, scalac.scalaInstance)
val analysisComp = new AnalysisCompile(conf, this, new SbtProgress())
-
val extraAnalysis = upstreamAnalysis(project)
logger.debug("Retrieved the following upstream analysis: " + extraAnalysis)
@@ -221,9 +220,9 @@ class EclipseSbtBuildManager(val project: ScalaProject, settings0: Settings)
def saveTo(file: AbstractFile, fromFile: AbstractFile => String) {}
def clean(implicit monitor: IProgressMonitor) {
- val dummy = new BasicConfiguration(project, Seq())
- dummy.cacheLocation.refreshLocal(IResource.DEPTH_ZERO, null)
- dummy.cacheLocation.delete(true, false, monitor)
+ val cacheLocation = ScalaCompilerConf.cacheLocation(project.underlying)
+ cacheLocation.refreshLocal(IResource.DEPTH_ZERO, null)
+ cacheLocation.delete(true, false, monitor)
// refresh explorer
}
def invalidateAfterLoad: Boolean = true
View
168 org.scala-ide.sdt.core/src/scala/tools/eclipse/buildmanager/sbtintegration/SbtConf.scala
@@ -3,94 +3,114 @@ package buildmanager
package sbtintegration
import sbt.{ ScalaInstance, Path }
-import xsbt.boot.{Launcher, Repository }
+import xsbt.boot.{ Launcher, Repository }
import java.io.File
import org.eclipse.core.resources.ResourcesPlugin
import scala.tools.eclipse.logging.HasLogger
import org.eclipse.core.resources.IProject
import org.eclipse.core.resources.IFile
+import scala.tools.nsc.Settings
+import sbt.compiler.CompilerArguments
+import sbt.ClasspathOptions
/** Create an sbt ScalaInstance given the library and compiler jar. An
* Sbt ScalaInstance can be used to compile source files, and encapsulates
* a classloader used to instantiate the Scala compiler.
*/
object ScalaCompilerConf {
- final val LIBRARY_SUFFIX = "scala-library.jar"
- final val COMPILER_SUFFIX = "scala-compiler.jar"
-
- final val CACHE_SUFFIX = ".cache"
-
- private val _bootdir = (new File("")).getAbsoluteFile
-
- def apply(scalaHome: File): ScalaInstance = {
- val launcher = Launcher(_bootdir, Nil)
- ScalaInstance(scalaHome, launcher)
- }
-
- def apply(libraryJar: File, compilerJar: File): ScalaInstance = {
- val repo:List[xsbti.Repository] = List(Repository.Predefined.apply(xsbti.Predefined.Local))
- val launcher = Launcher(_bootdir, repo)
- ScalaInstance(libraryJar, compilerJar, launcher)
- }
+ final val CACHE_SUFFIX = ".cache"
- def apply(version: String, libraryJar: File, compilerJar: File, extraJar: File): ScalaInstance = {
- val repo:List[xsbti.Repository] = List(Repository.Predefined.apply(xsbti.Predefined.Local))
- val launcher = Launcher(_bootdir, repo)
- ScalaInstance(version, libraryJar, compilerJar, launcher, extraJar)
- }
-
- def apply(version: String, eclipsePluginDir: File): ScalaInstance = {
- val launcher = Launcher(_bootdir, Nil)
- val libraryJar = findJar(eclipsePluginDir, LIBRARY_SUFFIX, version)
- val compilerJar = findJar(eclipsePluginDir, COMPILER_SUFFIX, version)
- //val libraryJar = ScalaPlugin.plugin.sbtScalaLib
- //val compilerJar = ScalaPlugin.plugin.sbtScalaCompiler
- ScalaInstance(libraryJar, compilerJar, launcher)
- }
-
- private def findJar(dir: File, prefix: String, version: String):File = {
- new File(dir, prefix + version + ".jar")
- }
-
- def cacheLocation(project: IProject): IFile =
- project.getFile(CACHE_SUFFIX)
+ private val _bootdir = (new File("")).getAbsoluteFile
+
+ def apply(scalaHome: File): ScalaInstance = {
+ val launcher = Launcher(_bootdir, Nil)
+ ScalaInstance(scalaHome, launcher)
+ }
+
+ def apply(libraryJar: File, compilerJar: File): ScalaInstance = {
+ val repo: List[xsbti.Repository] = List(Repository.Predefined.apply(xsbti.Predefined.Local))
+ val launcher = Launcher(_bootdir, repo)
+ ScalaInstance(libraryJar, compilerJar, launcher)
+ }
+
+ def apply(version: String, libraryJar: File, compilerJar: File, extraJar: File): ScalaInstance = {
+ val repo: List[xsbti.Repository] = List(Repository.Predefined.apply(xsbti.Predefined.Local))
+ val launcher = Launcher(_bootdir, repo)
+ ScalaInstance(version, libraryJar, compilerJar, launcher, extraJar)
+ }
+
+ def deployedInstance(): ScalaInstance = {
+ val launcher = Launcher(_bootdir, Nil)
+ ScalaInstance(ScalaPlugin.plugin.libClasses.get.toFile, ScalaPlugin.plugin.compilerClasses.get.toFile, launcher)
+ }
+
+ def cacheLocation(project: IProject): IFile =
+ project.getFile(CACHE_SUFFIX)
}
-class BasicConfiguration(
- val project: ScalaProject,
- val classpath: Seq[File]
- // If I put default None here, I get NPE
- // outputDir:Option[File]
- ) extends HasLogger {
- import Path._
-
- private final val outSuffix = "target"
-
- def cacheLocation: IFile =
- ScalaCompilerConf.cacheLocation(project.underlying)
-
- def outputDirectories: List[File] = {
- val outDirs = project.outputFolders.toList
- outDirs match {
- case Nil =>
- logger.info("[Warning] No output directory specified")
- List(project.underlying.getLocation().toFile / "default-bin")
- case dirs =>
- val root = ResourcesPlugin.getWorkspace().getRoot()
- dirs.map(dir => root.getFolder(dir).getLocation().toFile())
- }
- }
-
- def outputDirectory: File =
- // use projects directory. It doesn't really matter because this configuration
- // contains only a dummy value for sbt (though it needs to be real directory)
- project.underlying.getLocation().toFile
-
- def classesDirectory: File = {
- outputDirectory
+class BasicConfiguration(val project: ScalaProject, val scalaInstance: ScalaInstance) extends HasLogger {
+ import Path._
+
+ private final val outSuffix = "target"
+
+ def cacheLocation: IFile =
+ ScalaCompilerConf.cacheLocation(project.underlying)
+
+ def outputDirectories: List[File] = {
+ val outDirs = project.outputFolders.toList
+ outDirs match {
+ case Nil =>
+ logger.info("[Warning] No output directory specified")
+ List(project.underlying.getLocation().toFile / "default-bin")
+ case dirs =>
+ val root = ResourcesPlugin.getWorkspace().getRoot()
+ dirs.map(dir => root.getFolder(dir).getLocation().toFile())
}
-
- def fullClasspath: Seq[File] = {
- Seq(classesDirectory) ++ classpath
+ }
+
+ /** Return the arguments and Settings object that should be passed to build this project.
+ *
+ * The Scala compiler has certain pecularities regarding the classpath. In particular:
+ *
+ * - the JRE classpath has to go in `javabootclasspath`
+ * - the Scala library has to go in `bootclasspath`
+ * - setting just the `bootclasspath` without the `javabootclasspath` has no effect,
+ * because the Java runtime bootclasspath will be prepended to the Scala classpath,
+ * causing the Scala library that is used to *run* the compiler to appear on the compilation
+ * classpath *before* the one configured through `bootclasspath`.
+ */
+ def buildArguments(sources: Seq[File]): Seq[String] = {
+ val scalaClassPath = project.scalaClasspath
+
+ // Resolve classpath correctly
+ val compArgs = new CompilerArguments(scalaInstance, ClasspathOptions(bootLibrary = true, compiler = false, extra = true, autoBoot = false, filterLibrary = true))
+ val jrePath = scalaClassPath.jdkPaths.map(_.toFile)
+ val classpathWithoutJVM = scalaClassPath.userCp.map(_.toFile) // no scala library in here!
+
+ val argsWithoutOutput = removeSbtOutputDirs(compArgs(sources, classpathWithoutJVM.toSeq, outputDirectory, Seq()).toList)
+
+ val bootClasspathArgs: String = scalaClassPath.scalaLib.get.toFile.getAbsolutePath
+ Seq("-bootclasspath", bootClasspathArgs,
+ "-javabootclasspath", CompilerArguments.absString(jrePath)) ++ argsWithoutOutput
+ }
+
+ private def removeSbtOutputDirs(args: List[String]) = {
+ val (left, right) = args.span(_ != "-d")
+ right match {
+ case d :: out :: rest =>
+ (left ::: rest).toSeq
+ case _ =>
+ assert(false, "Incorrect configuration for compiler arguments: " + args)
+ args.toSeq
}
+ }
+
+ def outputDirectory: File =
+ // use projects directory. It doesn't really matter because this configuration
+ // contains only a dummy value for sbt (though it needs to be real directory)
+ project.underlying.getLocation().toFile
+
+ def classesDirectory: File = {
+ outputDirectory
+ }
}
Please sign in to comment.
Something went wrong with that request. Please try again.