diff options
-rw-r--r-- | src/compiler/scala/tools/util/PathResolver.scala | 47 | ||||
-rw-r--r-- | test/junit/scala/tools/nsc/classpath/FlatClassPathResolverTest.scala | 159 |
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) + } + } +} |