Skip to content
This repository was archived by the owner on Sep 8, 2022. It is now read-only.

Commit 3923d16

Browse files
committed
Use classloader as main mechanism to specify test class path.
ConsoleFileManager will soon be a thing of the past when we switch to using ant + maven for resolving the test classpath when launching partest from the command line using test/partest.
1 parent 498a463 commit 3923d16

File tree

5 files changed

+81
-104
lines changed

5 files changed

+81
-104
lines changed

src/main/scala/scala/tools/partest/PartestTask.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import org.apache.tools.ant.types.{ Reference, FileSet }
1717
import org.apache.tools.ant.types.Commandline.Argument
1818
import scala.tools.ant.ScalaTask
1919
import nest.NestUI
20+
import java.net.URLClassLoader
2021

2122
/** An Ant task to execute the Scala test suite (NSC).
2223
*
@@ -123,7 +124,7 @@ class PartestTask extends Task with CompilationPathProperty with ScalaTask {
123124
})
124125

125126
var failureCount = 0
126-
val summary = new scala.tools.partest.nest.AntRunner(srcDir.getOrElse(null), compilationPath.get.list, javacmd.getOrElse(null), javaccmd.getOrElse(null), scalacArgsFlat) {
127+
val summary = new scala.tools.partest.nest.AntRunner(srcDir.getOrElse(null), new URLClassLoader(compilationPath.get.list.map(Path(_).toURL)), javacmd.getOrElse(null), javaccmd.getOrElse(null), scalacArgsFlat) {
127128
def echo(msg: String): Unit = PartestTask.this.log(msg)
128129
def log(msg: String): Unit = PartestTask.this.log(msg)
129130

src/main/scala/scala/tools/partest/nest/AntRunner.scala

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,12 @@ import sbt.testing.Status
1818
import sbt.testing.OptionalThrowable
1919
import sbt.testing.SuiteSelector
2020
import sbt.testing.TestSelector
21+
import java.net.URLClassLoader
2122

2223
// not using any Scala types to ease calling across different scala versions
23-
abstract class AntRunner(srcDir: String, compilationPaths: Array[String], javaCmd: File, javacCmd: File, scalacArgs: Array[String]) extends SuiteRunner(
24+
abstract class AntRunner(srcDir: String, testClassLoader: URLClassLoader, javaCmd: File, javacCmd: File, scalacArgs: Array[String]) extends SuiteRunner(
2425
testSourcePath = Option(srcDir) getOrElse PartestDefaults.sourcePath,
25-
new FileManager(testClassPath = compilationPaths map { fs => Path(fs) } toList),
26+
new FileManager(testClassLoader = testClassLoader),
2627
updateCheck = false,
2728
failed = false,
2829
javaCmdPath = Option(javaCmd).map(_.getAbsolutePath) getOrElse PartestDefaults.javaCmd,
@@ -51,6 +52,7 @@ abstract class AntRunner(srcDir: String, compilationPaths: Array[String], javaCm
5152
}
5253
}
5354

55+
// called reflectively from scala-partest-test-interface
5456
final def execute(kinds: Array[String]): String = {
5557
echo(banner)
5658

@@ -68,9 +70,10 @@ abstract class AntRunner(srcDir: String, compilationPaths: Array[String], javaCm
6870
}
6971
}
7072

73+
// called reflectively from scala-partest-test-interface
7174
class SBTRunner(partestFingerprint: Fingerprint, eventHandler: EventHandler, loggers: Array[Logger],
72-
srcDir: String, compilationPaths: Array[String], javaCmd: File, javacCmd: File, scalacArgs: Array[String])
73-
extends AntRunner(srcDir, compilationPaths, javaCmd, javacCmd, scalacArgs) {
75+
srcDir: String, testClassLoader: URLClassLoader, javaCmd: File, javacCmd: File, scalacArgs: Array[String])
76+
extends AntRunner(srcDir, testClassLoader, javaCmd, javacCmd, scalacArgs) {
7477
override def error(msg: String): Nothing = sys.error(msg)
7578
def echo(msg: String): Unit = loggers foreach { l => l.info(msg) }
7679
def log(msg: String): Unit = loggers foreach { l => l.debug(msg) }

src/main/scala/scala/tools/partest/nest/ConsoleFileManager.scala

Lines changed: 45 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,27 +15,7 @@ import scala.tools.nsc.{ io, util }
1515
import PathResolver.{ Environment, Defaults }
1616

1717
object ConsoleFileManager {
18-
def classPath = {
19-
val srcDir = {
20-
val src = PathSettings.srcDir
21-
if (!src.isDirectory) {
22-
NestUI.failure("Source directory \"" + src.path + "\" not found")
23-
sys.exit(1)
24-
}
25-
src
26-
}
27-
val libs = (srcDir / Directory("lib")).files filter (_ hasExtension "jar") map (_.toCanonical.path)
28-
29-
// def classPath = propOrElse("partest.classpath", "")
30-
val userCp = ClassPath split PathResolver.Environment.javaUserClassPath // XXX
31-
32-
// add all jars in libs
33-
val cp = (userCp ++ libs.toList)
34-
vlog("testClassPath: " + cp)
35-
cp map (Path(_))
36-
}
37-
38-
def mostRecentTrifecta(testBuild: Option[String], testClasses: Option[String]) = {
18+
private def mostRecentTrifecta(testBuild: Option[String], testClasses: Option[String]) = {
3919
import PathSettings.testParent
4020
val testClassesDir = testClasses map (tc => Path(tc).toCanonical.toDirectory)
4121
val testBuildDir = testBuild map (b => (testParent / b).toCanonical.toDirectory)
@@ -103,7 +83,49 @@ object ConsoleFileManager {
10383
pairs(pairs.keys max)()
10484
}
10585
}
86+
87+
def classPathFromMostRecentTrifecta(testBuild: Option[String], testClasses: Option[String]): List[Path] = {
88+
val (library, reflect, compiler) = mostRecentTrifecta(testBuild, testClasses)
89+
90+
val srcDir = {
91+
val src = PathSettings.srcDir
92+
if (!src.isDirectory) {
93+
NestUI.failure("Source directory \"" + src.path + "\" not found")
94+
sys.exit(1)
95+
}
96+
src
97+
}
98+
val libs = (srcDir / Directory("lib")).files filter (_ hasExtension "jar") map (file => Path(file.toCanonical.path))
99+
100+
// def classPath = propOrElse("partest.classpath", "")
101+
val userCp = ClassPath split PathResolver.Environment.javaUserClassPath map (Path(_))
102+
103+
val usingJars = library.getAbsolutePath endsWith ".jar"
104+
// basedir for jars or classfiles on core classpath
105+
val baseDir = SFile(library).parent
106+
107+
def relativeToLibrary(what: String): Path = {
108+
if (usingJars) (baseDir / s"$what.jar")
109+
else (baseDir.parent / "classes" / what)
110+
}
111+
112+
// all jars or dirs with prefix `what`
113+
def relativeToLibraryAll(what: String): Iterator[Path] = (
114+
if (usingJars) FileManager.jarsWithPrefix(baseDir, what)
115+
else FileManager.dirsWithPrefix(baseDir.parent / "classes" toDirectory, what)
116+
)
117+
118+
userCp ++ List[Path](
119+
library, reflect, compiler,
120+
relativeToLibrary("scala-actors"),
121+
relativeToLibrary("scala-parser-combinators"),
122+
relativeToLibrary("scala-xml"),
123+
relativeToLibrary("scala-scaladoc"),
124+
relativeToLibrary("scala-interactive"),
125+
relativeToLibrary("scalap"),
126+
PathSettings.diffUtils.fold(sys.error, identity)
127+
) ++ relativeToLibraryAll("scala-partest") ++ libs
128+
}
106129
}
107130

108-
case class ConsoleFileManager(testBuild: Option[String] = PartestDefaults.testBuild, testClasses: Option[String] = None)
109-
extends FileManager(ConsoleFileManager.classPath, ConsoleFileManager.mostRecentTrifecta(testBuild, testClasses))
131+
case class ConsoleFileManager(testBuild: Option[String] = PartestDefaults.testBuild, testClasses: Option[String] = None) extends FileManager(ConsoleFileManager.classPathFromMostRecentTrifecta(testBuild, testClasses))

src/main/scala/scala/tools/partest/nest/FileManager.scala

Lines changed: 25 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import java.net.URI
2424
import scala.reflect.io.AbstractFile
2525
import scala.collection.mutable
2626
import scala.reflect.internal.util.ScalaClassLoader
27+
import java.net.URLClassLoader
2728

2829
object FileManager {
2930
def getLogFile(dir: File, fileBase: String, kind: String): File =
@@ -97,91 +98,41 @@ object FileManager {
9798
if (diff.getDeltas.isEmpty) ""
9899
else difflib.DiffUtils.generateUnifiedDiff(originalName, revisedName, original.asJava, diff, 1).asScala.mkString("\n")
99100
}
100-
101-
// only used by script-based partest, not recommended for anything else
102-
def classPathFromTrifecta(library: Path, reflect: Path, compiler: Path) = {
103-
val usingJars = library.getAbsolutePath endsWith ".jar"
104-
// basedir for jars or classfiles on core classpath
105-
val baseDir = SFile(library).parent
106-
107-
def relativeToLibrary(what: String): Path = {
108-
if (usingJars) (baseDir / s"$what.jar")
109-
else (baseDir.parent / "classes" / what)
110-
}
111-
112-
// all jars or dirs with prefix `what`
113-
def relativeToLibraryAll(what: String): Iterator[Path] = (
114-
if (usingJars) jarsWithPrefix(baseDir, what)
115-
else dirsWithPrefix(baseDir.parent / "classes" toDirectory, what)
116-
)
117-
118-
List[Path](
119-
library, reflect, compiler,
120-
relativeToLibrary("scala-actors"),
121-
relativeToLibrary("scala-parser-combinators"),
122-
relativeToLibrary("scala-xml"),
123-
relativeToLibrary("scala-scaladoc"),
124-
relativeToLibrary("scala-interactive"),
125-
relativeToLibrary("scalap"),
126-
PathSettings.diffUtils.fold(sys.error, identity)
127-
) ++ relativeToLibraryAll("scala-partest")
128-
}
129-
130-
// find library/reflect/compiler jar or subdir under build/$stage/classes/
131-
// TODO: make more robust -- for now, only matching on prefix of jar file so it works for ivy/maven-resolved versioned jars
132-
// can we use the ClassLoader to find the jar/directory that contains a characteristic class file?
133-
def fromClassPath(name: String, testClassPath: List[Path]): Path = {
134-
// the old approach:
135-
def fallback =
136-
testClassPath find (f =>
137-
(f.extension == "jar" && f.getName.startsWith(s"scala-$name"))
138-
|| (f.absolutePathSegments endsWith Seq("classes", name))
139-
) getOrElse sys.error(s"Provided compilationPath does not contain a Scala $name element.\nLooked in: ${testClassPath.mkString(":")}")
140-
141-
// more precise:
142-
try {
143-
val classLoader = ScalaClassLoader fromURLs (testClassPath map (_.toURI.toURL))
144-
val canaryClass =
145-
name match {
146-
case "library" => Class.forName("scala.Unit", false, classLoader)
147-
case "reflect" => Class.forName("scala.reflect.api.Symbols", false, classLoader)
148-
case "compiler" => Class.forName("scala.tools.nsc.Main", false, classLoader)
149-
}
150-
val path = Path(canaryClass.getProtectionDomain.getCodeSource.getLocation.getPath)
151-
if (path.extension == "jar" || path.absolutePathSegments.endsWith(Seq("classes", name))) path
152-
else fallback
153-
} catch {
154-
case everything: Exception => fallback
155-
}
156-
}
157101
}
158102

159-
class FileManager private (val testClassPath: List[Path],
160-
val libraryUnderTest: Path,
161-
val reflectUnderTest: Path,
162-
val compilerUnderTest: Path) {
163-
def this(testClassPath: List[Path]) {
164-
this(testClassPath,
165-
FileManager.fromClassPath("library", testClassPath),
166-
FileManager.fromClassPath("reflect", testClassPath),
167-
FileManager.fromClassPath("compiler", testClassPath))
168-
}
103+
class FileManager(val testClassLoader: URLClassLoader) {
104+
lazy val libraryUnderTest: Path = findArtifact("library")
105+
lazy val reflectUnderTest: Path = findArtifact("reflect")
106+
lazy val compilerUnderTest: Path = findArtifact("compiler")
169107

170-
protected[nest] def this(libraryUnderTest: Path, reflectUnderTest: Path, compilerUnderTest: Path) {
171-
this(FileManager.classPathFromTrifecta(libraryUnderTest, reflectUnderTest, compilerUnderTest), libraryUnderTest, reflectUnderTest, compilerUnderTest)
172-
}
108+
lazy val testClassPath = testClassLoader.getURLs().map(url => Path(new File(url.toURI))).toList
173109

174-
protected[nest] def this(testClassPath: List[Path], trifecta: (Path, Path, Path)) {
175-
this(testClassPath, trifecta._1, trifecta._2, trifecta._3)
110+
def this(testClassPath: List[Path]) {
111+
this(new URLClassLoader(testClassPath.toArray map (_.toURI.toURL)))
176112
}
177113

178-
lazy val testClassLoader = ScalaClassLoader fromURLs (testClassPath map (_.toURI.toURL))
179-
180114
def distKind = {
181115
val p = libraryUnderTest.getAbsolutePath
182116
if (p endsWith "build/quick/classes/library") "quick"
183117
else if (p endsWith "build/pack/lib/scala-library.jar") "pack"
184118
else if (p endsWith "dists/latest/lib/scala-library.jar") "latest"
185119
else "installed"
186120
}
121+
122+
// find library/reflect/compiler jar or subdir under build/$stage/classes/
123+
private def findArtifact(name: String): Path = {
124+
val canaryClass =
125+
name match {
126+
case "library" => Class.forName("scala.Unit", false, testClassLoader)
127+
case "reflect" => Class.forName("scala.reflect.api.Symbols", false, testClassLoader)
128+
case "compiler" => Class.forName("scala.tools.nsc.Main", false, testClassLoader)
129+
}
130+
131+
val path = Path(canaryClass.getProtectionDomain.getCodeSource.getLocation.getPath)
132+
if (path.extension == "jar"
133+
|| path.absolutePathSegments.endsWith(Seq("classes", name))) path
134+
else sys.error(
135+
s"""Test Classloader does not contain a $name jar or classes/$name directory.
136+
|Looked in: $testClassPath""")
137+
}
187138
}

src/main/scala/scala/tools/partest/nest/ReflectiveRunner.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ class ReflectiveRunner {
4141
val classPath = searchPath("--classpath", argList)
4242
val fileManager = new ConsoleFileManager(if (argList contains "--pack") Some("build/pack") else buildPath, classPath)
4343

44-
val classPathEntries = fileManager.testClassLoader.classPathURLs.map(_.toString)
44+
val classPathEntries = fileManager.testClassPath.map(_.toString)
4545

4646
if (isPartestDebug)
4747
println("Loading classes from:\n " + classPathEntries.mkString("\n "))
@@ -63,7 +63,7 @@ class ReflectiveRunner {
6363
case cnfe: ClassNotFoundException =>
6464
cnfe.printStackTrace()
6565
NestUI.failure(runnerClassName + " could not be loaded from:\n")
66-
fileManager.testClassLoader.classPathURLs foreach (x => NestUI.failure(x + "\n"))
66+
fileManager.testClassPath foreach (x => NestUI.failure(x + "\n"))
6767
}
6868
}
6969
}

0 commit comments

Comments
 (0)