Skip to content

Commit f1f8b7b

Browse files
author
Phil
committed
refactor CliCommand.expandArg method; fix script.path regression, Windows classpath regression
1 parent 29c20d0 commit f1f8b7b

File tree

12 files changed

+106
-130
lines changed

12 files changed

+106
-130
lines changed

compiler/src/dotty/tools/MainGenericRunner.scala

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ object MainGenericRunner {
126126
case (o @ javaOption(striped)) :: tail =>
127127
process(tail, settings.withJavaArgs(striped).withScalaArgs(o))
128128
case (o @ scalaOption(_*)) :: tail =>
129-
val remainingArgs = (expandArg(o) ++ tail).toList
129+
val remainingArgs = (CommandLineParser.expandArg(o) ++ tail).toList
130130
process(remainingArgs, settings)
131131
case (o @ colorOption(_*)) :: tail =>
132132
process(tail, settings.withScalaArgs(o))
@@ -141,19 +141,6 @@ object MainGenericRunner {
141141
val newSettings = if arg.startsWith("-") then settings else settings.withPossibleEntryPaths(arg).withModeShouldBePossibleRun
142142
process(tail, newSettings.withResidualArgs(arg))
143143

144-
// copy of method private to dotty.tools.dotc.config.CliCommand.distill()
145-
// TODO: make it available as a public method and remove this copy?
146-
def expandArg(arg: String): List[String] =
147-
def stripComment(s: String) = s takeWhile (_ != '#')
148-
val path = Paths.get(arg stripPrefix "@")
149-
if (!Files.exists(path))
150-
System.err.println(s"Argument file ${path.getFileName} could not be found")
151-
Nil
152-
else
153-
val lines = Files.readAllLines(path) // default to UTF-8 encoding
154-
val params = lines.asScala map stripComment mkString " "
155-
CommandLineParser.tokenize(params)
156-
157144
def main(args: Array[String]): Unit =
158145
val scalaOpts = envOrNone("SCALA_OPTS").toArray.flatMap(_.split(" "))
159146
val allArgs = scalaOpts ++ args
@@ -204,6 +191,8 @@ object MainGenericRunner {
204191
}
205192
errorFn("", res)
206193
case ExecuteMode.Script =>
194+
val targetScriptPath: String = settings.targetScript.toString.replace('\\', '/')
195+
System.setProperty("script.path", targetScriptPath)
207196
val properArgs =
208197
List("-classpath", settings.classPath.mkString(classpathSeparator)).filter(Function.const(settings.classPath.nonEmpty))
209198
++ settings.residualArgs

compiler/src/dotty/tools/dotc/config/CliCommand.scala

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@ import Settings._
77
import core.Contexts._
88
import Properties._
99

10-
import scala.PartialFunction.cond
11-
import scala.collection.JavaConverters._
10+
import scala.PartialFunction.cond
1211

1312
trait CliCommand:
1413

@@ -42,24 +41,10 @@ trait CliCommand:
4241

4342
/** Distill arguments into summary detailing settings, errors and files to main */
4443
def distill(args: Array[String], sg: Settings.SettingGroup)(ss: SettingsState = sg.defaultState)(using Context): ArgsSummary =
45-
/**
46-
* Expands all arguments starting with @ to the contents of the
47-
* file named like each argument.
48-
*/
49-
def expandArg(arg: String): List[String] =
50-
def stripComment(s: String) = s takeWhile (_ != '#')
51-
val path = Paths.get(arg stripPrefix "@")
52-
if (!Files.exists(path))
53-
report.error(s"Argument file ${path.getFileName} could not be found")
54-
Nil
55-
else
56-
val lines = Files.readAllLines(path) // default to UTF-8 encoding
57-
val params = lines.asScala map stripComment mkString " "
58-
CommandLineParser.tokenize(params)
5944

6045
// expand out @filename to the contents of that filename
6146
def expandedArguments = args.toList flatMap {
62-
case x if x startsWith "@" => expandArg(x)
47+
case x if x startsWith "@" => CommandLineParser.expandArg(x)
6348
case x => List(x)
6449
}
6550

compiler/src/dotty/tools/dotc/config/CommandLineParser.scala

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package dotty.tools.dotc.config
33
import scala.annotation.tailrec
44
import scala.collection.mutable.ArrayBuffer
55
import java.lang.Character.isWhitespace
6+
import java.nio.file.{Files, Paths}
7+
import scala.collection.JavaConverters._
68

79
/** A simple enough command line parser.
810
*/
@@ -93,4 +95,19 @@ object CommandLineParser:
9395

9496
def tokenize(line: String): List[String] = tokenize(line, x => throw new ParseException(x))
9597

98+
/**
99+
* Expands all arguments starting with @ to the contents of the
100+
* file named like each argument.
101+
*/
102+
def expandArg(arg: String): List[String] =
103+
def stripComment(s: String) = s takeWhile (_ != '#')
104+
val path = Paths.get(arg stripPrefix "@")
105+
if (!Files.exists(path))
106+
System.err.println(s"Argument file ${path.getFileName} could not be found")
107+
Nil
108+
else
109+
val lines = Files.readAllLines(path) // default to UTF-8 encoding
110+
val params = lines.asScala map stripComment mkString " "
111+
tokenize(params)
112+
96113
class ParseException(msg: String) extends RuntimeException(msg)

compiler/test-coursier/dotty/tools/coursier/CoursierScalaTests.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,15 @@ class CoursierScalaTests:
122122
assertTrue(output.mkString("\n").contains("Unable to create a system terminal")) // Scala attempted to create REPL so we can assume it is working
123123
replWithArgs()
124124

125+
def argumentFile() =
126+
// verify that an arguments file is accepted
127+
// verify that setting a user classpath does not remove compiler libraries from the classpath.
128+
// arguments file contains "-classpath .", adding current directory to classpath.
129+
val source = new File(getClass.getResource("/run/myfile.scala").getPath)
130+
val argsFile = new File(getClass.getResource("/run/myargs.txt").getPath)
131+
val output = CoursierScalaTests.csScalaCmd(s"@$argsFile", source.absPath)
132+
assertEquals(output.mkString("\n"), "Hello")
133+
125134
object CoursierScalaTests:
126135

127136
def execCmd(command: String, options: String*): List[String] =

compiler/test-coursier/run/myargs.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
-classpath .
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#!dist/target/pack/bin/scala @compiler/test-resources/scripting/cpArgumentsFile.txt
2+
3+
import java.nio.file.Paths
4+
5+
def main(args: Array[String]): Unit =
6+
val cwd = Paths.get(".").toAbsolutePath.toString.replace('\\', '/').replaceAll("/$", "")
7+
printf("cwd: %s\n", cwd)
8+
printf("classpath: %s\n", sys.props("java.class.path"))
9+
Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1-
#!dist/target/pack/bin/scala -classpath 'dist/target/pack/lib/*'
1+
#!dist/target/pack/bin/scala -classpath dist/target/pack/lib/*
22

33
import java.nio.file.Paths
44

55
def main(args: Array[String]): Unit =
6-
val cwd = Paths.get(".").toAbsolutePath.toString.replace('\\', '/').replaceAll("/$", "")
6+
val cwd = Paths.get(".").toAbsolutePath.normalize.toString.norm
77
printf("cwd: %s\n", cwd)
8-
printf("classpath: %s\n", sys.props("java.class.path"))
8+
printf("classpath: %s\n", sys.props("java.class.path").norm)
9+
10+
extension(s: String)
11+
def norm: String = s.replace('\\', '/')
912

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
-classpath dist/target/pack/lib/*
Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1-
#!/usr/bin/env scala
1+
#!dist/target/pack/bin/scala
22

33
def main(args: Array[String]): Unit =
44
args.zipWithIndex.foreach { case (arg,i) => printf("arg %d: [%s]\n",i,arg) }
55
val path = Option(sys.props("script.path")) match {
66
case None => printf("no script.path property is defined\n")
77
case Some(path) =>
8-
printf("script.path: %s\n",path)
8+
printf("script.path: %s\n",path.norm)
99
assert(path.endsWith("scriptPath.sc"),s"actual path [$path]")
1010
}
11+
12+
extension(s: String)
13+
def norm: String = s.replace('\\', '/')

compiler/test/dotty/tools/scripting/BashScriptsTests.scala

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -84,12 +84,12 @@ class BashScriptsTests:
8484
@Test def verifyScriptPathProperty =
8585
val scriptFile = testFiles.find(_.getName == "scriptPath.sc").get
8686
val expected = s"/${scriptFile.getName}"
87-
printf("===> verify valid system property script.path is reported by script [%s]\n", scriptFile.getName)
87+
System.err.printf("===> verify valid system property script.path is reported by script [%s]\n", scriptFile.getName)
8888
val (exitCode, stdout, stderr) = bashCommand(scriptFile.absPath)
8989
if exitCode == 0 && ! stderr.exists(_.contains("Permission denied")) then
9090
// var cmd = Array(bashExe, "-c", scriptFile.absPath)
9191
// val stdout = Process(cmd).lazyLines_!
92-
stdout.foreach { printf("######### [%s]\n", _) }
92+
stdout.foreach { System.err.printf("######### [%s]\n", _) }
9393
val valid = stdout.exists { _.endsWith(expected) }
9494
if valid then printf("# valid script.path reported by [%s]\n", scriptFile.getName)
9595
assert(valid, s"script ${scriptFile.absPath} did not report valid script.path value")
@@ -99,35 +99,39 @@ class BashScriptsTests:
9999
*/
100100
@Test def verifyScalaOpts =
101101
val scriptFile = testFiles.find(_.getName == "classpathReport.sc").get
102-
printf("===> verify valid system property script.path is reported by script [%s]\n", scriptFile.getName)
102+
printf("===> verify SCALA_OPTS -classpath setting in argument file seen by script [%s]\n", scriptFile.getName)
103103
val argsfile = createArgsFile() // avoid problems caused by drive letter
104104
val envPairs = List(("SCALA_OPTS", s"@$argsfile"))
105105
val (exitCode, stdout, stderr) = bashCommand(scriptFile.absPath, envPairs:_*)
106+
printf("\n")
106107
if exitCode != 0 || stderr.exists(_.contains("Permission denied")) then
107108
stderr.foreach { System.err.printf("stderr [%s]\n", _) }
108109
printf("unable to execute script, return value is %d\n", exitCode)
109110
else
110-
// val stdout: Seq[String] = Process(cmd, cwd, envPairs:_*).lazyLines_!.toList
111-
val expected = s"${cwd.toString}"
111+
val expected = cwd
112112
val List(line1: String, line2: String) = stdout.take(2)
113+
printf("line1 [%s]\n", line1)
113114
val valid = line2.dropWhile( _ != ' ').trim.startsWith(expected)
115+
val psep = if osname.startsWith("Windows") then ';' else ':'
116+
printf("line2 start [%s]\n", line2.take(100))
114117
if valid then printf(s"\n===> success: classpath begins with %s, as reported by [%s]\n", cwd, scriptFile.getName)
115-
assert(valid, s"script ${scriptFile.absPath} did not report valid java.class.path first entry")
118+
assert(valid, s"script ${scriptFile.getName} did not report valid java.class.path first entry")
116119

117-
lazy val cwd = Paths.get(dotty.tools.dotc.config.Properties.userDir).toFile
120+
lazy val cwd: String = Paths.get(".").toAbsolutePath.normalize.toString.norm
118121

119122
def createArgsFile(): String =
120123
val utfCharset = java.nio.charset.StandardCharsets.UTF_8.name
121-
val text = s"-classpath ${cwd.absPath}"
124+
val text = s"-classpath $cwd"
122125
val path = Files.createTempFile("scriptingTest", ".args")
123126
Files.write(path, text.getBytes(utfCharset))
124-
path.toFile.getAbsolutePath.replace('\\', '/')
127+
path.toFile.getAbsolutePath.norm
125128

126-
extension (str: String) def dropExtension: String =
127-
str.reverse.dropWhile(_ != '.').drop(1).reverse
129+
extension(str: String)
130+
def norm: String = str.replace('\\', '/')
131+
def dropExtension: String = str.reverse.dropWhile(_ != '.').drop(1).reverse
128132

129133
extension(f: File) def absPath: String =
130-
f.getAbsolutePath.replace('\\', '/')
134+
f.getAbsolutePath.norm
131135

132136
lazy val osname = Option(sys.props("os.name")).getOrElse("").toLowerCase
133137

0 commit comments

Comments
 (0)