From 9e1e9514a9c1e276f60ae25bbbc3042bb62c946e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Wro=C5=84ski?= Date: Fri, 19 Aug 2022 20:41:18 +0800 Subject: [PATCH 1/2] Fix SIP detection on Windows for native launcher --- .../scala/cli/internal/Argv0SubstWindows.java | 19 +++++++++++++++++ .../src/main/scala/scala/cli/ScalaCli.scala | 21 +++++++++++-------- 2 files changed, 31 insertions(+), 9 deletions(-) create mode 100644 modules/cli/src/main/java/scala/cli/internal/Argv0SubstWindows.java diff --git a/modules/cli/src/main/java/scala/cli/internal/Argv0SubstWindows.java b/modules/cli/src/main/java/scala/cli/internal/Argv0SubstWindows.java new file mode 100644 index 0000000000..1d95a15e4b --- /dev/null +++ b/modules/cli/src/main/java/scala/cli/internal/Argv0SubstWindows.java @@ -0,0 +1,19 @@ +package scala.cli.internal; + +import com.oracle.svm.core.annotate.Substitute; +import com.oracle.svm.core.annotate.TargetClass; +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; + +import java.nio.file.Path; + +@TargetClass(className = "scala.cli.internal.Argv0") +@Platforms({Platform.WINDOWS.class}) +final class Argv0SubstWindows { + + @Substitute + String get(String defaultValue) { + return coursier.jniutils.ModuleFileName.get(); + } + +} diff --git a/modules/cli/src/main/scala/scala/cli/ScalaCli.scala b/modules/cli/src/main/scala/scala/cli/ScalaCli.scala index beaa615b1b..fc9f05a5ee 100644 --- a/modules/cli/src/main/scala/scala/cli/ScalaCli.scala +++ b/modules/cli/src/main/scala/scala/cli/ScalaCli.scala @@ -16,12 +16,20 @@ import scala.util.Properties object ScalaCli { + if (Properties.isWin && isGraalvmNativeImage) + // have to be initialized before running (new Argv0).get because Argv0SubstWindows uses csjniutils library + // The DLL loaded by LoadWindowsLibrary is statically linke/d in + // the Scala CLI native image, no need to manually load it. + coursier.jniutils.LoadWindowsLibrary.assumeInitialized() + val progName = (new Argv0).get("scala-cli") - private def checkName(name: String) = - progName == name || - progName.endsWith(s"/$name") || - progName.endsWith(File.separator + name) + private def checkName(name: String) = { + val baseProgName = if (Properties.isWin) progName.stripSuffix(".exe") else progName + baseProgName == name || + baseProgName.endsWith(s"/$name") || + baseProgName.endsWith(File.separator + name) + } private var isSipScala = checkName("scala") || checkName("scala-cli-sip") @@ -171,11 +179,6 @@ object ScalaCli { if (!Properties.isWin && isGraalvmNativeImage) ignoreSigpipe() - if (Properties.isWin && isGraalvmNativeImage) - // The DLL loaded by LoadWindowsLibrary is statically linked in - // the Scala CLI native image, no need to manually load it. - coursier.jniutils.LoadWindowsLibrary.assumeInitialized() - if (Properties.isWin && System.console() != null && coursier.paths.Util.useJni()) // Enable ANSI output in Windows terminal coursier.jniutils.WindowsAnsiTerminal.enableAnsiOutput() From 1775999e1561e86585d6e46e213f2c6f0adff2a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Wro=C5=84ski?= Date: Wed, 17 Aug 2022 21:14:26 +0800 Subject: [PATCH 2/2] Add version of Scala to the output of version command --- .../scala/cli/commands/VersionOptions.scala | 8 ++++- .../scala/scala/cli/ScalaCliCommands.scala | 4 +-- .../main/scala/scala/cli/commands/About.scala | 7 +---- .../scala/scala/cli/commands/Default.scala | 5 +-- .../scala/cli/commands/InstallHome.scala | 4 +-- .../scala/scala/cli/commands/Update.scala | 2 +- .../scala/scala/cli/commands/Version.scala | 20 ++++++++++-- .../scala/cli/integration/SipScalaTests.scala | 31 +++++++++++++++++++ website/docs/reference/cli-options.md | 20 ++++++++++++ website/docs/reference/commands.md | 1 + 10 files changed, 86 insertions(+), 16 deletions(-) diff --git a/modules/cli-options/src/main/scala/scala/cli/commands/VersionOptions.scala b/modules/cli-options/src/main/scala/scala/cli/commands/VersionOptions.scala index 4fe66dc2f3..1963e0ce36 100644 --- a/modules/cli-options/src/main/scala/scala/cli/commands/VersionOptions.scala +++ b/modules/cli-options/src/main/scala/scala/cli/commands/VersionOptions.scala @@ -6,7 +6,13 @@ import caseapp._ @HelpMessage("Print `scala-cli` version") final case class VersionOptions( @Recurse - verbosity: VerbosityOptions = VerbosityOptions() + verbosity: VerbosityOptions = VerbosityOptions(), + @HelpMessage("Show only plain scala-cli version") + @Name("cli") + cliVersion: Boolean = false, + @HelpMessage("Show only plain scala version") + @Name("scala") + scalaVersion: Boolean = false ) // format: on diff --git a/modules/cli/src/main/scala/scala/cli/ScalaCliCommands.scala b/modules/cli/src/main/scala/scala/cli/ScalaCliCommands.scala index 3bf5b3b615..099d9b5cde 100644 --- a/modules/cli/src/main/scala/scala/cli/ScalaCliCommands.scala +++ b/modules/cli/src/main/scala/scala/cli/ScalaCliCommands.scala @@ -18,7 +18,7 @@ class ScalaCliCommands( isSipScala: Boolean ) extends CommandsEntryPoint { - lazy val actualDefaultCommand = new Default(help) + lazy val actualDefaultCommand = new Default(help, isSipScala) // for debugging purposes - allows to run the scala-cli-signing binary from the Scala CLI JVM launcher private lazy val pgpUseBinaryCommands = @@ -64,7 +64,7 @@ class ScalaCliCommands( Uninstall, UninstallCompletions, Update, - Version + new Version(isSipScala = isSipScala) ) ++ (if (pgpUseBinaryCommands) Nil else pgpCommands.allScalaCommands.toSeq) ++ (if (pgpUseBinaryCommands) pgpBinaryCommands.allScalaCommands.toSeq else Nil) diff --git a/modules/cli/src/main/scala/scala/cli/commands/About.scala b/modules/cli/src/main/scala/scala/cli/commands/About.scala index 60713824d4..567a6e06e7 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/About.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/About.scala @@ -11,12 +11,7 @@ class About(isSipScala: Boolean) extends ScalaCommand[AboutOptions] { def run(options: AboutOptions, args: RemainingArgs): Unit = { CurrentParams.verbosity = options.verbosity.verbosity - val version = Constants.version - val detailedVersionOpt = Constants.detailedVersion.filter(_ != version) - val appName = - if (isSipScala) "Scala command" - else "Scala CLI" - println(s"$appName version $version" + detailedVersionOpt.fold("")(" (" + _ + ")")) + println(Version.versionInfo(isSipScala)) val newestScalaCliVersion = Update.newestScalaCliVersion(options.ghToken.map(_.get())) val isOutdated = CommandUtils.isOutOfDateVersion( newestScalaCliVersion, diff --git a/modules/cli/src/main/scala/scala/cli/commands/Default.scala b/modules/cli/src/main/scala/scala/cli/commands/Default.scala index 0874dae9f2..8516d5c255 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/Default.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/Default.scala @@ -7,7 +7,8 @@ import scala.build.internal.Constants import scala.cli.{CurrentParams, ScalaCliHelp} class Default( - actualHelp: => RuntimeCommandsHelp + actualHelp: => RuntimeCommandsHelp, + isSipScala: Boolean ) extends ScalaCommand[DefaultOptions] { private def defaultHelp: String = actualHelp.help(ScalaCliHelp.helpFormat) @@ -30,7 +31,7 @@ class Default( def run(options: DefaultOptions, args: RemainingArgs): Unit = { CurrentParams.verbosity = options.runOptions.shared.logging.verbosity if (options.version) - println(Constants.version) + println(Version.versionInfo(isSipScala)) else if (anyArgs) Run.run( options.runOptions, diff --git a/modules/cli/src/main/scala/scala/cli/commands/InstallHome.scala b/modules/cli/src/main/scala/scala/cli/commands/InstallHome.scala index 849245c164..330aca77b7 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/InstallHome.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/InstallHome.scala @@ -51,12 +51,12 @@ object InstallHome extends ScalaCommand[InstallHomeOptions] { val newScalaCliBinPath = os.Path(options.scalaCliBinaryPath, os.pwd) val newVersion: String = - os.proc(newScalaCliBinPath, "version").call(cwd = os.pwd).out.text().trim + os.proc(newScalaCliBinPath, "version", "--cli-version").call(cwd = os.pwd).out.text().trim // Backward compatibility - previous versions not have the `--version` parameter val oldVersion: String = if (os.isFile(destBinPath)) { - val res = os.proc(destBinPath, "version").call(cwd = os.pwd, check = false) + val res = os.proc(destBinPath, "version", "--cli-version").call(cwd = os.pwd, check = false) if (res.exitCode == 0) res.out.text().trim else diff --git a/modules/cli/src/main/scala/scala/cli/commands/Update.scala b/modules/cli/src/main/scala/scala/cli/commands/Update.scala index 8c55489181..d742d1974d 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/Update.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/Update.scala @@ -95,7 +95,7 @@ object Update extends ScalaCommand[UpdateOptions] { } private def getCurrentVersion(scalaCliBinPath: os.Path): String = { - val res = os.proc(scalaCliBinPath, "version").call(cwd = os.pwd, check = false) + val res = os.proc(scalaCliBinPath, "version", "--cli-version").call(cwd = os.pwd, check = false) if (res.exitCode == 0) res.out.text().trim else diff --git a/modules/cli/src/main/scala/scala/cli/commands/Version.scala b/modules/cli/src/main/scala/scala/cli/commands/Version.scala index 0adf8ce974..36db312041 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/Version.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/Version.scala @@ -5,10 +5,26 @@ import caseapp._ import scala.build.internal.Constants import scala.cli.CurrentParams -object Version extends ScalaCommand[VersionOptions] { +class Version(isSipScala: Boolean) extends ScalaCommand[VersionOptions] { override def group = "Miscellaneous" def run(options: VersionOptions, args: RemainingArgs): Unit = { CurrentParams.verbosity = options.verbosity.verbosity - println(Constants.version) + if (options.cliVersion) + println(Constants.version) + else if (options.scalaVersion) + println(Constants.defaultScalaVersion) + else + println(Version.versionInfo(isSipScala)) } } + +object Version { + def versionInfo(isSipScala: Boolean) = + val version = Constants.version + val detailedVersionOpt = Constants.detailedVersion.filter(_ != version).fold("")(" (" + _ + ")") + val appName = + if (isSipScala) "Scala code runner" + else "Scala CLI" + s"""$appName version: $version$detailedVersionOpt + |Scala version (default): ${Constants.defaultScalaVersion}""".stripMargin +} diff --git a/modules/integration/src/test/scala/scala/cli/integration/SipScalaTests.scala b/modules/integration/src/test/scala/scala/cli/integration/SipScalaTests.scala index f041bd34d6..4a8640d181 100644 --- a/modules/integration/src/test/scala/scala/cli/integration/SipScalaTests.scala +++ b/modules/integration/src/test/scala/scala/cli/integration/SipScalaTests.scala @@ -39,4 +39,35 @@ class SipScalaTests extends ScalaCliSuite { noDirectoriesCommandTest("scala-cli-sip") } } + + def runVersionCommand(binaryName: String) = + TestInputs.empty.fromRoot { root => + val cliPath = os.Path(TestUtil.cliPath, os.pwd) + val ext = if (Properties.isWin) ".exe" else "" + val newCliPath = root / s"$binaryName$ext" + os.copy(cliPath, newCliPath) + + for { versionOption <- Seq("version", "-version", "--version") } { + val version = os.proc(newCliPath, versionOption).call(check = false) + assert( + version.exitCode == 0, + clues(version, version.out.text(), version.err.text(), version.exitCode) + ) + val expectedLauncherVersion = + if (binaryName == "scala") "Scala code runner version:" + else "Scala CLI version:" + expect(version.out.text().contains(expectedLauncherVersion)) + expect(version.out.text().contains(s"Scala version (default): ${Constants.defaultScala}")) + } + } + + if (TestUtil.isNativeCli) { + test("version command print detailed info run as scala") { + runVersionCommand("scala") + } + + test("version command print detailed info run as scala-cli") { + runVersionCommand("scala-cli") + } + } } diff --git a/website/docs/reference/cli-options.md b/website/docs/reference/cli-options.md index 1a1e324c1d..23ba43f523 100644 --- a/website/docs/reference/cli-options.md +++ b/website/docs/reference/cli-options.md @@ -1973,6 +1973,26 @@ Interactive mode Enable actionable diagnostics +## Version options + +Available in commands: +- [`version`](./commands.md#version) + + + + +#### `--cli-version` + +Aliases: `--cli` + +Show only plain scala-cli version + +#### `--scala-version` + +Aliases: `--scala` + +Show only plain scala version + ## Watch options Available in commands: diff --git a/website/docs/reference/commands.md b/website/docs/reference/commands.md index 5671778e00..d21596b74e 100644 --- a/website/docs/reference/commands.md +++ b/website/docs/reference/commands.md @@ -475,6 +475,7 @@ Print `scala-cli` version Accepts options: - [verbosity](./cli-options.md#verbosity-options) +- [version](./cli-options.md#version-options) ## Hidden commands