Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix 13618 - undo windows wildcard classpath globbing #13619

Merged
merged 4 commits into from Sep 30, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
19 changes: 9 additions & 10 deletions compiler/src/dotty/tools/MainGenericRunner.scala
Expand Up @@ -105,15 +105,15 @@ object MainGenericRunner {
case "-run" :: fqName :: tail =>
process(tail, settings.withExecuteMode(ExecuteMode.Run).withTargetToRun(fqName))
case ("-cp" | "-classpath" | "--class-path") :: cp :: tail =>
val globdir = cp.replaceAll("[\\/][^\\/]*$", "") // slash/backslash agnostic
val (tailargs, cpstr) = if globdir.nonEmpty && classpathSeparator != ";" || cp.contains(classpathSeparator) then
(tail, cp)
else
// combine globbed classpath entries into a classpath
val globdir = cp.replaceAll("[\\\\/][^\\\\/]*$", "") // slash/backslash agnostic
val (tailargs, cpstr) = if globdir.nonEmpty && !cp.contains(classpathSeparator) then
// globdir is wildcard directory for globbed jar files, reconstruct the intended classpath
val jarfiles = cp :: tail
val cpfiles = jarfiles.takeWhile( f => f.startsWith(globdir) && ((f.toLowerCase.endsWith(".jar") || f.endsWith(".zip"))) )
val tailargs = jarfiles.drop(cpfiles.size)
(tailargs, cpfiles.mkString(classpathSeparator))
else
(tail, cp)

process(tailargs, settings.copy(classPath = settings.classPath ++ cpstr.split(classpathSeparator).filter(_.nonEmpty)))

Expand Down Expand Up @@ -204,16 +204,15 @@ object MainGenericRunner {
val targetScript = Paths.get(settings.targetScript).toFile
val targetJar = settings.targetScript.replaceAll("[.][^\\/]*$", "")+".jar"
val precompiledJar = Paths.get(targetJar).toFile
def mainClass = Jar(targetJar).mainClass.getOrElse("") // throws exception if file not found
val jarIsValid = precompiledJar.isFile && mainClass.nonEmpty && precompiledJar.lastModified >= targetScript.lastModified
val mainClass = if !precompiledJar.isFile then "" else Jar(targetJar).mainClass.getOrElse("")
val jarIsValid = mainClass.nonEmpty && precompiledJar.lastModified >= targetScript.lastModified
if jarIsValid then
// precompiledJar exists, is newer than targetScript, and manifest defines a mainClass
sys.props("script.path") = targetScript.toPath.toAbsolutePath.normalize.toString
val scalaClasspath = ClasspathFromClassloader(Thread.currentThread().getContextClassLoader).split(classpathSeparator)
val newClasspath = (settings.classPath.flatMap(_.split(classpathSeparator).filter(_.nonEmpty)) ++ removeCompiler(scalaClasspath) :+ ".").map(File(_).toURI.toURL)
val mc = mainClass
if mc.nonEmpty then
ObjectRunner.runAndCatch(newClasspath :+ File(targetJar).toURI.toURL, mc, settings.scriptArgs)
if mainClass.nonEmpty then
ObjectRunner.runAndCatch(newClasspath :+ File(targetJar).toURI.toURL, mainClass, settings.scriptArgs)
else
Some(IllegalArgumentException(s"No main class defined in manifest in jar: $precompiledJar"))
else
Expand Down
8 changes: 8 additions & 0 deletions compiler/test-resources/scripting/unglobClasspath.sc
@@ -0,0 +1,8 @@
#!bin/scala -classpath 'dist/target/pack/lib/*'

// won't compile unless the hashbang line sets classpath
import org.jline.terminal.Terminal

def main(args: Array[String]) =
val cp = sys.props("java.class.path")
printf("unglobbed classpath: %s\n", cp)
38 changes: 32 additions & 6 deletions compiler/test/dotty/tools/scripting/ClasspathTests.scala
Expand Up @@ -18,19 +18,19 @@ class ClasspathTests:
val packBinDir = "dist/target/pack/bin"
val packLibDir = "dist/target/pack/lib"

// only interested in classpath test scripts
val testScriptName = "classpathReport.sc"
val testScript = scripts("/scripting").find { _.getName.matches(testScriptName) } match
case None => sys.error(s"test script not found: ${testScriptName}")
case Some(file) => file

def exists(scriptPath: Path): Boolean = Files.exists(scriptPath)
def packBinScalaExists:Boolean = exists(Paths.get(s"$packBinDir/scala"))

/*
* verify classpath reported by called script.
*/
@Test def hashbangClasspathVerifyTest = {
// only interested in classpath test scripts
val testScriptName = "classpathReport.sc"
val testScript = scripts("/scripting").find { _.getName.matches(testScriptName) } match
case None => sys.error(s"test script not found: ${testScriptName}")
case Some(file) => file

val relpath = testScript.toPath.relpath.norm
printf("===> hashbangClasspathVerifyTest for script [%s]\n", relpath)
printf("bash is [%s]\n", bashExe)
Expand All @@ -57,6 +57,32 @@ class ClasspathTests:

assert(hashbangClasspathJars.size == packlibJars.size)
}
/*
* verify classpath is unglobbed by MainGenericRunner.
*/
@Test def unglobClasspathVerifyTest = {
val testScriptName = "unglobClasspath.sc"
val testScript = scripts("/scripting").find { _.getName.matches(testScriptName) } match
case None => sys.error(s"test script not found: ${testScriptName}")
case Some(file) => file

val relpath = testScript.toPath.relpath.norm
printf("===> unglobClasspathVerifyTest for script [%s]\n", relpath)
printf("bash is [%s]\n", bashExe)

if packBinScalaExists then
val bashCmdline = s"SCALA_OPTS= $relpath"
val cmd = Array(bashExe, "-c", bashCmdline)

cmd.foreach { printf("[%s]\n", _) }

// test script reports the classpath it sees
val scriptOutput = exec(cmd:_*)
val scriptCp = findTaggedLine("unglobbed classpath", scriptOutput)
val classpathJars = scriptCp.split(psep).map { _.getName }.sorted.distinct
//classpathJars.foreach { printf("%s\n", _) }
assert(classpathJars.size > 1)
}


//////////////// end of tests ////////////////
Expand Down
2 changes: 1 addition & 1 deletion compiler/test/dotty/tools/scripting/ScriptingTests.scala
Expand Up @@ -19,7 +19,7 @@ class ScriptingTests:
f.getAbsolutePath.replace('\\', '/')

// classpath tests managed by scripting.ClasspathTests.scala
def testFiles = scripts("/scripting").filter { ! _.getName.startsWith("classpath") }
def testFiles = scripts("/scripting").filter { ! _.getName.toLowerCase.contains("classpath") }

def script2jar(scriptFile: File) =
val jarName = s"${scriptFile.getName.dropExtension}.jar"
Expand Down