From 476417efa8396e8fbce4067ed0eab9ef0e8d273b Mon Sep 17 00:00:00 2001 From: Piotr Chabelski Date: Fri, 29 Nov 2024 09:22:01 +0100 Subject: [PATCH 1/3] Support `coursier` `scala` launcher tests on Windows --- .../scala/cli/integration/SipScalaTests.scala | 142 +++++++++++------- 1 file changed, 90 insertions(+), 52 deletions(-) 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 bcb5f257ee..2d95f6f4de 100644 --- a/modules/integration/src/test/scala/scala/cli/integration/SipScalaTests.scala +++ b/modules/integration/src/test/scala/scala/cli/integration/SipScalaTests.scala @@ -3,6 +3,8 @@ package scala.cli.integration import com.eed3si9n.expecty.Expecty.expect import os.CommandResult +import java.nio.charset.Charset +import scala.jdk.CollectionConverters.IteratorHasAsScala import scala.util.Properties class SipScalaTests extends ScalaCliSuite with SbtTestHelper with MillTestHelper { @@ -833,62 +835,98 @@ class SipScalaTests extends ScalaCliSuite with SbtTestHelper with MillTestHelper } } - if (!Properties.isWin) // FIXME: run this test on Windows - test("coursier scala installation works in --offline mode") { - TestInputs.empty.fromRoot { root => - val localCache = root / "local-cache" - val localBin = root / "local-bin" - val sv = "3.5.0-RC4" - os.proc( - TestUtil.cs, - "install", - "--cache", - localCache, - "--install-dir", - localBin, - s"scala:$sv" - ).call(cwd = root) - val scalaBinary: os.Path = localBin / "scala" - val fileBytes = os.read.bytes(scalaBinary) - val shebang = new String(fileBytes.takeWhile(_ != '\n'), "UTF-8") - val binaryData = fileBytes.drop(shebang.length + 1) - val execLine = new String(binaryData.takeWhile(_ != '\n'), "UTF-8") - val scriptPathRegex = """exec "([^"]+/bin/scala).*"""".r - val scalaScript = execLine match { case scriptPathRegex(extractedPath) => extractedPath } - val scalaScriptPath = os.Path(scalaScript) - val lineToChange = "eval \"${SCALA_CLI_CMD_BASH[@]}\" \\" - // FIXME: the way the scala script calls the launcher currently ignores the --debug flag - val newContent = os.read(scalaScriptPath).replace( - lineToChange, - s"""SCALA_CLI_CMD_BASH=(\"\\\"${TestUtil.cliPath}\\\"\") - |$lineToChange""".stripMargin - ) - os.write.over(scalaScriptPath, newContent) - val r = - os.proc( - scalaScript, - "--offline", - "--power", - "--with-compiler", - "-e", - "println(dotty.tools.dotc.config.Properties.versionNumberString)" - ).call( - cwd = root, - env = Map("COURSIER_CACHE" -> localCache.toString), - check = false // need to clean up even on failure + test("coursier scala installation works in --offline mode") { + TestInputs.empty.fromRoot { root => + val localCache = root / "local-cache" + val localBin = root / "local-bin" + val sv = Constants.scala3NextAnnounced + os.proc( + TestUtil.cs, + "install", + "--cache", + localCache, + "--install-dir", + localBin, + s"scala:$sv" + ).call(cwd = root) + val launchScalaPath: os.Path = + if (Properties.isWin) { + val batchWrapperScript: os.Path = localBin / "scala.bat" + val charset = Charset.defaultCharset().toString + val batchWrapperContent = new String(os.read.bytes(batchWrapperScript), charset) + val setCommandLine = batchWrapperContent + .lines() + .iterator() + .asScala + .toList + .find(_.startsWith("SET CMDLINE=")) + .getOrElse("") + val scriptPathRegex = """SET CMDLINE="(.*\\bin\\scala\.bat)" %CMD_LINE_ARGS%""".r + val batchScript = + setCommandLine match { case scriptPathRegex(extractedPath) => extractedPath } + val batchScriptPath = os.Path(batchScript) + val oldContent = os.read(batchScriptPath) + val newContent = oldContent.replace( + "call %SCALA_CLI_CMD_WIN%", + s"""set "SCALA_CLI_CMD_WIN=${TestUtil.cliPath}" + |call %SCALA_CLI_CMD_WIN%""".stripMargin ) - // clean up cs local binaries - val csPrebuiltBinaryDir = - os.Path(scalaScript.substring(0, scalaScript.indexOf(sv) + sv.length)) - try os.remove.all(csPrebuiltBinaryDir) - catch { - case ex: java.nio.file.FileSystemException => - println(s"Failed to remove $csPrebuiltBinaryDir: $ex") + expect(newContent != oldContent) + os.write.over(batchScriptPath, newContent) + batchWrapperScript } - expect(r.exitCode == 0) - expect(r.out.trim() == sv) + else { + val scalaBinary: os.Path = localBin / "scala" + val fileBytes = os.read.bytes(scalaBinary) + val shebang = new String(fileBytes.takeWhile(_ != '\n'), "UTF-8") + val binaryData = fileBytes.drop(shebang.length + 1) + val execLine = new String(binaryData.takeWhile(_ != '\n'), "UTF-8") + val scriptPathRegex = """exec "([^"]+/bin/scala).*"""".r + val scalaScript = execLine match { case scriptPathRegex(extractedPath) => extractedPath } + val scalaScriptPath = os.Path(scalaScript) + val lineToChange = "eval \"${SCALA_CLI_CMD_BASH[@]}\" \\" + // FIXME: the way the scala script calls the launcher currently ignores the --debug flag + val newContent = os.read(scalaScriptPath).replace( + lineToChange, + s"""SCALA_CLI_CMD_BASH=(\"\\\"${TestUtil.cliPath}\\\"\") + |$lineToChange""".stripMargin + ) + os.write.over(scalaScriptPath, newContent) + scalaBinary + } + val wrapperVersion = os.proc(launchScalaPath, "version", "--cli-version") + .call(cwd = root).out.trim() + val cliVersion = os.proc(TestUtil.cli, "version", "--cli-version") + .call(cwd = root).out.trim() + expect(wrapperVersion == cliVersion) + val r = + os.proc( + launchScalaPath, + "--offline", + "--power", + "--with-compiler", + "-e", + "println(dotty.tools.dotc.config.Properties.versionNumberString)" + ).call( + cwd = root, + env = Map("COURSIER_CACHE" -> localCache.toString), + check = false // need to clean up even on failure + ) + // clean up cs local binaries + val csPrebuiltBinaryDir = + os.Path(launchScalaPath.toString().substring( + 0, + launchScalaPath.toString().indexOf(sv) + sv.length + )) + try os.remove.all(csPrebuiltBinaryDir) + catch { + case ex: java.nio.file.FileSystemException => + println(s"Failed to remove $csPrebuiltBinaryDir: $ex") } + expect(r.exitCode == 0) + expect(r.out.trim() == sv) } + } // this check is just to ensure this isn't being run for LTS RC jobs // should be adjusted when a new LTS line is released From 99ef3a91ab81b6d63d59ba451c4d3966a2f715fe Mon Sep 17 00:00:00 2001 From: Piotr Chabelski Date: Fri, 29 Nov 2024 10:33:09 +0100 Subject: [PATCH 2/3] Extract coursier scala installation setup & cleanup for tests to a helper trait for reuse --- .../CoursierScalaInstallationTestHelper.scala | 94 ++++++++++++++ .../scala/cli/integration/SipScalaTests.scala | 116 ++++-------------- 2 files changed, 121 insertions(+), 89 deletions(-) create mode 100644 modules/integration/src/test/scala/scala/cli/integration/CoursierScalaInstallationTestHelper.scala diff --git a/modules/integration/src/test/scala/scala/cli/integration/CoursierScalaInstallationTestHelper.scala b/modules/integration/src/test/scala/scala/cli/integration/CoursierScalaInstallationTestHelper.scala new file mode 100644 index 0000000000..2eb53db2e8 --- /dev/null +++ b/modules/integration/src/test/scala/scala/cli/integration/CoursierScalaInstallationTestHelper.scala @@ -0,0 +1,94 @@ +package scala.cli.integration + +import com.eed3si9n.expecty.Expecty.expect + +import java.nio.charset.Charset + +import scala.jdk.CollectionConverters.IteratorHasAsScala +import scala.util.Properties + +trait CoursierScalaInstallationTestHelper { + def withScalaRunnerWrapper( + root: os.Path, + localCache: os.Path, + localBin: os.Path, + scalaVersion: String + )(f: os.Path => Unit): Unit = { + os.proc( + TestUtil.cs, + "install", + "--cache", + localCache, + "--install-dir", + localBin, + s"scala:$scalaVersion" + ).call(cwd = root) + val (launchScalaPath: os.Path, underlyingScriptPath: os.Path) = + if (Properties.isWin) { + val batchWrapperScript: os.Path = localBin / "scala.bat" + val charset = Charset.defaultCharset().toString + val batchWrapperContent = new String(os.read.bytes(batchWrapperScript), charset) + val setCommandLine = batchWrapperContent + .lines() + .iterator() + .asScala + .toList + .find(_.startsWith("SET CMDLINE=")) + .getOrElse("") + val scriptPathRegex = """SET CMDLINE="(.*\\bin\\scala\.bat)" %CMD_LINE_ARGS%""".r + val batchScript = + setCommandLine match { case scriptPathRegex(extractedPath) => extractedPath } + val batchScriptPath = os.Path(batchScript) + val oldContent = os.read(batchScriptPath) + val newContent = oldContent.replace( + "call %SCALA_CLI_CMD_WIN%", + s"""set "SCALA_CLI_CMD_WIN=${TestUtil.cliPath}" + |call %SCALA_CLI_CMD_WIN%""".stripMargin + ) + expect(newContent != oldContent) + os.write.over(batchScriptPath, newContent) + batchWrapperScript -> batchScriptPath + } + else { + val scalaBinary: os.Path = localBin / "scala" + val fileBytes = os.read.bytes(scalaBinary) + val shebang = new String(fileBytes.takeWhile(_ != '\n'), "UTF-8") + val binaryData = fileBytes.drop(shebang.length + 1) + val execLine = new String(binaryData.takeWhile(_ != '\n'), "UTF-8") + val scriptPathRegex = """exec "([^"]+/bin/scala).*"""".r + val scalaScript = execLine match { case scriptPathRegex(extractedPath) => extractedPath } + val scalaScriptPath = os.Path(scalaScript) + val lineToChange = "eval \"${SCALA_CLI_CMD_BASH[@]}\" \\" + // FIXME: the way the scala script calls the launcher currently ignores the --debug flag + val newContent = os.read(scalaScriptPath).replace( + lineToChange, + s"""SCALA_CLI_CMD_BASH=(\"\\\"${TestUtil.cliPath}\\\"\") + |$lineToChange""".stripMargin + ) + os.write.over(scalaScriptPath, newContent) + scalaBinary -> scalaScriptPath + } + val wrapperVersion = os.proc(launchScalaPath, "version", "--cli-version") + .call(cwd = root).out.trim() + val cliVersion = os.proc(TestUtil.cli, "version", "--cli-version") + .call(cwd = root).out.trim() + expect(wrapperVersion == cliVersion) + f(launchScalaPath) + // clean up cs local binaries + val csPrebuiltBinaryDir = + os.Path(underlyingScriptPath.toString().substring( + 0, + underlyingScriptPath.toString().indexOf(scalaVersion) + scalaVersion.length + )) + System.err.println(s"Cleaning up, trying to remove $csPrebuiltBinaryDir") + try { + os.remove.all(csPrebuiltBinaryDir) + + System.err.println(s"Cleanup complete. Removed $csPrebuiltBinaryDir") + } + catch { + case ex: java.nio.file.FileSystemException => + System.err.println(s"Failed to remove $csPrebuiltBinaryDir: $ex") + } + } +} 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 2d95f6f4de..a76bd1b7d3 100644 --- a/modules/integration/src/test/scala/scala/cli/integration/SipScalaTests.scala +++ b/modules/integration/src/test/scala/scala/cli/integration/SipScalaTests.scala @@ -3,11 +3,12 @@ package scala.cli.integration import com.eed3si9n.expecty.Expecty.expect import os.CommandResult -import java.nio.charset.Charset -import scala.jdk.CollectionConverters.IteratorHasAsScala import scala.util.Properties -class SipScalaTests extends ScalaCliSuite with SbtTestHelper with MillTestHelper { +class SipScalaTests extends ScalaCliSuite + with SbtTestHelper + with MillTestHelper + with CoursierScalaInstallationTestHelper { implicit class StringEnrichment(s: String) { def containsExperimentalWarningOf(featureNameAndType: String): Boolean = s.contains(s"The $featureNameAndType is experimental") || @@ -837,94 +838,31 @@ class SipScalaTests extends ScalaCliSuite with SbtTestHelper with MillTestHelper test("coursier scala installation works in --offline mode") { TestInputs.empty.fromRoot { root => - val localCache = root / "local-cache" - val localBin = root / "local-bin" - val sv = Constants.scala3NextAnnounced - os.proc( - TestUtil.cs, - "install", - "--cache", - localCache, - "--install-dir", - localBin, - s"scala:$sv" - ).call(cwd = root) - val launchScalaPath: os.Path = - if (Properties.isWin) { - val batchWrapperScript: os.Path = localBin / "scala.bat" - val charset = Charset.defaultCharset().toString - val batchWrapperContent = new String(os.read.bytes(batchWrapperScript), charset) - val setCommandLine = batchWrapperContent - .lines() - .iterator() - .asScala - .toList - .find(_.startsWith("SET CMDLINE=")) - .getOrElse("") - val scriptPathRegex = """SET CMDLINE="(.*\\bin\\scala\.bat)" %CMD_LINE_ARGS%""".r - val batchScript = - setCommandLine match { case scriptPathRegex(extractedPath) => extractedPath } - val batchScriptPath = os.Path(batchScript) - val oldContent = os.read(batchScriptPath) - val newContent = oldContent.replace( - "call %SCALA_CLI_CMD_WIN%", - s"""set "SCALA_CLI_CMD_WIN=${TestUtil.cliPath}" - |call %SCALA_CLI_CMD_WIN%""".stripMargin - ) - expect(newContent != oldContent) - os.write.over(batchScriptPath, newContent) - batchWrapperScript - } - else { - val scalaBinary: os.Path = localBin / "scala" - val fileBytes = os.read.bytes(scalaBinary) - val shebang = new String(fileBytes.takeWhile(_ != '\n'), "UTF-8") - val binaryData = fileBytes.drop(shebang.length + 1) - val execLine = new String(binaryData.takeWhile(_ != '\n'), "UTF-8") - val scriptPathRegex = """exec "([^"]+/bin/scala).*"""".r - val scalaScript = execLine match { case scriptPathRegex(extractedPath) => extractedPath } - val scalaScriptPath = os.Path(scalaScript) - val lineToChange = "eval \"${SCALA_CLI_CMD_BASH[@]}\" \\" - // FIXME: the way the scala script calls the launcher currently ignores the --debug flag - val newContent = os.read(scalaScriptPath).replace( - lineToChange, - s"""SCALA_CLI_CMD_BASH=(\"\\\"${TestUtil.cliPath}\\\"\") - |$lineToChange""".stripMargin + val localCache = root / "local-cache" + val localBin = root / "local-bin" + val scalaVersion = Constants.scala3NextAnnounced + withScalaRunnerWrapper( + root = root, + localCache = localCache, + localBin = localBin, + scalaVersion = scalaVersion + ) { launchScalaPath => + val r = + os.proc( + launchScalaPath, + "--offline", + "--power", + "--with-compiler", + "-e", + "println(dotty.tools.dotc.config.Properties.versionNumberString)" + ).call( + cwd = root, + env = Map("COURSIER_CACHE" -> localCache.toString), + check = false // need to clean up even on failure ) - os.write.over(scalaScriptPath, newContent) - scalaBinary - } - val wrapperVersion = os.proc(launchScalaPath, "version", "--cli-version") - .call(cwd = root).out.trim() - val cliVersion = os.proc(TestUtil.cli, "version", "--cli-version") - .call(cwd = root).out.trim() - expect(wrapperVersion == cliVersion) - val r = - os.proc( - launchScalaPath, - "--offline", - "--power", - "--with-compiler", - "-e", - "println(dotty.tools.dotc.config.Properties.versionNumberString)" - ).call( - cwd = root, - env = Map("COURSIER_CACHE" -> localCache.toString), - check = false // need to clean up even on failure - ) - // clean up cs local binaries - val csPrebuiltBinaryDir = - os.Path(launchScalaPath.toString().substring( - 0, - launchScalaPath.toString().indexOf(sv) + sv.length - )) - try os.remove.all(csPrebuiltBinaryDir) - catch { - case ex: java.nio.file.FileSystemException => - println(s"Failed to remove $csPrebuiltBinaryDir: $ex") + expect(r.exitCode == 0) + expect(r.out.trim() == scalaVersion) } - expect(r.exitCode == 0) - expect(r.out.trim() == sv) } } From e33851336e2f38f17d0381494faefc537f3eaa17 Mon Sep 17 00:00:00 2001 From: Piotr Chabelski Date: Mon, 2 Dec 2024 09:20:46 +0100 Subject: [PATCH 3/3] Use the latest announced Scala 3 Next RC for `scala` wrapper tests --- build.sc | 1 + .../src/test/scala/scala/cli/integration/SipScalaTests.scala | 2 +- project/deps.sc | 5 +++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/build.sc b/build.sc index 6d6eb51aad..3868eec2e4 100644 --- a/build.sc +++ b/build.sc @@ -1037,6 +1037,7 @@ trait CliIntegration extends SbtModule with ScalaCliPublishModule with HasTests | def scala3LtsPrefix = "${Scala.scala3LtsPrefix}" | def scala3Lts = "${Scala.scala3Lts}" | def scala3NextRc = "${Scala.scala3NextRc}" + | def scala3NextRcAnnounced = "${Scala.scala3NextRcAnnounced}" | def scala3Next = "${Scala.scala3Next}" | def scala3NextAnnounced = "${Scala.scala3NextAnnounced}" | def defaultScala = "${Scala.defaultUser}" 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 a76bd1b7d3..992d111a1f 100644 --- a/modules/integration/src/test/scala/scala/cli/integration/SipScalaTests.scala +++ b/modules/integration/src/test/scala/scala/cli/integration/SipScalaTests.scala @@ -840,7 +840,7 @@ class SipScalaTests extends ScalaCliSuite TestInputs.empty.fromRoot { root => val localCache = root / "local-cache" val localBin = root / "local-bin" - val scalaVersion = Constants.scala3NextAnnounced + val scalaVersion = Constants.scala3NextRcAnnounced withScalaRunnerWrapper( root = root, localCache = localCache, diff --git a/project/deps.sc b/project/deps.sc index fe3a383ff5..c837c456d9 100644 --- a/project/deps.sc +++ b/project/deps.sc @@ -10,8 +10,9 @@ object Scala { def scala3Lts = s"$scala3LtsPrefix.4" // the LTS version currently used in the build def scala3NextPrefix = "3.5" def scala3Next = s"$scala3NextPrefix.2" // the newest/next version of Scala - def scala3NextAnnounced = scala3Next // the newest/next version of Scala that's been announced - def scala3NextRc = "3.6.2-RC3" // the latest RC version of Scala Next + def scala3NextAnnounced = scala3Next // the newest/next version of Scala that's been announced + def scala3NextRc = "3.6.2-RC3" // the latest RC version of Scala Next + def scala3NextRcAnnounced = scala3NextRc // the latest RC version of Scala Next // The Scala version used to build the CLI itself. def defaultInternal = sys.props.get("scala.version.internal").getOrElse(scala3Lts)