Skip to content

Commit

Permalink
Fixes for various problems related to scripting.
Browse files Browse the repository at this point in the history
1. set $scala_exit_status & trap onExit; script exit code correct whether called via scalac or java -jar
2. added missing 'scala -version' option to via call to 'scalac -version'
3. extended and verified REPL and scripting for Cygwin, MinGW, Msys2, and Windows shells with a cygpath tool
4. MinGW based Windows shell environments require TERM=dumb to support REPL
5. simpify and extend jar manifest classpath to support for changing default drive
6. tested for the following Windows and Linux environments:
7. added tests

  CYGWIN_NT-10.0 host 3.1.7(0.340/5/3) 2020-08-22 17:48 x86_64 Cygwin
  MINGW64_NT-10.0-19042 host 3.1.7-340.x86_64 2020-10-23 13:08 UTC x86_64 Msys
  MSYS_NT-10.0-19042 host 3.1.7-340.x86_64 2020-11-08 12:32 UTC x86_64 Msys
  Linux host 5.4.0-62-generic scala#70~18.04.1-Ubuntu SMP Tue Jan 12 17:18:00 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
  Linux host 5.4.72-microsoft-standard-WSL2 #1 SMP Wed Oct 28 23:40:43 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux

Manual test results for executing a script via compile mode and then java -jar mode:

uname -a :: CYGWIN_NT-10.0 d5 3.1.7(0.340/5/3) 2020-08-22 17:48 x86_64 Cygwin

uname -a :: Linux quadd 5.4.0-62-generic scala#70~18.04.1-Ubuntu SMP Tue Jan 12 17:18:00 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux

uname -a :: Linux d5 5.4.72-microsoft-standard-WSL2 #1 SMP Wed Oct 28 23:40:43 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux

uname -a :: MINGW64_NT-10.0-19042 d5 3.1.7-340.x86_64 2020-10-23 13:08 UTC x86_64 Msys

uname -a :: MSYS_NT-10.0-19042 d5 3.1.7-340.x86_64 2020-11-08 12:32 UTC x86_64 Msys

uname -a :: CYGWIN_NT-10.0 d5 3.1.7(0.340/5/3) 2020-08-22 17:48 x86_64 Cygwin
  • Loading branch information
philwalk committed Feb 21, 2021
1 parent aa7c21e commit 221b206
Show file tree
Hide file tree
Showing 8 changed files with 141 additions and 43 deletions.
3 changes: 2 additions & 1 deletion compiler/src/dotty/tools/repl/JLineTerminal.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ final class JLineTerminal extends java.io.Closeable {

private val terminal =
TerminalBuilder.builder()
.dumb(false) // fail early if not able to create a terminal
.dumb(dumbTerminal) // fail early if not able to create a terminal
.build()
private val history = new DefaultHistory
def dumbTerminal = Option(System.getenv("TERM")) == Some("dumb")

private def blue(str: String)(using Context) =
if (ctx.settings.color.value != "never") Console.BLUE + str + Console.RESET
Expand Down
17 changes: 7 additions & 10 deletions compiler/src/dotty/tools/scripting/Main.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package dotty.tools.scripting

import java.io.File
import java.nio.file.Path
import java.nio.file.{Path, Paths}
import dotty.tools.dotc.config.Properties.isWin

/** Main entry point to the Scripting execution engine */
Expand Down Expand Up @@ -60,7 +60,7 @@ object Main:
def scriptBasename = scriptFile.getName.takeWhile(_!='.')
val jarPath = s"$jarTargetDir/$scriptBasename.jar"

val cpPaths = runtimeClasspath.split(pathsep).map(_.absPath)
val cpPaths = runtimeClasspath.split(pathsep).map(_.toUrl)

import java.util.jar.Attributes.Name
val cpString:String = cpPaths.distinct.mkString(" ")
Expand All @@ -80,29 +80,26 @@ object Main:

def pathsep = sys.props("path.separator")


extension(file: File){
def norm: String = file.toString.norm
}

extension(path: String) {
// Normalize path separator, convert relative path to absolute
def norm: String =
path.replace('\\', '/') match {
case s if s.secondChar == ":" => s.drop(2)
case s if s.secondChar == ":" => s
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
case _ => s"/${sys.props("user.dir").norm}/$norm"
case _ => Paths.get(userDir,norm).toString.norm

def absFile: File = File(path.absPath)
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("")
}

lazy val userDir = sys.props("user.dir").norm
20 changes: 20 additions & 0 deletions compiler/test-resources/scripting/hashBang.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/usr/bin/env scala
# comment
STUFF=nada
!#

def main(args: Array[String]): Unit =
System.err.printf("mainClassFromStack: %s\n",mainFromStack)
assert(mainFromStack.contains("hashBang"),s"fromStack[$mainFromStack]")

lazy val mainFromStack:String = {
val result = new java.io.StringWriter()
new RuntimeException("stack").printStackTrace(new java.io.PrintWriter(result))
val stack = result.toString.split("[\r\n]+").toList
//for( s <- stack ){ System.err.printf("[%s]\n",s) }
stack.filter { str => str.contains(".main(") }.map {
_.replaceAll(".*[(]","").
replaceAll("\\.main\\(.*","").
replaceAll(".scala.*","")
}.distinct.take(1).mkString("")
}
22 changes: 22 additions & 0 deletions compiler/test-resources/scripting/mainClassOnStack.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/usr/bin/env scala
export STUFF=nada
#lots of other stuff that isn't valid scala
!#
object Zoo {
def main(args: Array[String]): Unit =
printf("script.name: %s\n",sys.props("script.name"))
printf("mainClassFromStack: %s\n",mainFromStack)
assert(mainFromStack == "Zoo",s"fromStack[$mainFromStack]")

lazy val mainFromStack:String = {
val result = new java.io.StringWriter()
new RuntimeException("stack").printStackTrace(new java.io.PrintWriter(result))
val stack = result.toString.split("[\r\n]+").toList
// for( s <- stack ){ System.err.printf("[%s]\n",s) }
val shortStack = stack.filter { str => str.contains(".main(") && ! str.contains("$") }.map {
_.replaceAll("[.].*","").replaceAll("\\s+at\\s+","")
}
// for( s <- shortStack ){ System.err.printf("[%s]\n",s) }
shortStack.take(1).mkString("|")
}
}
9 changes: 9 additions & 0 deletions compiler/test-resources/scripting/scriptName.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/usr/bin/env scala

def main(args: Array[String]): Unit =
val name = Option(sys.props("script.name")) match {
case None => printf("no script.name property is defined\n")
case Some(name) =>
printf("script.name: %s\n",name)
assert(name == "scriptName.scala")
}
57 changes: 46 additions & 11 deletions dist/bin/common
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,39 @@
# * Credits: This script is based on the script generated by sbt-pack.
# *--------------------------------------------------------------------------*/

# save terminal settings
saved_stty=$(stty -g 2>/dev/null)
# clear on error so we don't later try to restore them
if [[ ! $? ]]; then
saved_stty=""
fi

# restore stty settings (echo in particular)
function restoreSttySettings() {
stty $saved_stty
saved_stty=""
}

scala_exit_status=127
function onExit() {
[[ "$saved_stty" != "" ]] && restoreSttySettings
exit $scala_exit_status
}

# to reenable echo if we are interrupted before completing.
trap onExit INT TERM

cygwin=false
mingw=false
msys=false
darwin=false
case "`uname`" in
CYGWIN*) cygwin=true
;;
MINGW*) mingw=true
;;
MSYS*) msys=true
;;
Darwin*) darwin=true
if [ -z "$JAVA_VERSION" ] ; then
JAVA_VERSION="CurrentJDK"
Expand All @@ -25,6 +50,15 @@ case "`uname`" in
;;
esac

if [[ ($cygwin||$mingw||$msys) ]]; then
case "$TERM" in
rxvt* | xterm* | cygwin*)
stty -icanon min 1 -echo
SCALA_OPTS="$SCALA_OPTS -Djline.terminal=unix"
;;
esac
fi

# Resolve JAVA_HOME from javac command path
if [ -z "$JAVA_HOME" ]; then
javaExecutable="`which javac`"
Expand All @@ -41,9 +75,8 @@ if [ -z "$JAVA_HOME" ]; then
fi
fi


if [ -z "$JAVACMD" ] ; then
if [ -n "$JAVA_HOME" ] ; then
if [ -z "${JAVACMD-}" ] ; then
if [ -n "${JAVA_HOME-}" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
Expand All @@ -69,18 +102,20 @@ CLASSPATH_SUFFIX=""
# Path separator used in EXTRA_CLASSPATH
PSEP=":"

# For Cygwin, switch paths to Windows-mixed format before running java
if $cygwin; then
# cygpath is used by various windows shells: cygwin, git-sdk, gitbash, msys, etc.
has_cygpath=false
if `which cygpath >/dev/null 2>&1`; then has_cygpath=true; fi

# if $has_cypath, translate paths to Windows-mixed format before running java
if $has_cygpath; then
[ -n "$PROG_HOME" ] &&
PROG_HOME=`cygpath -am "$PROG_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath -am "$JAVA_HOME"`
CLASSPATH_SUFFIX=";"
PSEP=";"
fi

# For Migwn, ensure paths are in UNIX format before anything is touched
if $mingw ; then
elif ($mingw || $msys); then
# For Mingw / Msys, convert paths to UNIX format before anything is touched
[ -n "$PROG_HOME" ] &&
PROG_HOME="`(cd "$PROG_HOME"; pwd -W | sed 's|/|\\\\|g')`"
[ -n "$JAVA_HOME" ] &&
Expand All @@ -95,9 +130,9 @@ fi

find_lib () {
local lib=$(find $PROG_HOME/lib/ -name "$1")
if $cygwin; then
if $has_cygpath; then
cygpath -am $lib
elif $mingw; then
elif ($mingw || $msys); then
echo $lib | sed 's|/|\\\\|g'
else
echo $lib
Expand Down
26 changes: 20 additions & 6 deletions dist/bin/scala
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,14 @@ while [[ $# -gt 0 ]]; do
DEBUG="$DEBUG_STR"
shift
;;
-version)
# defer to scalac, then exit
addScalacOptions "${1}"
shift
eval "\"$PROG_HOME/bin/scalac\" ${cp_arg-} ${java_options[@]}"
scala_exit_status=$?
onExit
;;
-J*)
addJvmOptions "${1:2}"
addScalacOptions "${1}"
Expand All @@ -115,26 +123,29 @@ while [[ $# -gt 0 ]]; do
done

if [ $execute_script == true ]; then
[ -n "$script_trace" ] && set -x
[ -n "${script_trace-}" ] && set -x
if [ "$CLASS_PATH" ]; then
cp_arg="-classpath \"$CLASS_PATH\""
fi
java_options+=(${scala_script_options})
setScriptName="-Dscript.path=$target_script"
target_jar="${target_script%.*}.jar"
if [[ $save_compiled == true && "$target_jar" -nt "$target_script" ]]; then
eval exec "\"$JAVACMD\"" $setScriptName -jar "$target_jar" "${script_args[@]}"
eval "\"$JAVACMD\"" $setScriptName -jar "$target_jar" "${script_args[@]}"
scala_exit_status=$?
else
[[ $save_compiled == true ]] && rm -f $target_jar
residual_args+=($setScriptName)
eval "\"$PROG_HOME/bin/scalac\" $cp_arg ${java_options[@]} ${residual_args[@]} -script $target_script ${script_args[@]}"
eval "\"$PROG_HOME/bin/scalac\" ${cp_arg-} ${java_options[@]} ${residual_args[@]} -script $target_script ${script_args[@]}"
scala_exit_status=$?
fi
elif [ $execute_repl == true ] || ([ $execute_run == false ] && [ $options_indicator == 0 ]); then
if [ "$CLASS_PATH" ]; then
cp_arg="-classpath \"$CLASS_PATH\""
fi
echo "Starting scala3 REPL..."
eval "\"$PROG_HOME/bin/scalac\" $cp_arg ${java_options[@]} -repl ${residual_args[@]}"
eval "\"$PROG_HOME/bin/scalac\" ${cp_arg-} ${java_options[@]} -repl ${residual_args[@]}"
scala_exit_status=$?
elif [ $execute_repl == true ] || [ ${#residual_args[@]} -ne 0 ]; then
cp_arg="$DOTTY_LIB$PSEP$SCALA_LIB"
if [ -z "$CLASS_PATH" ]; then
Expand All @@ -148,7 +159,10 @@ elif [ $execute_repl == true ] || [ ${#residual_args[@]} -ne 0 ]; then
if [ $with_compiler == true ]; then
cp_arg+="$PSEP$DOTTY_COMP$PSEP$TASTY_CORE$PSEP$DOTTY_INTF$PSEP$SCALA_ASM$PSEP$DOTTY_STAGING$PSEP$DOTTY_TASTY_INSPECTOR"
fi
eval exec "\"$JAVACMD\"" "$DEBUG" "-classpath \"$cp_arg\"" "${jvm_options[@]}" "${residual_args[@]}"
# exec here would prevent onExit from being called, leaving terminal in unusable state
eval "\"$JAVACMD\"" "$DEBUG" "-classpath \"$cp_arg\"" "${jvm_options[@]}" "${residual_args[@]}"
scala_exit_status=$?
else
echo "warning: command option is not correct."
echo "warning: command option is not correct."
fi
onExit
30 changes: 15 additions & 15 deletions dist/bin/scalac
Original file line number Diff line number Diff line change
Expand Up @@ -60,21 +60,21 @@ classpathArgs () {
# echo "sbt-intface: $SBT_INTF"

toolchain=""
toolchain+="$SCALA_LIB$PSEP"
toolchain+="$DOTTY_LIB$PSEP"
toolchain+="$SCALA_ASM$PSEP"
toolchain+="$SBT_INTF$PSEP"
toolchain+="$DOTTY_INTF$PSEP"
toolchain+="$DOTTY_COMP$PSEP"
toolchain+="$TASTY_CORE$PSEP"
toolchain+="$DOTTY_STAGING$PSEP"
toolchain+="$DOTTY_TASTY_INSPECTOR$PSEP"
[ -n "${SCALA_LIB-}" ] && toolchain+="$SCALA_LIB$PSEP"
[ -n "${DOTTY_LIB-}" ] && toolchain+="$DOTTY_LIB$PSEP"
[ -n "${SCALA_ASM-}" ] && toolchain+="$SCALA_ASM$PSEP"
[ -n "${SBT_INTF-}" ] && toolchain+="$SBT_INTF$PSEP"
[ -n "${DOTTY_INTF-}" ] && toolchain+="$DOTTY_INTF$PSEP"
[ -n "${DOTTY_COMP-}" ] && toolchain+="$DOTTY_COMP$PSEP"
[ -n "${TASTY_CORE-}" ] && toolchain+="$TASTY_CORE$PSEP"
[ -n "${DOTTY_STAGING-}" ] && toolchain+="$DOTTY_STAGING$PSEP"
[ -n "${DOTTY_TASTY_INSPECTOR-}" ] && toolchain+="$DOTTY_TASTY_INSPECTOR$PSEP"

# jine
toolchain+="$JLINE_READER$PSEP"
toolchain+="$JLINE_TERMINAL$PSEP"
toolchain+="$JLINE_TERMINAL_JNA$PSEP"
toolchain+="$JNA"
[ -n "${JLINE_READER-}" ] && toolchain+="$JLINE_READER$PSEP"
[ -n "${JLINE_TERMINAL-}" ] && toolchain+="$JLINE_TERMINAL$PSEP"
[ -n "${JLINE_TERMINAL_JNA-}" ] && toolchain+="$JLINE_TERMINAL_JNA$PSEP"
[ -n "${JNA-}" ] && toolchain+="$JNA$PSEP"

jvm_cp_args="-classpath \"$toolchain\""
}
Expand Down Expand Up @@ -123,12 +123,12 @@ fi

eval exec "\"$JAVACMD\"" \
${JAVA_OPTS:-$default_java_opts} \
"$DEBUG" \
"${DEBUG-}" \
"${java_args[@]}" \
"$jvm_cp_args" \
-Dscala.usejavacp=true \
"$PROG_NAME" \
"${scala_args[@]}" \
"${residual_args[@]}" \
"$scripting_string"
"${scripting_string-}"
exit $?

0 comments on commit 221b206

Please sign in to comment.