From 121d746932a31e6acff98ef2c66ce549110c1f2b Mon Sep 17 00:00:00 2001 From: Phil Date: Sat, 6 Mar 2021 15:34:11 -0700 Subject: [PATCH 1/4] expand wildcard classpath entries; not os-dependent, required by jar manifest; fixes #10761 --- compiler/src/dotty/tools/scripting/Main.scala | 13 +++++---- .../tools/scripting/ScriptingDriver.scala | 29 ++++++++++++++----- .../tools/scripting/ScriptingTests.scala | 7 +++-- 3 files changed, 32 insertions(+), 17 deletions(-) mode change 100644 => 100755 compiler/src/dotty/tools/scripting/Main.scala mode change 100644 => 100755 compiler/src/dotty/tools/scripting/ScriptingDriver.scala 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..fd69fc0ff5ec --- a/compiler/src/dotty/tools/scripting/Main.scala +++ b/compiler/src/dotty/tools/scripting/Main.scala @@ -32,10 +32,10 @@ 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) => 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 +47,10 @@ object Main: throw e.getCause private def writeJarfile(outDir: Path, scriptFile: File, scriptArgs:Array[String], - classpath:String, mainClassName: String): Unit = + classpathEntries:Seq[Path], mainClassName: String): Unit = - val javaClasspath = sys.props("java.class.path") - val runtimeClasspath = s"${classpath}$pathsep$javaClasspath" + //val javaClasspath = sys.props("java.class.path") + //val runtimeClasspath = s"${classpath}$pathsep$javaClasspath" val jarTargetDir: Path = Option(scriptFile.toPath.toAbsolutePath.getParent) match { case None => sys.error(s"no parent directory for script file [$scriptFile]") @@ -60,7 +60,8 @@ object Main: def scriptBasename = scriptFile.getName.takeWhile(_!='.') val jarPath = s"$jarTargetDir/$scriptBasename.jar" - val cpPaths = runtimeClasspath.split(pathsep).map(_.toUrl) + //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(" ") 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..7c1109754e0f --- 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 } @@ -17,7 +17,7 @@ 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,23 @@ 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.split(pathsep).toIndexedSeq.flatMap { entry => + val f = Paths.get(entry).toAbsolutePath.normalize.toFile + // expand wildcard classpath entries + if (f.getName == "*" && f.getParentFile.isDirectory){ + f.getParentFile.listFiles.filter { _.getName.toLowerCase.endsWith(".jar") }.map { _.toPath }.toSeq + } else { + Seq(f.toPath) + } + }.toIndexedSeq + + 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 +64,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/dotty/tools/scripting/ScriptingTests.scala b/compiler/test/dotty/tools/scripting/ScriptingTests.scala index e7399c68f09a..c0198507ed4d 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 @@ -65,7 +66,7 @@ class ScriptingTests: ), scriptFile = scriptFile, scriptArgs = scriptArgs - ).compileAndRun { (path:java.nio.file.Path,classpath:String, mainClass:String) => + ).compileAndRun { (path:java.nio.file.Path,classpathEntries:Seq[Path], mainClass:String) => printf("mainClass from ScriptingDriver: %s\n",mainClass) true // call compiled script main method } @@ -128,7 +129,7 @@ class ScriptingTests: compilerArgs = Array("-classpath", TestConfiguration.basicClasspath), scriptFile = scriptFile, scriptArgs = Array.empty[String] - ).compileAndRun { (path:java.nio.file.Path,classpath:String, mainClass:String) => + ).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 } @@ -141,7 +142,7 @@ class ScriptingTests: compilerArgs = Array("-classpath", TestConfiguration.basicClasspath), scriptFile = scriptFile, scriptArgs = Array.empty[String] - ).compileAndRun { (path:java.nio.file.Path,classpath:String, mainClass:String) => + ).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 } From 54a2dc93301af8c192217a5c9de9f1a645f2470a Mon Sep 17 00:00:00 2001 From: Phil Date: Sat, 6 Mar 2021 15:54:56 -0700 Subject: [PATCH 2/4] removed obsolete comments --- compiler/src/dotty/tools/scripting/Main.scala | 4 ---- 1 file changed, 4 deletions(-) diff --git a/compiler/src/dotty/tools/scripting/Main.scala b/compiler/src/dotty/tools/scripting/Main.scala index fd69fc0ff5ec..19c44c7ca083 100755 --- a/compiler/src/dotty/tools/scripting/Main.scala +++ b/compiler/src/dotty/tools/scripting/Main.scala @@ -49,9 +49,6 @@ object Main: private def writeJarfile(outDir: Path, scriptFile: File, scriptArgs:Array[String], classpathEntries:Seq[Path], mainClassName: String): Unit = - //val javaClasspath = sys.props("java.class.path") - //val runtimeClasspath = s"${classpath}$pathsep$javaClasspath" - val jarTargetDir: Path = Option(scriptFile.toPath.toAbsolutePath.getParent) match { case None => sys.error(s"no parent directory for script file [$scriptFile]") case Some(parent) => parent @@ -60,7 +57,6 @@ 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 From 5648dadedc8885d6521e1f2146c203d4c944a2c3 Mon Sep 17 00:00:00 2001 From: Phil Date: Mon, 8 Mar 2021 12:45:01 -0700 Subject: [PATCH 3/4] eliminate needless 2nd sourcing of bin/common in script startup --- dist/bin/common | 21 +++++++++++++-------- dist/bin/scala | 11 ++++++++--- dist/bin/scalac | 5 +++-- 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/dist/bin/common b/dist/bin/common index a3847b2b1dbd..76f816733415 100755 --- a/dist/bin/common +++ b/dist/bin/common @@ -54,9 +54,9 @@ case "`uname`" in esac unset CYGPATHCMD -if [[ $cygwin || $mingw || $msys ]]; then +if [[ ${cygwin-} || ${mingw-} || ${msys-} ]]; then # ConEmu terminal is incompatible with jna-5.*.jar - [[ ($CONEMUANSI || $ConEmuANSI) ]] && conemu=true + [[ (${CONEMUANSI-} || ${ConEmuANSI-}) ]] && conemu=true # cygpath is used by various windows shells: cygwin, git-sdk, gitbash, msys, etc. CYGPATHCMD=`which cygpath 2>/dev/null` case "$TERM" in @@ -111,14 +111,14 @@ CLASSPATH_SUFFIX="" PSEP=":" # translate paths to Windows-mixed format before running java -if [ -n "$CYGPATHCMD" ]; then - [ -n "$PROG_HOME" ] && +if [ -n "${CYGPATHCMD-}" ]; then + [ -n "${PROG_HOME-}" ] && PROG_HOME=`"$CYGPATHCMD" -am "$PROG_HOME"` [ -n "$JAVA_HOME" ] && JAVA_HOME=`"$CYGPATHCMD" -am "$JAVA_HOME"` CLASSPATH_SUFFIX=";" PSEP=";" -elif [[ $mingw || $msys ]]; then +elif [[ ${mingw-} || ${msys-} ]]; then # For Mingw / Msys, convert paths from UNIX format before anything is touched [ -n "$PROG_HOME" ] && PROG_HOME="`(cd "$PROG_HOME"; pwd -W | sed 's|/|\\\\|g')`" @@ -134,9 +134,9 @@ fi find_lib () { local lib=$(find $PROG_HOME/lib/ -name "$1") - if [ -n "$CYGPATHCMD" ]; then + if [ -n "${CYGPATHCMD-}" ]; then $CYGPATHCMD -am $lib - elif [[ $mingw || $msys ]]; then + elif [[ ${mingw-} || ${msys-} ]]; then echo $lib | sed 's|/|\\\\|g' else echo $lib @@ -155,8 +155,13 @@ SBT_INTF=$(find_lib "*compiler-interface*") JLINE_READER=$(find_lib "*jline-reader-3*") JLINE_TERMINAL=$(find_lib "*jline-terminal-3*") JLINE_TERMINAL_JNA=$(find_lib "*jline-terminal-jna-3*") -[[ $conemu ]] || JNA=$(find_lib "*jna-5*") +[[ ${conemu-} ]] || JNA=$(find_lib "*jna-5*") # debug DEBUG_STR=-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005 + +export saved_stty PROG_HOME DEBUG_STR PSEP SCALA_LIB DOTTY_LIB SCALA_ASM SBT_INTF DOTTY_INTF DOTTY_COMP TASTY_CORE +export DOTTY_STAGING DOTTY_TASTY_INSPECTOR JLINE_READER JLINE_TERMINAL JLINE_TERMINAL_JNA JNA JAVACMD SCALA_OPTS +export cygwin mingw msys darwin conemu CLASSPATH_SUFFIX +export -f onExit restoreSttySettings diff --git a/dist/bin/scala b/dist/bin/scala index bfcd30c14cd0..ccc32111b1c6 100755 --- a/dist/bin/scala +++ b/dist/bin/scala @@ -1,8 +1,7 @@ #!/usr/bin/env bash # Try to autodetect real location of the script - -if [ -z "$PROG_HOME" ] ; then +if [ -z "${PROG_HOME-}" ] ; then ## resolve links - $0 may be a link to PROG_HOME PRG="$0" @@ -107,9 +106,15 @@ while [[ $# -gt 0 ]]; do *) if [ $execute_script == false ]; then # is a script if extension .scala or .sc or if has scala hash bang - if [[ -e "$1" && ("$1" == *.scala || "$1" == *.sc || -f "$1" && `head -n 1 -- "$1" | grep '#!.*scala'`) ]]; then + if [[ "$1" == *.scala || "$1" == *.sc || (-f "$1" && `head -n 1 -- "$1" | grep '#!.*scala'`) ]]; then execute_script=true target_script="$1" + 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 else residual_args+=("$1") fi diff --git a/dist/bin/scalac b/dist/bin/scalac index 74bfc4a603fe..df885de33ebd 100755 --- a/dist/bin/scalac +++ b/dist/bin/scalac @@ -1,6 +1,6 @@ #!/usr/bin/env bash -if [ -z "$PROG_HOME" ] ; then +if [ -z "${PROG_HOME-}" ] ; then ## resolve links - $0 may be a link to PROG_HOME PRG="$0" @@ -25,7 +25,8 @@ if [ -z "$PROG_HOME" ] ; then cd "$saveddir" fi -source "$PROG_HOME/bin/common" +# only source common if necessary +[ -z "${DEBUG_STR-}" -a -z "${PSEP-}" ] && source "$PROG_HOME/bin/common" default_java_opts="-Xmx768m -Xms768m" withCompiler=true From 3dad87e43f5175c4bdb7feba0a6f33af62690c7c Mon Sep 17 00:00:00 2001 From: Phil Date: Mon, 8 Mar 2021 15:39:14 -0700 Subject: [PATCH 4/4] revert #11633 changes --- compiler/src/dotty/tools/scripting/Main.scala | 11 ++++--- .../tools/scripting/ScriptingDriver.scala | 29 +++++-------------- .../tools/scripting/ScriptingTests.scala | 7 ++--- 3 files changed, 18 insertions(+), 29 deletions(-) diff --git a/compiler/src/dotty/tools/scripting/Main.scala b/compiler/src/dotty/tools/scripting/Main.scala index 19c44c7ca083..ded809b9f675 100755 --- a/compiler/src/dotty/tools/scripting/Main.scala +++ b/compiler/src/dotty/tools/scripting/Main.scala @@ -32,10 +32,10 @@ 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, classpathEntries:Seq[Path], mainClass: String) => + try driver.compileAndRun { (outDir:Path, classpath:String, mainClass: String) => if saveJar then // write a standalone jar to the script parent directory - writeJarfile(outDir, scriptFile, scriptArgs, classpathEntries, mainClass) + writeJarfile(outDir, scriptFile, scriptArgs, classpath, mainClass) invokeFlag } catch @@ -47,7 +47,10 @@ object Main: throw e.getCause private def writeJarfile(outDir: Path, scriptFile: File, scriptArgs:Array[String], - classpathEntries:Seq[Path], mainClassName: String): Unit = + classpath:String, mainClassName: String): Unit = + + val javaClasspath = sys.props("java.class.path") + val runtimeClasspath = s"${classpath}$pathsep$javaClasspath" val jarTargetDir: Path = Option(scriptFile.toPath.toAbsolutePath.getParent) match { case None => sys.error(s"no parent directory for script file [$scriptFile]") @@ -57,7 +60,7 @@ object Main: def scriptBasename = scriptFile.getName.takeWhile(_!='.') val jarPath = s"$jarTargetDir/$scriptBasename.jar" - val cpPaths = classpathEntries.map { _.toString.toUrl } + val cpPaths = runtimeClasspath.split(pathsep).map(_.toUrl) import java.util.jar.Attributes.Name val cpString:String = cpPaths.distinct.mkString(" ") diff --git a/compiler/src/dotty/tools/scripting/ScriptingDriver.scala b/compiler/src/dotty/tools/scripting/ScriptingDriver.scala index 7c1109754e0f..af0858acf658 100755 --- 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, Paths, Path } +import java.nio.file.{ Files, Path } import java.io.File import java.net.{ URL, URLClassLoader } import java.lang.reflect.{ Modifier, Method } @@ -17,7 +17,7 @@ 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, Seq[Path], String) => Boolean = null): Unit = + def compileAndRun(pack:(Path, String, String) => Boolean = null): Unit = val outDir = Files.createTempDirectory("scala3-scripting") setup(compilerArgs :+ scriptFile.getAbsolutePath, initCtx.fresh) match case Some((toCompile, rootCtx)) => @@ -28,23 +28,11 @@ class ScriptingDriver(compilerArgs: Array[String], scriptFile: File, scriptArgs: throw ScriptingException("Errors encountered during compilation") try - val classpath = s"${ctx.settings.classpath.value}${pathsep}${sys.props("java.class.path")}" - val classpathEntries: Seq[Path] = - classpath.split(pathsep).toIndexedSeq.flatMap { entry => - val f = Paths.get(entry).toAbsolutePath.normalize.toFile - // expand wildcard classpath entries - if (f.getName == "*" && f.getParentFile.isDirectory){ - f.getParentFile.listFiles.filter { _.getName.toLowerCase.endsWith(".jar") }.map { _.toPath }.toSeq - } else { - Seq(f.toPath) - } - }.toIndexedSeq - - val (mainClass, mainMethod) = detectMainClassAndMethod(outDir, classpathEntries, scriptFile) + val (mainClass, mainMethod) = detectMainClassAndMethod(outDir, ctx.settings.classpath.value, scriptFile) val invokeMain: Boolean = Option(pack) match case Some(func) => - func(outDir, classpathEntries, mainClass) + func(outDir, ctx.settings.classpath.value, mainClass) case None => true end match @@ -64,12 +52,11 @@ class ScriptingDriver(compilerArgs: Array[String], scriptFile: File, scriptArgs: target.delete() end deleteFile - private def detectMainClassAndMethod(outDir: Path, classpathEntries: Seq[Path], + private def detectMainClassAndMethod(outDir: Path, classpath: String, scriptFile: File): (String, Method) = - - val classpathUrls = (classpathEntries :+ outDir).map { _.toUri.toURL } - - val cl = URLClassLoader(classpathUrls.toArray) + val outDirURL = outDir.toUri.toURL + val classpathUrls = classpath.split(pathsep).map(File(_).toURI.toURL) + val cl = URLClassLoader(classpathUrls :+ outDirURL) def collectMainMethods(target: File, path: String): List[(String, Method)] = val nameWithoutExtension = target.getName.takeWhile(_ != '.') diff --git a/compiler/test/dotty/tools/scripting/ScriptingTests.scala b/compiler/test/dotty/tools/scripting/ScriptingTests.scala index c0198507ed4d..e7399c68f09a 100644 --- a/compiler/test/dotty/tools/scripting/ScriptingTests.scala +++ b/compiler/test/dotty/tools/scripting/ScriptingTests.scala @@ -3,7 +3,6 @@ package tools package scripting import java.io.File -import java.nio.file.Path import org.junit.Test @@ -66,7 +65,7 @@ class ScriptingTests: ), scriptFile = scriptFile, scriptArgs = scriptArgs - ).compileAndRun { (path:java.nio.file.Path,classpathEntries:Seq[Path], mainClass:String) => + ).compileAndRun { (path:java.nio.file.Path,classpath:String, mainClass:String) => printf("mainClass from ScriptingDriver: %s\n",mainClass) true // call compiled script main method } @@ -129,7 +128,7 @@ class ScriptingTests: compilerArgs = Array("-classpath", TestConfiguration.basicClasspath), scriptFile = scriptFile, scriptArgs = Array.empty[String] - ).compileAndRun { (path:java.nio.file.Path,classpathEntries:Seq[Path], mainClass:String) => + ).compileAndRun { (path:java.nio.file.Path,classpath:String, mainClass:String) => printf("success: no call to main method in mainClass: %s\n",mainClass) false // no call to compiled script main method } @@ -142,7 +141,7 @@ class ScriptingTests: compilerArgs = Array("-classpath", TestConfiguration.basicClasspath), scriptFile = scriptFile, scriptArgs = Array.empty[String] - ).compileAndRun { (path:java.nio.file.Path,classpathEntries:Seq[Path], mainClass:String) => + ).compileAndRun { (path:java.nio.file.Path,classpath:String, mainClass:String) => printf("call main method in mainClass: %s\n",mainClass) true // call compiled script main method, create touchedFile }