@@ -16,6 +16,9 @@ import java.util.jar._
1616import java .util .jar .Attributes .Name
1717import dotty .tools .io .Jar
1818import dotty .tools .runner .ScalaClassLoader
19+ import java .nio .file .{Files , Paths , Path }
20+ import scala .collection .JavaConverters ._
21+ import dotty .tools .dotc .config .CommandLineParser
1922
2023enum ExecuteMode :
2124 case Guess
@@ -102,7 +105,18 @@ object MainGenericRunner {
102105 case " -run" :: fqName :: tail =>
103106 process(tail, settings.withExecuteMode(ExecuteMode .Run ).withTargetToRun(fqName))
104107 case (" -cp" | " -classpath" | " --class-path" ) :: cp :: tail =>
105- process(tail, settings.copy(classPath = settings.classPath.appended(cp)))
108+ val globdir = cp.replaceAll(" [\\ /][^\\ /]*$" , " " ) // slash/backslash agnostic
109+ val (tailargs, cpstr) = if globdir.nonEmpty && classpathSeparator != " ;" || cp.contains(classpathSeparator) then
110+ (tail, cp)
111+ else
112+ // combine globbed classpath entries into a classpath
113+ val jarfiles = cp :: tail
114+ val cpfiles = jarfiles.takeWhile( f => f.startsWith(globdir) && ((f.toLowerCase.endsWith(" .jar" ) || f.endsWith(" .zip" ))) )
115+ val tailargs = jarfiles.drop(cpfiles.size)
116+ (tailargs, cpfiles.mkString(classpathSeparator))
117+
118+ process(tailargs, settings.copy(classPath = settings.classPath ++ cpstr.split(classpathSeparator).filter(_.nonEmpty)))
119+
106120 case (" -version" | " --version" ) :: _ =>
107121 settings.copy(
108122 executeMode = ExecuteMode .Repl ,
@@ -123,7 +137,8 @@ object MainGenericRunner {
123137 case (o @ javaOption(striped)) :: tail =>
124138 process(tail, settings.withJavaArgs(striped).withScalaArgs(o))
125139 case (o @ scalaOption(_* )) :: tail =>
126- process(tail, settings.withScalaArgs(o))
140+ val remainingArgs = (CommandLineParser .expandArg(o) ++ tail).toList
141+ process(remainingArgs, settings)
127142 case (o @ colorOption(_* )) :: tail =>
128143 process(tail, settings.withScalaArgs(o))
129144 case arg :: tail =>
@@ -143,6 +158,13 @@ object MainGenericRunner {
143158 val settings = process(allArgs.toList, Settings ())
144159 if settings.exitCode != 0 then System .exit(settings.exitCode)
145160
161+ def removeCompiler (cp : Array [String ]) =
162+ if (! settings.compiler) then // Let's remove compiler from the classpath
163+ val compilerLibs = Seq (" scala3-compiler" , " scala3-interfaces" , " tasty-core" , " scala-asm" , " scala3-staging" , " scala3-tasty-inspector" )
164+ cp.filterNot(c => compilerLibs.exists(c.contains))
165+ else
166+ cp
167+
146168 def run (settings : Settings ): Unit = settings.executeMode match
147169 case ExecuteMode .Repl =>
148170 val properArgs =
@@ -151,7 +173,7 @@ object MainGenericRunner {
151173 repl.Main .main(properArgs.toArray)
152174
153175 case ExecuteMode .PossibleRun =>
154- val newClasspath = (settings.classPath :+ " ." ).map(File (_).toURI.toURL)
176+ val newClasspath = (settings.classPath :+ " ." ).flatMap(_.split(classpathSeparator).filter(_.nonEmpty)). map(File (_).toURI.toURL)
155177 import dotty .tools .runner .RichClassLoader ._
156178 val newClassLoader = ScalaClassLoader .fromURLsParallelCapable(newClasspath)
157179 val targetToRun = settings.possibleEntryPaths.to(LazyList ).find { entryPath =>
@@ -166,15 +188,7 @@ object MainGenericRunner {
166188 run(settings.withExecuteMode(ExecuteMode .Repl ))
167189 case ExecuteMode .Run =>
168190 val scalaClasspath = ClasspathFromClassloader (Thread .currentThread().getContextClassLoader).split(classpathSeparator)
169-
170- def removeCompiler (cp : Array [String ]) =
171- if (! settings.compiler) then // Let's remove compiler from the classpath
172- val compilerLibs = Seq (" scala3-compiler" , " scala3-interfaces" , " tasty-core" , " scala-asm" , " scala3-staging" , " scala3-tasty-inspector" )
173- cp.filterNot(c => compilerLibs.exists(c.contains))
174- else
175- cp
176- val newClasspath = (settings.classPath ++ removeCompiler(scalaClasspath) :+ " ." ).map(File (_).toURI.toURL)
177-
191+ val newClasspath = (settings.classPath.flatMap(_.split(classpathSeparator).filter(_.nonEmpty)) ++ removeCompiler(scalaClasspath) :+ " ." ).map(File (_).toURI.toURL)
178192 val res = ObjectRunner .runAndCatch(newClasspath, settings.targetToRun, settings.residualArgs).flatMap {
179193 case ex : ClassNotFoundException if ex.getMessage == settings.targetToRun =>
180194 val file = settings.targetToRun
@@ -187,14 +201,30 @@ object MainGenericRunner {
187201 }
188202 errorFn(" " , res)
189203 case ExecuteMode .Script =>
190- val properArgs =
191- List (" -classpath" , settings.classPath.mkString(classpathSeparator)).filter(Function .const(settings.classPath.nonEmpty))
192- ++ settings.residualArgs
193- ++ (if settings.save then List (" -save" ) else Nil )
194- ++ List (" -script" , settings.targetScript)
195- ++ settings.scalaArgs
196- ++ settings.scriptArgs
197- scripting.Main .main(properArgs.toArray)
204+ val targetScript = Paths .get(settings.targetScript).toFile
205+ val targetJar = settings.targetScript.replaceAll(" [.][^\\ /]*$" , " " )+ " .jar"
206+ val precompiledJar = Paths .get(targetJar).toFile
207+ def mainClass = Jar (targetJar).mainClass.getOrElse(" " ) // throws exception if file not found
208+ val jarIsValid = precompiledJar.isFile && mainClass.nonEmpty && precompiledJar.lastModified >= targetScript.lastModified
209+ if jarIsValid then
210+ // precompiledJar exists, is newer than targetScript, and manifest defines a mainClass
211+ sys.props(" script.path" ) = targetScript.toPath.toAbsolutePath.normalize.toString
212+ val scalaClasspath = ClasspathFromClassloader (Thread .currentThread().getContextClassLoader).split(classpathSeparator)
213+ val newClasspath = (settings.classPath.flatMap(_.split(classpathSeparator).filter(_.nonEmpty)) ++ removeCompiler(scalaClasspath) :+ " ." ).map(File (_).toURI.toURL)
214+ val mc = mainClass
215+ if mc.nonEmpty then
216+ ObjectRunner .runAndCatch(newClasspath :+ File (targetJar).toURI.toURL, mc, settings.scriptArgs)
217+ else
218+ Some (IllegalArgumentException (s " No main class defined in manifest in jar: $precompiledJar" ))
219+ else
220+ val properArgs =
221+ List (" -classpath" , settings.classPath.mkString(classpathSeparator)).filter(Function .const(settings.classPath.nonEmpty))
222+ ++ settings.residualArgs
223+ ++ (if settings.save then List (" -save" ) else Nil )
224+ ++ settings.scalaArgs
225+ ++ List (" -script" , settings.targetScript)
226+ ++ settings.scriptArgs
227+ scripting.Main .main(properArgs.toArray)
198228 case ExecuteMode .Guess =>
199229 if settings.modeShouldBePossibleRun then
200230 run(settings.withExecuteMode(ExecuteMode .PossibleRun ))
0 commit comments