@@ -16,6 +16,9 @@ import java.util.jar._
16
16
import java .util .jar .Attributes .Name
17
17
import dotty .tools .io .Jar
18
18
import dotty .tools .runner .ScalaClassLoader
19
+ import java .nio .file .{Files , Paths , Path }
20
+ import scala .collection .JavaConverters ._
21
+ import dotty .tools .dotc .config .CommandLineParser
19
22
20
23
enum ExecuteMode :
21
24
case Guess
@@ -102,7 +105,18 @@ object MainGenericRunner {
102
105
case " -run" :: fqName :: tail =>
103
106
process(tail, settings.withExecuteMode(ExecuteMode .Run ).withTargetToRun(fqName))
104
107
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
+
106
120
case (" -version" | " --version" ) :: _ =>
107
121
settings.copy(
108
122
executeMode = ExecuteMode .Repl ,
@@ -123,7 +137,8 @@ object MainGenericRunner {
123
137
case (o @ javaOption(striped)) :: tail =>
124
138
process(tail, settings.withJavaArgs(striped).withScalaArgs(o))
125
139
case (o @ scalaOption(_* )) :: tail =>
126
- process(tail, settings.withScalaArgs(o))
140
+ val remainingArgs = (CommandLineParser .expandArg(o) ++ tail).toList
141
+ process(remainingArgs, settings)
127
142
case (o @ colorOption(_* )) :: tail =>
128
143
process(tail, settings.withScalaArgs(o))
129
144
case arg :: tail =>
@@ -143,6 +158,13 @@ object MainGenericRunner {
143
158
val settings = process(allArgs.toList, Settings ())
144
159
if settings.exitCode != 0 then System .exit(settings.exitCode)
145
160
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
+
146
168
def run (settings : Settings ): Unit = settings.executeMode match
147
169
case ExecuteMode .Repl =>
148
170
val properArgs =
@@ -151,7 +173,7 @@ object MainGenericRunner {
151
173
repl.Main .main(properArgs.toArray)
152
174
153
175
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)
155
177
import dotty .tools .runner .RichClassLoader ._
156
178
val newClassLoader = ScalaClassLoader .fromURLsParallelCapable(newClasspath)
157
179
val targetToRun = settings.possibleEntryPaths.to(LazyList ).find { entryPath =>
@@ -166,15 +188,7 @@ object MainGenericRunner {
166
188
run(settings.withExecuteMode(ExecuteMode .Repl ))
167
189
case ExecuteMode .Run =>
168
190
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)
178
192
val res = ObjectRunner .runAndCatch(newClasspath, settings.targetToRun, settings.residualArgs).flatMap {
179
193
case ex : ClassNotFoundException if ex.getMessage == settings.targetToRun =>
180
194
val file = settings.targetToRun
@@ -187,14 +201,30 @@ object MainGenericRunner {
187
201
}
188
202
errorFn(" " , res)
189
203
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)
198
228
case ExecuteMode .Guess =>
199
229
if settings.modeShouldBePossibleRun then
200
230
run(settings.withExecuteMode(ExecuteMode .PossibleRun ))
0 commit comments