-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Use nio.Path instead of io.File to properly support PlainNioFile #3395
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
Use nio.Path instead of io.File to properly support PlainNioFile #3395
Conversation
The CI should not have passed on 72fa00d. |
@@ -94,7 +95,7 @@ abstract class AbstractFile extends Iterable[AbstractFile] { | |||
def path: String | |||
|
|||
/** Returns the path of this abstract file in a canonical form. */ | |||
def canonicalPath: String = if (file == null) path else file.getCanonicalPath | |||
def canonicalPath: String = if (jpath == null) path else jpath.toFile.getCanonicalPath |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it is equivalent to jpath.normalize.toString
@@ -107,18 +108,26 @@ abstract class AbstractFile extends Iterable[AbstractFile] { | |||
def container : AbstractFile | |||
|
|||
/** Returns the underlying File if any and null otherwise. */ | |||
def file: JFile | |||
def file: JFile = try { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ideally we should remove this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That will be for next step. That might need changes outside of the Path/File/Directory abstractions. I would prefer to keep the changes only inside these abstractions for this first step.
val path = jpath.resolve(name) | ||
try { | ||
if (isDir) Files.createDirectories(path) | ||
else Files.createFile(path) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This does not create intermediate directories if necessary. We need to make sure it behaves as before
if (isDir) Files.createDirectories(path) | ||
else Files.createFile(path) | ||
} catch { | ||
case _: FileAlreadyExistsException => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess it should never happen, otherwise lookupName
would not have returned null
override val creationCodec = constructorCodec | ||
|
||
override def addExtension(ext: String): File = super.addExtension(ext).toFile | ||
override def toAbsolute: File = if (isAbsolute) this else super.toAbsolute.toFile | ||
override def toDirectory: Directory = new Directory(jfile) | ||
override def toDirectory: Directory = new Directory(jfile.toPath) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not simply new Directory(jpath)
?
case null => | ||
if (isAbsolute) toDirectory // it should be a root. BTW, don't need to worry about relative pathed root | ||
else Directory(".") // a dir under pwd | ||
case x => | ||
Directory(x) | ||
Directory(x.toFile) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There should probably be a constructor that takes a JPath
. And I think we should remove the JFile
constructor
} | ||
f.delete() | ||
def deleteRecursively(): Boolean = deleteRecursively(jpath) | ||
private def deleteRecursively(p: JPath): Boolean = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the proper way to delete recursively with nio is:
def deleteRecursively(dir: Path): Unit = if (Files.exists(dir)) {
Files.walkFileTree(dir, new SimpleFileVisitor[Path]() {
override def visitFile(file: Path, attrs: BasicFileAttributes) = {
Files.delete(file)
FileVisitResult.CONTINUE
}
override def postVisitDirectory(dir: Path, exc: IOException) = {
Files.delete(dir)
FileVisitResult.CONTINUE
}
})
Not sure what should be the behavior if the path does not exist
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment as before:
def deleteRecursively(dir: Path): Unit = if (Files.exists(dir)) {
Files.walkFileTree(dir, new SimpleFileVisitor[Path]() {
override def visitFile(file: Path, attrs: BasicFileAttributes) = {
Files.delete(file)
FileVisitResult.CONTINUE
}
override def postVisitDirectory(dir: Path, exc: IOException) = {
Files.delete(dir)
FileVisitResult.CONTINUE
}
})
} | ||
|
||
private def tryCreate(create: => JPath): Boolean = | ||
try { create; true } catch { case _: FileAlreadyExistsException => false } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Semantic is different for something like foo/bar/
if foo
does not exist
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As we saw together the semantics are the same. In the example, an IOException
would be thrown in both implementations.
} | ||
|
||
private def delete(path: JPath): Boolean = | ||
try { Files.delete(path); true } catch { case _: DirectoryNotEmptyException | _: NoSuchFileException => false } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can use deleteIfExists
and not catch NoSuchFileException
@@ -112,7 +112,7 @@ abstract class ZipArchive(override val file: JFile) extends AbstractFile with Eq | |||
} | |||
} | |||
/** ''Note: This library is considered experimental and should not be used unless you know what you are doing.'' */ | |||
final class FileZipArchive(file: JFile) extends ZipArchive(file) { | |||
final class FileZipArchive(file: JFile) extends ZipArchive(file.toPath) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't you use a JPath
here instead?
.drone.yml
Outdated
@@ -13,6 +13,7 @@ pipeline: | |||
image: lampepfl/dotty:2017-10-20 | |||
commands: | |||
- cp -R . /tmp/1/ && cd /tmp/1/ | |||
- ./project/scripts/sbt compile # to ensure that side projects (such as dotty doc) compile | |||
- ./project/scripts/sbt testAll | |||
- ./project/scripts/sbt ";dotty-bench/jmh:run 1 1 tests/run/arrays.scala" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was meant to make sure that dotty-bench
compiles. Should it be part of dottyRoot so that we can get rid of this line?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That would be a solution
In
|
|
@@ -409,7 +409,7 @@ case class Site( | |||
} | |||
|
|||
private def toSourceFile(f: JFile): SourceFile = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should probably be changed to take a JPath
instead
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will be for another PR.
I don't understand what's going on in this PR. What's the motivation? |
e2222dd
to
a914812
Compare
test performance please |
performance test scheduled: 2 job(s) in queue, 1 running. |
a914812
to
813d376
Compare
I still would like to know the rationale behind this PR :). Also it's likely incomplete, ExtractDependencies#recordDependency needs an extra case to handle PlainNioFiles: https://github.com/dotty-staging/dotty/blob/813d376d78ccf752b480882fbcb58cd6a35431a4/compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala#L98, otherwise incremental compilation will be broken |
813d376
to
2694273
Compare
@smarter I believe writing to a jar required using the |
ExtractDependencies#recordDependency won't have any problems, I removed |
test performance please |
performance test scheduled: 2 job(s) in queue, 1 running. |
Performance test finished successfully: Visit http://dotty-bench.epfl.ch/3395/ to see the changes. Benchmarks is based on merging with master (cbb481c) |
1 similar comment
Performance test finished successfully: Visit http://dotty-bench.epfl.ch/3395/ to see the changes. Benchmarks is based on merging with master (cbb481c) |
@retronym is there any reason scalac still has PlainFile and PlainNioFile or would the kind of refactoring done here make sense in scalac too? |
I added PlainNioFile as a minimal bridge from the AbtractFile to nio, as a
means to support JDK’s jrt:// filesystem. I think AbstractFile should
probably be removed from compiler APIs entirely in favour of direct use
NIO. A bit of experimentation is needed to make sure we can still support
in memory use cases (via a custom in memory filesystemprovider)
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still methods in File
are implemented in term of jfile
jfile.listFiles match { | ||
case null => Iterator.empty | ||
case xs => xs.iterator map Path.apply | ||
def list: Iterator[Path] = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It should be lazy
if (isDirectory) Files.list(jpath).iterator.asScala.map(Path.apply)
else Iterator.empty
|
||
def onlyDirs(xs: Iterator[Path]): Iterator[Directory] = xs filter (_.isDirectory) map (_.toDirectory) | ||
def onlyDirs(xs: List[Path]): List[Directory] = xs filter (_.isDirectory) map (_.toDirectory) | ||
def onlyFiles(xs: Iterator[Path]): Iterator[File] = xs filter (_.isFile) map (_.toFile) | ||
|
||
def roots: List[Path] = java.io.File.listRoots().toList map Path.apply | ||
def roots: List[Path] = java.io.File.listRoots().toList.map(r => Path.apply(r.toPath)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FileSystems.getDefault().getRootDirectories()
?
} | ||
def createFile(failIfExists: Boolean = false): File = { | ||
val res = jfile.createNewFile() | ||
val res = tryCreate(Files.createFile(jpath)) | ||
jfile.createNewFile() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be here?
} | ||
f.delete() | ||
def deleteRecursively(): Boolean = deleteRecursively(jpath) | ||
private def deleteRecursively(p: JPath): Boolean = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment as before:
def deleteRecursively(dir: Path): Unit = if (Files.exists(dir)) {
Files.walkFileTree(dir, new SimpleFileVisitor[Path]() {
override def visitFile(file: Path, attrs: BasicFileAttributes) = {
Files.delete(file)
FileVisitResult.CONTINUE
}
override def postVisitDirectory(dir: Path, exc: IOException) = {
Files.delete(dir)
FileVisitResult.CONTINUE
}
})
@@ -32,7 +33,7 @@ object ZipArchive { | |||
*/ | |||
def fromFile(file: File): FileZipArchive = fromFile(file.jfile) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would add fromPath
and make the other delegate to it
bfc9784
to
3108499
Compare
test performance please |
performance test scheduled: 2 job(s) in queue, 1 running. |
|
||
/** Obtains a OutputStream. */ | ||
def outputStream(append: Boolean = false) = new FileOutputStream(jfile, append) | ||
def outputStream(append: Boolean = false) = Files.newOutputStream(jpath, APPEND) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
APPEND
?
try { new FileZipArchive(file) } | ||
def fromFile(file: File): FileZipArchive = fromPath(file.jpath) | ||
def fromPath(jpath: JPath): FileZipArchive = | ||
try { new FileZipArchive(jpath) } | ||
catch { case _: IOException => null } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure what can throw an IOException
here
Performance test finished successfully: Visit http://dotty-bench.epfl.ch/3395/ to see the changes. Benchmarks is based on merging with master (22ff8a4) |
Also, I think you can implement |
5dea2a7
to
5065cf2
Compare
test performance please |
performance test scheduled: 1 job(s) in queue, 0 running. |
Performance test finished successfully: Visit http://dotty-bench.epfl.ch/3395/ to see the changes. Benchmarks is based on merging with master (22ff8a4) |
test performance please |
performance test scheduled: 1 job(s) in queue, 1 running. |
Performance test finished successfully: Visit http://dotty-bench.epfl.ch/3395/ to see the changes. Benchmarks is based on merging with master (45387e2) |
test performance please |
performance test scheduled: 1 job(s) in queue, 0 running. |
Performance test finished successfully: Visit http://dotty-bench.epfl.ch/3395/ to see the changes. Benchmarks is based on merging with master (45387e2) |
No description provided.