diff --git a/compiler/src/dotty/tools/dotc/core/MacroClassLoader.scala b/compiler/src/dotty/tools/dotc/core/MacroClassLoader.scala index 261b1f26a64d..896da4044b42 100644 --- a/compiler/src/dotty/tools/dotc/core/MacroClassLoader.scala +++ b/compiler/src/dotty/tools/dotc/core/MacroClassLoader.scala @@ -3,6 +3,7 @@ package dotty.tools.dotc.core import dotty.tools.dotc.core.Contexts._ import dotty.tools.dotc.util.Property import dotty.tools.dotc.reporting.trace +import dotty.tools.io.ClassPath import scala.collection.mutable @@ -20,7 +21,8 @@ object MacroClassLoader { ctx.setProperty(MacroClassLoaderKey, makeMacroClassLoader(using ctx)) private def makeMacroClassLoader(using Context): ClassLoader = trace("new macro class loader") { - val urls = ctx.settings.classpath.value.split(java.io.File.pathSeparatorChar).map(cp => java.nio.file.Paths.get(cp).toUri.toURL) + val entries = ClassPath.expandPath(ctx.settings.classpath.value, expandStar=true) + val urls = entries.map(cp => java.nio.file.Paths.get(cp).toUri.toURL).toArray val out = ctx.settings.outputDir.value.jpath.toUri.toURL // to find classes in case of suspended compilation new java.net.URLClassLoader(urls :+ out, getClass.getClassLoader) } diff --git a/compiler/src/dotty/tools/io/ClassPath.scala b/compiler/src/dotty/tools/io/ClassPath.scala index d7218108e944..eeefabddd19f 100644 --- a/compiler/src/dotty/tools/io/ClassPath.scala +++ b/compiler/src/dotty/tools/io/ClassPath.scala @@ -132,7 +132,8 @@ object ClassPath { dir.list.filter(x => filt(x.name) && (x.isDirectory || isJarOrZip(x))).map(_.path).toList if (pattern == "*") lsDir(Directory(".")) - else if (pattern.endsWith(wildSuffix)) lsDir(Directory(pattern dropRight 2)) + // On Windows the JDK supports forward slash or backslash in classpath entries + else if (pattern.endsWith(wildSuffix) || pattern.endsWith("/*")) lsDir(Directory(pattern dropRight 2)) else if (pattern.contains('*')) { try { val regexp = ("^" + pattern.replace("""\*""", """.*""") + "$").r diff --git a/compiler/src/dotty/tools/scripting/Main.scala b/compiler/src/dotty/tools/scripting/Main.scala old mode 100644 new mode 100755 index ded809b9f675..97d0b91d4440 --- a/compiler/src/dotty/tools/scripting/Main.scala +++ b/compiler/src/dotty/tools/scripting/Main.scala @@ -10,7 +10,7 @@ object Main: All arguments afterwards are script arguments.*/ private def distinguishArgs(args: Array[String]): (Array[String], File, Array[String], Boolean, Boolean) = val (leftArgs, rest) = args.splitAt(args.indexOf("-script")) - assert(rest.size >= 2,s"internal error: rest == Array(${rest.mkString(",")})") + assert(rest.size >= 2, s"internal error: rest == Array(${rest.mkString(",")})") val file = File(rest(1)) val scriptArgs = rest.drop(2) @@ -32,10 +32,12 @@ object Main: def main(args: Array[String]): Unit = val (compilerArgs, scriptFile, scriptArgs, saveJar, invokeFlag) = distinguishArgs(args) val driver = ScriptingDriver(compilerArgs, scriptFile, scriptArgs) - try driver.compileAndRun { (outDir:Path, classpath:String, mainClass: String) => + try driver.compileAndRun { (outDir:Path, classpathEntries:Seq[Path], mainClass: String) => + // write expanded classpath to java.class.path property, so called script can see it + sys.props("java.class.path") = classpathEntries.map(_.toString).mkString(pathsep) if saveJar then // write a standalone jar to the script parent directory - writeJarfile(outDir, scriptFile, scriptArgs, classpath, mainClass) + writeJarfile(outDir, scriptFile, scriptArgs, classpathEntries, mainClass) invokeFlag } catch @@ -47,10 +49,7 @@ object Main: throw e.getCause private def writeJarfile(outDir: Path, scriptFile: File, scriptArgs:Array[String], - classpath:String, mainClassName: String): Unit = - - val javaClasspath = sys.props("java.class.path") - val runtimeClasspath = s"${classpath}$pathsep$javaClasspath" + classpathEntries:Seq[Path], mainClassName: String): Unit = val jarTargetDir: Path = Option(scriptFile.toPath.toAbsolutePath.getParent) match { case None => sys.error(s"no parent directory for script file [$scriptFile]") @@ -60,7 +59,7 @@ object Main: def scriptBasename = scriptFile.getName.takeWhile(_!='.') val jarPath = s"$jarTargetDir/$scriptBasename.jar" - val cpPaths = runtimeClasspath.split(pathsep).map(_.toUrl) + val cpPaths = classpathEntries.map { _.toString.toUrl } import java.util.jar.Attributes.Name val cpString:String = cpPaths.distinct.mkString(" ") @@ -92,7 +91,7 @@ object Main: // convert to absolute path relative to cwd. def absPath: String = norm match case str if str.isAbsolute => norm - case _ => Paths.get(userDir,norm).toString.norm + case _ => Paths.get(userDir, norm).toString.norm def toUrl: String = Paths.get(absPath).toUri.toURL.toString diff --git a/compiler/src/dotty/tools/scripting/ScriptingDriver.scala b/compiler/src/dotty/tools/scripting/ScriptingDriver.scala old mode 100644 new mode 100755 index af0858acf658..ae428d069a29 --- a/compiler/src/dotty/tools/scripting/ScriptingDriver.scala +++ b/compiler/src/dotty/tools/scripting/ScriptingDriver.scala @@ -1,6 +1,6 @@ package dotty.tools.scripting -import java.nio.file.{ Files, Path } +import java.nio.file.{ Files, Paths, Path } import java.io.File import java.net.{ URL, URLClassLoader } import java.lang.reflect.{ Modifier, Method } @@ -10,14 +10,14 @@ import scala.jdk.CollectionConverters._ import dotty.tools.dotc.{ Driver, Compiler } import dotty.tools.dotc.core.Contexts, Contexts.{ Context, ContextBase, ctx } import dotty.tools.dotc.config.CompilerCommand -import dotty.tools.io.{ PlainDirectory, Directory } +import dotty.tools.io.{ PlainDirectory, Directory, ClassPath } import dotty.tools.dotc.reporting.Reporter import dotty.tools.dotc.config.Settings.Setting._ import sys.process._ class ScriptingDriver(compilerArgs: Array[String], scriptFile: File, scriptArgs: Array[String]) extends Driver: - def compileAndRun(pack:(Path, String, String) => Boolean = null): Unit = + def compileAndRun(pack:(Path, Seq[Path], String) => Boolean = null): Unit = val outDir = Files.createTempDirectory("scala3-scripting") setup(compilerArgs :+ scriptFile.getAbsolutePath, initCtx.fresh) match case Some((toCompile, rootCtx)) => @@ -28,11 +28,13 @@ class ScriptingDriver(compilerArgs: Array[String], scriptFile: File, scriptArgs: throw ScriptingException("Errors encountered during compilation") try - val (mainClass, mainMethod) = detectMainClassAndMethod(outDir, ctx.settings.classpath.value, scriptFile) + val classpath = s"${ctx.settings.classpath.value}${pathsep}${sys.props("java.class.path")}" + val classpathEntries: Seq[Path] = ClassPath.expandPath(classpath, expandStar=true).map { Paths.get(_) } + val (mainClass, mainMethod) = detectMainClassAndMethod(outDir, classpathEntries, scriptFile) val invokeMain: Boolean = Option(pack) match case Some(func) => - func(outDir, ctx.settings.classpath.value, mainClass) + func(outDir, classpathEntries, mainClass) case None => true end match @@ -52,11 +54,12 @@ class ScriptingDriver(compilerArgs: Array[String], scriptFile: File, scriptArgs: target.delete() end deleteFile - private def detectMainClassAndMethod(outDir: Path, classpath: String, + private def detectMainClassAndMethod(outDir: Path, classpathEntries: Seq[Path], scriptFile: File): (String, Method) = - val outDirURL = outDir.toUri.toURL - val classpathUrls = classpath.split(pathsep).map(File(_).toURI.toURL) - val cl = URLClassLoader(classpathUrls :+ outDirURL) + + val classpathUrls = (classpathEntries :+ outDir).map { _.toUri.toURL } + + val cl = URLClassLoader(classpathUrls.toArray) def collectMainMethods(target: File, path: String): List[(String, Method)] = val nameWithoutExtension = target.getName.takeWhile(_ != '.') diff --git a/compiler/test-resources/scripting/classpathReport.sc b/compiler/test-resources/scripting/classpathReport.sc new file mode 100755 index 000000000000..5ccfd6faca76 --- /dev/null +++ b/compiler/test-resources/scripting/classpathReport.sc @@ -0,0 +1,9 @@ +#!dist/target/pack/bin/scala -classpath 'dist/target/pack/lib/*' + +import java.nio.file.Paths + +def main(args: Array[String]): Unit = + val cwd = Paths.get(".").toAbsolutePath.toString.replace('\\', '/').replaceAll("/$", "") + printf("cwd: %s\n", cwd) + printf("classpath: %s\n", sys.props("java.class.path")) + diff --git a/compiler/test/dotty/tools/io/ClasspathTest.scala b/compiler/test/dotty/tools/io/ClasspathTest.scala new file mode 100755 index 000000000000..35437ef17390 --- /dev/null +++ b/compiler/test/dotty/tools/io/ClasspathTest.scala @@ -0,0 +1,70 @@ +package dotty.tools.io + +import org.junit.Test + +import java.io.File +import dotty.tools.io.AbstractFile +import java.nio.file.{Files, Paths} +import java.nio.file.StandardCopyOption._ +import java.nio.file.attribute.PosixFilePermissions +import dotty.tools.io.{ PlainDirectory, Directory, ClassPath } + +class ClasspathTest { + + def pathsep = sys.props("path.separator") + + // + // Cope with wildcard classpath entries, exercised with -classpath + // + // Verify that Windows users not forced to use backslash in classpath. + // + @Test def testWildcards(): Unit = + val outDir = Files.createTempDirectory("classpath-test") + try + val compilerLib = "dist/target/pack/lib" + val libdir = Paths.get(compilerLib).toFile + if libdir.exists then + val libjarFiles = libdir.listFiles.toList.take(5) + try + for src <- libjarFiles do + val dest = Paths.get(s"$outDir/${src.getName}") + printf("copy: %s\n", Files.copy(src.toPath, dest)) + + val cp = Seq(s"$outDir/*", "not-a-real-directory/*").mkString(pathsep).replace('\\', '/') + + val libjars = libjarFiles.map { _.getName }.toSet + + // expand wildcard classpath entries, ignoring invalid entries + val entries = ClassPath.expandPath(cp).map { Paths.get(_).toFile.getName } + + // require one-to-one matches + assert(libjars == entries.toSet) + + printf("%d entries\n", entries.size) + printf("%d libjars\n", libjars.size) + + for entry <- libjars do + printf("libdir[%s]\n", entry) + + for entry <- entries do + printf("expand[%s]\n", entry) + + // verify that expanded classpath has expected jar names + for jar <- libjars do + assert(entries.contains(jar)) + + catch + case _:NullPointerException => // no test if unable to copy jars to outDir + + + finally + deleteFile(outDir.toFile) + + + private def deleteFile(target: File): Unit = + if target.isDirectory then + for member <- target.listFiles.toList + do deleteFile(member) + target.delete() + end deleteFile +} diff --git a/compiler/test/dotty/tools/scripting/ClasspathTests.scala b/compiler/test/dotty/tools/scripting/ClasspathTests.scala new file mode 100755 index 000000000000..97db62d41240 --- /dev/null +++ b/compiler/test/dotty/tools/scripting/ClasspathTests.scala @@ -0,0 +1,228 @@ +package dotty +package tools +package scripting + +import java.io.File +import java.nio.file.{Files, Paths, Path} + +import org.junit.Test + +import vulpix.TestConfiguration + +import scala.sys.process._ +import scala.jdk.CollectionConverters._ +import dotty.tools.dotc.config.Properties._ + +/** Runs all tests contained in `compiler/test-resources/scripting/` */ +class ClasspathTests: + val packBinDir = "dist/target/pack/bin" + val scalaCopy = makeTestableScriptCopy("scala") + val scalacCopy = makeTestableScriptCopy("scalac") + + // only interested in classpath test scripts + def testFiles = scripts("/scripting").filter { _.getName.matches("classpath.*[.]sc") } + val testScriptName = "classpathReport.sc" + def testScript = testFiles.find { _.getName == testScriptName } match + case None => sys.error(s"test script not found: ${testScriptName}") + case Some(file) => file + + def getScriptPath(scriptName: String): Path = Paths.get(s"$packBinDir/$scriptName") + + def exists(scriptPath: Path): Boolean = Files.exists(scriptPath) + def packBinScalaExists:Boolean = exists(Paths.get(s"$packBinDir/scala")) + + // create edited copy of [dist/bin/scala] and [dist/bin/scalac] for scalacEchoTest + def makeTestableScriptCopy(scriptName: String): Path = + val scriptPath: Path = getScriptPath(scriptName) + val scriptCopy: Path = getScriptPath(s"$scriptName-copy") + if Files.exists(scriptPath) then + val lines = Files.readAllLines(scriptPath).asScala.map { + _.replaceAll("/scalac", "/scalac-copy"). + replaceFirst("^eval(.*JAVACMD.*)", "echo $1") + } + val bytes = (lines.mkString("\n")+"\n").getBytes + Files.write(scriptCopy, bytes) + + scriptCopy + + /* + * verify java command line generated by scalac. + */ + @Test def scalacEchoTest = + val relpath = testScript.toPath.relpath.norm + printf("===> scalacEchoTest for script [%s]\n", relpath) + printf("bash is [%s]\n", bashExe) + + if packBinScalaExists then + val echoTest = "" // "SCALAC_ECHO_TEST=1" + val bashCmdline = s"SCALA_OPTS= $echoTest ${scalaCopy.norm} -classpath '$wildcardEntry' $relpath" + + // ask [dist/bin/scalac] to echo generated command line so we can verify some things + val cmd = Array(bashExe, "-c", bashCmdline) + + //cmd.foreach { printf("[%s]\n", _) } + + val javaCommandLine = exec(cmd:_*).mkString(" ").split(" ").filter { _.trim.nonEmpty } + printf("\n==================== isWin[%s], cygwin[%s], mingw[%s], msys[%s]\n", isWin, cygwin, mingw, msys) + javaCommandLine.foreach { printf("java-command[%s]\n", _) } + + val output = scala.collection.mutable.Queue(javaCommandLine:_*) + output.dequeueWhile( _ != "dotty.tools.scripting.Main") + + def consumeNext = if output.isEmpty then "" else output.dequeue() + + // assert that we found "dotty.tools.scripting.Main" + val str = consumeNext + if str != "dotty.tools.scripting.Main" then + + assert(str == "dotty.tools.scripting.Main", s"found [$str]") + val mainArgs = output.copyToArray(Array.ofDim[String](output.length)) + + // display command line starting with "dotty.tools.scripting.Main" + output.foreach { line => + printf("%s\n", line) + } + + // expecting -classpath next + assert(consumeNext.replaceAll("'", "") == "-classpath") + + // 2nd arg to scripting.Main is 'lib/*', with semicolon added if Windows jdk + + // PR #10761: verify that [dist/bin/scala] -classpath processing adds $psep to wildcard if Windows + val classpathValue = consumeNext + printf("classpath value [%s]\n", classpathValue) + assert( !winshell || classpathValue.contains(psep) ) + + // expecting -script next + assert(consumeNext.replaceAll("'", "") == "-script") + + // PR #10761: verify that Windows jdk did not expand single wildcard classpath to multiple file paths + if javaCommandLine.last != relpath then + printf("last: %s\nrelp: %s\n", javaCommandLine.last, relpath) + assert(javaCommandLine.last == relpath, s"unexpected output passed to scripting.Main") + + /* + * verify classpath reported by called script. + */ + @Test def hashbangClasspathVerifyTest = + val relpath = testScript.toPath.relpath.norm + printf("===> hashbangClasspathVerifyTest for script [%s]\n", relpath) + printf("bash is [%s]\n", bashExe) + + if false && packBinScalaExists then + val bashCmdline = s"SCALA_OPTS= $relpath" + val cmd = Array(bashExe, "-c", bashCmdline) + + cmd.foreach { printf("[%s]\n", _) } + + // test script reports the classpath it sees + val scriptOutput = exec(cmd:_*) + val scriptCwd = findTaggedLine("cwd", scriptOutput) + printf("script ran in directory [%s]\n", scriptCwd) + val scriptCp = findTaggedLine("classpath", scriptOutput) + + val hashbangClasspathJars = scriptCp.split(psep).map { _.getName }.sorted.distinct + val packlibJars = listJars(s"$scriptCwd/dist/target/pack/lib").sorted.distinct + + // verify that the classpath set in the hashbang line is effective + if hashbangClasspathJars.size != packlibJars.size then + printf("%d test script jars in classpath\n", hashbangClasspathJars.size) + printf("%d jar files in dist/target/pack/lib\n", packlibJars.size) + + assert(hashbangClasspathJars.size == packlibJars.size) + + +//////////////// end of tests //////////////// +lazy val cwd = Paths.get(".").toAbsolutePath +lazy val wildcardEntry = "dist/target/pack/lib/*" + +def listJars(dir: String) = + val packlibDir = Paths.get(dir).toFile + if packlibDir.isDirectory then + packlibDir.listFiles.toList.map { _.getName }.filter { _.endsWith(".jar") } + else + Nil + +import scala.jdk.CollectionConverters._ +lazy val env:Map[String, String] = System.getenv.asScala.toMap + +// script output expected as ": " +def findTaggedLine(tag: String, lines: Seq[String]): String = + lines.find { _.startsWith(tag) } match + case None => sys.error(s"no $tag: found in script output") + case Some(cwd) => cwd.dropWhile( _ != ' ').trim // discard tag + +def exec(cmd: String *): Seq[String] = Process(cmd).lazyLines_!.toList + +def which(str:String) = + var out = "" + path.find { entry => + val it = Paths.get(entry).toAbsolutePath + it.toFile.isDirectory && { + var testpath = s"$it/$str".norm + val test = Paths.get(testpath) + if test.toFile.exists then + out = testpath + true + else + val test = Paths.get(s"$it/$str.exe".norm) + if test.toFile.exists then + out = testpath + true + else + false + } + } + + out + +def bashExe = which("bash") +def unameExe = which("uname") +def path = envOrElse("PATH", "").split(psep).toList +def psep = sys.props("path.separator") + +def cygwin = ostype == "cygwin" +def mingw = ostype == "mingw" +def msys = ostype == "msys" +def winshell = cygwin || mingw || msys + +def ostypeFull = if unameExe.nonEmpty then exec(unameExe).mkString else "" +def ostype = ostypeFull.toLowerCase.takeWhile{ cc => cc >= 'a' && cc <='z' || cc >= 'A' && cc <= 'Z' } + +extension(p:Path) + def relpath: Path = cwd.relativize(p) + def norm: String = p.toString.replace('\\', '/') + +extension(path: String) + def getName: String = norm.replaceAll(".*/", "") + + // Normalize path separator, convert relative path to absolute + def norm: String = + path.replace('\\', '/') match { + case s if s.secondChar == ":" => s // .drop(2) // path without drive letter + case s if s.startsWith("./") => s.drop(2) + case s => s + } + + def parent: String = norm.replaceAll("/[^/]*$", "") + + // convert to absolute path relative to cwd. + def absPath: String = norm match + case str if str.isAbsolute => norm + case _ => Paths.get(userDir, norm).toString.norm + + def isDir: Boolean = Files.isDirectory(Paths.get(path)) + + def toUrl: String = Paths.get(absPath).toUri.toURL.toString + + // Treat norm paths with a leading '/' as absolute. + // Windows java.io.File#isAbsolute treats them as relative. + def isAbsolute = path.norm.startsWith("/") || (isWin && path.secondChar == ":") + def secondChar: String = path.take(2).drop(1).mkString("") + +extension (str: String) def dropExtension = + str.reverse.dropWhile(_ != '.').drop(1).reverse + +//extension(f: File) def absPath = +//f.getAbsolutePath.replace('\\', '/') + diff --git a/compiler/test/dotty/tools/scripting/ScriptingTests.scala b/compiler/test/dotty/tools/scripting/ScriptingTests.scala index e7399c68f09a..25fdfe14f5c7 100644 --- a/compiler/test/dotty/tools/scripting/ScriptingTests.scala +++ b/compiler/test/dotty/tools/scripting/ScriptingTests.scala @@ -3,6 +3,7 @@ package tools package scripting import java.io.File +import java.nio.file.Path import org.junit.Test @@ -15,16 +16,17 @@ class ScriptingTests: str.reverse.dropWhile(_ != '.').drop(1).reverse extension(f: File) def absPath = - f.getAbsolutePath.replace('\\','/') + f.getAbsolutePath.replace('\\', '/') - def testFiles = scripts("/scripting") + // classpath tests managed by scripting.ClasspathTests.scala + def testFiles = scripts("/scripting").filter { ! _.getName.startsWith("classpath") } def script2jar(scriptFile: File) = val jarName = s"${scriptFile.getName.dropExtension}.jar" - File(scriptFile.getParent,jarName) + File(scriptFile.getParent, jarName) def showScriptUnderTest(scriptFile: File): Unit = - printf("===> test script name [%s]\n",scriptFile.getName) + printf("===> test script name [%s]\n", scriptFile.getName) val argss: Map[String, Array[String]] = ( for @@ -40,11 +42,11 @@ class ScriptingTests: if scriptFile.getName.endsWith(extension) name = scriptFile.getName.dropExtension scriptArgs = argss.getOrElse(name, Array.empty[String]) - yield scriptFile -> scriptArgs).toList.sortBy { (file,args) => file.getName } + yield scriptFile -> scriptArgs).toList.sortBy { (file, args) => file.getName } - def callExecutableJar(script: File,jar: File, scriptArgs: Array[String] = Array.empty[String]) = { + def callExecutableJar(script: File, jar: File, scriptArgs: Array[String] = Array.empty[String]) = { import scala.sys.process._ - val cmd = Array("java",s"-Dscript.path=${script.getName}","-jar",jar.absPath) + val cmd = Array("java", s"-Dscript.path=${script.getName}", "-jar", jar.absPath) ++ scriptArgs Process(cmd).lazyLines_!.foreach { println } } @@ -53,7 +55,7 @@ class ScriptingTests: * Call .scala scripts without -save option, verify no jar created */ @Test def scriptingDriverTests = - for (scriptFile,scriptArgs) <- scalaFilesWithArgs(".scala") do + for (scriptFile, scriptArgs) <- scalaFilesWithArgs(".scala") do showScriptUnderTest(scriptFile) val unexpectedJar = script2jar(scriptFile) unexpectedJar.delete @@ -65,8 +67,8 @@ class ScriptingTests: ), scriptFile = scriptFile, scriptArgs = scriptArgs - ).compileAndRun { (path:java.nio.file.Path,classpath:String, mainClass:String) => - printf("mainClass from ScriptingDriver: %s\n",mainClass) + ).compileAndRun { (path:java.nio.file.Path, classpathEntries:Seq[Path], mainClass:String) => + printf("mainClass from ScriptingDriver: %s\n", mainClass) true // call compiled script main method } assert(! unexpectedJar.exists, s"not expecting jar file: ${unexpectedJar.absPath}") @@ -75,7 +77,7 @@ class ScriptingTests: * Call .sc scripts without -save option, verify no jar created */ @Test def scriptingMainTests = - for (scriptFile,scriptArgs) <- scalaFilesWithArgs(".sc") do + for (scriptFile, scriptArgs) <- scalaFilesWithArgs(".sc") do showScriptUnderTest(scriptFile) val unexpectedJar = script2jar(scriptFile) unexpectedJar.delete @@ -93,7 +95,7 @@ class ScriptingTests: * Call .sc scripts with -save option, verify jar is created. */ @Test def scriptingJarTest = - for (scriptFile,scriptArgs) <- scalaFilesWithArgs(".sc") do + for (scriptFile, scriptArgs) <- scalaFilesWithArgs(".sc") do showScriptUnderTest(scriptFile) val expectedJar = script2jar(scriptFile) expectedJar.delete @@ -107,7 +109,7 @@ class ScriptingTests: Main.main(mainArgs) - printf("===> test script jar name [%s]\n",expectedJar.getName) + printf("===> test script jar name [%s]\n", expectedJar.getName) assert(expectedJar.exists) callExecutableJar(scriptFile, expectedJar, scriptArgs) @@ -128,8 +130,8 @@ class ScriptingTests: compilerArgs = Array("-classpath", TestConfiguration.basicClasspath), scriptFile = scriptFile, scriptArgs = Array.empty[String] - ).compileAndRun { (path:java.nio.file.Path,classpath:String, mainClass:String) => - printf("success: no call to main method in mainClass: %s\n",mainClass) + ).compileAndRun { (path:java.nio.file.Path, classpathEntries:Seq[Path], mainClass:String) => + printf("success: no call to main method in mainClass: %s\n", mainClass) false // no call to compiled script main method } touchedFile.delete @@ -141,14 +143,14 @@ class ScriptingTests: compilerArgs = Array("-classpath", TestConfiguration.basicClasspath), scriptFile = scriptFile, scriptArgs = Array.empty[String] - ).compileAndRun { (path:java.nio.file.Path,classpath:String, mainClass:String) => - printf("call main method in mainClass: %s\n",mainClass) + ).compileAndRun { (path:java.nio.file.Path, classpathEntries:Seq[Path], mainClass:String) => + printf("call main method in mainClass: %s\n", mainClass) true // call compiled script main method, create touchedFile } if touchedFile.exists then - printf("success: script created file %s\n",touchedFile) - if touchedFile.exists then printf("success: created file %s\n",touchedFile) + printf("success: script created file %s\n", touchedFile) + if touchedFile.exists then printf("success: created file %s\n", touchedFile) assert( touchedFile.exists, s"expected to find file ${touchedFile}" ) /* @@ -168,15 +170,15 @@ class ScriptingTests: expectedJar.delete Main.main(mainArgs) // create executable jar - printf("===> test script jar name [%s]\n",expectedJar.getName) - assert(expectedJar.exists,s"unable to create executable jar [$expectedJar]") + printf("===> test script jar name [%s]\n", expectedJar.getName) + assert(expectedJar.exists, s"unable to create executable jar [$expectedJar]") touchedFile.delete - assert(!touchedFile.exists,s"unable to delete ${touchedFile}") - printf("calling executable jar %s\n",expectedJar) + assert(!touchedFile.exists, s"unable to delete ${touchedFile}") + printf("calling executable jar %s\n", expectedJar) callExecutableJar(scriptFile, expectedJar) if touchedFile.exists then - printf("success: executable jar created file %s\n",touchedFile) + printf("success: executable jar created file %s\n", touchedFile) assert( touchedFile.exists, s"expected to find file ${touchedFile}" ) def touchFileScript = testFiles.find(_.getName == "touchFile.sc").get diff --git a/dist/bin/scala b/dist/bin/scala index bfcd30c14cd0..562e1c9be2e6 100755 --- a/dist/bin/scala +++ b/dist/bin/scala @@ -65,12 +65,18 @@ while [[ $# -gt 0 ]]; do execute_run=true shift ;; - -cp | -classpath) - CLASS_PATH="$2" + -cp | -classpath ) + CLASS_PATH="$2${PSEP}" class_path_count+=1 shift shift ;; + -cp*|-classpath*) + # hashbang can combine args, e.g. "-classpath 'lib/*'" + CLASS_PATH="${1#* *}${PSEP}" + class_path_count+=1 + shift + ;; -with-compiler) with_compiler=true shift