Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use the proper platform methods in the Scala launcher #229

Merged
merged 1 commit into from Nov 14, 2012
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -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 {
Expand All @@ -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 = """
Expand All @@ -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
Expand Down
Expand Up @@ -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"
Expand Down
Expand Up @@ -18,29 +18,18 @@ 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)
}

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" +
Expand Down
Expand Up @@ -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

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)

Expand All @@ -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] =
Expand Down