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

fail verifyScalacArgs if dist/target/pack/bin/scalac not found - fix for #12962 #13029

Merged
merged 3 commits into from
Sep 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions compiler/test-resources/scripting/scriptPath.sc
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,18 @@

def main(args: Array[String]): Unit =
args.zipWithIndex.foreach { case (arg,i) => printf("arg %d: [%s]\n",i,arg) }
val path = Option(sys.props("script.path")) match {
case None => printf("no script.path property is defined\n")

Option(sys.props("script.path")) match {
case Some(path) =>
printf("script.path: %s\n",path)
assert(path.endsWith("scriptPath.sc"),s"actual path [$path]")
if ! path.endsWith("scriptPath.sc") then
printf( s"incorrect script.path defined as [$path]")
else
printf("script.path: %s\n",path) // report the value
case None =>
printf("no script.path property is defined\n")
// report relevant environment factors that might explain the error
val psep: String = Option(sys.props("path.separator")).get
val pathEntries = System.getenv("PATH").split(psep).toList
System.err.printf("sun.java.command: %s\n", sys.props("sun.java.command"))
System.err.printf("first 5 PATH entries:\n%s\n",pathEntries.take(5).mkString("\n"))
}
164 changes: 106 additions & 58 deletions compiler/test/dotty/tools/scripting/BashScriptsTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import org.junit.Test

import vulpix.TestConfiguration

import dotty.tools.dotc.config.Properties._

/** Verifies correct handling of command line arguments by `dist/bin/scala` and `dist/bin/scalac`.
* +. arguments following a script path must be treated as script arguments
Expand All @@ -19,6 +20,13 @@ class BashScriptsTests:
// classpath tests managed by scripting.ClasspathTests.scala
def testFiles = scripts("/scripting")

printf("osname[%s]\n", osname)
printf("using JAVA_HOME=%s\n", javaHome)
printf("using SCALA_HOME=%s\n", scalaHome)
printf("first 5 PATH entries:\n%s\n", pathEntries.take(5).mkString("\n"))
printf("scala path: [%s]\n", scalaPath)
printf("scalac path: [%s]\n", scalacPath)

lazy val expectedOutput = List(
"arg 0:[a]",
"arg 1:[b]",
Expand All @@ -31,65 +39,52 @@ class BashScriptsTests:
lazy val testScriptArgs = Seq(
"a", "b", "c", "-repl", "-run", "-script", "-debug"
)
lazy val (bashExe, bashPath) =
val bexe = getBashPath
val bpath = Paths.get(bexe)
// printf("bashExe: [%s]\n", bexe)
(bexe, bpath)

val showArgsScript = testFiles.find(_.getName == "showArgs.sc").get.absPath

val scalacPath = "dist/target/pack/bin/scalac" // which("scalac")
val scalaPath = "dist/target/pack/bin/scala" // which("scala")

/* verify `dist/bin/scalac` */
/* verify `dist/bin/scalac` non-interference with command line args following script name */
@Test def verifyScalacArgs =
printf("scalacPath[%s]\n", scalacPath)
val commandline = (Seq(scalacPath, "-script", showArgsScript) ++ testScriptArgs).mkString(" ")
if bashPath.toFile.exists then
var cmd = Array(bashExe, "-c", commandline)
val output = Process(cmd).lazyLines_!
val (validTest, exitCode, stdout, stderr) = bashCommand(commandline)
if validTest then
var fail = false
printf("\n")
for (line, expect) <- output zip expectedOutput do
for (line, expect) <- stdout zip expectedOutput do
printf("expected: %-17s\nactual : %s\n", expect, line)
if line != expect then
fail = true

if fail then
assert(output == expectedOutput)
assert(stdout == expectedOutput)

/* verify `dist/bin/scala` */
/* verify `dist/bin/scala` non-interference with command line args following script name */
@Test def verifyScalaArgs =
val commandline = (Seq(scalaPath, showArgsScript) ++ testScriptArgs).mkString(" ")
if bashPath.toFile.exists then
var cmd = Array(bashExe, "-c", commandline)
val output = for {
line <- Process(cmd).lazyLines_!
} yield line
val (validTest, exitCode, stdout, stderr) = bashCommand(commandline)
if validTest then
var fail = false
printf("\n")
var mismatches = List.empty[(String, String)]
for (line, expect) <- output zip expectedOutput do
for (line, expect) <- stdout zip expectedOutput do
printf("expected: %-17s\nactual : %s\n", expect, line)
if line != expect then
fail = true

if fail then
assert(output == expectedOutput)
assert(stdout == expectedOutput)

/*
* verify that scriptPath.sc sees a valid script.path property.
* verify that scriptPath.sc sees a valid script.path property,
* and that it's value is the path to "scriptPath.sc".
*/
@Test def verifyScriptPathProperty =
val scriptFile = testFiles.find(_.getName == "scriptPath.sc").get
val expected = s"/${scriptFile.getName}"
printf("===> verify valid system property script.path is reported by script [%s]\n", scriptFile.getName)
val (exitCode, stdout, stderr) = bashCommand(scriptFile.absPath)
if exitCode == 0 && ! stderr.exists(_.contains("Permission denied")) then
// var cmd = Array(bashExe, "-c", scriptFile.absPath)
// val stdout = Process(cmd).lazyLines_!
stdout.foreach { printf("######### [%s]\n", _) }
printf("calling scriptFile: %s\n", scriptFile)
val (validTest, exitCode, stdout, stderr) = bashCommand(scriptFile.absPath)
if validTest then
stdout.foreach { printf("stdout: [%s]\n", _) }
stderr.foreach { printf("stderr: [%s]\n", _) }
val valid = stdout.exists { _.endsWith(expected) }
if valid then printf("# valid script.path reported by [%s]\n", scriptFile.getName)
assert(valid, s"script ${scriptFile.absPath} did not report valid script.path value")
Expand All @@ -99,58 +94,111 @@ class BashScriptsTests:
*/
@Test def verifyScalaOpts =
val scriptFile = testFiles.find(_.getName == "classpathReport.sc").get
printf("===> verify valid system property script.path is reported by script [%s]\n", scriptFile.getName)
val argsfile = createArgsFile() // avoid problems caused by drive letter
printf("===> verify SCALA_OPTS='@argsfile' is properly handled by `dist/bin/scala`\n")
val envPairs = List(("SCALA_OPTS", s"@$argsfile"))
val (exitCode, stdout, stderr) = bashCommand(scriptFile.absPath, envPairs:_*)
if exitCode != 0 || stderr.exists(_.contains("Permission denied")) then
stderr.foreach { System.err.printf("stderr [%s]\n", _) }
printf("unable to execute script, return value is %d\n", exitCode)
else
// val stdout: Seq[String] = Process(cmd, cwd, envPairs:_*).lazyLines_!.toList
val expected = s"${cwd.toString}"
val (validTest, exitCode, stdout, stderr) = bashCommand(scriptFile.absPath, envPairs)
if validTest then
val expected = s"${workingDirectory.toString}"
val List(line1: String, line2: String) = stdout.take(2)
val valid = line2.dropWhile( _ != ' ').trim.startsWith(expected)
if valid then printf(s"\n===> success: classpath begins with %s, as reported by [%s]\n", cwd, scriptFile.getName)
if valid then printf(s"\n===> success: classpath begins with %s, as reported by [%s]\n", workingDirectory, scriptFile.getName)
assert(valid, s"script ${scriptFile.absPath} did not report valid java.class.path first entry")

lazy val cwd = Paths.get(dotty.tools.dotc.config.Properties.userDir).toFile
def existingPath: String = envOrElse("PATH","").norm
def adjustedPath = s"$javaHome/bin$psep$scalaHome/bin$psep$existingPath"
def pathEntries = adjustedPath.split(psep).toList

lazy val argsfile = createArgsFile() // avoid problems caused by drive letter
def createArgsFile(): String =
val utfCharset = java.nio.charset.StandardCharsets.UTF_8.name
val text = s"-classpath ${cwd.absPath}"
val path = Files.createTempFile("scriptingTest", ".args")
val text = s"-classpath ${workingDirectory.absPath}"
Files.write(path, text.getBytes(utfCharset))
path.toFile.getAbsolutePath.replace('\\', '/')

extension (str: String) def dropExtension: String =
str.reverse.dropWhile(_ != '.').drop(1).reverse
def fixHome(s: String): String =
s.startsWith("~") match {
case false => s
case true => s.replaceFirst("~",userHome)
}

extension(s: String) {
def toPath: Path = Paths.get(fixHome(s)) // .toAbsolutePath
def toFile: File = s.toPath.toFile
def absPath: String = s.toFile.absPath
def norm: String = s.replace('\\', '/') // bash expects forward slash
def isFile: Boolean = s.toFile.isFile
def exists: Boolean = s.toPath.toFile.exists
def name: String = s.toFile.getName
def dropExtension: String = s.reverse.dropWhile(_ != '.').drop(1).reverse
}

extension(p: Path) {
def listFiles: Seq[File] = p.toFile.listFiles.toList
def norm: String = p.normalize.toString.replace('\\', '/')
def name: String = p.toFile.getName
}

extension(f: File) {
def name = f.getName
def norm: String = f.toPath.normalize.norm
def absPath: String = f.getAbsolutePath.norm
}

lazy val psep: String = propOrElse("path.separator","")
lazy val osname = propOrElse("os.name", "").toLowerCase

lazy val scalacPath = s"$workingDirectory/dist/target/pack/bin/scalac".norm
lazy val scalaPath = s"$workingDirectory/dist/target/pack/bin/scala".norm

extension(f: File) def absPath: String =
f.getAbsolutePath.replace('\\', '/')
// use optional working directory TEST_CWD, if defined
lazy val workingDirectory: String = envOrElse("TEST_CWD", userDir)

lazy val osname = Option(sys.props("os.name")).getOrElse("").toLowerCase
// use optional TEST_BASH if defined, otherwise, bash must be in PATH
lazy val bashExe: String = envOrElse("TEST_BASH", whichBash)

def getBashPath: String =
// test env SCALA_HOME is:
// dist/target/pack, if present
// else, SCALA_HOME if defined
// else, not defined
lazy val scalaHome =
if scalacPath.isFile then scalacPath.replaceAll("/bin/scalac","")
else envOrElse("SCALA_HOME", "").norm

lazy val javaHome = envOrElse("JAVA_HOME", "").norm

lazy val testEnvPairs = List(
("JAVA_HOME", javaHome),
("SCALA_HOME", scalaHome),
("PATH", adjustedPath),
).filter { case (name,valu) => valu.nonEmpty }

lazy val whichBash: String =
var whichBash = ""
//printf("osname[%s]\n", osname)
if osname.startsWith("windows") then
whichBash = which("bash.exe")
else
whichBash = which("bash")

whichBash

def bashCommand(cmdstr: String, envPairs: (String, String)*): (Int, Seq[String], Seq[String]) = {
import scala.sys.process._
val cmd = Seq(bashExe, "-c", cmdstr)
val proc = Process(cmd, None, envPairs *)
def bashCommand(cmdstr: String, additionalEnvPairs:List[(String, String)] = Nil): (Boolean, Int, Seq[String], Seq[String]) = {
var (stdout, stderr) = (List.empty[String], List.empty[String])
val exitVal = proc ! ProcessLogger (
(out: String) => stdout ::= out,
(err: String) => stderr ::= err
)
(exitVal, stdout.reverse, stderr.reverse)
if bashExe.toFile.exists then
val cmd = Seq(bashExe, "-c", cmdstr)
val envPairs = testEnvPairs ++ additionalEnvPairs
val proc = Process(cmd, None, envPairs *)
val exitVal = proc ! ProcessLogger (
(out: String) => stdout ::= out,
(err: String) => stderr ::= err
)
val validTest = exitVal == 0 && ! stderr.exists(_.contains("Permission denied"))
if ! validTest then
printf("\nunable to execute script, return value is %d\n", exitVal)
stderr.foreach { System.err.printf("stderr [%s]\n", _) }
(validTest, exitVal, stdout.reverse, stderr.reverse)
else
(false, -1, Nil, Nil)
}

def execCmd(command: String, options: String *): Seq[String] =
Expand Down