Skip to content

Commit f7b28bd

Browse files
Merge pull request scala#13246 from BarkingBad/fix-executable-jars
Fix running JARs from scala script
2 parents a195f23 + 447008e commit f7b28bd

File tree

2 files changed

+42
-2
lines changed

2 files changed

+42
-2
lines changed

compiler/src/dotty/tools/MainGenericRunner.scala

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import scala.annotation.internal.sharable
1212
import dotty.tools.dotc.util.ClasspathFromClassloader
1313
import dotty.tools.runner.ObjectRunner
1414
import dotty.tools.dotc.config.Properties.envOrNone
15+
import java.util.jar._
16+
import java.util.jar.Attributes.Name
1517

1618
enum ExecuteMode:
1719
case Guess
@@ -31,6 +33,7 @@ case class Settings(
3133
targetScript: String = "",
3234
save: Boolean = false,
3335
modeShouldBeRun: Boolean = false,
36+
compiler: Boolean = false,
3437
) {
3538
def withExecuteMode(em: ExecuteMode): Settings = this.executeMode match
3639
case ExecuteMode.Guess =>
@@ -65,6 +68,9 @@ case class Settings(
6568

6669
def withModeShouldBeRun: Settings =
6770
this.copy(modeShouldBeRun = true)
71+
72+
def withCompiler: Settings =
73+
this.copy(compiler = true)
6874
}
6975

7076
object MainGenericRunner {
@@ -97,6 +103,8 @@ object MainGenericRunner {
97103
)
98104
case "-save" :: tail =>
99105
process(tail, settings.withSave)
106+
case "-with-compiler" :: tail =>
107+
process(tail, settings.withCompiler)
100108
case (o @ javaOption(striped)) :: tail =>
101109
process(tail, settings.withJavaArgs(striped).withScalaArgs(o))
102110
case (o @ scalaOption(_*)) :: tail =>
@@ -128,8 +136,34 @@ object MainGenericRunner {
128136
repl.Main.main(properArgs.toArray)
129137
case ExecuteMode.Run =>
130138
val scalaClasspath = ClasspathFromClassloader(Thread.currentThread().getContextClassLoader).split(classpathSeparator)
131-
val newClasspath = (settings.classPath ++ scalaClasspath :+ ".").map(File(_).toURI.toURL)
132-
errorFn("", ObjectRunner.runAndCatch(newClasspath, settings.residualArgs.head, settings.residualArgs.drop(1)))
139+
140+
def removeCompiler(cp: Array[String]) =
141+
if (!settings.compiler) then // Let's remove compiler from the classpath
142+
val compilerLibs = Seq("scala3-compiler", "scala3-interfaces", "tasty-core", "scala-asm", "scala3-staging", "scala3-tasty-inspector")
143+
cp.filterNot(c => compilerLibs.exists(c.contains))
144+
else
145+
cp
146+
val newClasspath = (settings.classPath ++ removeCompiler(scalaClasspath) :+ ".").map(File(_).toURI.toURL)
147+
148+
val res = ObjectRunner.runAndCatch(newClasspath, settings.residualArgs.head, settings.residualArgs.drop(1)).flatMap {
149+
case ex: ClassNotFoundException if ex.getMessage == settings.residualArgs.head =>
150+
val file = settings.residualArgs.head
151+
def withJarInput[T](f: JarInputStream => T): T =
152+
val in = new JarInputStream(java.io.FileInputStream(file))
153+
try f(in)
154+
finally in.close()
155+
val manifest = withJarInput(s => Option(s.getManifest))
156+
manifest match
157+
case None => Some(IllegalArgumentException(s"Cannot find manifest in jar: $file"))
158+
case Some(f) =>
159+
f.getMainAttributes.get(Name.MAIN_CLASS) match
160+
case mainClass: String =>
161+
ObjectRunner.runAndCatch(newClasspath :+ File(file).toURI.toURL, mainClass, settings.residualArgs)
162+
case _ =>
163+
Some(IllegalArgumentException(s"No main class defined in manifest in jar: $file"))
164+
case ex => Some(ex)
165+
}
166+
errorFn("", res)
133167
case ExecuteMode.Script =>
134168
val properArgs =
135169
List("-classpath", settings.classPath.mkString(classpathSeparator)).filter(Function.const(settings.classPath.nonEmpty))

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,12 @@ class CoursierScalaTests:
9292
assertTrue(source.getParentFile.listFiles.find(_.getName == "myfile.jar").isDefined)
9393
jar()
9494

95+
def runThatJar() =
96+
val source = new File(getClass.getResource("/run/myfile.jar").getPath)
97+
val output = CoursierScalaTests.csCmd(source.absPath)
98+
assertEquals(output.mkString("\n"), "Hello")
99+
runThatJar()
100+
95101
object CoursierScalaTests:
96102

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

0 commit comments

Comments
 (0)