Skip to content

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

Merged
merged 11 commits into from
Nov 7, 2017

Conversation

nicolasstucki
Copy link
Contributor

No description provided.

@nicolasstucki
Copy link
Contributor Author

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
Copy link
Contributor

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 {
Copy link
Contributor

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

Copy link
Contributor Author

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)
Copy link
Contributor

@allanrenucci allanrenucci Oct 27, 2017

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 =>
Copy link
Contributor

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)
Copy link
Contributor

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)
Copy link
Contributor

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 = {
Copy link
Contributor

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

Copy link
Contributor

@allanrenucci allanrenucci Nov 3, 2017

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 }
Copy link
Contributor

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

Copy link
Contributor Author

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 }
Copy link
Contributor

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) {
Copy link
Contributor

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"
Copy link
Contributor

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?

Copy link
Contributor Author

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

@allanrenucci
Copy link
Contributor

allanrenucci commented Oct 27, 2017

In PlainFile, I believe the following methods can be rewritten using nio APIs:

  • def input
  • def output
  • def iterator

@allanrenucci
Copy link
Contributor

PlainNioFile should not exist anymore since PlainFile is now backed by nio. We should make sure, the methods in PlainFile are implemented as the ones in PlainNioFile.

@@ -409,7 +409,7 @@ case class Site(
}

private def toSourceFile(f: JFile): SourceFile =
Copy link
Contributor

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

Copy link
Contributor Author

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.

@smarter
Copy link
Member

smarter commented Oct 27, 2017

I don't understand what's going on in this PR. What's the motivation?

@nicolasstucki
Copy link
Contributor Author

test performance please

@dottybot
Copy link
Member

dottybot commented Nov 2, 2017

performance test scheduled: 2 job(s) in queue, 1 running.

@smarter
Copy link
Member

smarter commented Nov 2, 2017

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

@allanrenucci
Copy link
Contributor

@smarter I believe writing to a jar required using the java.nio API but the dotty.io abstraction is mainly based on java.io. So it required some glue code to make it work.
The idea of this PR is to constantly use the java.nio everywhere and avoid conversions between JFile and JPath. Also, java.nio is supposed to be faster

@nicolasstucki
Copy link
Contributor Author

ExtractDependencies#recordDependency won't have any problems, I removed PlainNioFile.

@nicolasstucki
Copy link
Contributor Author

test performance please

@dottybot
Copy link
Member

dottybot commented Nov 2, 2017

performance test scheduled: 2 job(s) in queue, 1 running.

@dottybot
Copy link
Member

dottybot commented Nov 2, 2017

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
@dottybot
Copy link
Member

dottybot commented Nov 2, 2017

Performance test finished successfully:

Visit http://dotty-bench.epfl.ch/3395/ to see the changes.

Benchmarks is based on merging with master (cbb481c)

@smarter
Copy link
Member

smarter commented Nov 2, 2017

@retronym is there any reason scalac still has PlainFile and PlainNioFile or would the kind of refactoring done here make sense in scalac too?

@retronym
Copy link
Member

retronym commented Nov 2, 2017 via email

Copy link
Contributor

@allanrenucci allanrenucci left a 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] = {
Copy link
Contributor

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))
Copy link
Contributor

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()
Copy link
Contributor

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 = {
Copy link
Contributor

@allanrenucci allanrenucci Nov 3, 2017

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)
Copy link
Contributor

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

@nicolasstucki
Copy link
Contributor Author

test performance please

@dottybot
Copy link
Member

dottybot commented Nov 3, 2017

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)
Copy link
Contributor

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 }
Copy link
Contributor

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

@dottybot
Copy link
Member

dottybot commented Nov 3, 2017

Performance test finished successfully:

Visit http://dotty-bench.epfl.ch/3395/ to see the changes.

Benchmarks is based on merging with master (22ff8a4)

@allanrenucci
Copy link
Contributor

Also, I think you can implement resolve, relativize, and parent in Path in term of nio

@allanrenucci
Copy link
Contributor

test performance please

@dottybot
Copy link
Member

dottybot commented Nov 6, 2017

performance test scheduled: 1 job(s) in queue, 0 running.

@dottybot
Copy link
Member

dottybot commented Nov 6, 2017

Performance test finished successfully:

Visit http://dotty-bench.epfl.ch/3395/ to see the changes.

Benchmarks is based on merging with master (22ff8a4)

@nicolasstucki
Copy link
Contributor Author

test performance please

@dottybot
Copy link
Member

dottybot commented Nov 6, 2017

performance test scheduled: 1 job(s) in queue, 1 running.

@dottybot
Copy link
Member

dottybot commented Nov 6, 2017

Performance test finished successfully:

Visit http://dotty-bench.epfl.ch/3395/ to see the changes.

Benchmarks is based on merging with master (45387e2)

@nicolasstucki
Copy link
Contributor Author

test performance please

@dottybot
Copy link
Member

dottybot commented Nov 6, 2017

performance test scheduled: 1 job(s) in queue, 0 running.

@dottybot
Copy link
Member

dottybot commented Nov 6, 2017

Performance test finished successfully:

Visit http://dotty-bench.epfl.ch/3395/ to see the changes.

Benchmarks is based on merging with master (45387e2)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants