From 4b2ac53dce04883024c22cb6a53091fb419a4bee Mon Sep 17 00:00:00 2001 From: Kacper Korban Date: Mon, 12 Jul 2021 11:42:27 +0200 Subject: [PATCH 01/10] MainGenericRunner should work almost --- compiler/src/MainGenericRunner.scala | 112 ++++++++++++++++++ compiler/src/dotty/tools/scripting/Main.scala | 4 +- 2 files changed, 114 insertions(+), 2 deletions(-) create mode 100644 compiler/src/MainGenericRunner.scala diff --git a/compiler/src/MainGenericRunner.scala b/compiler/src/MainGenericRunner.scala new file mode 100644 index 000000000000..bc06bfcbfb87 --- /dev/null +++ b/compiler/src/MainGenericRunner.scala @@ -0,0 +1,112 @@ +package dotty.tools + +import scala.annotation.tailrec +import scala.io.Source +import scala.util.Try +import scala.util.matching.Regex +import java.net.URLClassLoader + +enum ExecuteMode: + case Guess + case Script + case Repl + case Run + +case class Settings( + verbose: Boolean = false, + classPath: List[String] = List.empty, + executeMode: ExecuteMode = ExecuteMode.Guess, + exitCode: Int = 0, + residualArgs: List[String] = List.empty, + scriptArgs: List[String] = List.empty, + targetScript: String = "", +) { + def withExecuteMode(em: ExecuteMode): Settings = this.executeMode match + case ExecuteMode.Guess => + this.copy(executeMode = em) + case _ => + println(s"execute_mode==[$executeMode], attempted overwrite by [$em]") + this.copy(exitCode = 1) + end withExecuteMode + + def withResidualArgs(args: String*): Settings = + this.copy(residualArgs = residualArgs.appendedAll(args.toList)) + + def withScriptArgs(args: String*): Settings = + this.copy(scriptArgs = scriptArgs.appendedAll(args.toList)) + + def withTargetScript(file: String): Settings = + Try(Source.fromFile(file)).toOption match + case Some(_) => this.copy(targetScript = file) + case None => + println(s"not found $file") + this.copy(exitCode = 2) + end withTargetScript +} + +object MainGenericRunner { + + val shebangscala: Regex = """#!.*scala""".r + + @tailrec + def process(args: List[String], settings: Settings): Settings = args match + case Nil => + settings + case "-repl" :: tail => + process(tail, settings.withExecuteMode(ExecuteMode.Repl)) + case "-run" :: tail => + process(tail, settings.withExecuteMode(ExecuteMode.Run)) + case ("-cp" | "-classpath" | "--classpath") :: cp :: tail => + process(tail, settings.copy(classPath = settings.classPath.appended(cp))) + case ("-version" | "--version") :: _ => + settings.copy( + executeMode = ExecuteMode.Repl, + residualArgs = List("-version") + ) + case ("-v" | "-verbose" | "--verbose") :: tail => + process( + tail, + settings.copy( + verbose = true, + residualArgs = settings.residualArgs :+ "-verbose" + ) + ) + case arg :: tail => + val line = Try(Source.fromFile(arg).getLines.toList).toOption.flatMap(_.headOption) + val newSettings = if arg.endsWith(".scala") || arg.endsWith(".sc") || (line.nonEmpty && shebangscala.matches(line.get)) + then { + settings + .withExecuteMode(ExecuteMode.Script) + .withTargetScript(arg) + .withScriptArgs(tail*) + } else + settings.withResidualArgs(arg) + process(tail, newSettings.withResidualArgs(arg)) + + def main(args: Array[String]): Unit = + val settings = process(args.toList, Settings()) + if settings.exitCode != 0 then System.exit(settings.exitCode) + settings.executeMode match + case ExecuteMode.Repl => + val properArgs = + List("-classpath", settings.classPath.mkString(";")).filter(Function.const(settings.classPath.nonEmpty)) + ++ settings.residualArgs + repl.Main.main(properArgs.toArray) + case ExecuteMode.Run => + val properArgs = + List("-classpath", settings.classPath.mkString(";")).filter(Function.const(settings.classPath.nonEmpty)) + ++ settings.residualArgs + //TODO this is just a java proxy? + case ExecuteMode.Script => + val properArgs = + List("classpath", settings.classPath.mkString(";")).filter(Function.const(settings.classPath.nonEmpty)) + ++ settings.residualArgs + ++ List("-script", settings.targetScript) + ++ settings.scriptArgs + scripting.Main.main(properArgs.toArray) + case ExecuteMode.Guess => + val properArgs = + List("-classpath", settings.classPath.mkString(";")).filter(Function.const(settings.classPath.nonEmpty)) + ++ settings.residualArgs + repl.Main.main(properArgs.toArray) +} diff --git a/compiler/src/dotty/tools/scripting/Main.scala b/compiler/src/dotty/tools/scripting/Main.scala index 97d0b91d4440..70e606608d0b 100755 --- a/compiler/src/dotty/tools/scripting/Main.scala +++ b/compiler/src/dotty/tools/scripting/Main.scala @@ -2,7 +2,7 @@ package dotty.tools.scripting import java.io.File import java.nio.file.{Path, Paths} -import dotty.tools.dotc.config.Properties.isWin +import dotty.tools.dotc.config.Properties.isWin /** Main entry point to the Scripting execution engine */ object Main: @@ -87,7 +87,7 @@ object Main: case s if s.startsWith("./") => s.drop(2) case s => s } - + // convert to absolute path relative to cwd. def absPath: String = norm match case str if str.isAbsolute => norm From 2c46c3e43816ca53281ee4877b535316d48597c3 Mon Sep 17 00:00:00 2001 From: Andrzej Ratajczak Date: Thu, 15 Jul 2021 17:56:31 +0200 Subject: [PATCH 02/10] Add coursier tests --- .github/workflows/ci.yaml | 2 +- .gitignore | 3 + compiler/src/MainGenericRunner.scala | 16 ++-- .../tools/coursier/CoursierScalaTests.scala | 79 +++++++++++++++++++ compiler/test/dotty/tools/utils.scala | 6 ++ project/Build.scala | 1 + 6 files changed, 97 insertions(+), 10 deletions(-) create mode 100644 compiler/test/dotty/tools/coursier/CoursierScalaTests.scala diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 4f5216a1f9dc..dac8088caefc 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -111,7 +111,7 @@ jobs: - name: Cmd Tests run: | - ./project/scripts/sbt ";scala3-bootstrapped/compile ;scala3-bootstrapped/test;sjsSandbox/run;sjsSandbox/test;sjsJUnitTests/test;sjsCompilerTests/test ;sbt-test/scripted scala2-compat/* ;configureIDE ;stdlib-bootstrapped/test:run ;stdlib-bootstrapped-tasty-tests/test" + ./project/scripts/sbt ";scala3-bootstrapped/compile ; scala3-bootstrapped/publishLocal; scala3-bootstrapped/test;sjsSandbox/run;sjsSandbox/test;sjsJUnitTests/test;sjsCompilerTests/test ;sbt-test/scripted scala2-compat/* ;configureIDE ;stdlib-bootstrapped/test:run ;stdlib-bootstrapped-tasty-tests/test" ./project/scripts/bootstrapCmdTests - name: MiMa diff --git a/.gitignore b/.gitignore index 0ba8e15fd3f3..dfcaa9e52bee 100644 --- a/.gitignore +++ b/.gitignore @@ -96,3 +96,6 @@ community-build/dotty-community-build-deps # Bloop .bsp + +# Coursier +cs diff --git a/compiler/src/MainGenericRunner.scala b/compiler/src/MainGenericRunner.scala index bc06bfcbfb87..5e6c8048976e 100644 --- a/compiler/src/MainGenericRunner.scala +++ b/compiler/src/MainGenericRunner.scala @@ -73,15 +73,13 @@ object MainGenericRunner { ) case arg :: tail => val line = Try(Source.fromFile(arg).getLines.toList).toOption.flatMap(_.headOption) - val newSettings = if arg.endsWith(".scala") || arg.endsWith(".sc") || (line.nonEmpty && shebangscala.matches(line.get)) - then { - settings - .withExecuteMode(ExecuteMode.Script) - .withTargetScript(arg) - .withScriptArgs(tail*) - } else - settings.withResidualArgs(arg) - process(tail, newSettings.withResidualArgs(arg)) + if arg.endsWith(".scala") || arg.endsWith(".sc") || (line.nonEmpty && shebangscala.matches(line.get)) then + settings + .withExecuteMode(ExecuteMode.Script) + .withTargetScript(arg) + .withScriptArgs(tail*) + else + process(tail, settings.withResidualArgs(arg)) def main(args: Array[String]): Unit = val settings = process(args.toList, Settings()) diff --git a/compiler/test/dotty/tools/coursier/CoursierScalaTests.scala b/compiler/test/dotty/tools/coursier/CoursierScalaTests.scala new file mode 100644 index 000000000000..bf107a4ceee9 --- /dev/null +++ b/compiler/test/dotty/tools/coursier/CoursierScalaTests.scala @@ -0,0 +1,79 @@ +package dotty +package tools +package scripting + +import java.io.File +import java.nio.file.{Path, Paths, Files} +import scala.sys.process._ + +import org.junit.Test +import org.junit.BeforeClass + +import vulpix.TestConfiguration + +import dotty.tools.absPath +import scala.collection.mutable.ListBuffer + +class CoursierScalaTests: + + // classpath tests are managed by scripting.ClasspathTests.scala + def testFiles = scripts("/scripting").filter { ! _.getName.startsWith("classpath") } + + // Cannot run tests in parallel, more info here: https://stackoverflow.com/questions/6345660/java-executing-bash-script-error-26-text-file-busy + @Test def allTests = + def scriptArgs() = + val scriptPath = scripts("/scripting").find(_.getName == "showArgs.sc").get.absPath + val testScriptArgs = Seq("a", "b", "c", "-repl", "-run", "-script", "-debug") + + val args = scriptPath +: testScriptArgs + val output = CoursierScalaTests.csCmd(args*) + val expectedOutput = List( + "arg 0:[a]", + "arg 1:[b]", + "arg 2:[c]", + "arg 3:[-repl]", + "arg 4:[-run]", + "arg 5:[-script]", + "arg 6:[-debug]", + ) + for (line, expect) <- output zip expectedOutput do + printf("expected: %-17s\nactual : %s\n", expect, line) + assert(output == expectedOutput) + scriptArgs() + + def version() = + val output = CoursierScalaTests.csCmd("-version") + assert(output.mkString("\n").contains(sys.env("DOTTY_BOOTSTRAPPED_VERSION"))) + version() + + def emptyArgsEqualsRepl() = + val output = CoursierScalaTests.csCmd() + assert(output.mkString("\n").contains("Unable to create a system terminal")) // Scala attempted to create REPL so we can assume it is working + emptyArgsEqualsRepl() + + def repl() = + val output = CoursierScalaTests.csCmd("-repl") + assert(output.mkString("\n").contains("Unable to create a system terminal")) // Scala attempted to create REPL so we can assume it is working + repl() + + +object CoursierScalaTests: + + def execCmd(command: String, options: String*): List[String] = + val cmd = (command :: options.toList).toSeq.mkString(" ") + val out = new ListBuffer[String] + cmd.!(ProcessLogger(out += _, out += _)) + out.toList + + + def csCmd(options: String*): List[String] = + val newOptions = options match + case Nil => options + case _ => "--" +: options + execCmd("./cs", (s"""launch "org.scala-lang:scala3-compiler_3:${sys.env("DOTTY_BOOTSTRAPPED_VERSION")}" --main-class "dotty.tools.MainGenericRunner" --property "scala.usejavacp=true"""" +: newOptions)*) + + /** Get coursier script */ + @BeforeClass def setup(): Unit = + val ver = execCmd("uname").head.replace('L', 'l').replace('D', 'd') + execCmd("curl", s"-fLo cs https://git.io/coursier-cli-$ver") #&& execCmd("chmod", "+x cs") + diff --git a/compiler/test/dotty/tools/utils.scala b/compiler/test/dotty/tools/utils.scala index 6d5677f0e8f8..720a9dffd80d 100644 --- a/compiler/test/dotty/tools/utils.scala +++ b/compiler/test/dotty/tools/utils.scala @@ -15,6 +15,12 @@ def scripts(path: String): Array[File] = { dir.listFiles } +extension (f: File) def absPath = + f.getAbsolutePath.replace('\\', '/') + +extension (str: String) def dropExtension = + str.reverse.dropWhile(_ != '.').drop(1).reverse + private def withFile[T](file: File)(action: Source => T): T = resource(Source.fromFile(file, UTF_8.name))(action) diff --git a/project/Build.scala b/project/Build.scala index 9527c1449782..690a2cbc695e 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -190,6 +190,7 @@ object Build { // Avoid various sbt craziness involving classloaders and parallelism run / fork := true, Test / fork := true, + Test / envVars := Map("DOTTY_BOOTSTRAPPED_VERSION" -> dottyVersion), Test / parallelExecution := false, outputStrategy := Some(StdoutOutput), From 630579b4f31568d5f9af4f6663e2b5d1f87107e5 Mon Sep 17 00:00:00 2001 From: Andrzej Ratajczak Date: Fri, 16 Jul 2021 11:24:36 +0200 Subject: [PATCH 03/10] Fix tests --- .github/workflows/ci.yaml | 2 +- compiler/src/{ => dotty/tools}/MainGenericRunner.scala | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) rename compiler/src/{ => dotty/tools}/MainGenericRunner.scala (96%) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index dac8088caefc..892bb48e3831 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -111,7 +111,7 @@ jobs: - name: Cmd Tests run: | - ./project/scripts/sbt ";scala3-bootstrapped/compile ; scala3-bootstrapped/publishLocal; scala3-bootstrapped/test;sjsSandbox/run;sjsSandbox/test;sjsJUnitTests/test;sjsCompilerTests/test ;sbt-test/scripted scala2-compat/* ;configureIDE ;stdlib-bootstrapped/test:run ;stdlib-bootstrapped-tasty-tests/test" + ./project/scripts/sbt ";scala3-bootstrapped/compile; scala3-bootstrapped/publishLocal; scala3-bootstrapped/test;sjsSandbox/run;sjsSandbox/test;sjsJUnitTests/test;sjsCompilerTests/test ;sbt-test/scripted scala2-compat/* ;configureIDE ;stdlib-bootstrapped/test:run ;stdlib-bootstrapped-tasty-tests/test" ./project/scripts/bootstrapCmdTests - name: MiMa diff --git a/compiler/src/MainGenericRunner.scala b/compiler/src/dotty/tools/MainGenericRunner.scala similarity index 96% rename from compiler/src/MainGenericRunner.scala rename to compiler/src/dotty/tools/MainGenericRunner.scala index 5e6c8048976e..a72f11e7a604 100644 --- a/compiler/src/MainGenericRunner.scala +++ b/compiler/src/dotty/tools/MainGenericRunner.scala @@ -1,9 +1,9 @@ package dotty.tools + import scala.annotation.tailrec import scala.io.Source import scala.util.Try -import scala.util.matching.Regex import java.net.URLClassLoader enum ExecuteMode: @@ -46,8 +46,6 @@ case class Settings( object MainGenericRunner { - val shebangscala: Regex = """#!.*scala""".r - @tailrec def process(args: List[String], settings: Settings): Settings = args match case Nil => @@ -73,7 +71,7 @@ object MainGenericRunner { ) case arg :: tail => val line = Try(Source.fromFile(arg).getLines.toList).toOption.flatMap(_.headOption) - if arg.endsWith(".scala") || arg.endsWith(".sc") || (line.nonEmpty && shebangscala.matches(line.get)) then + if arg.endsWith(".scala") || arg.endsWith(".sc") || (line.nonEmpty && raw"#!.*scala".r.matches(line.get)) then settings .withExecuteMode(ExecuteMode.Script) .withTargetScript(arg) From 216b70b5663b233fe408e53f221c04a6c4ae667c Mon Sep 17 00:00:00 2001 From: Andrzej Ratajczak Date: Fri, 16 Jul 2021 16:17:04 +0200 Subject: [PATCH 04/10] Handle run setting in scala script --- .../src/dotty/tools/MainGenericRunner.scala | 36 +++++++++++++++--- compiler/test-resources/run/myfile$.class | Bin 0 -> 2210 bytes compiler/test-resources/run/myfile.class | Bin 0 -> 664 bytes compiler/test-resources/run/myfile.scala | 2 + compiler/test-resources/run/myfile.tasty | Bin 0 -> 514 bytes .../tools/coursier/CoursierScalaTests.scala | 4 ++ 6 files changed, 37 insertions(+), 5 deletions(-) create mode 100644 compiler/test-resources/run/myfile$.class create mode 100644 compiler/test-resources/run/myfile.class create mode 100644 compiler/test-resources/run/myfile.scala create mode 100644 compiler/test-resources/run/myfile.tasty diff --git a/compiler/src/dotty/tools/MainGenericRunner.scala b/compiler/src/dotty/tools/MainGenericRunner.scala index a72f11e7a604..ed4e7b4f1417 100644 --- a/compiler/src/dotty/tools/MainGenericRunner.scala +++ b/compiler/src/dotty/tools/MainGenericRunner.scala @@ -5,6 +5,9 @@ import scala.annotation.tailrec import scala.io.Source import scala.util.Try import java.net.URLClassLoader +import sys.process._ +import java.io.File +import java.lang.Thread enum ExecuteMode: case Guess @@ -20,6 +23,7 @@ case class Settings( residualArgs: List[String] = List.empty, scriptArgs: List[String] = List.empty, targetScript: String = "", + areWithCompiler: Boolean = false, ) { def withExecuteMode(em: ExecuteMode): Settings = this.executeMode match case ExecuteMode.Guess => @@ -42,10 +46,15 @@ case class Settings( println(s"not found $file") this.copy(exitCode = 2) end withTargetScript + + def withCompiler: Settings = + this.copy(areWithCompiler = true) } object MainGenericRunner { + final val classpathSeparator = ":" + @tailrec def process(args: List[String], settings: Settings): Settings = args match case Nil => @@ -69,6 +78,8 @@ object MainGenericRunner { residualArgs = settings.residualArgs :+ "-verbose" ) ) + case "-with-compiler" :: tail => + process(tail, settings.withCompiler) case arg :: tail => val line = Try(Source.fromFile(arg).getLines.toList).toOption.flatMap(_.headOption) if arg.endsWith(".scala") || arg.endsWith(".sc") || (line.nonEmpty && raw"#!.*scala".r.matches(line.get)) then @@ -85,24 +96,39 @@ object MainGenericRunner { settings.executeMode match case ExecuteMode.Repl => val properArgs = - List("-classpath", settings.classPath.mkString(";")).filter(Function.const(settings.classPath.nonEmpty)) + List("-classpath", settings.classPath.mkString(classpathSeparator)).filter(Function.const(settings.classPath.nonEmpty)) ++ settings.residualArgs repl.Main.main(properArgs.toArray) case ExecuteMode.Run => val properArgs = - List("-classpath", settings.classPath.mkString(";")).filter(Function.const(settings.classPath.nonEmpty)) + val newClasspath = settings.classPath ++ getClasspath :+ "." + List("-classpath", newClasspath.mkString(classpathSeparator)).filter(Function.const(newClasspath.nonEmpty)) ++ settings.residualArgs - //TODO this is just a java proxy? + s"java ${properArgs.mkString(" ")}".! // For now we collect classpath that coursier provides for convenience case ExecuteMode.Script => val properArgs = - List("classpath", settings.classPath.mkString(";")).filter(Function.const(settings.classPath.nonEmpty)) + List("-classpath", settings.classPath.mkString(classpathSeparator)).filter(Function.const(settings.classPath.nonEmpty)) ++ settings.residualArgs ++ List("-script", settings.targetScript) ++ settings.scriptArgs scripting.Main.main(properArgs.toArray) case ExecuteMode.Guess => val properArgs = - List("-classpath", settings.classPath.mkString(";")).filter(Function.const(settings.classPath.nonEmpty)) + List("-classpath", settings.classPath.mkString(classpathSeparator)).filter(Function.const(settings.classPath.nonEmpty)) ++ settings.residualArgs repl.Main.main(properArgs.toArray) + + + private def getClasspath(cl: ClassLoader): Array[String] = cl match + case null => Array() + case u: URLClassLoader => u.getURLs.map(_.toURI.toString) ++ getClasspath(cl.getParent) + case cl if cl.getClass.getName == "jdk.internal.loader.ClassLoaders$AppClassLoader" => + // Required with JDK >= 9 + sys.props.getOrElse("java.class.path", "") + .split(File.pathSeparator) + .filter(_.nonEmpty) + case _ => getClasspath(cl.getParent) + + private def getClasspath: List[String] = + getClasspath(Thread.currentThread().getContextClassLoader).toList } diff --git a/compiler/test-resources/run/myfile$.class b/compiler/test-resources/run/myfile$.class new file mode 100644 index 0000000000000000000000000000000000000000..45c3bf51e8bc60f12b0720a771a3fde95a819f2a GIT binary patch literal 2210 zcma)6ZBr9h7(F-Sg@hGCL|UyaYNIC55~)~UKm|cm8iX35rM9KZ=7Q^Nvgu~iLjO}| zT9_$Jo#_wgkLvW?yPISk873c+`?AkD&w1YNzyJOH4}fWy0;$^mj_t^N3PK?J#{9)B zIc9ygw7UI9T7C+B0_mn@I%a9U(MTa7FrtlYw^WgyZ94XE=C&hKNC_AL*QB-=7?gX` zYWcQXulT0t3y5X9&>!<8%jdUDZ?`FM_Eov-x8i$teRqbp5ktFf`wMPW3S2D*Vl3Bj zsE^#GTFX}oN@ct0KWgplNRQ~$%Ie~J`Ej1=%VC7PP0lK&2LgSC;)a17&SntDh(O$b zZBt4AtmSBn3}P5Dki~GCyYocm)jjD33!wr=Fr!F5!o>k3@v*>}K-!uot8#}49TOM| zjl;VcMR_@mOSmGCcq$#o?FUIy4F>Hyb%Aq*NG^gJXfM}rJwsT57J(P9Rrl?hEHSmd zZ8cLEXZ}6uNV6%IWZjYmK2zaM;By`BvVjZ+R5Uk~m*NRIH(=n5vbZU5y-@5a(*_1H zq%dEyF(MVyrQOQQp9Mw>C-^FEP}N?3iWTjmwwluSrAOF1jT6ws3(Fyh0gRsj>R#zu zseTENg?7nKZyLoO4>~fT7S`ob8>x$ZLKp_-@ko__L13UN9dln+pRrMxj5c(sRo8ub zgLR~XZMVA5N=0nxXRK|k{DgUr;tV{+Gv$T@H2mIoverzS&67|I=`Kge;@0Asdcqal%Ge`R+J|EhunJr?=E@yA`+Ea%ATPP=mPUxqJI5ywtIG85SI~ z+0;Fp!$t<{_=YoCv3KjH-}2}$9t`$S*{;jyt=hKqUaD~x$dz5bI~yh+8J@$-?27bX zyVW(*Gi#E)!lBJ9%7!N`)0b5qDsIcOE=;jK}xl7^+%5?c7 zacHZ0qE)SQYVr>8*S!+YMT;`W6vprZ75Y*soTg?KXyz@}H*>Kge0y57nNV*GTg1eW I#!p=FKPz#ZumAu6 literal 0 HcmV?d00001 diff --git a/compiler/test-resources/run/myfile.class b/compiler/test-resources/run/myfile.class new file mode 100644 index 0000000000000000000000000000000000000000..64edd6ebf56e2d564638593b0e2794308c70671b GIT binary patch literal 664 zcmZuu%Ps^#6g_2()}t{P?>8-J5}L%0jb}nyEEq+-CO6Jd+({2x5sAy^O#_0C|wl%KxkkL*<*gj?SO|xd;92Ecv3@( z!Kfi-Djp9wLsMP^{6ge6LSHiEEGMbk>v8BwzZ}jkOztsgHIAvFk&>-JWYC?D z`htNBnsn$e3^YK?(5;1`MVt#SRx+;S5y_{l$xQ=Uv?)M4Lw$++Aw!pS;C#>O%E%9k z)Ym``oeJ5-Fp-R9E}fj1hZP~Ehz`x)>Xe_7qP3}rJ?K@$K8C4(h~FjCr?pqgc~N6q zpX+<&ARxnNa=R2ueiR7X@hfsUE)+zRj5dHl)nSNQDn6Ov`o)mTIHG&TEys~3*6Al0 z(mPA;&OSv~IgUBhp`N}ANE4sT0>cPpIjS>?jSpy^s-pFt?jQuY^?~RhggP1_s_3{& z5?z@@44)TgMXlhUIhIE35wTjv!NhT5KE&MErcjz^hQ zz&dZVpnKb1@d(ah$3nzrQK%Pg1M+zwdpex@wtSX6SU$f(W)Cqe&{jK{K; z^Qxva_UyV>Hyi8Eu@f3KF2aP@G#h+tBjLR;CbBV>-JG{+wv8{##?hr&Dcx6Y-lsv`10D11CQR&%REl$IX_neg3EYm`(@U SxA9?TKWhK+kEQfK`2PVz!` Date: Mon, 19 Jul 2021 19:04:50 +0200 Subject: [PATCH 05/10] Handle save scala option, move coursier tests to isolated configuration, apply requested changes --- .github/workflows/ci.yaml | 2 +- .gitignore | 3 + .../src/dotty/tools/MainGenericRunner.scala | 45 ++-- .../tools/coursier/CoursierScalaTests.scala | 51 +++-- .../run/myfile.scala | 2 + compiler/test-resources/run/myfile$.class | Bin 2210 -> 0 bytes compiler/test-resources/run/myfile.class | Bin 664 -> 0 bytes compiler/test-resources/run/myfile.tasty | Bin 514 -> 0 bytes dist/bin/scala | 203 +----------------- project/Build.scala | 18 +- 10 files changed, 91 insertions(+), 233 deletions(-) rename compiler/{test => test-coursier}/dotty/tools/coursier/CoursierScalaTests.scala (61%) rename compiler/{test-resources => test-coursier}/run/myfile.scala (77%) delete mode 100644 compiler/test-resources/run/myfile$.class delete mode 100644 compiler/test-resources/run/myfile.class delete mode 100644 compiler/test-resources/run/myfile.tasty mode change 100644 => 100755 dist/bin/scala diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 892bb48e3831..8f0d96f8052e 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -111,7 +111,7 @@ jobs: - name: Cmd Tests run: | - ./project/scripts/sbt ";scala3-bootstrapped/compile; scala3-bootstrapped/publishLocal; scala3-bootstrapped/test;sjsSandbox/run;sjsSandbox/test;sjsJUnitTests/test;sjsCompilerTests/test ;sbt-test/scripted scala2-compat/* ;configureIDE ;stdlib-bootstrapped/test:run ;stdlib-bootstrapped-tasty-tests/test" + ./project/scripts/sbt ";scala3-bootstrapped/compile; scala3-bootstrapped/test;sjsSandbox/run;sjsSandbox/test;sjsJUnitTests/test;sjsCompilerTests/test ;sbt-test/scripted scala2-compat/* ;configureIDE ;stdlib-bootstrapped/test:run ;stdlib-bootstrapped-tasty-tests/test; scala3-compiler-bootstrapped/scala3CompilerCoursierTest:test" ./project/scripts/bootstrapCmdTests - name: MiMa diff --git a/.gitignore b/.gitignore index dfcaa9e52bee..20675f9397f0 100644 --- a/.gitignore +++ b/.gitignore @@ -99,3 +99,6 @@ community-build/dotty-community-build-deps # Coursier cs + +# Coursier test product +compiler/test-coursier/run/myfile.jar diff --git a/compiler/src/dotty/tools/MainGenericRunner.scala b/compiler/src/dotty/tools/MainGenericRunner.scala index ed4e7b4f1417..98ce4900b4a8 100644 --- a/compiler/src/dotty/tools/MainGenericRunner.scala +++ b/compiler/src/dotty/tools/MainGenericRunner.scala @@ -8,6 +8,7 @@ import java.net.URLClassLoader import sys.process._ import java.io.File import java.lang.Thread +import scala.annotation.internal.sharable enum ExecuteMode: case Guess @@ -20,10 +21,12 @@ case class Settings( classPath: List[String] = List.empty, executeMode: ExecuteMode = ExecuteMode.Guess, exitCode: Int = 0, + javaArgs: List[String] = List.empty, + scalaArgs: List[String] = List.empty, residualArgs: List[String] = List.empty, scriptArgs: List[String] = List.empty, targetScript: String = "", - areWithCompiler: Boolean = false, + save: Boolean = false, ) { def withExecuteMode(em: ExecuteMode): Settings = this.executeMode match case ExecuteMode.Guess => @@ -33,6 +36,12 @@ case class Settings( this.copy(exitCode = 1) end withExecuteMode + def withScalaArgs(args: String*): Settings = + this.copy(scalaArgs = scalaArgs.appendedAll(args.toList)) + + def withJavaArgs(args: String*): Settings = + this.copy(javaArgs = javaArgs.appendedAll(args.toList)) + def withResidualArgs(args: String*): Settings = this.copy(residualArgs = residualArgs.appendedAll(args.toList)) @@ -47,23 +56,23 @@ case class Settings( this.copy(exitCode = 2) end withTargetScript - def withCompiler: Settings = - this.copy(areWithCompiler = true) + def withSave: Settings = + this.copy(save = true) } object MainGenericRunner { - final val classpathSeparator = ":" + val classpathSeparator = File.pathSeparator + + @sharable val javaOption = raw"""-J(.*)""".r @tailrec def process(args: List[String], settings: Settings): Settings = args match case Nil => settings - case "-repl" :: tail => - process(tail, settings.withExecuteMode(ExecuteMode.Repl)) case "-run" :: tail => process(tail, settings.withExecuteMode(ExecuteMode.Run)) - case ("-cp" | "-classpath" | "--classpath") :: cp :: tail => + case ("-cp" | "-classpath" | "--class-path") :: cp :: tail => process(tail, settings.copy(classPath = settings.classPath.appended(cp))) case ("-version" | "--version") :: _ => settings.copy( @@ -78,8 +87,10 @@ object MainGenericRunner { residualArgs = settings.residualArgs :+ "-verbose" ) ) - case "-with-compiler" :: tail => - process(tail, settings.withCompiler) + case "-save" :: tail => + process(tail, settings.withSave) + case (o @ javaOption(striped)) :: tail => + process(tail, settings.withJavaArgs(striped).withScalaArgs(o)) case arg :: tail => val line = Try(Source.fromFile(arg).getLines.toList).toOption.flatMap(_.headOption) if arg.endsWith(".scala") || arg.endsWith(".sc") || (line.nonEmpty && raw"#!.*scala".r.matches(line.get)) then @@ -93,7 +104,8 @@ object MainGenericRunner { def main(args: Array[String]): Unit = val settings = process(args.toList, Settings()) if settings.exitCode != 0 then System.exit(settings.exitCode) - settings.executeMode match + + def run(mode: ExecuteMode): Unit = mode match case ExecuteMode.Repl => val properArgs = List("-classpath", settings.classPath.mkString(classpathSeparator)).filter(Function.const(settings.classPath.nonEmpty)) @@ -104,20 +116,23 @@ object MainGenericRunner { val newClasspath = settings.classPath ++ getClasspath :+ "." List("-classpath", newClasspath.mkString(classpathSeparator)).filter(Function.const(newClasspath.nonEmpty)) ++ settings.residualArgs - s"java ${properArgs.mkString(" ")}".! // For now we collect classpath that coursier provides for convenience + s"java ${settings.javaArgs.mkString(" ")} ${properArgs.mkString(" ")}".! // For now we collect classpath that coursier provides for convenience case ExecuteMode.Script => val properArgs = List("-classpath", settings.classPath.mkString(classpathSeparator)).filter(Function.const(settings.classPath.nonEmpty)) ++ settings.residualArgs + ++ (if settings.save then List("-save") else Nil) ++ List("-script", settings.targetScript) + ++ settings.scalaArgs ++ settings.scriptArgs scripting.Main.main(properArgs.toArray) case ExecuteMode.Guess => - val properArgs = - List("-classpath", settings.classPath.mkString(classpathSeparator)).filter(Function.const(settings.classPath.nonEmpty)) - ++ settings.residualArgs - repl.Main.main(properArgs.toArray) + if args.toList.forall(_.startsWith("-")) then // all are options + run(ExecuteMode.Repl) + else + run(ExecuteMode.Run) + run(settings.executeMode) private def getClasspath(cl: ClassLoader): Array[String] = cl match case null => Array() diff --git a/compiler/test/dotty/tools/coursier/CoursierScalaTests.scala b/compiler/test-coursier/dotty/tools/coursier/CoursierScalaTests.scala similarity index 61% rename from compiler/test/dotty/tools/coursier/CoursierScalaTests.scala rename to compiler/test-coursier/dotty/tools/coursier/CoursierScalaTests.scala index 5ab4b30d280c..fb8e6e2f3074 100644 --- a/compiler/test/dotty/tools/coursier/CoursierScalaTests.scala +++ b/compiler/test-coursier/dotty/tools/coursier/CoursierScalaTests.scala @@ -1,21 +1,32 @@ package dotty package tools -package scripting +package coursier import java.io.File import java.nio.file.{Path, Paths, Files} import scala.sys.process._ - import org.junit.Test import org.junit.BeforeClass - -import vulpix.TestConfiguration - -import dotty.tools.absPath +import org.junit.Assert._ import scala.collection.mutable.ListBuffer +import java.net.URLClassLoader +import java.net.URL + class CoursierScalaTests: + private def scripts(path: String): Array[File] = { + val dir = new File(getClass.getResource(path).getPath) + assert(dir.exists && dir.isDirectory, "Couldn't load scripts dir") + dir.listFiles + } + + extension (f: File) private def absPath = + f.getAbsolutePath.replace('\\', '/') + + extension (str: String) private def dropExtension = + str.reverse.dropWhile(_ != '.').drop(1).reverse + // classpath tests are managed by scripting.ClasspathTests.scala def testFiles = scripts("/scripting").filter { ! _.getName.startsWith("classpath") } @@ -38,29 +49,36 @@ class CoursierScalaTests: ) for (line, expect) <- output zip expectedOutput do printf("expected: %-17s\nactual : %s\n", expect, line) - assert(output == expectedOutput) + assertEquals(expectedOutput, output) scriptArgs() def version() = val output = CoursierScalaTests.csCmd("-version") - assert(output.mkString("\n").contains(sys.env("DOTTY_BOOTSTRAPPED_VERSION"))) + assertTrue(output.mkString("\n").contains(sys.env("DOTTY_BOOTSTRAPPED_VERSION"))) version() def emptyArgsEqualsRepl() = val output = CoursierScalaTests.csCmd() - assert(output.mkString("\n").contains("Unable to create a system terminal")) // Scala attempted to create REPL so we can assume it is working + assertTrue(output.mkString("\n").contains("Unable to create a system terminal")) // Scala attempted to create REPL so we can assume it is working emptyArgsEqualsRepl() - def repl() = - val output = CoursierScalaTests.csCmd("-repl") - assert(output.mkString("\n").contains("Unable to create a system terminal")) // Scala attempted to create REPL so we can assume it is working - repl() - def run() = - val output = CoursierScalaTests.csCmd("-run", "-classpath", scripts("/run").head.getParent, "myfile") - assert(output.mkString("\n") == "Hello") + val output = CoursierScalaTests.csCmd("-run", "-classpath", scripts("/run").head.getParentFile.getParent, "run.myfile") + assertEquals(output.mkString("\n"), "Hello") run() + def notOnlyOptionsEqualsRun() = + val output = CoursierScalaTests.csCmd("-classpath", scripts("/run").head.getParentFile.getParent, "run.myfile") + assertEquals(output.mkString("\n"), "Hello") + notOnlyOptionsEqualsRun() + + def jar() = + val source = new File(getClass.getResource("/run/myfile.scala").getPath) + val output = CoursierScalaTests.csCmd("-save", source.absPath) + assertEquals(output.mkString("\n"), "Hello") + assertTrue(source.getParentFile.listFiles.find(_.getName == "myfile.jar").isDefined) + jar() + object CoursierScalaTests: def execCmd(command: String, options: String*): List[String] = @@ -69,7 +87,6 @@ object CoursierScalaTests: cmd.!(ProcessLogger(out += _, out += _)) out.toList - def csCmd(options: String*): List[String] = val newOptions = options match case Nil => options diff --git a/compiler/test-resources/run/myfile.scala b/compiler/test-coursier/run/myfile.scala similarity index 77% rename from compiler/test-resources/run/myfile.scala rename to compiler/test-coursier/run/myfile.scala index 6070bf94178c..c9ed2cfb1683 100644 --- a/compiler/test-resources/run/myfile.scala +++ b/compiler/test-coursier/run/myfile.scala @@ -1,2 +1,4 @@ +package run + object myfile extends App: println("Hello") diff --git a/compiler/test-resources/run/myfile$.class b/compiler/test-resources/run/myfile$.class deleted file mode 100644 index 45c3bf51e8bc60f12b0720a771a3fde95a819f2a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2210 zcma)6ZBr9h7(F-Sg@hGCL|UyaYNIC55~)~UKm|cm8iX35rM9KZ=7Q^Nvgu~iLjO}| zT9_$Jo#_wgkLvW?yPISk873c+`?AkD&w1YNzyJOH4}fWy0;$^mj_t^N3PK?J#{9)B zIc9ygw7UI9T7C+B0_mn@I%a9U(MTa7FrtlYw^WgyZ94XE=C&hKNC_AL*QB-=7?gX` zYWcQXulT0t3y5X9&>!<8%jdUDZ?`FM_Eov-x8i$teRqbp5ktFf`wMPW3S2D*Vl3Bj zsE^#GTFX}oN@ct0KWgplNRQ~$%Ie~J`Ej1=%VC7PP0lK&2LgSC;)a17&SntDh(O$b zZBt4AtmSBn3}P5Dki~GCyYocm)jjD33!wr=Fr!F5!o>k3@v*>}K-!uot8#}49TOM| zjl;VcMR_@mOSmGCcq$#o?FUIy4F>Hyb%Aq*NG^gJXfM}rJwsT57J(P9Rrl?hEHSmd zZ8cLEXZ}6uNV6%IWZjYmK2zaM;By`BvVjZ+R5Uk~m*NRIH(=n5vbZU5y-@5a(*_1H zq%dEyF(MVyrQOQQp9Mw>C-^FEP}N?3iWTjmwwluSrAOF1jT6ws3(Fyh0gRsj>R#zu zseTENg?7nKZyLoO4>~fT7S`ob8>x$ZLKp_-@ko__L13UN9dln+pRrMxj5c(sRo8ub zgLR~XZMVA5N=0nxXRK|k{DgUr;tV{+Gv$T@H2mIoverzS&67|I=`Kge;@0Asdcqal%Ge`R+J|EhunJr?=E@yA`+Ea%ATPP=mPUxqJI5ywtIG85SI~ z+0;Fp!$t<{_=YoCv3KjH-}2}$9t`$S*{;jyt=hKqUaD~x$dz5bI~yh+8J@$-?27bX zyVW(*Gi#E)!lBJ9%7!N`)0b5qDsIcOE=;jK}xl7^+%5?c7 zacHZ0qE)SQYVr>8*S!+YMT;`W6vprZ75Y*soTg?KXyz@}H*>Kge0y57nNV*GTg1eW I#!p=FKPz#ZumAu6 diff --git a/compiler/test-resources/run/myfile.class b/compiler/test-resources/run/myfile.class deleted file mode 100644 index 64edd6ebf56e2d564638593b0e2794308c70671b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 664 zcmZuu%Ps^#6g_2()}t{P?>8-J5}L%0jb}nyEEq+-CO6Jd+({2x5sAy^O#_0C|wl%KxkkL*<*gj?SO|xd;92Ecv3@( z!Kfi-Djp9wLsMP^{6ge6LSHiEEGMbk>v8BwzZ}jkOztsgHIAvFk&>-JWYC?D z`htNBnsn$e3^YK?(5;1`MVt#SRx+;S5y_{l$xQ=Uv?)M4Lw$++Aw!pS;C#>O%E%9k z)Ym``oeJ5-Fp-R9E}fj1hZP~Ehz`x)>Xe_7qP3}rJ?K@$K8C4(h~FjCr?pqgc~N6q zpX+<&ARxnNa=R2ueiR7X@hfsUE)+zRj5dHl)nSNQDn6Ov`o)mTIHG&TEys~3*6Al0 z(mPA;&OSv~IgUBhp`N}ANE4sT0>cPpIjS>?jSpy^s-pFt?jQuY^?~RhggP1_s_3{& z5?z@@44)TgMXlhUIhIE35wTjv!NhT5KE&MErcjz^hQ zz&dZVpnKb1@d(ah$3nzrQK%Pg1M+zwdpex@wtSX6SU$f(W)Cqe&{jK{K; z^Qxva_UyV>Hyi8Eu@f3KF2aP@G#h+tBjLR;CbBV>-JG{+wv8{##?hr&Dcx6Y-lsv`10D11CQR&%REl$IX_neg3EYm`(@U SxA9?TKWhK+kEQfK`2PVz!`&2 - exit 1 - ;; - esac -} +# exec here would prevent onExit from being called, leaving terminal in unusable state +compilerJavaClasspathArgs +eval "\"$JAVACMD\"" "-classpath \"$jvm_cp_args\"" "dotty.tools.MainGenericRunner" "-classpath \"$jvm_cp_args\"" "$@" +scala_exit_status=$? -with_compiler=false # to add compiler jars to repl classpath -let class_path_count=0 || true # count classpath args, warning if more than 1 -save_compiled=false # to save compiled script jar in script directory -CLASS_PATH="" || true # scala classpath - -# Little hack to check if all arguments are options -all_params="$*" -truncated_params="${*#-}" -# options_indicator != 0 if at least one parameter is not an option -options_indicator=$(( ${#all_params} - ${#truncated_params} - $# )) - -[ -n "${SCALA_OPTS-}" ] && set -- $SCALA_OPTS "$@" - -while [[ $# -gt 0 ]]; do - case "$1" in - -repl) - setExecuteMode 'repl' - shift - ;; - -run) - setExecuteMode 'run' - shift - ;; - -cp | -classpath) - CLASS_PATH="$2${PSEP}" - let class_path_count+=1 - shift - shift - ;; - -cp*|-classpath*) # partial fix for #10761 - # hashbang can combine args, e.g. "-classpath 'lib/*'" - CLASS_PATH="${1#* *}${PSEP}" - let class_path_count+=1 - shift - ;; - -with-compiler) - with_compiler=true - shift - ;; - @*|-color:*) - addScala "${1}" - shift - ;; - -save|-savecompiled) - save_compiled=true - addScala "$1" - shift - ;; - -compile-only) - addScala "$1" - shift - ;; - -version) - # defer to scalac, then exit - shift - eval "\"$PROG_HOME/bin/scalac\" -version" - scala_exit_status=$? - onExit - ;; - -J*) - addJava "${1:2}" - addScala "${1}" - shift ;; - -v|-verbose) - verbose=true - addScala "-verbose" - shift ;; - -run) - setExecuteMode 'run' - shift ;; - - *) - # script if extension .scala or .sc, or if has scala hashbang line - # no -f test, issue meaningful error message (file not found) - if [[ "$1" == *.scala || "$1" == *.sc ]]; then - setExecuteMode 'script' # execute_script=true - - # -f test needed before we examine the hashbang line - elif [[ (-f "$1" && `head -n 1 -- "$1" | grep '#!.*scala'`) ]]; then - setExecuteMode 'script' # execute_script=true - fi - - if [ "${execute_mode-}" == 'script' ]; then - target_script="$1" - shift - if [ ! -f $target_script ]; then - # likely a typo or missing script file, quit early - echo "not found: $target_script" 1>&2 - scala_exit_status=2 - onExit - fi - # all are script args - while [[ $# -gt 0 ]]; do - addScript "${1}" - shift - done - else - # all unrecognized args appearing prior to a script name - addResidual "$1" - shift - fi - ;; - - esac -done - -#[ -n "${dump_args}" ] && dumpArgs ; exit 2 -if [ -z "${execute_mode-}" ]; then - # no script was specified, set run or repl mode - if [[ $options_indicator -eq 0 ]]; then - setExecuteMode 'repl' - else - setExecuteMode 'run' - fi -fi - -[ -n "${script_trace-}" ] && set -x - -case "${execute_mode-}" in -script) - if [ "$CLASS_PATH" ]; then - script_cp_arg="-classpath '$CLASS_PATH'" - fi - setScriptName="-Dscript.path=$target_script" - target_jar="${target_script%.*}.jar" - if [[ $save_compiled == true && "$target_jar" -nt "$target_script" ]]; then - eval "\"$JAVACMD\"" $setScriptName -jar "$target_jar" "${script_args[@]}" - scala_exit_status=$? - else - [[ $save_compiled == true ]] && rm -f $target_jar - PROG_NAME=$ScriptingMain - compilerJavaClasspathArgs # initialize jvm_cp_args with toolchain classpath - scripting_string="-script $target_script ${script_args[@]}" - # use eval instead of exec, to insure that onExit is subsequently called - - # $script_cp_arg must be the first argument to $ScriptingMain - # $scripting_string must be last - eval "\"$JAVACMD\"" \ - ${JAVA_OPTS:-$default_java_opts} \ - "${java_args[@]}" \ - "-classpath \"$jvm_cp_args\"" \ - -Dscala.usejavacp=true \ - "$setScriptName" \ - "$ScriptingMain" \ - ${script_cp_arg-} \ - "${scala_args[@]}" \ - "${residual_args[@]}" \ - "${scripting_string-}" # must be the last arguments - scala_exit_status=$? - fi - ;; - -repl) - if [ "$CLASS_PATH" ]; then - repl_cparg="-classpath \"$CLASS_PATH\"" - fi - eval "\"$PROG_HOME/bin/scalac\" ${repl_cparg-} ${scalac_options[@]} -repl ${residual_args[@]}" - scala_exit_status=$? - ;; - -run) - run_cparg="$DOTTY_LIB$PSEP$SCALA_LIB" - if [ -z "$CLASS_PATH" ]; then - run_cparg+="$PSEP." - else - run_cparg+="$PSEP$CLASS_PATH" - fi - if [ "$class_path_count" -gt 1 ]; then - echo "warning: multiple classpaths are found, scala only use the last one." - fi - if [ $with_compiler == true ]; then - run_cparg+="$PSEP$DOTTY_COMP$PSEP$TASTY_CORE$PSEP$DOTTY_INTF$PSEP$SCALA_ASM$PSEP$DOTTY_STAGING$PSEP$DOTTY_TASTY_INSPECTOR" - fi - # exec here would prevent onExit from being called, leaving terminal in unusable state - eval "\"$JAVACMD\"" "-classpath \"$run_cparg\"" "${java_args[@]}" "${residual_args[@]}" - scala_exit_status=$? - ;; - -*) - echo "warning: command option is not correct." - ;; -esac - onExit diff --git a/project/Build.scala b/project/Build.scala index 690a2cbc695e..d5ecd0d7db0c 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -190,7 +190,6 @@ object Build { // Avoid various sbt craziness involving classloaders and parallelism run / fork := true, Test / fork := true, - Test / envVars := Map("DOTTY_BOOTSTRAPPED_VERSION" -> dottyVersion), Test / parallelExecution := false, outputStrategy := Some(StdoutOutput), @@ -760,7 +759,24 @@ object Build { if (mode == NonBootstrapped) nonBootstrapedDottyCompilerSettings else bootstrapedDottyCompilerSettings lazy val `scala3-compiler` = project.in(file("compiler")).asDottyCompiler(NonBootstrapped) + + lazy val Scala3CompilerCoursierTest = config("scala3CompilerCoursierTest") extend Test lazy val `scala3-compiler-bootstrapped` = project.in(file("compiler")).asDottyCompiler(Bootstrapped) + .configs(Scala3CompilerCoursierTest) + .settings( + inConfig(Scala3CompilerCoursierTest)(Defaults.testSettings), + Scala3CompilerCoursierTest / scalaSource := baseDirectory.value / "test-coursier", + Scala3CompilerCoursierTest / fork := true, + Scala3CompilerCoursierTest / envVars := Map("DOTTY_BOOTSTRAPPED_VERSION" -> dottyVersion), + Scala3CompilerCoursierTest / unmanagedClasspath += (Scala3CompilerCoursierTest / scalaSource).value, + Scala3CompilerCoursierTest / test := ((Scala3CompilerCoursierTest / test) dependsOn ( + publishLocal, // Had to enumarate all deps since calling `scala3-bootstrap` / publishLocal will lead to recursive dependency => stack overflow + `scala3-interfaces` / publishLocal, + dottyLibrary(Bootstrapped) / publishLocal, + tastyCore(Bootstrapped) / publishLocal, + ), + ).value, + ) def dottyCompiler(implicit mode: Mode): Project = mode match { case NonBootstrapped => `scala3-compiler` From 0c340bc365a8c2a2e5590f54f63c22a66f3a1757 Mon Sep 17 00:00:00 2001 From: Andrzej Ratajczak Date: Tue, 20 Jul 2021 11:10:09 +0200 Subject: [PATCH 06/10] Fix running REPL from the shell script --- compiler/src/dotty/tools/MainGenericRunner.scala | 13 +++++++++---- .../dotty/tools/coursier/CoursierScalaTests.scala | 5 +++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/MainGenericRunner.scala b/compiler/src/dotty/tools/MainGenericRunner.scala index 98ce4900b4a8..0742edb4b65d 100644 --- a/compiler/src/dotty/tools/MainGenericRunner.scala +++ b/compiler/src/dotty/tools/MainGenericRunner.scala @@ -27,6 +27,7 @@ case class Settings( scriptArgs: List[String] = List.empty, targetScript: String = "", save: Boolean = false, + modeShouldBeRun: Boolean = false, ) { def withExecuteMode(em: ExecuteMode): Settings = this.executeMode match case ExecuteMode.Guess => @@ -58,6 +59,9 @@ case class Settings( def withSave: Settings = this.copy(save = true) + + def withModeShouldBeRun: Settings = + this.copy(modeShouldBeRun = true) } object MainGenericRunner { @@ -99,7 +103,8 @@ object MainGenericRunner { .withTargetScript(arg) .withScriptArgs(tail*) else - process(tail, settings.withResidualArgs(arg)) + val newSettings = if arg.startsWith("-") then settings else settings.withModeShouldBeRun + process(tail, newSettings.withResidualArgs(arg)) def main(args: Array[String]): Unit = val settings = process(args.toList, Settings()) @@ -127,10 +132,10 @@ object MainGenericRunner { ++ settings.scriptArgs scripting.Main.main(properArgs.toArray) case ExecuteMode.Guess => - if args.toList.forall(_.startsWith("-")) then // all are options - run(ExecuteMode.Repl) - else + if settings.modeShouldBeRun then run(ExecuteMode.Run) + else + run(ExecuteMode.Repl) run(settings.executeMode) diff --git a/compiler/test-coursier/dotty/tools/coursier/CoursierScalaTests.scala b/compiler/test-coursier/dotty/tools/coursier/CoursierScalaTests.scala index fb8e6e2f3074..277b31ec246a 100644 --- a/compiler/test-coursier/dotty/tools/coursier/CoursierScalaTests.scala +++ b/compiler/test-coursier/dotty/tools/coursier/CoursierScalaTests.scala @@ -72,6 +72,11 @@ class CoursierScalaTests: assertEquals(output.mkString("\n"), "Hello") notOnlyOptionsEqualsRun() + def help() = + val output = CoursierScalaTests.csCmd("-help") + assertTrue(output.mkString("\n").contains("Usage: scala ")) + help() + def jar() = val source = new File(getClass.getResource("/run/myfile.scala").getPath) val output = CoursierScalaTests.csCmd("-save", source.absPath) From df7293052b666c36b77c7f1fc70d2f17b01e0bc2 Mon Sep 17 00:00:00 2001 From: Andrzej Ratajczak Date: Wed, 21 Jul 2021 13:48:22 +0200 Subject: [PATCH 07/10] Add support for scala opts, change the way scala program is run by scala main runner --- .../src/dotty/tools/MainGenericRunner.scala | 33 ++- .../src/dotty/tools/runner/ObjectRunner.scala | 48 ++++ .../dotty/tools/runner/ScalaClassLoader.scala | 242 ++++++++++++++++++ 3 files changed, 305 insertions(+), 18 deletions(-) create mode 100644 compiler/src/dotty/tools/runner/ObjectRunner.scala create mode 100644 compiler/src/dotty/tools/runner/ScalaClassLoader.scala diff --git a/compiler/src/dotty/tools/MainGenericRunner.scala b/compiler/src/dotty/tools/MainGenericRunner.scala index 0742edb4b65d..c95e30bb04b0 100644 --- a/compiler/src/dotty/tools/MainGenericRunner.scala +++ b/compiler/src/dotty/tools/MainGenericRunner.scala @@ -9,6 +9,9 @@ import sys.process._ import java.io.File import java.lang.Thread import scala.annotation.internal.sharable +import dotty.tools.dotc.util.ClasspathFromClassloader +import dotty.tools.runner.ObjectRunner +import dotty.tools.dotc.config.Properties.envOrNone enum ExecuteMode: case Guess @@ -107,7 +110,9 @@ object MainGenericRunner { process(tail, newSettings.withResidualArgs(arg)) def main(args: Array[String]): Unit = - val settings = process(args.toList, Settings()) + val scalaOpts = envOrNone("SCALA_OPTS").toArray.flatMap(_.split(" ")) + val allArgs = scalaOpts ++ args + val settings = process(allArgs.toList, Settings()) if settings.exitCode != 0 then System.exit(settings.exitCode) def run(mode: ExecuteMode): Unit = mode match @@ -117,11 +122,9 @@ object MainGenericRunner { ++ settings.residualArgs repl.Main.main(properArgs.toArray) case ExecuteMode.Run => - val properArgs = - val newClasspath = settings.classPath ++ getClasspath :+ "." - List("-classpath", newClasspath.mkString(classpathSeparator)).filter(Function.const(newClasspath.nonEmpty)) - ++ settings.residualArgs - s"java ${settings.javaArgs.mkString(" ")} ${properArgs.mkString(" ")}".! // For now we collect classpath that coursier provides for convenience + val scalaClasspath = ClasspathFromClassloader(Thread.currentThread().getContextClassLoader).split(classpathSeparator) + val newClasspath = (settings.classPath ++ scalaClasspath :+ ".").map(File(_).toURI.toURL) + errorFn("", ObjectRunner.runAndCatch(newClasspath, settings.residualArgs.head, settings.residualArgs.drop(1))) case ExecuteMode.Script => val properArgs = List("-classpath", settings.classPath.mkString(classpathSeparator)).filter(Function.const(settings.classPath.nonEmpty)) @@ -139,16 +142,10 @@ object MainGenericRunner { run(settings.executeMode) - private def getClasspath(cl: ClassLoader): Array[String] = cl match - case null => Array() - case u: URLClassLoader => u.getURLs.map(_.toURI.toString) ++ getClasspath(cl.getParent) - case cl if cl.getClass.getName == "jdk.internal.loader.ClassLoaders$AppClassLoader" => - // Required with JDK >= 9 - sys.props.getOrElse("java.class.path", "") - .split(File.pathSeparator) - .filter(_.nonEmpty) - case _ => getClasspath(cl.getParent) - - private def getClasspath: List[String] = - getClasspath(Thread.currentThread().getContextClassLoader).toList + + def errorFn(str: String, e: Option[Throwable] = None, isFailure: Boolean = true): Boolean = { + if (str.nonEmpty) Console.err.println(str) + e.foreach(_.printStackTrace()) + !isFailure + } } diff --git a/compiler/src/dotty/tools/runner/ObjectRunner.scala b/compiler/src/dotty/tools/runner/ObjectRunner.scala new file mode 100644 index 000000000000..112b55f2e464 --- /dev/null +++ b/compiler/src/dotty/tools/runner/ObjectRunner.scala @@ -0,0 +1,48 @@ +package dotty.tools +package runner + +import java.net.URL +import scala.util.control.NonFatal +import java.lang.reflect.InvocationTargetException +import java.lang.reflect.UndeclaredThrowableException +import java.util.concurrent.ExecutionException + +/** + * This is a copy implementation from scala/scala scala.tools.nsc.CommonRunner trait + */ +trait CommonRunner { + /** Run a given object, specified by name, using a + * specified classpath and argument list. + * + * @throws java.lang.ClassNotFoundException + * @throws java.lang.NoSuchMethodException + * @throws java.lang.reflect.InvocationTargetException + */ + def run(urls: Seq[URL], objectName: String, arguments: Seq[String]): Unit = { + import RichClassLoader._ + ScalaClassLoader.fromURLsParallelCapable(urls).run(objectName, arguments) + } + + /** Catches any non-fatal exception thrown by run (in the case of InvocationTargetException, + * unwrapping it) and returns it in an Option. + */ + def runAndCatch(urls: Seq[URL], objectName: String, arguments: Seq[String]): Option[Throwable] = + try { run(urls, objectName, arguments) ; None } + catch { case NonFatal(e) => Some(rootCause(e)) } + + private def rootCause(x: Throwable): Throwable = x match { + case _: InvocationTargetException | + _: ExceptionInInitializerError | + _: UndeclaredThrowableException | + _: ExecutionException + if x.getCause != null => + rootCause(x.getCause) + case _ => x + } +} + +/** An object that runs another object specified by name. + * + * @author Lex Spoon + */ +object ObjectRunner extends CommonRunner diff --git a/compiler/src/dotty/tools/runner/ScalaClassLoader.scala b/compiler/src/dotty/tools/runner/ScalaClassLoader.scala new file mode 100644 index 000000000000..71c332a48846 --- /dev/null +++ b/compiler/src/dotty/tools/runner/ScalaClassLoader.scala @@ -0,0 +1,242 @@ +package dotty.tools +package runner + +import java.lang.invoke.{MethodHandles, MethodType} + +import scala.language.implicitConversions +import java.lang.{ClassLoader => JClassLoader} +import java.lang.reflect.Modifier +import java.net.{URLClassLoader => JURLClassLoader} +import java.net.URL + +import scala.annotation.tailrec +import scala.util.control.Exception.catching +import scala.reflect.{ClassTag, classTag} +import java.lang.reflect.InvocationTargetException +import java.lang.reflect.UndeclaredThrowableException +import dotty.tools.repl.AbstractFileClassLoader +import dotty.tools.io.AbstractFile +import dotty.tools.io.Streamable +import scala.annotation.internal.sharable + +trait HasClassPath { + def classPathURLs: Seq[URL] +} + +final class RichClassLoader(private val self: JClassLoader) extends AnyVal { + /** Executing an action with this classloader as context classloader */ + def asContext[T](action: => T): T = { + val saved = Thread.currentThread.getContextClassLoader + try { ScalaClassLoader.setContext(self) ; action } + finally ScalaClassLoader.setContext(saved) + } + + /** Load and link a class with this classloader */ + def tryToLoadClass[T <: AnyRef](path: String): Option[Class[T]] = tryClass(path, initialize = false) + /** Load, link and initialize a class with this classloader */ + def tryToInitializeClass[T <: AnyRef](path: String): Option[Class[T]] = tryClass(path, initialize = true) + + private def tryClass[T <: AnyRef](path: String, initialize: Boolean): Option[Class[T]] = + catching(classOf[ClassNotFoundException], classOf[SecurityException]) opt + Class.forName(path, initialize, self).asInstanceOf[Class[T]] + + /** Create an instance of a class with this classloader */ + def create(path: String): AnyRef = + tryToInitializeClass[AnyRef](path).map(_.getConstructor().newInstance()).orNull + + /** Create an instance with ctor args, or invoke errorFn before throwing. */ + def create[T <: AnyRef : ClassTag](path: String, errorFn: String => Unit)(args: AnyRef*): T = { + def fail(msg: String) = error(msg, new IllegalArgumentException(msg)) + def error(msg: String, e: Throwable) = { errorFn(msg) ; throw e } + try { + val clazz = Class.forName(path, /*initialize =*/ true, /*loader =*/ self) + if (classTag[T].runtimeClass isAssignableFrom clazz) { + val ctor = { + val maybes = clazz.getConstructors filter (c => c.getParameterCount == args.size && + (c.getParameterTypes zip args).forall { case (k, a) => k isAssignableFrom a.getClass }) + if (maybes.size == 1) maybes.head + else fail(s"Constructor must accept arg list (${args map (_.getClass.getName) mkString ", "}): ${path}") + } + (ctor.newInstance(args: _*)).asInstanceOf[T] + } else { + errorFn(s"""Loader for ${classTag[T]}: [${show(classTag[T].runtimeClass.getClassLoader)}] + |Loader for ${clazz.getName}: [${show(clazz.getClassLoader)}]""".stripMargin) + fail(s"Not a ${classTag[T]}: ${path}") + } + } catch { + case e: ClassNotFoundException => + error(s"Class not found: ${path}", e) + case e @ (_: LinkageError | _: ReflectiveOperationException) => + error(s"Unable to create instance: ${path}: ${e.toString}", e) + } + } + + /** The actual bytes for a class file, or an empty array if it can't be found. */ + def classBytes(className: String): Array[Byte] = classAsStream(className) match { + case null => Array() + case stream => Streamable.bytes(stream) + } + + /** An InputStream representing the given class name, or null if not found. */ + def classAsStream(className: String) = self.getResourceAsStream { + if (className endsWith ".class") className + else s"${className.replace('.', '/')}.class" // classNameToPath + } + + /** Run the main method of a class to be loaded by this classloader */ + def run(objectName: String, arguments: Seq[String]): Unit = { + val clsToRun = tryToInitializeClass(objectName) getOrElse ( + throw new ClassNotFoundException(objectName) + ) + val method = clsToRun.getMethod("main", classOf[Array[String]]) + if (!Modifier.isStatic(method.getModifiers)) + throw new NoSuchMethodException(objectName + ".main is not static") + + try asContext(method.invoke(null, Array(arguments.toArray: AnyRef): _*)) // !!! : AnyRef shouldn't be necessary + catch unwrapHandler({ case ex => throw ex }) + } + + @tailrec + def unwrapThrowable(x: Throwable): Throwable = x match { + case _: InvocationTargetException | // thrown by reflectively invoked method or constructor + _: ExceptionInInitializerError | // thrown when running a static initializer (e.g. a scala module constructor) + _: UndeclaredThrowableException | // invocation on a proxy instance if its invocation handler's `invoke` throws an exception + _: ClassNotFoundException | // no definition for a class instantiated by name + _: NoClassDefFoundError // the definition existed when the executing class was compiled, but can no longer be found + if x.getCause != null => + unwrapThrowable(x.getCause) + case _ => x + } + // Transforms an exception handler into one which will only receive the unwrapped + // exceptions (for the values of wrap covered in unwrapThrowable.) + def unwrapHandler[T](pf: PartialFunction[Throwable, T]): PartialFunction[Throwable, T] = + pf.compose({ case ex => unwrapThrowable(ex) }) + + def show(cl: ClassLoader): String = { + import scala.reflect.Selectable.reflectiveSelectable + + @tailrec + def isAbstractFileClassLoader(clazz: Class[_]): Boolean = { + if (clazz == null) return false + if (clazz == classOf[AbstractFileClassLoader]) return true + isAbstractFileClassLoader(clazz.getSuperclass) + } + def inferClasspath(cl: ClassLoader): String = cl match { + case cl: java.net.URLClassLoader if cl.getURLs != null => + (cl.getURLs mkString ",") + case cl if cl != null && isAbstractFileClassLoader(cl.getClass) => + cl.asInstanceOf[{val root: AbstractFile}].root.canonicalPath + case null => + val loadBootCp = (flavor: String) => scala.util.Properties.propOrNone(flavor + ".boot.class.path") + loadBootCp("sun") orElse loadBootCp("java") getOrElse "" + case _ => + "" + } + cl match { + case null => s"primordial classloader with boot classpath [${inferClasspath(cl)}]" + case _ => s"$cl of type ${cl.getClass} with classpath [${inferClasspath(cl)}] and parent being ${show(cl.getParent)}" + } + } +} + +object RichClassLoader { + implicit def wrapClassLoader(loader: ClassLoader): RichClassLoader = new RichClassLoader(loader) +} + +/** A wrapper around java.lang.ClassLoader to lower the annoyance + * of java reflection. + */ +trait ScalaClassLoader extends JClassLoader { + private def wrap = new RichClassLoader(this) + /** Executing an action with this classloader as context classloader */ + def asContext[T](action: => T): T = wrap.asContext(action) + + /** Load and link a class with this classloader */ + def tryToLoadClass[T <: AnyRef](path: String): Option[Class[T]] = wrap.tryToLoadClass[T](path) + /** Load, link and initialize a class with this classloader */ + def tryToInitializeClass[T <: AnyRef](path: String): Option[Class[T]] = wrap.tryToInitializeClass(path) + + /** Create an instance of a class with this classloader */ + def create(path: String): AnyRef = wrap.create(path) + + /** Create an instance with ctor args, or invoke errorFn before throwing. */ + def create[T <: AnyRef : ClassTag](path: String, errorFn: String => Unit)(args: AnyRef*): T = + wrap.create[T](path, errorFn)(args: _*) + + /** The actual bytes for a class file, or an empty array if it can't be found. */ + def classBytes(className: String): Array[Byte] = wrap.classBytes(className) + + /** An InputStream representing the given class name, or null if not found. */ + def classAsStream(className: String) = wrap.classAsStream(className) + + /** Run the main method of a class to be loaded by this classloader */ + def run(objectName: String, arguments: Seq[String]): Unit = wrap.run(objectName, arguments) +} + + +/** Methods for obtaining various classloaders. + * appLoader: the application classloader. (Also called the java system classloader.) + * extLoader: the extension classloader. + * bootLoader: the boot classloader. + * contextLoader: the context classloader. + */ +object ScalaClassLoader { + /** Returns loaders which are already ScalaClassLoaders unaltered, + * and translates java.net.URLClassLoaders into scala URLClassLoaders. + * Otherwise creates a new wrapper. + */ + implicit def apply(cl: JClassLoader): ScalaClassLoader = cl match { + case cl: ScalaClassLoader => cl + case cl: JURLClassLoader => new URLClassLoader(cl.getURLs.toSeq, cl.getParent) + case _ => new JClassLoader(cl) with ScalaClassLoader + } + def contextLoader = apply(Thread.currentThread.getContextClassLoader) + def appLoader = apply(JClassLoader.getSystemClassLoader) + def setContext(cl: JClassLoader) = Thread.currentThread.setContextClassLoader(cl) + + class URLClassLoader(urls: Seq[URL], parent: JClassLoader) + extends JURLClassLoader(urls.toArray, parent) + with ScalaClassLoader + with HasClassPath { + private[this] var classloaderURLs: Seq[URL] = urls + def classPathURLs: Seq[URL] = classloaderURLs + + /** Override to widen to public */ + override def addURL(url: URL) = { + classloaderURLs :+= url + super.addURL(url) + } + override def close(): Unit = { + super.close() + classloaderURLs = null + } + } + + def fromURLs(urls: Seq[URL], parent: ClassLoader = null): URLClassLoader = { + new URLClassLoader(urls, if (parent == null) bootClassLoader else parent) + } + + def fromURLsParallelCapable(urls: Seq[URL], parent: ClassLoader = null): JURLClassLoader = { + new JURLClassLoader(urls.toArray, if (parent == null) bootClassLoader else parent) + } + + /** True if supplied class exists in supplied path */ + def classExists(urls: Seq[URL], name: String): Boolean = + (fromURLs(urls) tryToLoadClass name).isDefined + + /** Finding what jar a clazz or instance came from */ + def originOfClass(x: Class[_]): Option[URL] = + Option(x.getProtectionDomain.getCodeSource) flatMap (x => Option(x.getLocation)) + + @sharable private[this] val bootClassLoader: ClassLoader = { + if (!scala.util.Properties.isJavaAtLeast("9")) null + else { + try { + MethodHandles.lookup().findStatic(classOf[ClassLoader], "getPlatformClassLoader", MethodType.methodType(classOf[ClassLoader])).invoke().asInstanceOf[ClassLoader] + } catch { + case _: Throwable => + null + } + } + } +} From 12eeb9f89ef94b77f72c0d21714808ff749c22c2 Mon Sep 17 00:00:00 2001 From: Andrzej Ratajczak Date: Tue, 27 Jul 2021 13:32:09 +0200 Subject: [PATCH 08/10] Fix cygwin problems. Handle @ and -color settings. --- compiler/src/dotty/tools/MainGenericRunner.scala | 7 ++++++- compiler/src/dotty/tools/scripting/Main.scala | 2 ++ .../dotty/tools/coursier/CoursierScalaTests.scala | 7 +++++++ dist/bin/scala | 1 + 4 files changed, 16 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/MainGenericRunner.scala b/compiler/src/dotty/tools/MainGenericRunner.scala index c95e30bb04b0..eac96c799310 100644 --- a/compiler/src/dotty/tools/MainGenericRunner.scala +++ b/compiler/src/dotty/tools/MainGenericRunner.scala @@ -72,7 +72,8 @@ object MainGenericRunner { val classpathSeparator = File.pathSeparator @sharable val javaOption = raw"""-J(.*)""".r - + @sharable val scalaOption = raw"""@.*""".r + @sharable val colorOption = raw"""-color:.*""".r @tailrec def process(args: List[String], settings: Settings): Settings = args match case Nil => @@ -98,6 +99,10 @@ object MainGenericRunner { process(tail, settings.withSave) case (o @ javaOption(striped)) :: tail => process(tail, settings.withJavaArgs(striped).withScalaArgs(o)) + case (o @ scalaOption(_*)) :: tail => + process(tail, settings.withScalaArgs(o)) + case (o @ colorOption(_*)) :: tail => + process(tail, settings.withScalaArgs(o)) case arg :: tail => val line = Try(Source.fromFile(arg).getLines.toList).toOption.flatMap(_.headOption) if arg.endsWith(".scala") || arg.endsWith(".sc") || (line.nonEmpty && raw"#!.*scala".r.matches(line.get)) then diff --git a/compiler/src/dotty/tools/scripting/Main.scala b/compiler/src/dotty/tools/scripting/Main.scala index 70e606608d0b..3a86155f1fe9 100755 --- a/compiler/src/dotty/tools/scripting/Main.scala +++ b/compiler/src/dotty/tools/scripting/Main.scala @@ -13,6 +13,8 @@ object Main: assert(rest.size >= 2, s"internal error: rest == Array(${rest.mkString(",")})") val file = File(rest(1)) + // write script path to script.path property, so called script can see it + sys.props("script.path") = file.toPath.toAbsolutePath.toString val scriptArgs = rest.drop(2) var saveJar = false var invokeFlag = true // by default, script main method is invoked diff --git a/compiler/test-coursier/dotty/tools/coursier/CoursierScalaTests.scala b/compiler/test-coursier/dotty/tools/coursier/CoursierScalaTests.scala index 277b31ec246a..dc2d95cc398d 100644 --- a/compiler/test-coursier/dotty/tools/coursier/CoursierScalaTests.scala +++ b/compiler/test-coursier/dotty/tools/coursier/CoursierScalaTests.scala @@ -52,6 +52,13 @@ class CoursierScalaTests: assertEquals(expectedOutput, output) scriptArgs() + def scriptPath() = + val scriptPath = scripts("/scripting").find(_.getName == "showArgs.sc").get.absPath + val args = scriptPath + val output = CoursierScalaTests.csCmd(args*) + assertTrue(output.startsWith("script.path:")) + scriptPath() + def version() = val output = CoursierScalaTests.csCmd("-version") assertTrue(output.mkString("\n").contains(sys.env("DOTTY_BOOTSTRAPPED_VERSION"))) diff --git a/dist/bin/scala b/dist/bin/scala index bb301a0d2217..b1e3d4e66e3e 100755 --- a/dist/bin/scala +++ b/dist/bin/scala @@ -30,6 +30,7 @@ source "$PROG_HOME/bin/common" # exec here would prevent onExit from being called, leaving terminal in unusable state compilerJavaClasspathArgs +[ -z "${ConEmuPID-}" -o -n "${cygwin-}" ] && export MSYSTEM= PWD= # workaround for #12405 eval "\"$JAVACMD\"" "-classpath \"$jvm_cp_args\"" "dotty.tools.MainGenericRunner" "-classpath \"$jvm_cp_args\"" "$@" scala_exit_status=$? From f3a72f2da4c2d3cf65df6deed2edca168f261a62 Mon Sep 17 00:00:00 2001 From: Andrzej Ratajczak Date: Tue, 27 Jul 2021 17:59:38 +0200 Subject: [PATCH 09/10] Fix coursier test --- .../dotty/tools/coursier/CoursierScalaTests.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/test-coursier/dotty/tools/coursier/CoursierScalaTests.scala b/compiler/test-coursier/dotty/tools/coursier/CoursierScalaTests.scala index dc2d95cc398d..2ad0a5612b3e 100644 --- a/compiler/test-coursier/dotty/tools/coursier/CoursierScalaTests.scala +++ b/compiler/test-coursier/dotty/tools/coursier/CoursierScalaTests.scala @@ -53,10 +53,10 @@ class CoursierScalaTests: scriptArgs() def scriptPath() = - val scriptPath = scripts("/scripting").find(_.getName == "showArgs.sc").get.absPath + val scriptPath = scripts("/scripting").find(_.getName == "scriptPath.sc").get.absPath val args = scriptPath - val output = CoursierScalaTests.csCmd(args*) - assertTrue(output.startsWith("script.path:")) + val output = CoursierScalaTests.csCmd(args) + assertTrue(output.mkString("\n").startsWith("script.path:")) scriptPath() def version() = From 314c3c94a5ddd2293c2fecda2dbc273cbcdd9e77 Mon Sep 17 00:00:00 2001 From: Andrzej Ratajczak Date: Mon, 2 Aug 2021 11:47:15 +0200 Subject: [PATCH 10/10] Test if script path provided by env var is correct --- .../test-coursier/dotty/tools/coursier/CoursierScalaTests.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/test-coursier/dotty/tools/coursier/CoursierScalaTests.scala b/compiler/test-coursier/dotty/tools/coursier/CoursierScalaTests.scala index 2ad0a5612b3e..e80f187321df 100644 --- a/compiler/test-coursier/dotty/tools/coursier/CoursierScalaTests.scala +++ b/compiler/test-coursier/dotty/tools/coursier/CoursierScalaTests.scala @@ -57,6 +57,7 @@ class CoursierScalaTests: val args = scriptPath val output = CoursierScalaTests.csCmd(args) assertTrue(output.mkString("\n").startsWith("script.path:")) + assertTrue(output.mkString("\n").endsWith("scriptPath.sc")) scriptPath() def version() =