summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormpociecha <michal.pociecha@gmail.com>2014-11-30 18:12:49 +0100
committermpociecha <michal.pociecha@gmail.com>2014-11-30 22:31:33 +0100
commit1cefcb8fbc7d22c093cc5a87254054a84ff445b2 (patch)
treee2e2342be62d6e12f19b803c2e9ba2e4b6d717f2
parentbb91785d6de488cf0b04ee8f43f789cbc4cb219a (diff)
downloadscala-1cefcb8fbc7d22c093cc5a87254054a84ff445b2.tar.gz
scala-1cefcb8fbc7d22c093cc5a87254054a84ff445b2.tar.bz2
scala-1cefcb8fbc7d22c093cc5a87254054a84ff445b2.zip
Create dedicated path resolver for the flat classpath representation
This commit adds dedicated FlatClassPathResolver loading classpath entries as FlatClassPath. Most of the common logic from PathResolver for the old classpath has been moved to the base, separate class which isn't dependent on a particular classpath representation. Thanks to that it was possible to reuse it when creating an adequate path resolver for the flat classpath representation. This change doesn't modify the way the compiler works. It also doesn't change nothing from the perspective of someone who already uses PathResolver in some project or even extends it - at least as long as he/she doesn't need to use flat classpath. There are also added JUnit tests inter alia comparing entries created using the old and the new classpath representations (whether the flat one created using the new path resolver returns the same entries as the recursive one).
-rw-r--r--src/compiler/scala/tools/util/PathResolver.scala47
-rw-r--r--test/junit/scala/tools/nsc/classpath/FlatClassPathResolverTest.scala159
2 files changed, 195 insertions, 11 deletions
diff --git a/src/compiler/scala/tools/util/PathResolver.scala b/src/compiler/scala/tools/util/PathResolver.scala
index 5526660509..797abc085c 100644
--- a/src/compiler/scala/tools/util/PathResolver.scala
+++ b/src/compiler/scala/tools/util/PathResolver.scala
@@ -7,14 +7,16 @@ package scala
package tools
package util
+import java.net.URL
import scala.tools.reflect.WrappedProperties.AccessControl
import scala.tools.nsc.{ Settings }
-import scala.tools.nsc.util.{ ClassPath, JavaClassPath }
+import scala.tools.nsc.util.{ ClassFileLookup, ClassPath, JavaClassPath }
import scala.reflect.io.{ File, Directory, Path, AbstractFile }
import scala.reflect.runtime.ReflectionUtils
import ClassPath.{ JavaContext, DefaultJavaContext, join, split }
import PartialFunction.condOpt
import scala.language.postfixOps
+import scala.tools.nsc.classpath.{ AggregateFlatClassPath, ClassPathFactory, FlatClassPath, FlatClassPathFactory }
// Loosely based on the draft specification at:
// https://wiki.scala-lang.org/display/SIW/Classpath
@@ -197,12 +199,10 @@ object PathResolver {
}
}
-class PathResolver(settings: Settings, context: JavaContext) {
- import PathResolver.{ Defaults, Environment, AsLines, MkLines, ppcp }
+abstract class PathResolverBase[BaseClassPathType <: ClassFileLookup[AbstractFile], ResultClassPathType <: BaseClassPathType]
+(settings: Settings, classPathFactory: ClassPathFactory[BaseClassPathType]) {
- def this(settings: Settings) = this(settings,
- if (settings.YnoLoadImplClass) PathResolver.NoImplClassJavaContext
- else DefaultJavaContext)
+ import PathResolver.{ AsLines, Defaults, ppcp }
private def cmdLineOrElse(name: String, alt: String) = {
(commandLineFor(name) match {
@@ -232,6 +232,7 @@ class PathResolver(settings: Settings, context: JavaContext) {
def javaUserClassPath = if (useJavaClassPath) Defaults.javaUserClassPath else ""
def scalaBootClassPath = cmdLineOrElse("bootclasspath", Defaults.scalaBootClassPath)
def scalaExtDirs = cmdLineOrElse("extdirs", Defaults.scalaExtDirs)
+
/** Scaladoc doesn't need any bootstrapping, otherwise will create errors such as:
* [scaladoc] ../scala-trunk/src/reflect/scala/reflect/macros/Reifiers.scala:89: error: object api is not a member of package reflect
* [scaladoc] case class ReificationException(val pos: reflect.api.PositionApi, val msg: String) extends Throwable(msg)
@@ -256,10 +257,10 @@ class PathResolver(settings: Settings, context: JavaContext) {
else sys.env.getOrElse("CLASSPATH", ".")
)
- import context._
+ import classPathFactory._
// Assemble the elements!
- def basis = List[Traversable[ClassPath[AbstractFile]]](
+ def basis = List[Traversable[BaseClassPathType]](
classesInPath(javaBootClassPath), // 1. The Java bootstrap class path.
contentsOfDirsInPath(javaExtDirs), // 2. The Java extension class path.
classesInExpandedPath(javaUserClassPath), // 3. The Java application class path.
@@ -288,8 +289,10 @@ class PathResolver(settings: Settings, context: JavaContext) {
def containers = Calculated.containers
- lazy val result = {
- val cp = new JavaClassPath(containers.toIndexedSeq, context)
+ import PathResolver.MkLines
+
+ def result: ResultClassPathType = {
+ val cp = computeResult()
if (settings.Ylogcp) {
Console print f"Classpath built from ${settings.toConciseString} %n"
Console print s"Defaults: ${PathResolver.Defaults}"
@@ -301,5 +304,27 @@ class PathResolver(settings: Settings, context: JavaContext) {
cp
}
- def asURLs = result.asURLs
+ def asURLs: List[URL] = result.asURLs.toList
+
+ protected def computeResult(): ResultClassPathType
+}
+
+class PathResolver(settings: Settings, context: JavaContext)
+ extends PathResolverBase[ClassPath[AbstractFile], JavaClassPath](settings, context) {
+
+ def this(settings: Settings) =
+ this(settings,
+ if (settings.YnoLoadImplClass) PathResolver.NoImplClassJavaContext
+ else DefaultJavaContext)
+
+ override protected def computeResult(): JavaClassPath =
+ new JavaClassPath(containers.toIndexedSeq, context)
+}
+
+class FlatClassPathResolver(settings: Settings, flatClassPathFactory: ClassPathFactory[FlatClassPath])
+ extends PathResolverBase[FlatClassPath, AggregateFlatClassPath](settings, flatClassPathFactory) {
+
+ def this(settings: Settings) = this(settings, new FlatClassPathFactory(settings))
+
+ override protected def computeResult(): AggregateFlatClassPath = AggregateFlatClassPath(containers.toIndexedSeq)
}
diff --git a/test/junit/scala/tools/nsc/classpath/FlatClassPathResolverTest.scala b/test/junit/scala/tools/nsc/classpath/FlatClassPathResolverTest.scala
new file mode 100644
index 0000000000..a37ba31b31
--- /dev/null
+++ b/test/junit/scala/tools/nsc/classpath/FlatClassPathResolverTest.scala
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2014 Contributor. All rights reserved.
+ */
+package scala.tools.nsc.classpath
+
+import java.io.File
+import org.junit.Assert._
+import org.junit._
+import org.junit.rules.TemporaryFolder
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import scala.annotation.tailrec
+import scala.tools.nsc.io.AbstractFile
+import scala.tools.nsc.util.ClassPath
+import scala.tools.nsc.Settings
+import scala.tools.util.FlatClassPathResolver
+import scala.tools.util.PathResolver
+
+@RunWith(classOf[JUnit4])
+class FlatClassPathResolverTest {
+
+ val tempDir = new TemporaryFolder()
+
+ private val packagesToTest = List(FlatClassPath.RootPackage, "scala", "scala.reflect", "scala.reflect.io")
+ private val classFilesToFind = List("scala.tools.util.FlatClassPathResolver",
+ "scala.reflect.io.AbstractFile",
+ "scala.collection.immutable.List",
+ "scala.Option",
+ "scala.collection.immutable.Vector",
+ "scala.util.hashing.MurmurHash3",
+ "java.lang.Object",
+ "java.util.Date")
+
+ private val classesToFind = classFilesToFind ++ List("TestSourceInRootPackage",
+ "scala.reflect.io.TestScalaSource",
+ "scala.reflect.io.TestJavaSource")
+
+ private val settings = new Settings
+
+ @Before
+ def initTempDirAndSourcePath: Unit = {
+ // In Java TemporaryFolder in JUnit is managed automatically using @Rule.
+ // It would work also in Scala after adding and extending a class like
+ // TestWithTempFolder.java containing it. But in this case it doesn't work when running tests
+ // from the command line - java class is not compiled due to some, misterious reasons.
+ // That's why such dirs are here created and deleted manually.
+ tempDir.create()
+ tempDir.newFile("TestSourceInRootPackage.scala")
+ val ioDir = tempDir.newFolder("scala", "reflect", "io")
+ new File(ioDir, "AbstractFile.scala").createNewFile()
+ new File(ioDir, "ZipArchive.java").createNewFile()
+ new File(ioDir, "TestScalaSource.scala").createNewFile()
+ new File(ioDir, "TestJavaSource.java").createNewFile()
+
+ settings.usejavacp.value = true
+ settings.sourcepath.value = tempDir.getRoot.getAbsolutePath
+ }
+
+ @After
+ def deleteTempDir: Unit = tempDir.delete()
+
+ private def createFlatClassPath(settings: Settings) =
+ new FlatClassPathResolver(settings).result
+
+ @Test
+ def testEntriesFromListOperationAgainstSeparateMethods: Unit = {
+ val classPath = createFlatClassPath(settings)
+
+ def compareEntriesInPackage(inPackage: String): Unit = {
+ val packages = classPath.packages(inPackage)
+ val classes = classPath.classes(inPackage)
+ val sources = classPath.sources(inPackage)
+ val FlatClassPathEntries(packagesFromList, classesAndSourcesFromList) = classPath.list(inPackage)
+
+ val packageNames = packages.map(_.name).sorted
+ val packageNamesFromList = packagesFromList.map(_.name).sorted
+ assertEquals(s"Methods list and packages for package '$inPackage' should return the same packages",
+ packageNames, packageNamesFromList)
+
+ val classFileNames = classes.map(_.name).sorted
+ val classFileNamesFromList = classesAndSourcesFromList.filter(_.binary.isDefined).map(_.name).sorted
+ assertEquals(s"Methods list and classes for package '$inPackage' should return entries for the same class files",
+ classFileNames, classFileNamesFromList)
+
+ val sourceFileNames = sources.map(_.name).sorted
+ val sourceFileNamesFromList = classesAndSourcesFromList.filter(_.source.isDefined).map(_.name).sorted
+ assertEquals(s"Methods list and sources for package '$inPackage' should return entries for the same source files",
+ sourceFileNames, sourceFileNamesFromList)
+
+ val uniqueNamesOfClassAndSourceFiles = (classFileNames ++ sourceFileNames).toSet
+ assertEquals(s"Class and source entries with the same name obtained via list for package '$inPackage' should be merged into one containing both files",
+ uniqueNamesOfClassAndSourceFiles.size, classesAndSourcesFromList.length)
+ }
+
+ packagesToTest foreach compareEntriesInPackage
+ }
+
+ @Test
+ def testCreatedEntriesAgainstRecursiveClassPath: Unit = {
+ val flatClassPath = createFlatClassPath(settings)
+ val recursiveClassPath = new PathResolver(settings).result
+
+ def compareEntriesInPackage(inPackage: String): Unit = {
+
+ @tailrec
+ def traverseToPackage(packageNameParts: Seq[String], cp: ClassPath[AbstractFile]): ClassPath[AbstractFile] = {
+ packageNameParts match {
+ case Nil => cp
+ case h :: t =>
+ cp.packages.find(_.name == h) match {
+ case Some(nestedCp) => traverseToPackage(t, nestedCp)
+ case _ => throw new Exception(s"There's no package $inPackage in recursive classpath - error when searching for '$h'")
+ }
+ }
+ }
+
+ val packageNameParts = if (inPackage == FlatClassPath.RootPackage) Nil else inPackage.split('.').toList
+ val recursiveClassPathInPackage = traverseToPackage(packageNameParts, recursiveClassPath)
+
+ val flatCpPackages = flatClassPath.packages(inPackage).map(_.name)
+ val pkgPrefix = PackageNameUtils.packagePrefix(inPackage)
+ val recursiveCpPackages = recursiveClassPathInPackage.packages.map(pkgPrefix + _.name)
+ assertEquals(s"Packages in package '$inPackage' on flat cp should be the same as on the recursive cp",
+ recursiveCpPackages, flatCpPackages)
+
+ val flatCpSources = flatClassPath.sources(inPackage).map(_.name).sorted
+ val recursiveCpSources = recursiveClassPathInPackage.classes
+ .filter(_.source.nonEmpty)
+ .map(_.name).sorted
+ assertEquals(s"Source entries in package '$inPackage' on flat cp should be the same as on the recursive cp",
+ recursiveCpSources, flatCpSources)
+
+ val flatCpClasses = flatClassPath.classes(inPackage).map(_.name).sorted
+ val recursiveCpClasses = recursiveClassPathInPackage.classes
+ .filter(_.binary.nonEmpty)
+ .map(_.name).sorted
+ assertEquals(s"Class entries in package '$inPackage' on flat cp should be the same as on the recursive cp",
+ recursiveCpClasses, flatCpClasses)
+ }
+
+ packagesToTest foreach compareEntriesInPackage
+ }
+
+ @Test
+ def testFindClassFile: Unit = {
+ val classPath = createFlatClassPath(settings)
+ classFilesToFind foreach { className =>
+ assertTrue(s"File for $className should be found", classPath.findClassFile(className).isDefined)
+ }
+ }
+
+ @Test
+ def testFindClass: Unit = {
+ val classPath = createFlatClassPath(settings)
+ classesToFind foreach { className =>
+ assertTrue(s"File for $className should be found", classPath.findClass(className).isDefined)
+ }
+ }
+}