Skip to content

Commit

Permalink
Swap @main error with 'unsupported by Scala CLI'
Browse files Browse the repository at this point in the history
  • Loading branch information
MaciejG604 committed Apr 25, 2023
1 parent ed50769 commit a8a0a61
Show file tree
Hide file tree
Showing 9 changed files with 243 additions and 29 deletions.
2 changes: 1 addition & 1 deletion modules/build/src/main/scala/scala/build/Build.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1081,7 +1081,7 @@ object Build {
options.notForBloopOptions.packageOptions.packageTypeOpt.exists(_.sourceBased)
}

val success = partial || compiler.compile(project, logger)
val success = partial || compiler.compile(project, logger, generatedSources)

if (success)
Successful(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import java.nio.file.Paths

import scala.build.errors.Severity
import scala.build.internal.util.ConsoleUtils.ScalaCliConsole
import scala.build.internal.util.ErrorMessages
import scala.build.options.Scope
import scala.collection.mutable
import scala.jdk.CollectionConverters.*
Expand Down Expand Up @@ -58,7 +59,18 @@ class ConsoleBloopBuildClient(
)
val range = new bsp4j.Range(start, end)

val updatedDiag = new bsp4j.Diagnostic(range, diag.getMessage)
val message =
if (
diag.getMessage.contains(
"cannot be a main method since it cannot be accessed statically"
) &&
(originalPath.exists(_.ext == "sc") || originalPath.left.exists(_ == "snippet"))
)
ErrorMessages.mainAnnotationNotSupported
else
diag.getMessage

val updatedDiag = new bsp4j.Diagnostic(range, message)
updatedDiag.setCode(diag.getCode)
updatedDiag.setRelatedInformation(diag.getRelatedInformation)
updatedDiag.setSeverity(diag.getSeverity)
Expand Down
14 changes: 13 additions & 1 deletion modules/build/src/main/scala/scala/build/bsp/BspClient.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package scala.build.bsp

import ch.epfl.scala.{bsp4j => b}
import ch.epfl.scala.bsp4j as b

import java.lang.Boolean as JBoolean
import java.net.URI
Expand All @@ -10,6 +10,7 @@ import java.util.concurrent.{ConcurrentHashMap, ExecutorService}
import scala.build.Position.File
import scala.build.bsp.protocol.TextEdit
import scala.build.errors.{BuildException, CompositeBuildException, Diagnostic, Severity}
import scala.build.internal.util.ErrorMessages
import scala.build.postprocessing.LineConversion
import scala.build.{BloopBuildClient, GeneratedSource, Logger}
import scala.jdk.CollectionConverters.*
Expand Down Expand Up @@ -48,6 +49,17 @@ class BspClient(
val diag0 = diag.duplicate()
diag0.getRange.getStart.setLine(startLine)
diag0.getRange.getEnd.setLine(endLine)

if (
diag.getMessage.contains(
"cannot be a main method since it cannot be accessed statically"
) &&
(genSource.reportingPath.exists(
_.ext == "sc"
) || genSource.reportingPath.left.exists(_ == "snippet"))
)
diag0.setMessage(ErrorMessages.mainAnnotationNotSupported)

diag0
}
updatedDiagOpt.getOrElse(diag)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package scala.build.compiler
import ch.epfl.scala.bsp4j

import scala.annotation.tailrec
import scala.build.{Bloop, Logger, Position, Positioned, Project}
import scala.build.{Bloop, GeneratedSource, Logger, Position, Positioned, Project}
import scala.concurrent.duration.FiniteDuration

final class BloopCompiler(
Expand Down Expand Up @@ -32,7 +32,8 @@ final class BloopCompiler(

def compile(
project: Project,
logger: Logger
logger: Logger,
generatedSources: Seq[GeneratedSource]
): Boolean = {
@tailrec
def helper(remainingAttempts: Int): Boolean =
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package scala.build.compiler

import scala.build.{Logger, Positioned, Project}
import scala.build.{GeneratedSource, Logger, Positioned, Project}

trait ScalaCompiler {
def jvmVersion: Option[Positioned[Int]]
Expand All @@ -10,7 +10,8 @@ trait ScalaCompiler {
): Boolean
def compile(
project: Project,
logger: Logger
logger: Logger,
generatedSources: Seq[GeneratedSource]
): Boolean
def shutdown(): Unit

Expand Down Expand Up @@ -40,10 +41,11 @@ object ScalaCompiler {
compiler.prepareProject(project, logger)
def compile(
project: Project,
logger: Logger
logger: Logger,
generatedSources: Seq[GeneratedSource]
): Boolean =
ignore(project, logger) ||
compiler.compile(project, logger)
compiler.compile(project, logger, generatedSources)
def shutdown(): Unit =
compiler.shutdown()
override def usesClassDir: Boolean =
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package scala.build.compiler

import java.io.File
import java.io.{BufferedInputStream, File}

import scala.build.internal.Runner
import scala.build.{Logger, Positioned, Project}
import scala.build.internal.util.ErrorMessages
import scala.build.postprocessing.LineConversion
import scala.build.{GeneratedSource, Logger, Positioned, Project}
import scala.jdk.CollectionConverters.*

/** A simple Scala compiler designed to handle scaladocs, Java projects & get `scalac` outputs.
*
Expand Down Expand Up @@ -68,7 +71,8 @@ final case class SimpleScalaCompiler(
sources: Seq[String],
outputDir: Option[os.Path],
cwd: os.Path,
logger: Logger
logger: Logger,
generatedSources: Seq[GeneratedSource]
): Int = {

outputDir.foreach(os.makeDir.all(_))
Expand All @@ -90,15 +94,65 @@ final case class SimpleScalaCompiler(
.filter(_.startsWith("-J"))
.map(_.stripPrefix("-J"))

Runner.runJvm(
val compileProcess = Runner.runJvm(
javaCommand,
javaOptions,
compilerClassPath,
mainClass,
args,
logger,
cwd = Some(cwd)
).waitFor()
cwd = Some(cwd),
redirectStdErr = true
)

if (mainClass == "dotty.tools.dotc.Main") {
val processErrorStream = new BufferedInputStream(compileProcess.getErrorStream)
val errorOutput = new String(processErrorStream.readAllBytes())

val errorLines = errorOutput.linesIterator.toList

val errorBeginRegex = "^.*--.*Error: (.*):\\d+:\\d+.*".r
val mainAnnotMsg = "cannot be a main method since it cannot be accessed statically"

val generatedSourcesToOrigin = generatedSources
.map(gs => (gs.generated, gs.reportingPath))
.toMap

def maybeSwapAndPrint(errorLines: List[String], wasErrorInScript: Boolean = false): Unit =
errorLines match {
case h :: t => h match {
case errorBeginRegex(path) => generatedSourcesToOrigin.get(os.Path(path)) match {
case Some(Left(origin)) =>
System.err.println(h.replace(path, origin))
maybeSwapAndPrint(t, origin == "snippet")
case Some(Right(originPath)) =>
System.err.println(h.replace(path, originPath.toString))
maybeSwapAndPrint(t, originPath.ext == "sc")
case _ =>
System.err.println(h)
maybeSwapAndPrint(t)
}
case l if l.contains(mainAnnotMsg) && wasErrorInScript =>
System.err.println(
l.take(l.indexOf('|') + 1 + Console.RESET.size) +
ErrorMessages.mainAnnotationNotSupported
)
maybeSwapAndPrint(t)
case l =>
System.err.println(l)
maybeSwapAndPrint(t, wasErrorInScript)
}
case Nil => ()
}

maybeSwapAndPrint(errorLines)
}
else {
val processErrorStream = new BufferedInputStream(compileProcess.getErrorStream)
processErrorStream.transferTo(System.err)
}

compileProcess.waitFor()
}

/** Run a synthetic (created in runtime) `scalac` as a JVM process for a given
Expand All @@ -119,7 +173,8 @@ final case class SimpleScalaCompiler(
project: Project,
mainClass: String,
outputDir: os.Path,
logger: Logger
logger: Logger,
generatedSources: Seq[GeneratedSource]
): Boolean = {
val res = runScalacLike(
mainClass = mainClass,
Expand All @@ -131,7 +186,8 @@ final case class SimpleScalaCompiler(
sources = project.sources.map(_.toString),
outputDir = Some(outputDir),
cwd = project.workspace,
logger = logger
logger = logger,
generatedSources = generatedSources
)
res == 0
}
Expand Down Expand Up @@ -177,7 +233,8 @@ final case class SimpleScalaCompiler(
sources = Nil,
outputDir = None,
cwd = os.pwd,
logger = logger
logger = logger,
generatedSources = Nil
)
case _ => 1
}
Expand All @@ -193,7 +250,8 @@ final case class SimpleScalaCompiler(

def compile(
project: Project,
logger: Logger
logger: Logger,
generatedSources: Seq[GeneratedSource]
): Boolean =
if (project.sources.isEmpty) true
else
Expand All @@ -206,7 +264,7 @@ final case class SimpleScalaCompiler(
if (isScala2 && scaladoc) project.scaladocDir
else project.classesDir

runScalacLikeForProject(project, mainClass, outputDir, logger)
runScalacLikeForProject(project, mainClass, outputDir, logger, generatedSources)
}

case None =>
Expand Down
34 changes: 25 additions & 9 deletions modules/build/src/main/scala/scala/build/internal/Runner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import org.scalajs.jsenv.{Input, RunConfig}
import sbt.testing.{Framework, Status}

import java.io.File
import java.lang.ProcessBuilder.Redirect
import java.nio.file.{Files, Path, Paths}

import scala.build.EitherCps.{either, value}
Expand All @@ -28,30 +29,34 @@ object Runner {
command: Seq[String],
logger: Logger,
cwd: Option[os.Path] = None,
extraEnv: Map[String, String] = Map.empty
extraEnv: Map[String, String] = Map.empty,
redirectStdErr: Boolean = false
): Process =
run0(
commandName,
command,
logger,
allowExecve = true,
cwd,
extraEnv
extraEnv,
redirectStdErr
)

def run(
command: Seq[String],
logger: Logger,
cwd: Option[os.Path] = None,
extraEnv: Map[String, String] = Map.empty
extraEnv: Map[String, String] = Map.empty,
redirectStdErr: Boolean = false
): Process =
run0(
"unused",
command,
logger,
allowExecve = false,
cwd,
extraEnv
extraEnv,
redirectStdErr
)

def run0(
Expand All @@ -60,7 +65,8 @@ object Runner {
logger: Logger,
allowExecve: Boolean,
cwd: Option[os.Path],
extraEnv: Map[String, String]
extraEnv: Map[String, String],
redirectStdErr: Boolean = false
): Process = {

import logger.{log, debug}
Expand All @@ -86,7 +92,9 @@ object Runner {
}
else {
val b = new ProcessBuilder(command: _*)
.inheritIO()
.redirectInput(Redirect.INHERIT)
.redirectOutput(Redirect.INHERIT)
.redirectError(if redirectStdErr then Redirect.PIPE else Redirect.INHERIT)
if (extraEnv.nonEmpty) {
val env = b.environment()
for ((k, v) <- extraEnv)
Expand Down Expand Up @@ -158,7 +166,8 @@ object Runner {
cwd: Option[os.Path] = None,
extraEnv: Map[String, String] = Map.empty,
useManifest: Option[Boolean] = None,
scratchDirOpt: Option[os.Path] = None
scratchDirOpt: Option[os.Path] = None,
redirectStdErr: Boolean = false
): Process = {

val command = jvmCommand(
Expand All @@ -173,9 +182,16 @@ object Runner {
)

if (allowExecve)
maybeExec("java", command, logger, cwd = cwd, extraEnv = extraEnv)
maybeExec(
"java",
command,
logger,
cwd = cwd,
extraEnv = extraEnv,
redirectStdErr = redirectStdErr
)
else
run(command, logger, cwd = cwd, extraEnv = extraEnv)
run(command, logger, cwd = cwd, extraEnv = extraEnv, redirectStdErr = redirectStdErr)
}

private def endsWithCaseInsensitive(s: String, suffix: String): Boolean =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package scala.build.internal.util

object ErrorMessages {

/** Using @main is impossible in new [[scala.build.internal.ClassCodeWrapper]] since none of the
* definitions can be accessed statically, so those errors are swapped with this text
*/
val mainAnnotationNotSupported: String =
"Scala CLI does not support @main in .sc scripts, use .scala format instead"
}

0 comments on commit a8a0a61

Please sign in to comment.