diff --git a/src/compiler/scala/tools/nsc/classpath/ZipAndJarFileLookupFactory.scala b/src/compiler/scala/tools/nsc/classpath/ZipAndJarFileLookupFactory.scala index 43a24e0f4d2..b0bce4125b6 100644 --- a/src/compiler/scala/tools/nsc/classpath/ZipAndJarFileLookupFactory.scala +++ b/src/compiler/scala/tools/nsc/classpath/ZipAndJarFileLookupFactory.scala @@ -36,15 +36,16 @@ sealed trait ZipAndJarFileLookupFactory { private val cache = new FileBasedCache[ZipSettings, ClassPath with Closeable] def create(zipFile: AbstractFile, settings: Settings, closeableRegistry: CloseableRegistry): ClassPath = { - val disabled = (settings.YdisableFlatCpCaching.value && !settings.YforceFlatCpCaching.value) || zipFile.file == null + val jfile = zipFile.file + val disabled = (settings.YdisableFlatCpCaching.value && !settings.YforceFlatCpCaching.value) || jfile == null val zipSettings = ZipSettings(settings.releaseValue) - cache.checkCacheability(zipFile.toURL :: Nil, checkStamps = true, disableCache = disabled) match { - case Left(_) => - val result: ClassPath with Closeable = createForZipFile(zipFile, zipSettings) - closeableRegistry.registerCloseable(result) - result - case Right(paths) => - cache.getOrCreate(zipSettings, paths, () => createForZipFile(zipFile, zipSettings), closeableRegistry, checkStamps = true) + + if (disabled) { + val result: ClassPath with Closeable = createForZipFile(zipFile, zipSettings) + closeableRegistry.registerCloseable(result) + result + } else { + cache.getOrCreate(zipSettings, Seq(jfile.toPath -> zipFile.basicFileAttributes), () => createForZipFile(zipFile, zipSettings), closeableRegistry, checkStamps = true) } } @@ -258,14 +259,18 @@ final class FileBasedCache[K, T] { } } - def getOrCreate(k: K, paths: Seq[Path], create: () => T, closeableRegistry: CloseableRegistry, checkStamps: Boolean): T = cache.synchronized { - val stamps = if (!checkStamps) Nil else paths.map { path => + def getOrCreate(k: K, paths: Seq[Path], create: () => T, + closeableRegistry: CloseableRegistry, checkStamps: Boolean): T = { + val pathsAndAttrs = paths.map(x => (x, Files.readAttributes(x, classOf[BasicFileAttributes]))) + getOrCreate0(k, pathsAndAttrs, create, closeableRegistry, checkStamps) + } + def getOrCreate0(k: K, paths: Seq[(Path, BasicFileAttributes)], create: () => T, closeableRegistry: CloseableRegistry, checkStamps: Boolean): T = cache.synchronized { + val stamps = if (!checkStamps) Nil else paths.map { case (path, attrs) => try { - val attrs = Files.readAttributes(path, classOf[BasicFileAttributes]) - val lastModified = attrs.lastModifiedTime() - // only null on some platforms, but that's okay, we just use the last modified timestamp as our stamp - val fileKey = attrs.fileKey() - Stamp(lastModified, attrs.size(), if (fileKey == null) NoFileKey else fileKey) + val lastModified = attrs.lastModifiedTime() + // only null on some platforms, but that's okay, we just use the last modified timestamp as our stamp + val fileKey = attrs.fileKey() + Stamp(lastModified, attrs.size(), if (fileKey == null) NoFileKey else fileKey) } catch { case _: java.nio.file.NoSuchFileException => // Dummy stamp for (currently) non-existent file. diff --git a/src/compiler/scala/tools/nsc/settings/PathFactory.scala b/src/compiler/scala/tools/nsc/settings/PathFactory.scala index d16b8de3600..73f1ef89e57 100644 --- a/src/compiler/scala/tools/nsc/settings/PathFactory.scala +++ b/src/compiler/scala/tools/nsc/settings/PathFactory.scala @@ -12,6 +12,7 @@ package scala.tools.nsc.settings +import java.io.File import scala.reflect.io.{AbstractFile, PlainFile} /** Converts paths provided in compiler options (e.g elements of `-classpath` or the target directory of `-d`) to @@ -37,6 +38,6 @@ trait PathFactory { } object DefaultPathFactory extends PathFactory { - override def getDirectory(path: String): AbstractFile = AbstractFile.getDirectory(path) + override def getDirectory(path: String): AbstractFile = AbstractFile.getDirectory(new File(path)) override def getFile(path: String): AbstractFile = new PlainFile(path) } diff --git a/src/reflect/scala/reflect/io/AbstractFile.scala b/src/reflect/scala/reflect/io/AbstractFile.scala index 8463a4a076d..31c910fa4d9 100644 --- a/src/reflect/scala/reflect/io/AbstractFile.scala +++ b/src/reflect/scala/reflect/io/AbstractFile.scala @@ -18,7 +18,8 @@ import java.io.{BufferedOutputStream, ByteArrayOutputStream, IOException, InputS import java.io.{File => JFile} import java.net.URL import java.nio.ByteBuffer - +import java.nio.file.attribute.BasicFileAttributes +import java.nio.file.Files import scala.collection.AbstractIterable /** @@ -46,10 +47,22 @@ object AbstractFile { * readable zip or jar archive, returns an abstract directory * backed by it. Otherwise, returns `null`. */ - def getDirectory(file: File): AbstractFile = - if (file.isDirectory) new PlainFile(file) - else if (file.isFile && Path.isExtensionJarOrZip(file.jfile)) ZipArchive.fromFile(file) + def getDirectory(file: File): AbstractFile = { + val attrs = try { + Files.readAttributes(file.jfile.toPath, classOf[BasicFileAttributes]) + } catch { + case _: IOException => + return null + } + + if (attrs.isDirectory) new PlainFile(file) + else if (attrs.isRegularFile && Path.isExtensionJarOrZip(file.jfile)) { + val za = ZipArchive.fromFile(file) + za.setBasicFileAttributesCached(attrs) + za + } else null + } /** * If the specified URL exists and is a regular file or a directory, returns an @@ -115,6 +128,18 @@ abstract class AbstractFile extends AbstractIterable[AbstractFile] { /** Returns the underlying File if any and null otherwise. */ def file: JFile + def basicFileAttributes: BasicFileAttributes = { + if (basicFileAttributesCached != null) + basicFileAttributesCached + else file match { + case null => null + case f => Files.readAttributes(f.toPath, classOf[BasicFileAttributes]) + } + } + private var basicFileAttributesCached: BasicFileAttributes = null + final def setBasicFileAttributesCached(attrs: BasicFileAttributes): Unit = { + basicFileAttributesCached = attrs + } /** An underlying source, if known. Mostly, a zip/jar file. */ def underlyingSource: Option[AbstractFile] = None