Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

re #1000631. Adding a classpath validator. Initial commit. Breaks Sbt…

…BuilderTest@dependencyTest.

Added validator methods to ScalaProject.
Added link for Java Model listener to validator when classpath is modified in ScalaPlugin.
Added check if project classpath is valid in ScalaBuilder.
Added set of tests for different configuration of the classpath.
  • Loading branch information...
commit 809cf3ba02331dd03fe1c715e4296b1b47cfd645 1 parent 17cdf11
Luc Bourlier skyluc authored

Showing 16 changed files with 446 additions and 40 deletions. Show diff stats Hide diff stats

  1. +3 1 org.scala-ide.sdt.core.tests/src/scala/tools/eclipse/TestsSuite.java
  2. +244 0 org.scala-ide.sdt.core.tests/src/scala/tools/eclipse/classpath/ClasspathTests.scala
  3. +7 0 org.scala-ide.sdt.core.tests/test-workspace/classpath/.classpath
  4. +18 0 org.scala-ide.sdt.core.tests/test-workspace/classpath/.project
  5. BIN  org.scala-ide.sdt.core.tests/test-workspace/classpath/lib/2.10.x/my-scala-library.jar
  6. BIN  org.scala-ide.sdt.core.tests/test-workspace/classpath/lib/2.10.x/scala-library.jar
  7. BIN  org.scala-ide.sdt.core.tests/test-workspace/classpath/lib/2.8.x/my-scala-library.jar
  8. BIN  org.scala-ide.sdt.core.tests/test-workspace/classpath/lib/2.8.x/scala-library.jar
  9. BIN  org.scala-ide.sdt.core.tests/test-workspace/classpath/lib/2.9.x/my-scala-library.jar
  10. BIN  org.scala-ide.sdt.core.tests/test-workspace/classpath/lib/2.9.x/scala-library.jar
  11. BIN  org.scala-ide.sdt.core.tests/test-workspace/classpath/lib/noproperties/scala-library.jar
  12. BIN  org.scala-ide.sdt.core.tests/test-workspace/classpath/lib/noversion/scala-library.jar
  13. +12 0 org.scala-ide.sdt.core.tests/test-workspace/classpath/src/test/Test.scala
  14. +12 13 org.scala-ide.sdt.core/src/scala/tools/eclipse/ScalaBuilder.scala
  15. +23 6 org.scala-ide.sdt.core/src/scala/tools/eclipse/ScalaPlugin.scala
  16. +127 20 org.scala-ide.sdt.core/src/scala/tools/eclipse/ScalaProject.scala
4 org.scala-ide.sdt.core.tests/src/scala/tools/eclipse/TestsSuite.java
... ... @@ -1,5 +1,6 @@
1 1 package scala.tools.eclipse;
2 2
  3 +import scala.tools.eclipse.classpath.ClasspathTests;
3 4 import scala.tools.eclipse.completion.CompletionTests;
4 5
5 6 import scala.tools.eclipse.hyperlinks.HyperlinkDetectorTests;
@@ -33,6 +34,7 @@
33 34 CompletionTests.class,
34 35 AbstractMethodVerifierTest.class,
35 36 SbtBuilderTest.class,
36   - PCTest.class
  37 + PCTest.class,
  38 + ClasspathTests.class
37 39 })
38 40 class TestsSuite { }
244 org.scala-ide.sdt.core.tests/src/scala/tools/eclipse/classpath/ClasspathTests.scala
... ... @@ -0,0 +1,244 @@
  1 +package scala.tools.eclipse.classpath
  2 +
  3 +import scala.tools.eclipse.testsetup.TestProjectSetup
  4 +import org.junit.Assert._
  5 +import org.junit.Test
  6 +import org.eclipse.jdt.core.JavaCore
  7 +import org.eclipse.core.resources.IResource
  8 +import org.eclipse.core.resources.IncrementalProjectBuilder
  9 +import org.eclipse.core.runtime.NullProgressMonitor
  10 +import org.eclipse.core.runtime.Path
  11 +import org.junit.Before
  12 +import org.eclipse.jdt.core.IClasspathEntry
  13 +import org.eclipse.core.resources.IMarker
  14 +import scala.tools.eclipse.ScalaPlugin
  15 +import org.junit.After
  16 +import org.junit.Ignore
  17 +
  18 +object ClasspathTests extends TestProjectSetup("classpath")
  19 +
  20 +class ClasspathTests {
  21 +
  22 + import ClasspathTests._
  23 +
  24 + /**
  25 + * The default classpath, with the eclipse scala container.
  26 + */
  27 + val baseRawClasspath= project.javaProject.getRawClasspath()
  28 +
  29 + /**
  30 + * The classpath, with the eclipse scala container removed.
  31 + */
  32 + def cleanRawClasspath= for (classpathEntry <- baseRawClasspath
  33 + if classpathEntry.getPath().toPortableString() != "org.scala-ide.sdt.launching.SCALA_CONTAINER")
  34 + yield classpathEntry
  35 +
  36 + @After
  37 + def resetClasspath() {
  38 + setRawClasspathAndCheckMarkers(baseRawClasspath, 0, 0)
  39 + }
  40 +
  41 + /**
  42 + * The scala library is defined as part of the eclipse container in the classpath (default case)
  43 + */
  44 + @Test
  45 + def eclipseContainerScalaLibrary() {
  46 + setRawClasspathAndCheckMarkers(baseRawClasspath, 0, 0)
  47 + }
  48 +
  49 + /**
  50 + * No scala library defined in the classpath
  51 + */
  52 + @Test
  53 + def noScalaLibrary() {
  54 + setRawClasspathAndCheckMarkers(cleanRawClasspath, 0, 1)
  55 + }
  56 +
  57 + /**
  58 + * Two scala library defined in the classpath, the eclipse container one, and one from the lib folder
  59 + */
  60 + @Test
  61 + def twoScalaLibraries() {
  62 + setRawClasspathAndCheckMarkers(baseRawClasspath :+ JavaCore.newLibraryEntry(new Path("/classpath/lib/2.10.x/scala-library.jar"), null, null), 0, 1)
  63 + }
  64 +
  65 + /**
  66 + * Two scala library defined in the classpath, the eclipse container one, and one with a different name.
  67 + */
  68 + @Test
  69 + def twoScalaLibrariesWithDifferentName() {
  70 + setRawClasspathAndCheckMarkers(baseRawClasspath :+ JavaCore.newLibraryEntry(new Path("/classpath/lib/2.10.x/my-scala-library.jar"), null, null), 0, 1)
  71 + }
  72 +
  73 + /**
  74 + * The scala library is defined using a classpath variable, with a different but compatible version
  75 + */
  76 + @Test
  77 + def usingClasspathVariable() {
  78 + // create a classpath variable
  79 + JavaCore.setClasspathVariable("CLASSPATH_TEST_LIB", new Path("/classpath/lib/" + ScalaPlugin.plugin.shortScalaVer + ".x/"), new NullProgressMonitor)
  80 + setRawClasspathAndCheckMarkers(cleanRawClasspath :+ JavaCore.newVariableEntry(new Path("CLASSPATH_TEST_LIB/scala-library.jar"), null, null), 1, 0)
  81 + }
  82 +
  83 + /**
  84 + * The scala-library.jar from the lib folder is marked as being a different version, but compatible
  85 + */
  86 + @Test
  87 + def differentButCompatibleVersion() {
  88 + setRawClasspathAndCheckMarkers(cleanRawClasspath :+ JavaCore.newLibraryEntry(new Path("/classpath/lib/" + ScalaPlugin.plugin.shortScalaVer + ".x/scala-library.jar"), null, null), 1, 0)
  89 + }
  90 +
  91 + /**
  92 + * The scala-library.jar is marked as being a different, incompatible version
  93 + */
  94 + @Test
  95 + def differentAndIncompatibleVersion() {
  96 + val newRawClasspath= cleanRawClasspath :+
  97 + JavaCore.newLibraryEntry(new Path("/classpath/lib/" +
  98 + (ScalaPlugin.plugin.shortScalaVer match {
  99 + case "2.8" => "2.9"
  100 + case "2.9" => "2.10"
  101 + case "2.10" => "2.8"
  102 + case _ =>
  103 + fail("Unsupported embedded scala library version " + ScalaPlugin.plugin.scalaVer +". Please update the test.")
  104 + ""
  105 + }) + ".x/scala-library.jar"), null, null)
  106 +
  107 + setRawClasspathAndCheckMarkers(newRawClasspath, 0, 1)
  108 + }
  109 +
  110 + /**
  111 + * The properties file in scala-library.jar doesn't contain the version information
  112 + */
  113 + @Test
  114 + def noVersionInPropertiesFile() {
  115 + setRawClasspathAndCheckMarkers(cleanRawClasspath :+ JavaCore.newLibraryEntry(new Path("/classpath/lib/noversion/scala-library.jar"), null, null), 0, 1)
  116 + }
  117 +
  118 + /**
  119 + * The scala-library.jar doesn't contain a properties file.
  120 + */
  121 + @Test
  122 + def noPropertiesFile() {
  123 + setRawClasspathAndCheckMarkers(cleanRawClasspath :+ JavaCore.newLibraryEntry(new Path("/classpath/lib/noproperties/scala-library.jar"), null, null), 0, 1)
  124 + }
  125 +
  126 + /**
  127 + * The library has a different name, but with a compatible version and contains scala.Predef
  128 + */
  129 + @Test
  130 + def differentNameWithCompatibleVersion() {
  131 + setRawClasspathAndCheckMarkers(cleanRawClasspath :+ JavaCore.newLibraryEntry(new Path("/classpath/lib/" + ScalaPlugin.plugin.shortScalaVer + ".x/my-scala-library.jar"), null, null), 1, 0)
  132 + }
  133 +
  134 + /**
  135 + * The library has a different name, but with a compatible version and contains scala.Predef
  136 + */
  137 + @Test
  138 + def differentNameWithIncompatibleVersion() {
  139 + val newRawClasspath= cleanRawClasspath :+
  140 + JavaCore.newLibraryEntry(new Path("/classpath/lib/" +
  141 + (ScalaPlugin.plugin.shortScalaVer match {
  142 + case "2.8" => "2.9"
  143 + case "2.9" => "2.10"
  144 + case "2.10" => "2.8"
  145 + case _ =>
  146 + fail("Unsupported embedded scala library version " + ScalaPlugin.plugin.scalaVer +". Please update the test.")
  147 + ""
  148 + }) + ".x/my-scala-library.jar"), null, null)
  149 +
  150 + setRawClasspathAndCheckMarkers(newRawClasspath, 0, 1)
  151 + }
  152 +
  153 + /**
  154 + *
  155 + */
  156 +
  157 + /**
  158 + * check that the error marker is kept even after a clean
  159 + */
  160 + @Test
  161 + def errorKeptAfterClean() {
  162 + setRawClasspathAndCheckMarkers(cleanRawClasspath, 0, 1)
  163 +
  164 + project.underlying.build(IncrementalProjectBuilder.CLEAN_BUILD, new NullProgressMonitor)
  165 + project.underlying.build(IncrementalProjectBuilder.FULL_BUILD, new NullProgressMonitor)
  166 +
  167 + checkMarkers(0, 1)
  168 + }
  169 +
  170 + /**
  171 + * check the code is not compiled if the classpath is not right (no error reported in scala files)
  172 + */
  173 + @Test
  174 + def errorInClasspathStopBuild() {
  175 + project.underlying.build(IncrementalProjectBuilder.CLEAN_BUILD, new NullProgressMonitor)
  176 + project.underlying.build(IncrementalProjectBuilder.INCREMENTAL_BUILD, new NullProgressMonitor)
  177 +
  178 + // no error on the project itself
  179 + checkMarkers(0, 0)
  180 +
  181 + // two excepted code errors
  182 + var markers= project.underlying.findMarkers("org.scala-ide.sdt.core.problem", false, IResource.DEPTH_INFINITE)
  183 + assertEquals("Unexpected number of scala problems in project", 2, markers.length)
  184 +
  185 + // switch to an invalid classpath
  186 + setRawClasspathAndCheckMarkers(cleanRawClasspath, 0, 1)
  187 +
  188 + project.underlying.build(IncrementalProjectBuilder.CLEAN_BUILD, new NullProgressMonitor)
  189 + project.underlying.build(IncrementalProjectBuilder.INCREMENTAL_BUILD, new NullProgressMonitor)
  190 +
  191 + // one error on the project
  192 + checkMarkers(0, 1)
  193 +
  194 + // no additional code errors
  195 + markers= project.underlying.findMarkers("org.scala-ide.sdt.core.problem", false, IResource.DEPTH_INFINITE)
  196 + assertEquals("Unexpected number of scala problems in project", 1, markers.length)
  197 + }
  198 +
  199 + /**
  200 + * Set the new classpath and check the number of errors and warnings attached to the project.
  201 + */
  202 + private def setRawClasspathAndCheckMarkers(newRawClasspath: Array[IClasspathEntry], expectedNbOfWarningMarker: Int, expectedNbOfErrorMarker: Int) {
  203 + project.javaProject.setRawClasspath(newRawClasspath, new NullProgressMonitor)
  204 + checkMarkers(expectedNbOfWarningMarker, expectedNbOfErrorMarker)
  205 + }
  206 +
  207 + /**
  208 + * Check the number of errors and warnings attached to the project.
  209 + */
  210 + private def checkMarkers(expectedNbOfWarningMarker: Int, expectedNbOfErrorMarker: Int) {
  211 + val TIMEOUT= 5000
  212 +
  213 + // check the classpathValid state
  214 + assertEquals("Unexpected classpath validity state", expectedNbOfErrorMarker == 0, project.isClasspathValid())
  215 +
  216 + var nbOfWarningMarker= 0
  217 + var nbOfErrorMarker= 0
  218 +
  219 + for (i <- 1 to (TIMEOUT / 200)) {
  220 + // count the markers on the project
  221 + nbOfWarningMarker= 0
  222 + nbOfErrorMarker= 0
  223 + for (marker <- project.underlying.findMarkers("org.scala-ide.sdt.core.problem", false, IResource.DEPTH_ZERO))
  224 + marker.getAttribute(IMarker.SEVERITY, 0) match {
  225 + case IMarker.SEVERITY_ERROR => nbOfErrorMarker+=1
  226 + case IMarker.SEVERITY_WARNING => nbOfWarningMarker+=1
  227 + case _ =>
  228 + }
  229 +
  230 + if (nbOfWarningMarker == expectedNbOfWarningMarker && nbOfErrorMarker == expectedNbOfErrorMarker) {
  231 + // markers are fine, we're done
  232 + return
  233 + }
  234 +
  235 + // wait a bit before trying again
  236 + Thread.sleep(200)
  237 + }
  238 +
  239 + // after TIMEOUT, we didn't get the expected value
  240 + assertEquals("Unexpected nb of warning markers", expectedNbOfWarningMarker, nbOfWarningMarker)
  241 + assertEquals("Unexpected nb of error markers", expectedNbOfErrorMarker, nbOfErrorMarker)
  242 + }
  243 +
  244 +}
7 org.scala-ide.sdt.core.tests/test-workspace/classpath/.classpath
... ... @@ -0,0 +1,7 @@
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<classpath>
  3 + <classpathentry kind="src" path="src"/>
  4 + <classpathentry kind="con" path="org.scala-ide.sdt.launching.SCALA_CONTAINER"/>
  5 + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
  6 + <classpathentry kind="output" path="bin"/>
  7 +</classpath>
18 org.scala-ide.sdt.core.tests/test-workspace/classpath/.project
... ... @@ -0,0 +1,18 @@
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<projectDescription>
  3 + <name>classpath</name>
  4 + <comment></comment>
  5 + <projects>
  6 + </projects>
  7 + <buildSpec>
  8 + <buildCommand>
  9 + <name>org.scala-ide.sdt.core.scalabuilder</name>
  10 + <arguments>
  11 + </arguments>
  12 + </buildCommand>
  13 + </buildSpec>
  14 + <natures>
  15 + <nature>org.scala-ide.sdt.core.scalanature</nature>
  16 + <nature>org.eclipse.jdt.core.javanature</nature>
  17 + </natures>
  18 +</projectDescription>
BIN  org.scala-ide.sdt.core.tests/test-workspace/classpath/lib/2.10.x/my-scala-library.jar
Binary file not shown
BIN  org.scala-ide.sdt.core.tests/test-workspace/classpath/lib/2.10.x/scala-library.jar
Binary file not shown
BIN  org.scala-ide.sdt.core.tests/test-workspace/classpath/lib/2.8.x/my-scala-library.jar
Binary file not shown
BIN  org.scala-ide.sdt.core.tests/test-workspace/classpath/lib/2.8.x/scala-library.jar
Binary file not shown
BIN  org.scala-ide.sdt.core.tests/test-workspace/classpath/lib/2.9.x/my-scala-library.jar
Binary file not shown
BIN  org.scala-ide.sdt.core.tests/test-workspace/classpath/lib/2.9.x/scala-library.jar
Binary file not shown
BIN  org.scala-ide.sdt.core.tests/test-workspace/classpath/lib/noproperties/scala-library.jar
Binary file not shown
BIN  org.scala-ide.sdt.core.tests/test-workspace/classpath/lib/noversion/scala-library.jar
Binary file not shown
12 org.scala-ide.sdt.core.tests/test-workspace/classpath/src/test/Test.scala
... ... @@ -0,0 +1,12 @@
  1 +package test
  2 +
  3 +class Test {
  4 +
  5 + var x: ArrayList= null
  6 +
  7 + def foo() {
  8 + var s= "s"
  9 + s= 2
  10 + }
  11 +
  12 +}
25 org.scala-ide.sdt.core/src/scala/tools/eclipse/ScalaBuilder.scala
@@ -6,16 +6,18 @@
6 6 package scala.tools.eclipse
7 7
8 8 import scala.collection.mutable.HashSet
  9 +
9 10 import java.{ lang => jl, util => ju }
  11 +
10 12 import org.eclipse.core.resources.{ IFile, IncrementalProjectBuilder, IProject, IResource, IResourceDelta, IResourceDeltaVisitor, IResourceVisitor }
11 13 import org.eclipse.core.runtime.{ IProgressMonitor, IPath, SubMonitor }
12 14 import org.eclipse.jdt.internal.core.JavaModelManager
13 15 import org.eclipse.jdt.internal.core.builder.{ JavaBuilder, NameEnvironment, State }
  16 +
14 17 import scala.tools.eclipse.javaelements.JDTUtils
15 18 import scala.tools.eclipse.util.{ FileUtils, ReflectionUtils }
16   -import util.HasLogger
17 19
18   -class ScalaBuilder extends IncrementalProjectBuilder with HasLogger {
  20 +class ScalaBuilder extends IncrementalProjectBuilder {
19 21 def plugin = ScalaPlugin.plugin
20 22
21 23 private val scalaJavaBuilder = new GeneralScalaJavaBuilder
@@ -34,6 +36,12 @@ class ScalaBuilder extends IncrementalProjectBuilder with HasLogger {
34 36 override def build(kind : Int, ignored : ju.Map[_, _], monitor : IProgressMonitor) : Array[IProject] = {
35 37 import IncrementalProjectBuilder._
36 38 import buildmanager.sbtintegration.EclipseSbtBuildManager
  39 +
  40 + // check the classpath
  41 + if (!plugin.getScalaProject(getProject).isClasspathValid()) {
  42 + // bail out is the classpath in not valid
  43 + return new Array[IProject](0)
  44 + }
37 45
38 46 val project = plugin.getScalaProject(getProject)
39 47
@@ -66,17 +74,8 @@ class ScalaBuilder extends IncrementalProjectBuilder with HasLogger {
66 74 // Only for sbt which is able to track external dependencies properly
67 75 project.buildManager match {
68 76 case _: EclipseSbtBuildManager =>
69   -
70   - def hasChanges(prj: IProject): Boolean = {
71   - val delta = getDelta(prj)
72   - delta == null || delta.getKind != IResourceDelta.NO_CHANGE
73   - }
74   -
75   - if (project.externalDepends.exists(hasChanges)) {
76   - // reset presentation compilers if a dependency has been rebuilt
77   - logger.debug("Resetting presentation compiler for %s due to dependent project change".format(project.underlying.getName()))
78   - project.resetPresentationCompiler
79   -
  77 + if (project.externalDepends.exists(
  78 + x => { val delta = getDelta(x); delta == null || delta.getKind != IResourceDelta.NO_CHANGE})) {
80 79 // in theory need to be able to identify the exact dependencies
81 80 // but this is deeply rooted inside the sbt dependency tracking mechanism
82 81 // so we just tell it to have a look at all the files
29 org.scala-ide.sdt.core/src/scala/tools/eclipse/ScalaPlugin.scala
@@ -84,7 +84,7 @@ class ScalaPlugin extends AbstractUIPlugin with IResourceChangeListener with IEl
84 84 val javaFileExtn = ".java"
85 85 val jarFileExtn = ".jar"
86 86
87   - private def cutVersion(version: String): String = {
  87 + def cutVersion(version: String): String = {
88 88 val pattern = "(\\d)\\.(\\d+)\\..*".r
89 89 version match {
90 90 case pattern(major, minor)=>
@@ -135,12 +135,12 @@ class ScalaPlugin extends AbstractUIPlugin with IResourceChangeListener with IEl
135 135
136 136 if (!headlessMode) {
137 137 ResourcesPlugin.getWorkspace.addResourceChangeListener(this, IResourceChangeEvent.PRE_CLOSE)
138   - JavaCore.addElementChangedListener(this)
139 138 PlatformUI.getWorkbench.getEditorRegistry.setDefaultEditor("*.scala", editorId)
140 139 ScalaPlugin.getWorkbenchWindow map (_.getPartService().addPartListener(ScalaPlugin.this))
141 140 diagnostic.StartupDiagnostics.run
142 141 }
143   - logger.info("Scala compiler bundle: " + scalaCompilerBundle.getLocation)
  142 + JavaCore.addElementChangedListener(this)
  143 + println("Scala compiler bundle: " + scalaCompilerBundle.getLocation)
144 144 }
145 145
146 146 override def stop(context: BundleContext) = {
@@ -195,12 +195,29 @@ class ScalaPlugin extends AbstractUIPlugin with IResourceChangeListener with IEl
195 195
196 196 override def elementChanged(event: ElementChangedEvent) {
197 197 import scala.collection.mutable.ListBuffer
  198 + import IJavaElement._
  199 + import IJavaElementDelta._
  200 +
  201 + // check if the changes are linked with the build path
  202 + val modelDelta= event.getDelta()
  203 + if (JAVA_MODEL == modelDelta.getElement().getElementType() && modelDelta.getKind() == CHANGED && (modelDelta.getFlags() & F_CHILDREN) != 0) {
  204 + val innerDelta= modelDelta.getAffectedChildren()(0)
  205 + if (innerDelta.getKind() == IJavaElementDelta.CHANGED && (innerDelta.getFlags() & IJavaElementDelta.F_RESOLVED_CLASSPATH_CHANGED) != 0) {
  206 + innerDelta.getElement() match {
  207 + case javaProject: IJavaProject => {
  208 + if (isScalaProject(javaProject)) {
  209 + getScalaProject(javaProject.getProject()).classpathHasChanged()
  210 + }
  211 + }
  212 + case _ =>
  213 + }
  214 + }
  215 + }
  216 +
  217 + // process deleted files
198 218 val buff = new ListBuffer[ScalaSourceFile]
199 219
200 220 def findRemovedSources(delta: IJavaElementDelta) {
201   - import IJavaElement._
202   - import IJavaElementDelta._
203   -
204 221 val isChanged = delta.getKind == CHANGED
205 222 val isRemoved = delta.getKind == REMOVED
206 223 def hasFlag(flag: Int) = (delta.getFlags & flag) != 0
147 org.scala-ide.sdt.core/src/scala/tools/eclipse/ScalaProject.scala
@@ -26,9 +26,13 @@ import util.SWTUtils.asyncExec
26 26 import EclipseUtils.workspaceRunnableIn
27 27 import scala.tools.eclipse.properties.CompilerSettings
28 28 import scala.tools.eclipse.util.HasLogger
  29 +import scala.collection.mutable.ListBuffer
  30 +import scala.actors.Actor
  31 +import org.eclipse.jdt.core.IJarEntryResource
  32 +import java.util.Properties
  33 +import org.eclipse.jdt.core.IPackageFragmentRoot
29 34
30   -
31   -trait BuildSuccessListener {
  35 +trait BuildSuccessListener {
32 36 def buildSuccessful(): Unit
33 37 }
34 38
@@ -38,8 +42,10 @@ class ScalaProject(val underlying: IProject) extends HasLogger {
38 42 private var classpathUpdate: Long = IResource.NULL_STAMP
39 43 private var buildManager0: EclipseBuildManager = null
40 44 private var hasBeenBuilt = false
41   - private val resetPendingLock = new Object
42   - private var resetPending = false
  45 +
  46 + private var classpathCheckLock= new Object
  47 + private var classpathHasBeenChecked= false
  48 + private var classpathValid= false;
43 49
44 50 private val buildListeners = new HashSet[BuildSuccessListener]
45 51
@@ -49,7 +55,6 @@ class ScalaProject(val underlying: IProject) extends HasLogger {
49 55
50 56 private val presentationCompiler = new Cached[Option[ScalaPresentationCompiler]] {
51 57 override def create() = {
52   - checkClasspathTimeStamp(shouldReset = false)
53 58 try {
54 59 val settings = new Settings
55 60 settings.printtypes.tryToSet(Nil)
@@ -341,22 +346,121 @@ class ScalaProject(val underlying: IProject) extends HasLogger {
341 346 }
342 347 }
343 348
344   - /** Check if the .classpath file has been changed since the last check.
345   - * If the saved timestamp does not match the file timestamp, reset the
346   - * two compilers.
  349 +// /** Check if the .classpath file has been changed since the last check.
  350 +// * If the saved timestamp does not match the file timestamp, reset the
  351 +// * two compilers.
  352 +// */
  353 +// def checkClasspathTimeStamp(shouldReset: Boolean): Unit = plugin.check {
  354 +// val cp = underlying.getFile(".classpath")
  355 +// if (cp.exists)
  356 +// classpathUpdate match {
  357 +// case IResource.NULL_STAMP => classpathUpdate = cp.getModificationStamp()
  358 +// case stamp if stamp == cp.getModificationStamp() =>
  359 +// case _ =>
  360 +// classpathUpdate = cp.getModificationStamp()
  361 +// if (shouldReset) resetCompilers()
  362 +// }
  363 +// }
  364 +
  365 + /**
  366 + * Manage the possible classpath error/warning reported on the project.
347 367 */
348   - def checkClasspathTimeStamp(shouldReset: Boolean): Unit = plugin.check {
349   - val cp = underlying.getFile(".classpath")
350   - if (cp.exists)
351   - classpathUpdate match {
352   - case IResource.NULL_STAMP => classpathUpdate = cp.getModificationStamp()
353   - case stamp if stamp == cp.getModificationStamp() =>
354   - case _ =>
355   - classpathUpdate = cp.getModificationStamp()
356   - if (shouldReset) resetCompilers()
  368 + private def setClasspathError(severity: Int, message: String) {
  369 + // set the state
  370 + classpathValid= severity != IMarker.SEVERITY_ERROR
  371 + classpathHasBeenChecked= true
  372 + new Thread() {
  373 + override def run() {
  374 + // clean the markers
  375 + underlying.deleteMarkers(plugin.problemMarkerId, false, IResource.DEPTH_ZERO)
  376 +
  377 + // add a new marker if needed
  378 + severity match {
  379 + case IMarker.SEVERITY_ERROR | IMarker.SEVERITY_WARNING =>
  380 + val marker= underlying.createMarker(plugin.problemMarkerId)
  381 + marker.setAttribute(IMarker.MESSAGE, message)
  382 + marker.setAttribute(IMarker.SEVERITY, severity)
  383 + case _ =>
  384 + }
  385 +
357 386 }
  387 + }.start()
358 388 }
359   -
  389 +
  390 + /**
  391 + * Return <code>true</code> if the classpath is deemed valid.
  392 + * Check the classpath if it has not been checked yet.
  393 + */
  394 + def isClasspathValid(): Boolean = {
  395 + classpathCheckLock.synchronized {
  396 + if (!classpathHasBeenChecked)
  397 + checkClasspath()
  398 + classpathValid
  399 + }
  400 + }
  401 +
  402 + /**
  403 + * Check if the classpath is valid for scala.
  404 + * It is said valid if it contains one and only scala library jar, with a version compatible
  405 + * with the one from the scala-ide plug-in
  406 + */
  407 + def classpathHasChanged() {
  408 + classpathCheckLock.synchronized {
  409 + try {
  410 + resetCompilers()
  411 + // mark as in progress
  412 + classpathHasBeenChecked= false
  413 + checkClasspath()
  414 + }
  415 + }
  416 + }
  417 +
  418 + private def checkClasspath() {
  419 + // look for all package fragment roots containing instances of scala.Predef
  420 + val fragmentRoots= new ListBuffer[IPackageFragmentRoot]
  421 + for (fragmentRoot <- javaProject.getAllPackageFragmentRoots()) {
  422 + val fragment= fragmentRoot.getPackageFragment("scala")
  423 + fragmentRoot.getKind() match {
  424 + case IPackageFragmentRoot.K_BINARY =>
  425 + if (fragment.getClassFile("Predef.class").exists())
  426 + fragmentRoots+= fragmentRoot
  427 + case _ => // look only in jars. SBT doesn't start without one, and refined is not really happy either
  428 + }
  429 + }
  430 +
  431 + // check the found package fragment roots
  432 + fragmentRoots.length match {
  433 + case 0 => // unable to find any trace of scala library
  434 + setClasspathError(IMarker.SEVERITY_ERROR, "Unable to find a scala library. Please add the scala container or a scala library jar to the build path.")
  435 + case 1 => // one and only one, now check if the version number is contained in library.properties
  436 + for (resource <- fragmentRoots(0).getNonJavaResources())
  437 + resource match {
  438 + case jarEntry: IJarEntryResource if jarEntry.isFile() && "library.properties".equals(jarEntry.getName) =>
  439 + val properties= new Properties()
  440 + properties.load(jarEntry.getContents())
  441 + val version= properties.getProperty("version.number")
  442 + if (version != null && version == plugin.scalaVer) {
  443 + // exactly the same version, should be from the container. Perfect
  444 + setClasspathError(0, null)
  445 + } else {
  446 + if (version != null && plugin.cutVersion(version) == plugin.shortScalaVer) {
  447 + // compatible version. Still, add warning message
  448 + 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.")
  449 + } else {
  450 + // incompatible version
  451 + 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.")
  452 + }
  453 + }
  454 + return
  455 + case _ =>
  456 + }
  457 + // no library.properties, not good
  458 + 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")
  459 + case _ => // 2 or more of them, not good
  460 + 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")
  461 + }
  462 + }
  463 +
360 464 private def refreshOutput: Unit = {
361 465 val res = plugin.workspaceRoot.findMember(javaProject.getOutputLocation)
362 466 if (res ne null)
@@ -388,7 +492,7 @@ class ScalaProject(val underlying: IProject) extends HasLogger {
388 492 setting <- box.userSettings; if filter(setting)
389 493 ) {
390 494 val value0 = store.getString(SettingConverterUtil.convertNameToProperty(setting.name))
391   -// logger.info("[%s] initializing %s to %s".format(underlying.getName(), setting.name, value0.toString))
  495 + logger.info("[%s] initializing %s to %s".format(underlying.getName(), setting.name, value0.toString))
392 496 try {
393 497 val value = if (setting ne settings.pluginsDir) value0 else {
394 498 ScalaPlugin.plugin.continuationsClasses map {
@@ -479,7 +583,6 @@ class ScalaProject(val underlying: IProject) extends HasLogger {
479 583 }
480 584
481 585 def buildManager = {
482   - checkClasspathTimeStamp(shouldReset = true)
483 586 if (buildManager0 == null) {
484 587 val settings = new Settings
485 588 initialize(settings, _ => true)
@@ -539,6 +642,10 @@ class ScalaProject(val underlying: IProject) extends HasLogger {
539 642
540 643 def clean(implicit monitor: IProgressMonitor) = {
541 644 underlying.deleteMarkers(plugin.problemMarkerId, true, IResource.DEPTH_INFINITE)
  645 + // mark the classpath as not checked
  646 + classpathCheckLock.synchronized {
  647 + classpathHasBeenChecked= false
  648 + }
542 649 resetCompilers
543 650 if (buildManager0 != null)
544 651 buildManager0.clean(monitor)

0 comments on commit 809cf3b

Please sign in to comment.
Something went wrong with that request. Please try again.