diff --git a/org.scala-ide.sdt.core.tests/src/scala/tools/eclipse/TestsSuite.java b/org.scala-ide.sdt.core.tests/src/scala/tools/eclipse/TestsSuite.java
index f36b4b17a2..eae85b3fe2 100644
--- a/org.scala-ide.sdt.core.tests/src/scala/tools/eclipse/TestsSuite.java
+++ b/org.scala-ide.sdt.core.tests/src/scala/tools/eclipse/TestsSuite.java
@@ -1,5 +1,6 @@
package scala.tools.eclipse;
+import scala.tools.eclipse.classpath.ClasspathTests;
import scala.tools.eclipse.completion.CompletionTests;
import scala.tools.eclipse.hyperlinks.HyperlinkDetectorTests;
@@ -36,5 +37,6 @@
SbtBuilderTest.class,
PresentationCompilerTest.class,
OutputFoldersTest.class,
+ ClasspathTests.class,
})
class TestsSuite { }
diff --git a/org.scala-ide.sdt.core.tests/src/scala/tools/eclipse/classpath/ClasspathTests.scala b/org.scala-ide.sdt.core.tests/src/scala/tools/eclipse/classpath/ClasspathTests.scala
new file mode 100644
index 0000000000..74aa3f72d3
--- /dev/null
+++ b/org.scala-ide.sdt.core.tests/src/scala/tools/eclipse/classpath/ClasspathTests.scala
@@ -0,0 +1,244 @@
+package scala.tools.eclipse.classpath
+
+import scala.tools.eclipse.testsetup.TestProjectSetup
+import org.junit.Assert._
+import org.junit.Test
+import org.eclipse.jdt.core.JavaCore
+import org.eclipse.core.resources.IResource
+import org.eclipse.core.resources.IncrementalProjectBuilder
+import org.eclipse.core.runtime.NullProgressMonitor
+import org.eclipse.core.runtime.Path
+import org.junit.Before
+import org.eclipse.jdt.core.IClasspathEntry
+import org.eclipse.core.resources.IMarker
+import scala.tools.eclipse.ScalaPlugin
+import org.junit.After
+import org.junit.Ignore
+
+object ClasspathTests extends TestProjectSetup("classpath")
+
+class ClasspathTests {
+
+ import ClasspathTests._
+
+ /**
+ * The default classpath, with the eclipse scala container.
+ */
+ val baseRawClasspath= project.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
+
+ @After
+ def resetClasspath() {
+ setRawClasspathAndCheckMarkers(baseRawClasspath, 0, 0)
+ }
+
+ /**
+ * The scala library is defined as part of the eclipse container in the classpath (default case)
+ */
+ @Test
+ def eclipseContainerScalaLibrary() {
+ setRawClasspathAndCheckMarkers(baseRawClasspath, 0, 0)
+ }
+
+ /**
+ * No scala library defined in the classpath
+ */
+ @Test
+ def noScalaLibrary() {
+ setRawClasspathAndCheckMarkers(cleanRawClasspath, 0, 1)
+ }
+
+ /**
+ * Two scala library defined in the classpath, the eclipse container one, and one from the lib folder
+ */
+ @Test
+ def twoScalaLibraries() {
+ setRawClasspathAndCheckMarkers(baseRawClasspath :+ JavaCore.newLibraryEntry(new Path("/classpath/lib/2.10.x/scala-library.jar"), null, null), 0, 1)
+ }
+
+ /**
+ * Two scala library defined in the classpath, the eclipse container one, and one with a different name.
+ */
+ @Test
+ def twoScalaLibrariesWithDifferentName() {
+ setRawClasspathAndCheckMarkers(baseRawClasspath :+ JavaCore.newLibraryEntry(new Path("/classpath/lib/2.10.x/my-scala-library.jar"), null, null), 0, 1)
+ }
+
+ /**
+ * The scala library is defined using a classpath variable, with a different but compatible version
+ */
+ @Test
+ def usingClasspathVariable() {
+ // create a classpath variable
+ JavaCore.setClasspathVariable("CLASSPATH_TEST_LIB", new Path("/classpath/lib/" + ScalaPlugin.plugin.shortScalaVer + ".x/"), new NullProgressMonitor)
+ setRawClasspathAndCheckMarkers(cleanRawClasspath :+ JavaCore.newVariableEntry(new Path("CLASSPATH_TEST_LIB/scala-library.jar"), null, null), 1, 0)
+ }
+
+ /**
+ * The scala-library.jar from the lib folder is marked as being a different version, but compatible
+ */
+ @Test
+ def differentButCompatibleVersion() {
+ setRawClasspathAndCheckMarkers(cleanRawClasspath :+ JavaCore.newLibraryEntry(new Path("/classpath/lib/" + ScalaPlugin.plugin.shortScalaVer + ".x/scala-library.jar"), null, null), 1, 0)
+ }
+
+ /**
+ * The scala-library.jar is marked as being a different, incompatible version
+ */
+ @Test
+ def differentAndIncompatibleVersion() {
+ val newRawClasspath= cleanRawClasspath :+
+ JavaCore.newLibraryEntry(new Path("/classpath/lib/" +
+ (ScalaPlugin.plugin.shortScalaVer match {
+ case "2.8" => "2.9"
+ case "2.9" => "2.10"
+ case "2.10" => "2.8"
+ case _ =>
+ fail("Unsupported embedded scala library version " + ScalaPlugin.plugin.scalaVer +". Please update the test.")
+ ""
+ }) + ".x/scala-library.jar"), null, null)
+
+ setRawClasspathAndCheckMarkers(newRawClasspath, 0, 1)
+ }
+
+ /**
+ * The properties file in scala-library.jar doesn't contain the version information
+ */
+ @Test
+ def noVersionInPropertiesFile() {
+ setRawClasspathAndCheckMarkers(cleanRawClasspath :+ JavaCore.newLibraryEntry(new Path("/classpath/lib/noversion/scala-library.jar"), null, null), 0, 1)
+ }
+
+ /**
+ * The scala-library.jar doesn't contain a properties file.
+ */
+ @Test
+ def noPropertiesFile() {
+ setRawClasspathAndCheckMarkers(cleanRawClasspath :+ JavaCore.newLibraryEntry(new Path("/classpath/lib/noproperties/scala-library.jar"), null, null), 0, 1)
+ }
+
+ /**
+ * The library has a different name, but with a compatible version and contains scala.Predef
+ */
+ @Test
+ def differentNameWithCompatibleVersion() {
+ setRawClasspathAndCheckMarkers(cleanRawClasspath :+ JavaCore.newLibraryEntry(new Path("/classpath/lib/" + ScalaPlugin.plugin.shortScalaVer + ".x/my-scala-library.jar"), null, null), 1, 0)
+ }
+
+ /**
+ * The library has a different name, but with a compatible version and contains scala.Predef
+ */
+ @Test
+ def differentNameWithIncompatibleVersion() {
+ val newRawClasspath= cleanRawClasspath :+
+ JavaCore.newLibraryEntry(new Path("/classpath/lib/" +
+ (ScalaPlugin.plugin.shortScalaVer match {
+ case "2.8" => "2.9"
+ case "2.9" => "2.10"
+ case "2.10" => "2.8"
+ case _ =>
+ fail("Unsupported embedded scala library version " + ScalaPlugin.plugin.scalaVer +". Please update the test.")
+ ""
+ }) + ".x/my-scala-library.jar"), null, null)
+
+ setRawClasspathAndCheckMarkers(newRawClasspath, 0, 1)
+ }
+
+ /**
+ *
+ */
+
+ /**
+ * check that the error marker is kept even after a clean
+ */
+ @Test
+ def errorKeptAfterClean() {
+ setRawClasspathAndCheckMarkers(cleanRawClasspath, 0, 1)
+
+ project.underlying.build(IncrementalProjectBuilder.CLEAN_BUILD, new NullProgressMonitor)
+ project.underlying.build(IncrementalProjectBuilder.FULL_BUILD, new NullProgressMonitor)
+
+ checkMarkers(0, 1)
+ }
+
+ /**
+ * check the code is not compiled if the classpath is not right (no error reported in scala files)
+ */
+ @Test
+ def errorInClasspathStopBuild() {
+ project.underlying.build(IncrementalProjectBuilder.CLEAN_BUILD, new NullProgressMonitor)
+ project.underlying.build(IncrementalProjectBuilder.INCREMENTAL_BUILD, new NullProgressMonitor)
+
+ // no error on the project itself
+ checkMarkers(0, 0)
+
+ // two excepted code errors
+ var markers= project.underlying.findMarkers("org.scala-ide.sdt.core.problem", false, IResource.DEPTH_INFINITE)
+ assertEquals("Unexpected number of scala problems in project", 2, markers.length)
+
+ // switch to an invalid classpath
+ setRawClasspathAndCheckMarkers(cleanRawClasspath, 0, 1)
+
+ project.underlying.build(IncrementalProjectBuilder.CLEAN_BUILD, new NullProgressMonitor)
+ project.underlying.build(IncrementalProjectBuilder.INCREMENTAL_BUILD, new NullProgressMonitor)
+
+ // one error on the project
+ checkMarkers(0, 1)
+
+ // no additional code errors
+ markers= project.underlying.findMarkers("org.scala-ide.sdt.core.problem", false, IResource.DEPTH_INFINITE)
+ assertEquals("Unexpected number of scala problems in project", 1, markers.length)
+ }
+
+ /**
+ * Set the new classpath and check the number of errors and warnings attached to the project.
+ */
+ private def setRawClasspathAndCheckMarkers(newRawClasspath: Array[IClasspathEntry], expectedNbOfWarningMarker: Int, expectedNbOfErrorMarker: Int) {
+ project.javaProject.setRawClasspath(newRawClasspath, new NullProgressMonitor)
+ checkMarkers(expectedNbOfWarningMarker, expectedNbOfErrorMarker)
+ }
+
+ /**
+ * Check the number of errors and warnings attached to the project.
+ */
+ private def checkMarkers(expectedNbOfWarningMarker: Int, expectedNbOfErrorMarker: Int) {
+ val TIMEOUT= 5000
+
+ // check the classpathValid state
+ assertEquals("Unexpected classpath validity state", expectedNbOfErrorMarker == 0, project.isClasspathValid())
+
+ var nbOfWarningMarker= 0
+ var nbOfErrorMarker= 0
+
+ for (i <- 1 to (TIMEOUT / 200)) {
+ // count the markers on the project
+ nbOfWarningMarker= 0
+ nbOfErrorMarker= 0
+ for (marker <- project.underlying.findMarkers("org.scala-ide.sdt.core.problem", false, IResource.DEPTH_ZERO))
+ marker.getAttribute(IMarker.SEVERITY, 0) match {
+ case IMarker.SEVERITY_ERROR => nbOfErrorMarker+=1
+ case IMarker.SEVERITY_WARNING => nbOfWarningMarker+=1
+ case _ =>
+ }
+
+ if (nbOfWarningMarker == expectedNbOfWarningMarker && nbOfErrorMarker == expectedNbOfErrorMarker) {
+ // markers are fine, we're done
+ return
+ }
+
+ // wait a bit before trying again
+ Thread.sleep(200)
+ }
+
+ // after TIMEOUT, we didn't get the expected value
+ assertEquals("Unexpected nb of warning markers", expectedNbOfWarningMarker, nbOfWarningMarker)
+ assertEquals("Unexpected nb of error markers", expectedNbOfErrorMarker, nbOfErrorMarker)
+ }
+
+}
\ No newline at end of file
diff --git a/org.scala-ide.sdt.core.tests/test-workspace/classpath/.classpath b/org.scala-ide.sdt.core.tests/test-workspace/classpath/.classpath
new file mode 100644
index 0000000000..bc01bff4ca
--- /dev/null
+++ b/org.scala-ide.sdt.core.tests/test-workspace/classpath/.classpath
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/org.scala-ide.sdt.core.tests/test-workspace/classpath/.project b/org.scala-ide.sdt.core.tests/test-workspace/classpath/.project
new file mode 100644
index 0000000000..bc9a97d178
--- /dev/null
+++ b/org.scala-ide.sdt.core.tests/test-workspace/classpath/.project
@@ -0,0 +1,18 @@
+
+
+ classpath
+
+
+
+
+
+ org.scala-ide.sdt.core.scalabuilder
+
+
+
+
+
+ org.scala-ide.sdt.core.scalanature
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/org.scala-ide.sdt.core.tests/test-workspace/classpath/lib/2.10.x/my-scala-library.jar b/org.scala-ide.sdt.core.tests/test-workspace/classpath/lib/2.10.x/my-scala-library.jar
new file mode 100644
index 0000000000..dae1b1d591
Binary files /dev/null and b/org.scala-ide.sdt.core.tests/test-workspace/classpath/lib/2.10.x/my-scala-library.jar differ
diff --git a/org.scala-ide.sdt.core.tests/test-workspace/classpath/lib/2.10.x/scala-library.jar b/org.scala-ide.sdt.core.tests/test-workspace/classpath/lib/2.10.x/scala-library.jar
new file mode 100644
index 0000000000..dae1b1d591
Binary files /dev/null and b/org.scala-ide.sdt.core.tests/test-workspace/classpath/lib/2.10.x/scala-library.jar differ
diff --git a/org.scala-ide.sdt.core.tests/test-workspace/classpath/lib/2.8.x/my-scala-library.jar b/org.scala-ide.sdt.core.tests/test-workspace/classpath/lib/2.8.x/my-scala-library.jar
new file mode 100644
index 0000000000..2e990c53e6
Binary files /dev/null and b/org.scala-ide.sdt.core.tests/test-workspace/classpath/lib/2.8.x/my-scala-library.jar differ
diff --git a/org.scala-ide.sdt.core.tests/test-workspace/classpath/lib/2.8.x/scala-library.jar b/org.scala-ide.sdt.core.tests/test-workspace/classpath/lib/2.8.x/scala-library.jar
new file mode 100644
index 0000000000..2e990c53e6
Binary files /dev/null and b/org.scala-ide.sdt.core.tests/test-workspace/classpath/lib/2.8.x/scala-library.jar differ
diff --git a/org.scala-ide.sdt.core.tests/test-workspace/classpath/lib/2.9.x/my-scala-library.jar b/org.scala-ide.sdt.core.tests/test-workspace/classpath/lib/2.9.x/my-scala-library.jar
new file mode 100644
index 0000000000..44c2bc65e8
Binary files /dev/null and b/org.scala-ide.sdt.core.tests/test-workspace/classpath/lib/2.9.x/my-scala-library.jar differ
diff --git a/org.scala-ide.sdt.core.tests/test-workspace/classpath/lib/2.9.x/scala-library.jar b/org.scala-ide.sdt.core.tests/test-workspace/classpath/lib/2.9.x/scala-library.jar
new file mode 100644
index 0000000000..44c2bc65e8
Binary files /dev/null and b/org.scala-ide.sdt.core.tests/test-workspace/classpath/lib/2.9.x/scala-library.jar differ
diff --git a/org.scala-ide.sdt.core.tests/test-workspace/classpath/lib/noproperties/scala-library.jar b/org.scala-ide.sdt.core.tests/test-workspace/classpath/lib/noproperties/scala-library.jar
new file mode 100644
index 0000000000..75e2a37dfd
Binary files /dev/null and b/org.scala-ide.sdt.core.tests/test-workspace/classpath/lib/noproperties/scala-library.jar differ
diff --git a/org.scala-ide.sdt.core.tests/test-workspace/classpath/lib/noversion/scala-library.jar b/org.scala-ide.sdt.core.tests/test-workspace/classpath/lib/noversion/scala-library.jar
new file mode 100644
index 0000000000..d09df06d74
Binary files /dev/null and b/org.scala-ide.sdt.core.tests/test-workspace/classpath/lib/noversion/scala-library.jar differ
diff --git a/org.scala-ide.sdt.core.tests/test-workspace/classpath/src/test/Test.scala b/org.scala-ide.sdt.core.tests/test-workspace/classpath/src/test/Test.scala
new file mode 100644
index 0000000000..db194937ed
--- /dev/null
+++ b/org.scala-ide.sdt.core.tests/test-workspace/classpath/src/test/Test.scala
@@ -0,0 +1,12 @@
+package test
+
+class Test {
+
+ var x: ArrayList= null
+
+ def foo() {
+ var s= "s"
+ s= 2
+ }
+
+}
\ No newline at end of file
diff --git a/org.scala-ide.sdt.core/src/scala/tools/eclipse/ScalaBuilder.scala b/org.scala-ide.sdt.core/src/scala/tools/eclipse/ScalaBuilder.scala
index e474e9759f..d6144c205a 100644
--- a/org.scala-ide.sdt.core/src/scala/tools/eclipse/ScalaBuilder.scala
+++ b/org.scala-ide.sdt.core/src/scala/tools/eclipse/ScalaBuilder.scala
@@ -6,16 +6,18 @@
package scala.tools.eclipse
import scala.collection.mutable.HashSet
+
import java.{ lang => jl, util => ju }
+
import org.eclipse.core.resources.{ IFile, IncrementalProjectBuilder, IProject, IResource, IResourceDelta, IResourceDeltaVisitor, IResourceVisitor }
import org.eclipse.core.runtime.{ IProgressMonitor, IPath, SubMonitor }
import org.eclipse.jdt.internal.core.JavaModelManager
import org.eclipse.jdt.internal.core.builder.{ JavaBuilder, State }
+
import scala.tools.eclipse.javaelements.JDTUtils
import scala.tools.eclipse.util.{ FileUtils, ReflectionUtils }
-import util.HasLogger
-class ScalaBuilder extends IncrementalProjectBuilder with HasLogger {
+class ScalaBuilder extends IncrementalProjectBuilder {
def plugin = ScalaPlugin.plugin
private val scalaJavaBuilder = new GeneralScalaJavaBuilder
@@ -34,6 +36,12 @@ class ScalaBuilder extends IncrementalProjectBuilder with HasLogger {
override def build(kind : Int, ignored : ju.Map[_, _], monitor : IProgressMonitor) : Array[IProject] = {
import IncrementalProjectBuilder._
import buildmanager.sbtintegration.EclipseSbtBuildManager
+
+ // check the classpath
+ if (!plugin.getScalaProject(getProject).isClasspathValid()) {
+ // bail out is the classpath in not valid
+ return new Array[IProject](0)
+ }
val project = plugin.getScalaProject(getProject)
@@ -66,17 +74,8 @@ class ScalaBuilder extends IncrementalProjectBuilder with HasLogger {
// Only for sbt which is able to track external dependencies properly
project.buildManager match {
case _: EclipseSbtBuildManager =>
-
- def hasChanges(prj: IProject): Boolean = {
- val delta = getDelta(prj)
- delta == null || delta.getKind != IResourceDelta.NO_CHANGE
- }
-
- if (project.externalDepends.exists(hasChanges)) {
- // reset presentation compilers if a dependency has been rebuilt
- logger.debug("Resetting presentation compiler for %s due to dependent project change".format(project.underlying.getName()))
- project.resetPresentationCompiler
-
+ if (project.externalDepends.exists(
+ x => { val delta = getDelta(x); delta == null || delta.getKind != IResourceDelta.NO_CHANGE})) {
// in theory need to be able to identify the exact dependencies
// but this is deeply rooted inside the sbt dependency tracking mechanism
// so we just tell it to have a look at all the files
diff --git a/org.scala-ide.sdt.core/src/scala/tools/eclipse/ScalaPlugin.scala b/org.scala-ide.sdt.core/src/scala/tools/eclipse/ScalaPlugin.scala
index 32ce9f64b2..33c96b79df 100644
--- a/org.scala-ide.sdt.core/src/scala/tools/eclipse/ScalaPlugin.scala
+++ b/org.scala-ide.sdt.core/src/scala/tools/eclipse/ScalaPlugin.scala
@@ -84,7 +84,7 @@ class ScalaPlugin extends AbstractUIPlugin with IResourceChangeListener with IEl
val javaFileExtn = ".java"
val jarFileExtn = ".jar"
- private def cutVersion(version: String): String = {
+ def cutVersion(version: String): String = {
val pattern = "(\\d)\\.(\\d+)\\..*".r
version match {
case pattern(major, minor)=>
@@ -135,12 +135,12 @@ class ScalaPlugin extends AbstractUIPlugin with IResourceChangeListener with IEl
if (!headlessMode) {
ResourcesPlugin.getWorkspace.addResourceChangeListener(this, IResourceChangeEvent.PRE_CLOSE)
- JavaCore.addElementChangedListener(this)
PlatformUI.getWorkbench.getEditorRegistry.setDefaultEditor("*.scala", editorId)
ScalaPlugin.getWorkbenchWindow map (_.getPartService().addPartListener(ScalaPlugin.this))
diagnostic.StartupDiagnostics.run
}
- logger.info("Scala compiler bundle: " + scalaCompilerBundle.getLocation)
+ JavaCore.addElementChangedListener(this)
+ println("Scala compiler bundle: " + scalaCompilerBundle.getLocation)
}
override def stop(context: BundleContext) = {
@@ -195,12 +195,29 @@ class ScalaPlugin extends AbstractUIPlugin with IResourceChangeListener with IEl
override def elementChanged(event: ElementChangedEvent) {
import scala.collection.mutable.ListBuffer
+ import IJavaElement._
+ import IJavaElementDelta._
+
+ // check if the changes are linked with the build path
+ val modelDelta= event.getDelta()
+ if (JAVA_MODEL == modelDelta.getElement().getElementType() && modelDelta.getKind() == CHANGED && (modelDelta.getFlags() & F_CHILDREN) != 0) {
+ val innerDelta= modelDelta.getAffectedChildren()(0)
+ if (innerDelta.getKind() == IJavaElementDelta.CHANGED && (innerDelta.getFlags() & IJavaElementDelta.F_RESOLVED_CLASSPATH_CHANGED) != 0) {
+ innerDelta.getElement() match {
+ case javaProject: IJavaProject => {
+ if (isScalaProject(javaProject)) {
+ getScalaProject(javaProject.getProject()).classpathHasChanged()
+ }
+ }
+ case _ =>
+ }
+ }
+ }
+
+ // process deleted files
val buff = new ListBuffer[ScalaSourceFile]
def findRemovedSources(delta: IJavaElementDelta) {
- import IJavaElement._
- import IJavaElementDelta._
-
val isChanged = delta.getKind == CHANGED
val isRemoved = delta.getKind == REMOVED
def hasFlag(flag: Int) = (delta.getFlags & flag) != 0
diff --git a/org.scala-ide.sdt.core/src/scala/tools/eclipse/ScalaProject.scala b/org.scala-ide.sdt.core/src/scala/tools/eclipse/ScalaProject.scala
index 583efd130d..f78c5a62e3 100644
--- a/org.scala-ide.sdt.core/src/scala/tools/eclipse/ScalaProject.scala
+++ b/org.scala-ide.sdt.core/src/scala/tools/eclipse/ScalaProject.scala
@@ -25,9 +25,13 @@ import util.SWTUtils.asyncExec
import EclipseUtils.workspaceRunnableIn
import scala.tools.eclipse.properties.CompilerSettings
import scala.tools.eclipse.util.HasLogger
+import scala.collection.mutable.ListBuffer
+import scala.actors.Actor
+import org.eclipse.jdt.core.IJarEntryResource
+import java.util.Properties
+import org.eclipse.jdt.core.IPackageFragmentRoot
-
-trait BuildSuccessListener {
+trait BuildSuccessListener {
def buildSuccessful(): Unit
}
@@ -37,8 +41,10 @@ class ScalaProject(val underlying: IProject) extends HasLogger {
private var classpathUpdate: Long = IResource.NULL_STAMP
private var buildManager0: EclipseBuildManager = null
private var hasBeenBuilt = false
- private val resetPendingLock = new Object
- private var resetPending = false
+
+ private var classpathCheckLock= new Object
+ private var classpathHasBeenChecked= false
+ private var classpathValid= false;
private val buildListeners = new mutable.HashSet[BuildSuccessListener]
@@ -48,7 +54,6 @@ class ScalaProject(val underlying: IProject) extends HasLogger {
private val presentationCompiler = new Cached[Option[ScalaPresentationCompiler]] {
override def create() = {
- checkClasspathTimeStamp(shouldReset = false)
try {
val settings = new Settings
settings.printtypes.tryToSet(Nil)
@@ -342,22 +347,121 @@ class ScalaProject(val underlying: IProject) extends HasLogger {
}
}
- /** Check if the .classpath file has been changed since the last check.
- * If the saved timestamp does not match the file timestamp, reset the
- * two compilers.
+// /** Check if the .classpath file has been changed since the last check.
+// * If the saved timestamp does not match the file timestamp, reset the
+// * two compilers.
+// */
+// def checkClasspathTimeStamp(shouldReset: Boolean): Unit = plugin.check {
+// val cp = underlying.getFile(".classpath")
+// if (cp.exists)
+// classpathUpdate match {
+// case IResource.NULL_STAMP => classpathUpdate = cp.getModificationStamp()
+// case stamp if stamp == cp.getModificationStamp() =>
+// case _ =>
+// classpathUpdate = cp.getModificationStamp()
+// if (shouldReset) resetCompilers()
+// }
+// }
+
+ /**
+ * Manage the possible classpath error/warning reported on the project.
*/
- def checkClasspathTimeStamp(shouldReset: Boolean): Unit = plugin.check {
- val cp = underlying.getFile(".classpath")
- if (cp.exists)
- classpathUpdate match {
- case IResource.NULL_STAMP => classpathUpdate = cp.getModificationStamp()
- case stamp if stamp == cp.getModificationStamp() =>
- case _ =>
- classpathUpdate = cp.getModificationStamp()
- if (shouldReset) resetCompilers()
+ private def setClasspathError(severity: Int, message: String) {
+ // set the state
+ classpathValid= severity != IMarker.SEVERITY_ERROR
+ classpathHasBeenChecked= true
+ new Thread() {
+ override def run() {
+ // clean the markers
+ underlying.deleteMarkers(plugin.problemMarkerId, false, IResource.DEPTH_ZERO)
+
+ // add a new marker if needed
+ severity match {
+ case IMarker.SEVERITY_ERROR | IMarker.SEVERITY_WARNING =>
+ val marker= underlying.createMarker(plugin.problemMarkerId)
+ marker.setAttribute(IMarker.MESSAGE, message)
+ marker.setAttribute(IMarker.SEVERITY, severity)
+ case _ =>
+ }
+
}
+ }.start()
}
-
+
+ /**
+ * Return true
if the classpath is deemed valid.
+ * Check the classpath if it has not been checked yet.
+ */
+ def isClasspathValid(): Boolean = {
+ classpathCheckLock.synchronized {
+ if (!classpathHasBeenChecked)
+ checkClasspath()
+ classpathValid
+ }
+ }
+
+ /**
+ * Check if the classpath is valid for scala.
+ * It is said valid if it contains one and only scala library jar, with a version compatible
+ * with the one from the scala-ide plug-in
+ */
+ def classpathHasChanged() {
+ classpathCheckLock.synchronized {
+ try {
+ resetCompilers()
+ // mark as in progress
+ classpathHasBeenChecked= false
+ checkClasspath()
+ }
+ }
+ }
+
+ private def checkClasspath() {
+ // look for all package fragment roots containing instances of scala.Predef
+ val fragmentRoots= new ListBuffer[IPackageFragmentRoot]
+ for (fragmentRoot <- javaProject.getAllPackageFragmentRoots()) {
+ val fragment= fragmentRoot.getPackageFragment("scala")
+ fragmentRoot.getKind() match {
+ case IPackageFragmentRoot.K_BINARY =>
+ if (fragment.getClassFile("Predef.class").exists())
+ fragmentRoots+= fragmentRoot
+ case _ => // look only in jars. SBT doesn't start without one, and refined is not really happy either
+ }
+ }
+
+ // check the found package fragment roots
+ fragmentRoots.length match {
+ case 0 => // unable to find any trace of scala library
+ setClasspathError(IMarker.SEVERITY_ERROR, "Unable to find a scala library. Please add the scala container or a scala library jar to the build path.")
+ case 1 => // one and only one, now check if the version number is contained in library.properties
+ for (resource <- fragmentRoots(0).getNonJavaResources())
+ resource match {
+ case jarEntry: IJarEntryResource if jarEntry.isFile() && "library.properties".equals(jarEntry.getName) =>
+ val properties= new Properties()
+ properties.load(jarEntry.getContents())
+ val version= properties.getProperty("version.number")
+ if (version != null && version == plugin.scalaVer) {
+ // exactly the same version, should be from the container. Perfect
+ setClasspathError(0, null)
+ } else {
+ if (version != null && plugin.cutVersion(version) == plugin.shortScalaVer) {
+ // compatible version. Still, add warning message
+ setClasspathError(IMarker.SEVERITY_WARNING, "The version of scala library found in the build path is different from the one provided by scala IDE: " + version + ". Expected: " + plugin.scalaVer + ". Make sure you know what you are doing.")
+ } else {
+ // incompatible version
+ setClasspathError(IMarker.SEVERITY_ERROR, "The version of scala library found in the build path is incompatible with the one provided by scala IDE: " + version + ". Expected: " + plugin.scalaVer + ". Please replace the scala library with the scala container or a compatible scala library jar.")
+ }
+ }
+ return
+ case _ =>
+ }
+ // no library.properties, not good
+ setClasspathError(IMarker.SEVERITY_ERROR, "The scala library found in the build path doesn't contain a library.properties file. Please replace the scala library with the scala container or a valid scala library jar")
+ case _ => // 2 or more of them, not good
+ setClasspathError(IMarker.SEVERITY_ERROR, "More than one scala library found in the build path. Please update the project build path so it contains only one scala library reference")
+ }
+ }
+
private def refreshOutput: Unit = {
val res = plugin.workspaceRoot.findMember(javaProject.getOutputLocation)
if (res ne null)
@@ -393,7 +497,7 @@ class ScalaProject(val underlying: IProject) extends HasLogger {
setting <- box.userSettings; if filter(setting)
) {
val value0 = store.getString(SettingConverterUtil.convertNameToProperty(setting.name))
-// logger.info("[%s] initializing %s to %s".format(underlying.getName(), setting.name, value0.toString))
+ logger.info("[%s] initializing %s to %s".format(underlying.getName(), setting.name, value0.toString))
try {
val value = if (setting ne settings.pluginsDir) value0 else {
ScalaPlugin.plugin.continuationsClasses map {
@@ -484,7 +588,6 @@ class ScalaProject(val underlying: IProject) extends HasLogger {
}
def buildManager = {
- checkClasspathTimeStamp(shouldReset = true)
if (buildManager0 == null) {
val settings = new Settings
initialize(settings, _ => true)
@@ -544,6 +647,10 @@ class ScalaProject(val underlying: IProject) extends HasLogger {
def clean(implicit monitor: IProgressMonitor) = {
underlying.deleteMarkers(plugin.problemMarkerId, true, IResource.DEPTH_INFINITE)
+ // mark the classpath as not checked
+ classpathCheckLock.synchronized {
+ classpathHasBeenChecked= false
+ }
resetCompilers
if (buildManager0 != null)
buildManager0.clean(monitor)