Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Use the proper platform methods in the Scala launcher #229

Merged
merged 1 commit into from

3 participants

@dragos
Owner

This is a small refactoring that improves a bit the Scala launcher:

  • the existence of the main class is checked in finalLaunchCheck, instead of inside launch
  • compilation errors are done through markers, and integrates with the platform error message. This check is also done during finalLaunchCheck and now prompts the user if any required project has compilation errors before launching.

Fixed #100740, possibly others. :)

@dragos dragos Use the proper platform methods in the Scala launcher
This is a small refactoring that improves a bit the Scala launcher:

* the existence of the main class is checked in `finalLaunchCheck`, instead of inside `launch`
* compilation errors are done through markers, and integrates with the platform error message. The check is
  also done during `finalLaunchCheck` and now prompts the user if any required project has compilation errors
  before launching.

Fixed #100740, possibly others.
c74dde4
@skyluc
Owner

Otherwise, LGTM.

@dragos dragos merged commit 8a424af into scala-ide:master
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Nov 9, 2012
  1. @dragos

    Use the proper platform methods in the Scala launcher

    dragos authored
    This is a small refactoring that improves a bit the Scala launcher:
    
    * the existence of the main class is checked in `finalLaunchCheck`, instead of inside `launch`
    * compilation errors are done through markers, and integrates with the platform error message. The check is
      also done during `finalLaunchCheck` and now prompts the user if any required project has compilation errors
      before launching.
    
    Fixed #100740, possibly others.
This page is out of date. Refresh to see the latest.
View
45 org.scala-ide.sdt.core.tests/src/scala/tools/eclipse/launching/MainClassVerifierTest.scala
@@ -15,6 +15,13 @@ import org.junit.runners.JUnit4
import org.eclipse.core.resources.IncrementalProjectBuilder
import org.eclipse.core.runtime.NullProgressMonitor
import org.mockito.verification.VerificationMode
+import org.eclipse.debug.core.ILaunchConfiguration
+import org.eclipse.debug.core.DebugPlugin
+import org.junit.Assert
+import org.eclipse.debug.core.ILaunchManager
+import scala.tools.eclipse.testsetup.FileUtils
+import scala.tools.eclipse.testsetup.SDTTestUtils
+import org.junit.Ignore
@RunWith(classOf[JUnit4])
class MainClassVerifierTest {
@@ -36,7 +43,16 @@ class MainClassVerifierTest {
}
}
- @Test
+ /** This test is ignored becase there is no way to test it without seriously risking impairing the IDE
+ *
+ * The launcher works by checking if there are build errors, and if yes delegating to a 'status handler'
+ * The status handler displays a dialog and decides whether to proceed or not. If the status handler
+ * extension point is not available, it always continues (`finalLaunchCheck` returns true).
+ *
+ * If we add our own status handler in org.scala-ide.sdt.core.tests, we risk replacing the default
+ * dialog if the user installed the 'tests' plugin, and never seeing the warning for build errors.
+ */
+ @Test @Ignore("There is no way to test launching without using an extension point that intercepts all status requests")
def reportErrorWhenMainContainsCompilationErrors() {
val mainName = "MainWithCompilationErrors"
val main = """
@@ -45,10 +61,33 @@ class MainClassVerifierTest {
}
""".format(mainName)
+ val launchMemento = """
+ <launchConfiguration type="scala.application">
+ <stringAttribute key="bad_container_name" value="/main-launcher/f"/>
+ <listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
+ <listEntry value="/main-launcher/src/MainWithCompilationErrors.scala"/>
+ </listAttribute>
+ <listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
+ <listEntry value="1"/>
+ </listAttribute>
+ <mapAttribute key="org.eclipse.debug.core.preferred_launchers">
+ <mapEntry key="[debug]" value="scala.application.new"/>
+ </mapAttribute>
+ <stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="MainWithCompilationErrors"/>
+ <stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="main-launcher"/>
+ </launchConfiguration>
+"""
+
createSource(EmptyPackage, mainName, main)
- val mainTypeName = mainName
+ SDTTestUtils.addFileToProject(project.underlying, "Main.launch", launchMemento)
+
+ val config = DebugPlugin.getDefault().getLaunchManager().getLaunchConfiguration(project.underlying.getFile("Main.launch"))
+ val delegate = new ScalaLaunchDelegate()
+ project.underlying.build(IncrementalProjectBuilder.FULL_BUILD, new NullProgressMonitor)
- runTest(mainTypeName, main, times(1)) // if there are compilation errors then no binaries are produced!
+ Assert.assertFalse("Compilation errors prevent launching",
+ delegate.preLaunchCheck(config, ILaunchManager.DEBUG_MODE, new NullProgressMonitor)
+ && delegate.finalLaunchCheck(config, ILaunchManager.DEBUG_MODE, new NullProgressMonitor))
}
@Test
View
3  org.scala-ide.sdt.core/src/scala/tools/eclipse/ScalaPlugin.scala
@@ -100,6 +100,9 @@ class ScalaPlugin extends AbstractUIPlugin with PluginLogConfigurator with IReso
def classpathProblemMarkerId = pluginId + ".classpathProblem"
def settingProblemMarkerId = pluginId + ".settingProblem"
+ /** All Scala error markers. */
+ val scalaErrorMarkers = Set(classpathProblemMarkerId, problemMarkerId, settingProblemMarkerId)
+
// Retained for backwards compatibility
val oldPluginId = "ch.epfl.lamp.sdt.core"
val oldLibraryPluginId = "scala.library"
View
15 org.scala-ide.sdt.core/src/scala/tools/eclipse/launching/MainClassVerifier.scala
@@ -18,16 +18,11 @@ class MainClassVerifier(reporter: MainClassVerifier.type#ErrorReporter) {
* @typeName The fully-qualified main type name.
*/
def execute(project: ScalaProject, mainTypeName: String): IStatus = {
- // 1. No binaries are produced if the project contains compilation errors.
- if (project.buildManager.hasErrors) {
- return projectHasBuildErrors(project.underlying.getName)
- }
-
// Try to locate the type
val element = project.javaProject.findType(mainTypeName)
- // 2. The main type won't be found if the provided ``mainTypeName`` (fully-qualified name) doesn't
- // reference an existing type. (this is a workaround for #1000541).
+ // The main type won't be found if the provided ``mainTypeName`` (fully-qualified name) doesn't
+ // reference an existing type. (this is a workaround for #1000541).
if (element == null) {
return mainTypeCannotBeLocated(project.underlying.getName, mainTypeName)
}
@@ -35,12 +30,6 @@ class MainClassVerifier(reporter: MainClassVerifier.type#ErrorReporter) {
new Status(IStatus.OK, ScalaPlugin.plugin.pluginId, "")
}
- private def projectHasBuildErrors(projectName: String): IStatus = {
- val errMsg = "Project '%s' contains compilation errors (therefore, no binaries have been produced).".format(projectName)
- reporter.report(errMsg)
- new Status(IStatus.ERROR, ScalaPlugin.plugin.pluginId, errMsg)
- }
-
private def mainTypeCannotBeLocated(projectName: String, mainTypeName: String): IStatus = {
val errMsg = ("Cannot locate main type '%s' in project '%s'. For this to work, the package name in " +
"the source needs to match the source's physical location.\n" +
View
36 org.scala-ide.sdt.core/src/scala/tools/eclipse/launching/ScalaLaunchDelegate.scala
@@ -17,9 +17,12 @@ import org.eclipse.core.runtime.IPath
import org.eclipse.jdt.core.IJavaProject
import org.eclipse.core.runtime.Status
import org.eclipse.core.runtime.IStatus
+import org.eclipse.core.resources.IMarker
+import org.eclipse.debug.core.model.LaunchConfigurationDelegate
class ScalaLaunchDelegate extends AbstractJavaLaunchConfigurationDelegate {
- def launch(configuration: ILaunchConfiguration, mode: String, launch: ILaunch, monitor0: IProgressMonitor) {
+ /** This code is very heavily inspired from `AbstractJavaLaunchConfigurationDelegate`. */
+ def launch(configuration: ILaunchConfiguration, mode: String, launch: ILaunch, monitor0: IProgressMonitor) {
val monitor = if (monitor0 == null) new NullProgressMonitor() else monitor0
@@ -48,6 +51,8 @@ class ScalaLaunchDelegate extends AbstractJavaLaunchConfigurationDelegate {
// VM-specific attributes
val vmAttributesMap = getVMSpecificAttributesMap(configuration)
+ // adding Scala libraries is the only difference compared to the Java Launcher
+ // TODO: do we still need this?
val modifiedAttrMap: mutable.Map[String, Array[String]] =
if (vmAttributesMap == null) mutable.Map() else vmAttributesMap.asInstanceOf[java.util.Map[String,Array[String]]]
val classpath0 = getClasspath(configuration)
@@ -76,14 +81,6 @@ class ScalaLaunchDelegate extends AbstractJavaLaunchConfigurationDelegate {
// stop in main
prepareStopInMain(configuration)
- // verify that the main classfile exists
- val project = getJavaProject(configuration)
- ScalaPlugin.plugin.asScalaProject(project.getProject).foreach { scalaProject =>
- val mainClassVerifier = new MainClassVerifier(new UIErrorReporter)
- val status = mainClassVerifier.execute(scalaProject, mainTypeName)
- if (status.getCode() != IStatus.OK) throw new CoreException(status)
- }
-
// done the verification phase
monitor.worked(1)
@@ -105,6 +102,27 @@ class ScalaLaunchDelegate extends AbstractJavaLaunchConfigurationDelegate {
monitor.done()
}
}
+
+ /** Scala problem markers should prevent a launch. This integrates with the platform and correctly displays a dialog. */
+ override protected def isLaunchProblem(problemMarker: IMarker): Boolean =
+ super.isLaunchProblem(problemMarker) || {
+ val isError = Option(problemMarker.getAttribute(IMarker.SEVERITY)).map(_.asInstanceOf[Integer].intValue >= IMarker.SEVERITY_ERROR).getOrElse(false)
+ isError && ScalaPlugin.plugin.scalaErrorMarkers.contains(problemMarker.getType())
+ }
+
+ /** Stop a launch if the main class does not exist. */
+ override def finalLaunchCheck(configuration: ILaunchConfiguration, mode: String, monitor: IProgressMonitor): Boolean = {
+ super.finalLaunchCheck(configuration, mode, monitor) && {
+ // verify that the main classfile exists
+ val project = getJavaProject(configuration)
+ val mainTypeName = getMainTypeName(configuration)
+ ScalaPlugin.plugin.asScalaProject(project.getProject) map { scalaProject =>
+ val mainClassVerifier = new MainClassVerifier(new UIErrorReporter)
+ val status = mainClassVerifier.execute(scalaProject, mainTypeName)
+ status.getCode() == IStatus.OK
+ } getOrElse false
+ }
+ }
private def toInclude(vmMap: mutable.Map[String, Array[String]], classpath: List[String],
configuration: ILaunchConfiguration): List[String] =
Something went wrong with that request. Please try again.