diff --git a/build.mill.scala b/build.mill.scala index 0f6a0513de..63e4287c8f 100644 --- a/build.mill.scala +++ b/build.mill.scala @@ -115,9 +115,6 @@ object `scala-cli-bsp` extends JavaModule with ScalaCliPublishModule { override def ivyDeps: T[Agg[Dep]] = super.ivyDeps() ++ Seq( Deps.bsp4j ) - override def javacOptions: T[Seq[String]] = Task { - super.javacOptions() ++ Seq("-target", "8", "-source", "8") - } } object integration extends CliIntegration { object test extends IntegrationScalaTests { @@ -773,14 +770,7 @@ trait Build extends ScalaCliCrossSbtModule trait SpecificationLevel extends ScalaCliCrossSbtModule with ScalaCliPublishModule { - override def crossScalaVersion: String = crossValue - override def scalacOptions: T[Seq[String]] = Task { - val isScala213 = crossScalaVersion.startsWith("2.13.") - val extraOptions = - if (isScala213) Seq("-Xsource:3") - else Nil - super.scalacOptions() ++ extraOptions ++ Seq("-release", "8") - } + override def crossScalaVersion: String = crossValue } trait Cli extends CrossSbtModule with ProtoBuildModule with CliLaunchers @@ -1038,6 +1028,10 @@ trait CliIntegration extends SbtModule with ScalaCliPublishModule with HasTests | def scala3Next = "${Scala.scala3Next}" | def scala3NextAnnounced = "${Scala.scala3NextAnnounced}" | def defaultScala = "${Scala.defaultUser}" + | def scala38Versions = Seq(${Scala.scala38Versions + .sorted + .map(s => s"\"$s\"") + .mkString(", ")}) | def defaultScalafmtVersion = "${Deps.scalafmtCli.dep.versionConstraint.asString}" | def maxAmmoniteScala212Version = "${Scala.maxAmmoniteScala212Version}" | def maxAmmoniteScala213Version = "${Scala.maxAmmoniteScala213Version}" @@ -1240,7 +1234,7 @@ trait Runner extends CrossSbtModule with ScalaCliPublishModule with ScalaCliScalafixModule { override def scalacOptions: T[Seq[String]] = Task { - super.scalacOptions() ++ Seq("-release", "8", "-deprecation") + super.scalacOptions() ++ Seq("-deprecation") } override def mainClass: T[Option[String]] = Some("scala.cli.runner.Runner") override def sources: T[Seq[PathRef]] = Task.Sources { @@ -1255,7 +1249,7 @@ trait TestRunner extends CrossSbtModule with ScalaCliPublishModule with ScalaCliScalafixModule { override def scalacOptions: T[Seq[String]] = Task { - super.scalacOptions() ++ Seq("-release", "8", "-deprecation") + super.scalacOptions() ++ Seq("-deprecation") } override def ivyDeps: T[Agg[Dep]] = super.ivyDeps() ++ Agg( Deps.asm, diff --git a/modules/build/src/test/scala/scala/build/tests/SourceGeneratorTests.scala b/modules/build/src/test/scala/scala/build/tests/SourceGeneratorTests.scala index 98e57a75b4..ad42204478 100644 --- a/modules/build/src/test/scala/scala/build/tests/SourceGeneratorTests.scala +++ b/modules/build/src/test/scala/scala/build/tests/SourceGeneratorTests.scala @@ -4,7 +4,7 @@ import com.eed3si9n.expecty.Expecty.expect import scala.Console.println import scala.build.Ops.EitherThrowOps -import scala.build.options.{BuildOptions, InternalOptions, Scope} +import scala.build.options.{BuildOptions, InternalOptions} import scala.build.tests.util.BloopServer import scala.build.{Build, BuildThreads, Directories, LocalRepo, Position} diff --git a/modules/config/src/main/scala/scala/cli/config/Keys.scala b/modules/config/src/main/scala/scala/cli/config/Keys.scala index 39867623a4..3c0f2bbc03 100644 --- a/modules/config/src/main/scala/scala/cli/config/Keys.scala +++ b/modules/config/src/main/scala/scala/cli/config/Keys.scala @@ -1,7 +1,4 @@ package scala.cli.config - -import com.github.plokhotnyuk.jsoniter_scala.core.Key as _ - import scala.cli.commands.SpecificationLevel object Keys { diff --git a/modules/integration/src/test/scala/scala/cli/integration/BspTestDefinitions.scala b/modules/integration/src/test/scala/scala/cli/integration/BspTestDefinitions.scala index ed6829ef86..101d3adf22 100644 --- a/modules/integration/src/test/scala/scala/cli/integration/BspTestDefinitions.scala +++ b/modules/integration/src/test/scala/scala/cli/integration/BspTestDefinitions.scala @@ -458,16 +458,17 @@ abstract class BspTestDefinitions extends ScalaCliSuite } test("directives in multiple files diagnostics") { - val inputs = TestInputs( + val javaVersion = Constants.allJavaVersions.filter(_ > Constants.defaultJvmVersion).min + val inputs = TestInputs( os.rel / "Foo.scala" -> - s"""//> using scala 3.3.0 + s"""//> using scala $actualScalaVersion | |object Foo extends App { | println("Foo") |} |""".stripMargin, os.rel / "Bar.scala" -> "", - os.rel / "Hello.java" -> "//> using jvm 11" + os.rel / "Hello.java" -> s"//> using jvm $javaVersion" ) withBsp(inputs, Seq(".")) { (root, localClient, remoteServer) => @@ -522,8 +523,20 @@ abstract class BspTestDefinitions extends ScalaCliSuite ) } - checkDirectivesInMultipleFilesWarnings("Foo.scala", 0, 0, 0, 21) - checkDirectivesInMultipleFilesWarnings("Hello.java", 0, 0, 0, 16) + checkDirectivesInMultipleFilesWarnings( + fileName = "Foo.scala", + expectedStartLine = 0, + expectedStartCharacter = 0, + expectedEndLine = 0, + expectedEndCharacter = 16 + actualScalaVersion.length + ) + checkDirectivesInMultipleFilesWarnings( + fileName = "Hello.java", + expectedStartLine = 0, + expectedStartCharacter = 0, + expectedEndLine = 0, + expectedEndCharacter = 16 + ) } } } @@ -2232,7 +2245,7 @@ abstract class BspTestDefinitions extends ScalaCliSuite if actualScalaVersion.coursierVersion >= "3.5.0".coursierVersion scalaVersion = if actualScalaVersion == Constants.scala3NextRc then Constants.scala3NextRcAnnounced - if actualScalaVersion == Constants.scala3Next then Constants.scala3NextAnnounced + else if actualScalaVersion == Constants.scala3Next then Constants.scala3NextAnnounced else actualScalaVersion withLauncher = (root: os.Path) => (f: Seq[os.Shellable] => Unit) => diff --git a/modules/integration/src/test/scala/scala/cli/integration/CompileTestDefinitions.scala b/modules/integration/src/test/scala/scala/cli/integration/CompileTestDefinitions.scala index 33796fe82c..311eef2601 100644 --- a/modules/integration/src/test/scala/scala/cli/integration/CompileTestDefinitions.scala +++ b/modules/integration/src/test/scala/scala/cli/integration/CompileTestDefinitions.scala @@ -328,58 +328,95 @@ abstract class CompileTestDefinitions TestInputs(os.rel / "Main.scala" -> s"object Main{java.util.Optional.of(1).isPresent}") val scalaJvm11Project: TestInputs = TestInputs(os.rel / "Main.scala" -> s"object Main{java.util.Optional.of(1).isEmpty}") + val scalaJvm17Project: TestInputs = + TestInputs(os.rel / "Main.scala" -> s"object Main{java.util.HexFormat.of().toHexDigits(255)}") + val scalaJvm23Project: TestInputs = + TestInputs( + os.rel / "Main.scala" -> s"object Main{System.out.println(javax.print.attribute.standard.OutputBin.LEFT)}" + ) val javaJvm8Project: TestInputs = TestInputs(os.rel / "Main.java" -> """|public class Main{ | public static void main(String[] args) { | java.util.Optional.of(1).isPresent(); | } |}""".stripMargin) - val javaJvm11Project: TestInputs = TestInputs(os.rel / "Main.java" -> """|public class Main{ | public static void main(String[] args) { | java.util.Optional.of(1).isEmpty(); | } |}""".stripMargin) + val javaJvm17Project: TestInputs = + TestInputs(os.rel / "Main.java" -> """|public class Main{ + | public static void main(String[] args) { + | java.util.HexFormat.of().toHexDigits(255); + | } + |}""".stripMargin) + val javaJvm23Project: TestInputs = + TestInputs(os.rel / "Main.java" -> """|public class Main{ + | public static void main(String[] args) { + | System.out.println(javax.print.attribute.standard.OutputBin.LEFT); + | } + |}""".stripMargin) - val inputs: Map[(String, Int), TestInputs] = Map( - ("scala", 8) -> scalaJvm8Project, - ("scala", 11) -> scalaJvm11Project, - ("java", 8) -> javaJvm8Project, - ("java", 11) -> javaJvm11Project - ) + def inputs: Map[(String, Int), TestInputs] = + if isScala38OrNewer + then + Map( + ("scala", 17) -> scalaJvm17Project, + ("scala", 23) -> scalaJvm23Project, + ("java", 17) -> javaJvm17Project, + ("java", 23) -> javaJvm23Project + ) + else + Map( + ("scala", 8) -> scalaJvm8Project, + ("scala", 11) -> scalaJvm11Project, + ("scala", 17) -> scalaJvm17Project, + ("scala", 23) -> scalaJvm23Project, + ("java", 8) -> javaJvm8Project, + ("java", 11) -> javaJvm11Project, + ("java", 17) -> javaJvm17Project, + ("java", 23) -> javaJvm23Project + ) - for { - bloopJvm <- List(8, 11) - targetJvm <- List(8, 11) - ((lang, sourcesJvm), project) <- inputs - } test(s"JvmCompatibilityTest: bloopJvm:$bloopJvm/targetJvm:$targetJvm/lang:$lang/sourcesJvm:$sourcesJvm" - .tag(jvmT)) { - compileToADifferentJvmThanBloops( - bloopJvm.toString, - targetJvm.toString, - targetJvm >= sourcesJvm, - project - ) + { + val legacyJvms = List(8, 11) + val currentJvms = List(17, 23) + val jvms = if isScala38OrNewer then currentJvms else legacyJvms ++ currentJvms + for { + bloopJvm <- jvms + targetJvm <- jvms + ((lang, sourcesJvm), project) <- inputs + } test(s"JvmCompatibilityTest: bloopJvm:$bloopJvm/targetJvm:$targetJvm/lang:$lang/sourcesJvm:$sourcesJvm" + .tag(jvmT)) { + compileToADifferentJvmThanBloops( + bloopJvm.toString, + targetJvm.toString, + targetJvm >= sourcesJvm, + project + ) + } } test("Scala CLI should not infer scalac --release if --release is passed".tag(jvmT)) { - scalaJvm11Project.fromRoot { root => + scalaJvm23Project.fromRoot { root => val res = os.proc( TestUtil.cli, "compile", extraOptions, "--jvm", - "11", + "23", "-release", - "8", + "17", "." ).call(cwd = root, check = false, stderr = os.Pipe) expect(res.exitCode != 0) val errOutput = res.err.trim() - expect(errOutput.contains("isEmpty is not a member")) + System.err.println(errOutput) + expect(errOutput.contains("OutputBin is not a member")) expect(errOutput.contains( - "Warning: different target JVM (11) and scala compiler target JVM (8) were passed." + "Warning: different target JVM (23) and scala compiler target JVM (17) were passed." )) } } @@ -426,12 +463,13 @@ abstract class CompileTestDefinitions ) val res = os.proc(TestUtil.cli, "compile", extraOptions, "--jvm", targetJvm, ".") .call(cwd = root, check = false, stderr = os.Pipe) - expect((res.exitCode == 0) == shouldSucceed) - if (!shouldSucceed) + val succeeded = res.exitCode == 0 + if succeeded != shouldSucceed then System.err.println(res.err.text()) + expect(succeeded == shouldSucceed) + if !shouldSucceed then expect( - res.err.text().contains("value isEmpty is not a member") || res.err.text().contains( - "cannot find symbol" - ) + res.err.text().contains("is not a member") || + res.err.text().contains("cannot find symbol") ) } if (actualScalaVersion.startsWith("2.12")) @@ -743,7 +781,7 @@ abstract class CompileTestDefinitions val filename = "Main.scala" val inputs = TestInputs( os.rel / filename -> - """|object Main extends App { + """|object Main { | val msg: String = "1" |} |""".stripMargin @@ -755,17 +793,16 @@ abstract class CompileTestDefinitions mergeErrIntoOut = true ) - assertEquals( - TestUtil.fullStableOutput(result), - s"""|Compiling project (Scala $actualScalaVersion, JVM (${Constants - .defaultGraalVMJavaVersion})) - |Compiled project (Scala $actualScalaVersion, JVM (${Constants - .defaultGraalVMJavaVersion}))""".stripMargin - ) + val jvmVersion = Constants.defaultGraalVMJavaVersion + val expectedOutput = + s"""|Compiling project (Scala $actualScalaVersion, JVM ($jvmVersion)) + |Compiled project (Scala $actualScalaVersion, JVM ($jvmVersion))""".stripMargin + val actualOutput = TestUtil.fullStableOutput(result) + assertEquals(actualOutput, expectedOutput) os.write.over( root / filename, - """|object Main extends App { + """|object Main { | val msg: String = 1 |} |""".stripMargin @@ -777,30 +814,29 @@ abstract class CompileTestDefinitions mergeErrIntoOut = true ) - val expectedError = if (actualScalaVersion.startsWith("2")) - """|[error] type mismatch; - |[error] found : Int(1) - |[error] required: String""".stripMargin - else - """|[error] Found: (1 : Int) - |[error] Required: String""".stripMargin + val expectedError = + if actualScalaVersion.startsWith("2") then + """|[error] type mismatch; + |[error] found : Int(1) + |[error] required: String""".stripMargin + else + """|[error] Found: (1 : Int) + |[error] Required: String""".stripMargin - assertEquals( - TestUtil.fullStableOutput(result2).trim, - s"""|Compiling project (Scala $actualScalaVersion, JVM (${Constants - .defaultGraalVMJavaVersion})) + val actualOutput2 = TestUtil.fullStableOutput(result2).trim + val expectedOutput2 = + s"""|Compiling project (Scala $actualScalaVersion, JVM ($jvmVersion)) |[error] .${File.separatorChar}Main.scala:2:23 |$expectedError |[error] val msg: String = 1 |[error] ^ - |Error compiling project (Scala $actualScalaVersion, JVM (${Constants - .defaultGraalVMJavaVersion})) + |Error compiling project (Scala $actualScalaVersion, JVM ($jvmVersion)) |Compilation failed""".stripMargin - ) + assertEquals(actualOutput2, expectedOutput2) os.write.over( root / filename, - """|object Main extends App { + """|object Main { | val msg: String = "1" |} |""".stripMargin @@ -812,14 +848,11 @@ abstract class CompileTestDefinitions mergeErrIntoOut = true ) - assertEquals( - TestUtil.fullStableOutput(result3), - s"""|Compiling project (Scala $actualScalaVersion, JVM (${Constants - .defaultGraalVMJavaVersion})) - |Compiled project (Scala $actualScalaVersion, JVM (${Constants - .defaultGraalVMJavaVersion}))""".stripMargin - ) - + val actualOutput3 = TestUtil.fullStableOutput(result3) + val expectedOutput3 = + s"""|Compiling project (Scala $actualScalaVersion, JVM ($jvmVersion)) + |Compiled project (Scala $actualScalaVersion, JVM ($jvmVersion))""".stripMargin + assertEquals(actualOutput3, expectedOutput3) } } diff --git a/modules/integration/src/test/scala/scala/cli/integration/PublishTestDefinitions.scala b/modules/integration/src/test/scala/scala/cli/integration/PublishTestDefinitions.scala index 8d29409993..6691fe8f02 100644 --- a/modules/integration/src/test/scala/scala/cli/integration/PublishTestDefinitions.scala +++ b/modules/integration/src/test/scala/scala/cli/integration/PublishTestDefinitions.scala @@ -189,109 +189,118 @@ abstract class PublishTestDefinitions extends ScalaCliSuite with TestScalaVersio } } - test("simple sign with external JVM process, java version too low") { - TestUtil.retryOnCi() { - val publicKey = { - val uri = Thread.currentThread().getContextClassLoader - .getResource("test-keys/key.asc") - .toURI - os.Path(Paths.get(uri)) - } - val secretKey = { - val uri = Thread.currentThread().getContextClassLoader - .getResource("test-keys/key.skr") - .toURI - os.Path(Paths.get(uri)) - } - - val signingOptions = Seq( - "--secret-key", - s"file:$secretKey", - "--secret-key-password", - "value:1234", - "--signer", - "bc", - "--force-signing-externally", - "--force-jvm-signing-cli" - ) - - val java8Home = - os.Path(os.proc(TestUtil.cs, "java-home", "--jvm", "zulu:8").call().out.trim(), os.pwd) - - val extraEnv = Map( - "JAVA_HOME" -> java8Home.toString, - "PATH" -> ((java8Home / "bin").toString + File.pathSeparator + System.getenv("PATH")) - ) - - TestCase.testInputs().fromRoot { root => - val publishRes = os.proc( - TestUtil.cli, - "--power", - "publish", - extraOptions, - signingOptions, - "project", - "-R", - "test-repo", - "-v", - "-v", - "-v" - ).call( - cwd = root, - mergeErrIntoOut = true, - env = extraEnv + if !isScala38OrNewer then { + // I'm not sure this test is even relevant anymore, but in the current shape it won't pass for 3.8+ + // TODO potentially consider if we want to port this to 3.8 + test("simple sign with external JVM process, java version too low") { + TestUtil.retryOnCi() { + val publicKey = { + val uri = Thread.currentThread().getContextClassLoader + .getResource("test-keys/key.asc") + .toURI + os.Path(Paths.get(uri)) + } + val secretKey = { + val uri = Thread.currentThread().getContextClassLoader + .getResource("test-keys/key.skr") + .toURI + os.Path(Paths.get(uri)) + } + + val signingOptions = Seq( + "--secret-key", + s"file:$secretKey", + "--secret-key-password", + "value:1234", + "--signer", + "bc", + "--force-signing-externally", + "--force-jvm-signing-cli" ) - val javaCommandOpt = publishRes.out.text() - .linesIterator - .find(_.contains("Running command ")) - - expect(javaCommandOpt.isDefined) - expect(javaCommandOpt.get.contains(" -cp,")) - expect(javaCommandOpt.get.split(" -cp,").headOption.exists(_.contains("17"))) + val java8Home = + os.Path(os.proc(TestUtil.cs, "java-home", "--jvm", "zulu:8").call().out.trim(), os.pwd) - val files = os.walk(root / "test-repo") - .filter(os.isFile(_)) - .map(_.relativeTo(root / "test-repo")) - val notInDir = files.filter(!_.startsWith(TestCase.expectedArtifactsDir)) - expect(notInDir.isEmpty) - - val files0 = files.map(_.relativeTo(TestCase.expectedArtifactsDir)).toSet - - expect((files0 -- expectedArtifacts).isEmpty) - expect((expectedArtifacts -- files0).isEmpty) - expect(files0 == expectedArtifacts) // just in case… - - val repoArgs = - Seq[os.Shellable]("-r", "!central", "-r", (root / "test-repo").toNIO.toUri.toASCIIString) - val dep = s"org.virtuslab.scalacli.test:simple${TestCase.scalaSuffix}:0.2.0-SNAPSHOT" - val res = os.proc(TestUtil.cs, "launch", repoArgs, dep).call(cwd = root) - val output = res.out.trim() - expect(output == "Hello") + val extraEnv = Map( + "JAVA_HOME" -> java8Home.toString, + "PATH" -> ((java8Home / "bin").toString + File.pathSeparator + System.getenv("PATH")) + ) - val sourceJarViaCsStr = - os.proc(TestUtil.cs, "fetch", repoArgs, "--sources", "--intransitive", dep) - .call(cwd = root) - .out.trim() - val sourceJarViaCs = os.Path(sourceJarViaCsStr, os.pwd) - val zf = new ZipFile(sourceJarViaCs.toIO) - val entries = zf.entries().asScala.toVector.map(_.getName).toSet - expect(entries == expectedSourceEntries()) + TestCase.testInputs().fromRoot { root => + val publishRes = os.proc( + TestUtil.cli, + "--power", + "publish", + extraOptions, + signingOptions, + "project", + "-R", + "test-repo", + "-v", + "-v", + "-v" + ).call( + cwd = root, + mergeErrIntoOut = true, + env = extraEnv + ) - val signatures = expectedArtifacts.filter(_.last.endsWith(".asc")) - assert(signatures.nonEmpty) - val verifyProc = os.proc( - TestUtil.cli, - "--power", - "pgp", - "verify", - "--key", - publicKey, - signatures.map(os.rel / "test-repo" / TestCase.expectedArtifactsDir / _) - ) - .call(cwd = root, mergeErrIntoOut = true) + val javaCommandOpt = publishRes.out.text() + .linesIterator + .find(_.contains("Running command ")) + + expect(javaCommandOpt.isDefined) + expect(javaCommandOpt.get.contains(" -cp,")) + expect(javaCommandOpt.get.split(" -cp,").headOption.exists(_.contains("17"))) + + val files = os.walk(root / "test-repo") + .filter(os.isFile(_)) + .map(_.relativeTo(root / "test-repo")) + val notInDir = files.filter(!_.startsWith(TestCase.expectedArtifactsDir)) + expect(notInDir.isEmpty) + + val files0 = files.map(_.relativeTo(TestCase.expectedArtifactsDir)).toSet + + expect((files0 -- expectedArtifacts).isEmpty) + expect((expectedArtifacts -- files0).isEmpty) + expect(files0 == expectedArtifacts) // just in case… + + val repoArgs = + Seq[os.Shellable]( + "-r", + "!central", + "-r", + (root / "test-repo").toNIO.toUri.toASCIIString + ) + val dep = s"org.virtuslab.scalacli.test:simple${TestCase.scalaSuffix}:0.2.0-SNAPSHOT" + val res = os.proc(TestUtil.cs, "launch", repoArgs, dep).call(cwd = root) + val output = res.out.trim() + expect(output == "Hello") + + val sourceJarViaCsStr = + os.proc(TestUtil.cs, "fetch", repoArgs, "--sources", "--intransitive", dep) + .call(cwd = root) + .out.trim() + val sourceJarViaCs = os.Path(sourceJarViaCsStr, os.pwd) + val zf = new ZipFile(sourceJarViaCs.toIO) + val entries = zf.entries().asScala.toVector.map(_.getName).toSet + expect(entries == expectedSourceEntries()) + + val signatures = expectedArtifacts.filter(_.last.endsWith(".asc")) + assert(signatures.nonEmpty) + val verifyProc = os.proc( + TestUtil.cli, + "--power", + "pgp", + "verify", + "--key", + publicKey, + signatures.map(os.rel / "test-repo" / TestCase.expectedArtifactsDir / _) + ) + .call(cwd = root, mergeErrIntoOut = true) - expect(!verifyProc.out.text().contains(s"invalid signature")) + expect(!verifyProc.out.text().contains(s"invalid signature")) + } } } } diff --git a/modules/integration/src/test/scala/scala/cli/integration/RunJdkTestDefinitions.scala b/modules/integration/src/test/scala/scala/cli/integration/RunJdkTestDefinitions.scala index 86e8297d7c..ffb7238a00 100644 --- a/modules/integration/src/test/scala/scala/cli/integration/RunJdkTestDefinitions.scala +++ b/modules/integration/src/test/scala/scala/cli/integration/RunJdkTestDefinitions.scala @@ -10,9 +10,12 @@ trait RunJdkTestDefinitions { this: RunTestDefinitions => actualScalaVersion.startsWith("3") && actualScalaVersion.split('.').drop(1).head.toInt >= 5 for { - javaVersion <- - if (!TestUtil.isJvmCli) Constants.allJavaVersions - else Constants.allJavaVersions.filter(_ >= Constants.minimumLauncherJavaVersion) + javaVersion <- isScala38OrNewer -> TestUtil.isJvmCli match { + case (false, true) => + Constants.allJavaVersions.filter(_ >= Constants.minimumLauncherJavaVersion) + case (true, _) => Constants.allJavaVersions.filter(_ >= Constants.defaultJvmVersion) + case _ => Constants.allJavaVersions + } index = javaVersion useScalaInstallationWrapper <- if (canUseScalaInstallationWrapper) Seq(false, true) else Seq(false) diff --git a/modules/integration/src/test/scala/scala/cli/integration/RunScalaPyTestDefinitions.scala b/modules/integration/src/test/scala/scala/cli/integration/RunScalaPyTestDefinitions.scala index b8bb80cc7e..48c94d3ae8 100644 --- a/modules/integration/src/test/scala/scala/cli/integration/RunScalaPyTestDefinitions.scala +++ b/modules/integration/src/test/scala/scala/cli/integration/RunScalaPyTestDefinitions.scala @@ -160,7 +160,8 @@ trait RunScalaPyTestDefinitions { this: RunTestDefinitions => } // disabled on Windows for now, for context, see // https://github.com/VirtusLab/scala-cli/pull/1270#issuecomment-1237904394 - if (!Properties.isWin) + // disabled on Scala 3.8 and above, behavior seems to have changed here + if !Properties.isWin && !isScala38OrNewer then test("Python and Scala sources (native)") { pythonAndScalaSourcesTest(native = true) } diff --git a/modules/integration/src/test/scala/scala/cli/integration/RunScriptTestDefinitions.scala b/modules/integration/src/test/scala/scala/cli/integration/RunScriptTestDefinitions.scala index 56897679a6..00a2df485d 100644 --- a/modules/integration/src/test/scala/scala/cli/integration/RunScriptTestDefinitions.scala +++ b/modules/integration/src/test/scala/scala/cli/integration/RunScriptTestDefinitions.scala @@ -865,7 +865,8 @@ trait RunScriptTestDefinitions { this: RunTestDefinitions => test("verify drive-relative JAVA_HOME works") { TestUtil.retryOnCi() { val jvmIndex = - if (TestUtil.isJvmCli) Constants.minimumLauncherJavaVersion + if TestUtil.isJvmCli && !isScala38OrNewer then Constants.minimumLauncherJavaVersion + else if isScala38OrNewer then Constants.defaultJvmVersion else 8 val oldJavaHome = os.Path(os.proc(TestUtil.cs, "java-home", "--jvm", jvmIndex).call().out.trim(), os.pwd) diff --git a/modules/integration/src/test/scala/scala/cli/integration/RunTestDefinitions.scala b/modules/integration/src/test/scala/scala/cli/integration/RunTestDefinitions.scala index 39b841120f..304c626ab0 100644 --- a/modules/integration/src/test/scala/scala/cli/integration/RunTestDefinitions.scala +++ b/modules/integration/src/test/scala/scala/cli/integration/RunTestDefinitions.scala @@ -215,15 +215,15 @@ abstract class RunTestDefinitions "--java-prop", "scala.colored-stack-traces=false" ) - val res = os.proc(cmd).call(cwd = root, check = false, mergeErrIntoOut = true) - val output = res.out.lines() + val res = os.proc(cmd).call(cwd = root, check = false, mergeErrIntoOut = true) + val output: Vector[String] = TestUtil.fullStableOutputLines(res) // FIXME We need to have the pretty-stacktraces stuff take scala.colored-stack-traces into account val exceptionLines = output.map(stripAnsi).dropWhile(!_.startsWith("Exception in thread ")) val tab = "\t" val expectedLines = - if (actualScalaVersion.startsWith("2.12.")) + if actualScalaVersion.startsWith("2.12.") then s"""Exception in thread "main" java.lang.Exception: Caught exception during processing |${tab}at Throws$$.main(Throws.scala:8) |${tab}at Throws.main(Throws.scala) @@ -233,7 +233,17 @@ abstract class RunTestDefinitions |${tab}at Throws$$.main(Throws.scala:5) |$tab... 1 more |""".stripMargin.linesIterator.toVector - else if (actualScalaVersion.startsWith("3.") || actualScalaVersion.startsWith("2.13.")) + else if isScala38OrNewer then + s"""Exception in thread "main" java.lang.Exception: Caught exception during processing + |${tab}at Throws$$.main(Throws.scala:8) + |${tab}at Throws.main(Throws.scala) + |Caused by: java.lang.RuntimeException: nope + |${tab}at scala.sys.package$$.error(package.scala:28) + |${tab}at Throws$$.something(Throws.scala:3) + |${tab}at Throws$$.main(Throws.scala:5) + |$tab... 1 more + |""".stripMargin.linesIterator.toVector + else if actualScalaVersion.startsWith("3.") || actualScalaVersion.startsWith("2.13.") then s"""Exception in thread "main" java.lang.Exception: Caught exception during processing |${tab}at Throws$$.main(Throws.scala:8) |${tab}at Throws.main(Throws.scala) @@ -243,9 +253,8 @@ abstract class RunTestDefinitions |${tab}at Throws$$.main(Throws.scala:5) |$tab... 1 more |""".stripMargin.linesIterator.toVector - else - sys.error(s"Unexpected Scala version: $actualScalaVersion") - if (exceptionLines != expectedLines) { + else sys.error(s"Unexpected Scala version: $actualScalaVersion") + if exceptionLines != expectedLines then { pprint.log(exceptionLines) pprint.log(expectedLines) } @@ -1570,11 +1579,12 @@ abstract class RunTestDefinitions } test("BuildInfo fields should be reachable") { + val jvmId = "18" val inputs = TestInputs( os.rel / "Main.scala" -> s"""//> using dep com.lihaoyi::os-lib:0.9.1 |//> using option -Xasync - |//> using jvm 11 + |//> using jvm $jvmId |//> using mainClass Main |//> using resourceDir ./resources |//> using jar TEST1.jar TEST2.jar @@ -1586,7 +1596,7 @@ abstract class RunTestDefinitions |object Main extends App { | assert(BuildInfo.scalaVersion == "$actualScalaVersion") | assert(BuildInfo.platform == "JVM") - | assert(BuildInfo.jvmVersion == Some("11")) + | assert(BuildInfo.jvmVersion == Some("$jvmId")) | assert(BuildInfo.scalaJsVersion == None) | assert(BuildInfo.jsEsVersion == None) | assert(BuildInfo.scalaNativeVersion == None) @@ -2097,13 +2107,14 @@ abstract class RunTestDefinitions test("JVM id is printed with compilation info correctly") { val msg = "Hello" val input = "jvm.sc" + val jvmId = "18" TestInputs(os.rel / input -> - s"""//> using jvm 11 + s"""//> using jvm $jvmId |println("$msg") |""".stripMargin).fromRoot { root => val res = os.proc(TestUtil.cli, "run", extraOptions, input).call(cwd = root, stderr = os.Pipe) expect(res.out.trim() == msg) - expect(res.err.trim().contains("JVM (11)")) + expect(res.err.trim().contains(s"JVM ($jvmId)")) } } diff --git a/modules/integration/src/test/scala/scala/cli/integration/TestScalaVersionArgs.scala b/modules/integration/src/test/scala/scala/cli/integration/TestScalaVersionArgs.scala index 0e92ec6374..fa30d73510 100644 --- a/modules/integration/src/test/scala/scala/cli/integration/TestScalaVersionArgs.scala +++ b/modules/integration/src/test/scala/scala/cli/integration/TestScalaVersionArgs.scala @@ -18,6 +18,11 @@ trait TestScalaVersionArgs extends ScalaCliSuite { this: TestScalaVersion => } lazy val actualScalaVersion: String = scalaVersionOpt.getOrElse(Constants.defaultScala) + + def isScala38OrNewer: Boolean = + Constants.scala38Versions + .map(_.coursierVersion) + .exists(_ <= actualScalaVersion.coursierVersion) } sealed trait TestScalaVersion { this: TestScalaVersionArgs => diff --git a/modules/integration/src/test/scala/scala/cli/integration/TestUtil.scala b/modules/integration/src/test/scala/scala/cli/integration/TestUtil.scala index 939b2dadf8..3386af0f09 100644 --- a/modules/integration/src/test/scala/scala/cli/integration/TestUtil.scala +++ b/modules/integration/src/test/scala/scala/cli/integration/TestUtil.scala @@ -12,6 +12,7 @@ import scala.Console.* import scala.annotation.tailrec import scala.concurrent.duration.{Duration, DurationInt, FiniteDuration} import scala.concurrent.{Await, ExecutionContext, Future} +import scala.jdk.CollectionConverters.* import scala.util.{Properties, Try} object TestUtil { @@ -116,9 +117,9 @@ object TestUtil { if (uri.startsWith("file:///")) "file:/" + uri.stripPrefix("file:///") else uri - def removeAnsiColors(str: String) = str.replaceAll("\\e\\[[0-9]+m", "") + def removeAnsiColors(str: String): String = str.replaceAll("\\e\\[[0-9]+m", "") - def fullStableOutput(result: os.CommandResult) = + def fullStableOutput(result: os.CommandResult): String = removeAnsiColors(result.toString).trim().linesIterator.filterNot { str => // these lines are not stable and can easily change val shouldNotContain = @@ -134,6 +135,9 @@ object TestUtil { shouldNotContain.exists(str.contains) }.mkString(System.lineSeparator()) + def fullStableOutputLines(result: os.CommandResult): Vector[String] = + fullStableOutput(result).lines().toList.asScala.toVector + def retry[T]( maxAttempts: Int = 3, waitDuration: FiniteDuration = 5.seconds diff --git a/modules/test-runner/src/main/scala/scala/build/testrunner/AsmTestRunner.scala b/modules/test-runner/src/main/scala/scala/build/testrunner/AsmTestRunner.scala index 031a45851c..bfffee9d1a 100644 --- a/modules/test-runner/src/main/scala/scala/build/testrunner/AsmTestRunner.scala +++ b/modules/test-runner/src/main/scala/scala/build/testrunner/AsmTestRunner.scala @@ -1,7 +1,7 @@ package scala.build.testrunner import org.objectweb.asm -import sbt.testing.{Logger as _, *} +import sbt.testing.* import java.io.{ByteArrayInputStream, ByteArrayOutputStream, InputStream} import java.nio.charset.StandardCharsets diff --git a/project/deps/package.mill.scala b/project/deps/package.mill.scala index 5f3b8912fc..f6982b7cc1 100644 --- a/project/deps/package.mill.scala +++ b/project/deps/package.mill.scala @@ -1,4 +1,5 @@ package build.project.deps +import coursier.version.Version import mill._ import scalalib._ @@ -19,9 +20,8 @@ object Scala { def scala3Next = s"$scala3NextPrefix.4" // the newest/next version of Scala def scala3NextAnnounced = s"$scala3NextPrefix.3" // the newest/next version of Scala that's been announced - def scala3NextRc = s"$scala3NextPrefix.4-RC3" // the latest RC version of Scala Next - def scala3NextRcAnnounced = - s"$scala3NextPrefix.4-RC1" // the latest announced RC version of Scala Next + def scala3NextRc = "3.8.0-RC1" // the latest RC version of Scala Next + def scala3NextRcAnnounced = "3.7.4-RC3" // the latest announced RC version of Scala Next // The Scala version used to build the CLI itself. def defaultInternal = sys.props.get("scala.version.internal").getOrElse(scala3Lts) @@ -29,6 +29,11 @@ object Scala { // The Scala version used by default to compile user input. def defaultUser = sys.props.get("scala.version.user").getOrElse(scala3Next) + // as Scala 3.8 changes a lot of compatibilities, + // these are the cutoff versions for the stable, RC and nightly series, + // respectively + def scala38Versions = Seq("3.8.0", "3.8.0-RC1", "3.8.0-RC1-bin-20250901-ca400bd-NIGHTLY") + val allScala2 = Seq(scala213, scala212) val defaults = Seq(defaultInternal, defaultUser).distinct val allScala3 = Seq(scala3Lts, scala3Next, scala3NextAnnounced, scala3NextRc).distinct