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

Misc improvements in compiler options handling #3253

Merged
merged 8 commits into from
Nov 21, 2024
13 changes: 9 additions & 4 deletions modules/cli/src/main/scala/scala/cli/commands/ScalaCommand.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import scala.build.input.{ScalaCliInvokeData, SubCommand}
import scala.build.internal.util.WarningMessages
import scala.build.internal.{Constants, Runner}
import scala.build.internals.{EnvVar, FeatureType}
import scala.build.options.ScalacOpt.noDashPrefixes
import scala.build.options.{BuildOptions, ScalacOpt, Scope}
import scala.build.{Artifacts, Directories, Logger, Positioned, ReplArtifacts}
import scala.cli.commands.default.LegacyScalaOptions
Expand Down Expand Up @@ -175,9 +176,13 @@ abstract class ScalaCommand[T <: HasGlobalOptions](implicit myParser: Parser[T],
val logger = options.global.logging.logger
sharedOptions(options).foreach { so =>
val scalacOpts = so.scalacOptions.toScalacOptShadowingSeq
if scalacOpts.keys.contains(ScalacOpt(YScriptRunnerOption)) then
logger.message(
LegacyScalaOptions.yScriptRunnerWarning(scalacOpts.getOption(YScriptRunnerOption))
scalacOpts.keys
.find(k =>
k == ScalacOpt(s"-$YScriptRunnerOption") || k == ScalacOpt(s"--$YScriptRunnerOption")
)
.map(_.value)
.foreach(k =>
logger.message(LegacyScalaOptions.yScriptRunnerWarning(k, scalacOpts.getOption(k)))
)
}
}
Expand All @@ -192,7 +197,7 @@ abstract class ScalaCommand[T <: HasGlobalOptions](implicit myParser: Parser[T],
shared <- sharedOptions(options)
scalacOptions = shared.scalacOptions
updatedScalacOptions = scalacOptions.withScalacExtraOptions(shared.scalacExtra)
if updatedScalacOptions.exists(ScalacOptions.ScalacPrintOptions)
if updatedScalacOptions.map(_.noDashPrefixes).exists(ScalacOptions.ScalacPrintOptions)
logger = shared.logger
fixedBuildOptions = buildOptions.copy(scalaOptions =
buildOptions.scalaOptions.copy(defaultScalaVersion = Some(ScalaCli.getDefaultScalaVersion))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import scala.cli.commands.default.LegacyScalaOptions.*
import scala.cli.commands.package0.Package
import scala.cli.commands.shared.HelpGroup
import scala.cli.commands.shared.HelpMessages.PowerString
import scala.cli.commands.shared.ScalacOptions.YScriptRunnerOption
import scala.cli.commands.tags

/** Options covering backwards compatibility with the old scala runner.
Expand Down Expand Up @@ -168,7 +167,7 @@ object LegacyScalaOptions {
implicit lazy val parser: Parser[LegacyScalaOptions] = Parser.derive
implicit lazy val help: Help[LegacyScalaOptions] = Help.derive

def yScriptRunnerWarning(yScriptRunnerValue: Option[String]): String = {
def yScriptRunnerWarning(yScriptRunnerKey: String, yScriptRunnerValue: Option[String]): String = {
val valueSpecificMsg = yScriptRunnerValue match {
case Some(v @ "default") =>
s"scala.tools.nsc.DefaultScriptRunner (the $v script runner) is no longer available."
Expand All @@ -185,7 +184,7 @@ object LegacyScalaOptions {
s"Using $className as the script runner is no longer supported and will not be attempted."
case _ => ""
}
s"""Deprecated option '$YScriptRunnerOption' is ignored.
s"""Deprecated option '$yScriptRunnerKey' is ignored.
|The script runner can no longer be picked as before.
|$valueSpecificMsg""".stripMargin
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,6 @@ final case class PackageOptions(
@Name("sourcesJar")
@Name("jarSources")
@Name("sources")
@Name("source")
@Tag(tags.deprecated("source")) // alias to be removed in 1.6.x
@Tag(tags.restricted)
@Tag(tags.inShortHelp)
src: Boolean = false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import caseapp.core.{Arg, Error}
import com.github.plokhotnyuk.jsoniter_scala.core.*
import com.github.plokhotnyuk.jsoniter_scala.macros.*

import scala.build.options.ScalacOpt.noDashPrefixes
import scala.cli.commands.tags

// format: off
Expand All @@ -26,6 +27,13 @@ final case class ScalacOptions(
// format: on

object ScalacOptions {
extension (opt: String) {
private def hasValidScalacOptionDashes: Boolean =
opt.startsWith("-") && opt.length > 1 && (
if opt.length > 2 then opt.charAt(2) != '-'
else opt.charAt(1) != '-'
)
}

private val scalacOptionsArg = Arg("scalacOption").copy(
extraNames = Seq(Name("scala-opt"), Name("O"), Name("scala-option")),
Expand All @@ -37,46 +45,58 @@ object ScalacOptions {
origin = Some("ScalacOptions")
)
// .withIsFlag(true) // The scalac options we handle accept no value after the -… argument
val YScriptRunnerOption = "-Yscriptrunner"
private val scalacOptionsPurePrefixes =
Set("-V", "-W", "-X", "-Y")
private val scalacOptionsPrefixes =
Set("-g", "-language", "-opt", "-P", "-target", "-source") ++ scalacOptionsPurePrefixes
val YScriptRunnerOption = "Yscriptrunner"
private val scalacOptionsPurePrefixes = Set("V", "W", "X", "Y")
private val scalacOptionsPrefixes = Set("P") ++ scalacOptionsPurePrefixes
private val scalacAliasedOptions = // these options don't require being passed after -O and accept an arg
Set("-encoding", "-release", "-color", YScriptRunnerOption)
Set(
"coverage-exclude-classlikes",
"coverage-exclude-files",
"encoding",
"release",
"color",
"g",
"language",
"opt",
"target",
"source",
YScriptRunnerOption
)
private val scalacNoArgAliasedOptions = // these options don't require being passed after -O and don't accept an arg
Set(
"-unchecked",
"-nowarn",
"-feature",
"-deprecation",
"-rewrite",
"-old-syntax",
"-new-syntax",
"-indent",
"-no-indent"
"experimental",
"explain",
"unchecked",
"nowarn",
"feature",
"deprecation",
"rewrite",
"old-syntax",
"new-syntax",
"indent",
"no-indent"
)

/** This includes all the scalac options which disregard inputs and print a help and/or context
* message instead.
*/
val ScalacPrintOptions: Set[String] =
scalacOptionsPurePrefixes ++ Set(
"-help",
"-opt:help",
"-Xshow-phases",
"-Xsource:help",
"-Xplugin-list",
"-Xmixin-force-forwarders:help",
"-Xlint:help",
"-Vphases"
"help",
"opt:help",
"Xshow-phases",
"Xsource:help",
"Xplugin-list",
"Xmixin-force-forwarders:help",
"Xlint:help",
"Vphases"
)

/** This includes all the scalac options which are redirected to native Scala CLI options. */
val ScalaCliRedirectedOptions = Set(
"-classpath",
"-cp", // redirected to --extra-jars
"-d" // redirected to --compilation-output
val ScalaCliRedirectedOptions: Set[String] = Set(
"classpath",
"cp", // redirected to --extra-jars
"d" // redirected to --compilation-output
)
val ScalacDeprecatedOptions: Set[String] = Set(
YScriptRunnerOption // old 'scala' runner specific, no longer supported
Expand All @@ -99,12 +119,20 @@ object ScalacOptions {
): Either[(Error, List[String]), Option[(Option[List[String]], List[String])]] =
args match {
case h :: t
if scalacOptionsPrefixes.exists(h.startsWith) &&
!ScalacDeprecatedOptions.contains(h) =>
if h.hasValidScalacOptionDashes &&
scalacOptionsPrefixes.exists(h.noDashPrefixes.startsWith) &&
!ScalacDeprecatedOptions.contains(h.noDashPrefixes) =>
Right(Some((Some(acc.getOrElse(Nil) :+ h), t)))
case h :: t if scalacNoArgAliasedOptions.contains(h) =>
case h :: t
if h.hasValidScalacOptionDashes &&
scalacNoArgAliasedOptions.contains(h.noDashPrefixes) =>
Right(Some((Some(acc.getOrElse(Nil) :+ h), t)))
case h :: t if scalacAliasedOptions.contains(h) =>
case h :: t
if h.hasValidScalacOptionDashes &&
scalacAliasedOptions.exists(o => h.noDashPrefixes.startsWith(o + ":")) &&
h.count(_ == ':') == 1 => Right(Some((Some(acc.getOrElse(Nil) :+ h), t)))
case h :: t
if h.hasValidScalacOptionDashes && scalacAliasedOptions.contains(h.noDashPrefixes) =>
// check if the next scalac arg is a different option or a param to the current option
val maybeOptionArg = t.headOption.filter(!_.startsWith("-"))
// if it's a param, it'll be treated as such and considered already parsed
Expand All @@ -118,8 +146,8 @@ object ScalacOptions {
}

implicit lazy val parser: Parser[ScalacOptions] = {
val baseParser = scalacOptionsArgument :: NilParser
implicit val p = ArgFileOption.parser
val baseParser = scalacOptionsArgument :: NilParser
implicit val p: Parser[List[ArgFileOption]] = ArgFileOption.parser
baseParser.addAll[List[ArgFileOption]].to[ScalacOptions]
}

Expand All @@ -130,7 +158,7 @@ object ScalacOptions {
case class ArgFileOption(file: String) extends AnyVal

object ArgFileOption {
val arg = Arg(
val arg: Arg = Arg(
name = Name("args-file"),
valueDescription = Some(ValueDescription("@arguments-file")),
helpMessage = Some(HelpMessage("File with scalac options.")),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package scala.cli.commands.util

import scala.build.Logger
import scala.build.options.ScalacOpt.filterScalacOptionKeys
import scala.build.options.ScalacOpt.{filterScalacOptionKeys, noDashPrefixes}
import scala.build.options.{ScalacOpt, ShadowingSeq}
import scala.cli.commands.bloop.BloopExit
import scala.cli.commands.default.LegacyScalaOptions
Expand Down Expand Up @@ -33,9 +33,13 @@ object ScalacOptionsUtil {

extension (opts: ShadowingSeq[ScalacOpt]) {
def filterNonRedirected: ShadowingSeq[ScalacOpt] =
opts.filterScalacOptionKeys(!ScalacOptions.ScalaCliRedirectedOptions.contains(_))
opts.filterScalacOptionKeys(k =>
!ScalacOptions.ScalaCliRedirectedOptions.contains(k.noDashPrefixes)
)
def filterNonDeprecated: ShadowingSeq[ScalacOpt] =
opts.filterScalacOptionKeys(!ScalacOptions.ScalacDeprecatedOptions.contains(_))
opts.filterScalacOptionKeys(k =>
!ScalacOptions.ScalacDeprecatedOptions.contains(k.noDashPrefixes)
)
def getOption(key: String): Option[String] =
opts.get(ScalacOpt(key)).headOption.map(_.value)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1712,7 +1712,7 @@ abstract class BspTestDefinitions extends ScalaCliSuite with TestScalaVersionArg
"--power",
"package",
jarSources,
"--source",
"--src",
"-o",
sourceJarPath,
extraOptions
Expand Down
Loading
Loading