From 59e1bda2a0694645fd615bd24c1f5b2265a4cebb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sakib=20Had=C5=BEiavdi=C4=87?= Date: Thu, 17 Oct 2024 13:23:57 +0200 Subject: [PATCH] Improve file handling on Windows (#3764) Changes: - replace `new FileInputStream` with `Files.newInputStream` - replace `new FileOutputStream` with `Files.newOutputStream` Motivation: https://github.com/com-lihaoyi/mill/issues/1939 https://bugs.openjdk.org/browse/JDK-6357433 (in a comment) says: > For jdk7, the workaround is to **use new file system API**. So instead of new FileInputStream(f) and new FileOutputStream(f), use f.toPath().newInputStream() and f.toPath().newOutputStream() So seems like new NIO API does use, FILE_SHARE_DELETE, which is needed to delete files in unix-style. --- .../SonatypeCentralPublisher.scala | 8 ++++---- main/api/src/mill/api/IO.scala | 4 +++- main/api/src/mill/api/JarOps.scala | 8 ++++---- .../src/mill/main/client/ServerLauncher.java | 4 ++-- .../main/client/FileToStreamTailerTest.java | 13 ++++++------- main/util/src/mill/util/Util.scala | 18 +++++++----------- .../runner/client/MillProcessLauncher.java | 4 ++-- runner/src/mill/runner/MillMain.scala | 7 +++++-- .../src/mill/testrunner/TestRunnerUtils.scala | 4 ++-- 9 files changed, 35 insertions(+), 35 deletions(-) diff --git a/contrib/sonatypecentral/src/mill/contrib/sonatypecentral/SonatypeCentralPublisher.scala b/contrib/sonatypecentral/src/mill/contrib/sonatypecentral/SonatypeCentralPublisher.scala index f8e7f0b3d29..edfa92c5a5c 100644 --- a/contrib/sonatypecentral/src/mill/contrib/sonatypecentral/SonatypeCentralPublisher.scala +++ b/contrib/sonatypecentral/src/mill/contrib/sonatypecentral/SonatypeCentralPublisher.scala @@ -10,7 +10,7 @@ import mill.api.Logger import mill.scalalib.publish.Artifact import mill.scalalib.publish.SonatypeHelpers.getArtifactMappings -import java.io.FileOutputStream +import java.nio.file.Files import java.util.jar.JarOutputStream import java.util.zip.ZipEntry @@ -110,15 +110,15 @@ class SonatypeCentralPublisher( wd: os.Path )(func: JarOutputStream => Unit): java.io.File = { val zipFile = - (wd / s"$fileNameWithoutExtension.zip").toIO - val fileOutputStream = new FileOutputStream(zipFile) + (wd / s"$fileNameWithoutExtension.zip") + val fileOutputStream = Files.newOutputStream(zipFile.toNIO) val jarOutputStream = new JarOutputStream(fileOutputStream) try { func(jarOutputStream) } finally { jarOutputStream.close() } - zipFile + zipFile.toIO } private def zipFilesToJar( diff --git a/main/api/src/mill/api/IO.scala b/main/api/src/mill/api/IO.scala index 01521bb1364..9f313218f75 100644 --- a/main/api/src/mill/api/IO.scala +++ b/main/api/src/mill/api/IO.scala @@ -1,5 +1,7 @@ package mill.api +import java.nio.file.Files + /** * Misc IO utilities, eventually probably should be pushed upstream into * ammonite-ops @@ -29,7 +31,7 @@ object IO extends StreamSupport { if (!entry.isDirectory) { val entryDest = ctx.dest / dest / os.SubPath(entry.getName) os.makeDir.all(entryDest / os.up) - val fileOut = new java.io.FileOutputStream(entryDest.toString) + val fileOut = Files.newOutputStream(entryDest.toNIO) IO.stream(zipStream, fileOut) fileOut.close() } diff --git a/main/api/src/mill/api/JarOps.scala b/main/api/src/mill/api/JarOps.scala index 540c706ca29..59801c66474 100644 --- a/main/api/src/mill/api/JarOps.scala +++ b/main/api/src/mill/api/JarOps.scala @@ -1,10 +1,10 @@ package mill.api -import mill.api.Loose.Agg - -import java.io.{BufferedOutputStream, FileOutputStream} +import java.io.BufferedOutputStream +import java.nio.file.Files import java.util.jar.{JarEntry, JarOutputStream, Manifest} import scala.collection.mutable +import mill.api.Loose.Agg @experimental trait JarOps { @@ -76,7 +76,7 @@ trait JarOps { val _ = seen.add(os.sub / "META-INF/MANIFEST.MF") val jarStream = new JarOutputStream( - new BufferedOutputStream(new FileOutputStream(jar.toIO)), + new BufferedOutputStream(Files.newOutputStream(jar.toNIO)), manifest ) diff --git a/main/client/src/mill/main/client/ServerLauncher.java b/main/client/src/mill/main/client/ServerLauncher.java index ab162de1aee..14bdb210f1d 100644 --- a/main/client/src/mill/main/client/ServerLauncher.java +++ b/main/client/src/mill/main/client/ServerLauncher.java @@ -8,7 +8,6 @@ import org.newsclub.net.unix.AFUNIXSocketAddress; import java.io.File; -import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintStream; @@ -126,7 +125,8 @@ int run(Path serverDir, boolean setJnaNoSys, Locks locks) throws Exception { ) { stdoutTailer.start(); stderrTailer.start(); - try (FileOutputStream f = new FileOutputStream(serverDir + "/" + ServerFiles.runArgs)) { + String serverPath = serverDir + "/" + ServerFiles.runArgs; + try (OutputStream f = Files.newOutputStream(Paths.get(serverPath))) { f.write(System.console() != null ? 1 : 0); Util.writeString(f, BuildInfo.millVersion); Util.writeArgs(args, f); diff --git a/main/client/test/src/mill/main/client/FileToStreamTailerTest.java b/main/client/test/src/mill/main/client/FileToStreamTailerTest.java index 0fb1bbc1bac..5fc22d7641e 100644 --- a/main/client/test/src/mill/main/client/FileToStreamTailerTest.java +++ b/main/client/test/src/mill/main/client/FileToStreamTailerTest.java @@ -3,9 +3,8 @@ import java.io.ByteArrayOutputStream; import java.io.File; -import java.io.FileOutputStream; import java.io.PrintStream; - +import java.nio.file.Files; import org.junit.Ignore; import org.junit.Test; @@ -49,7 +48,7 @@ public void handleNoExistingFileThatAppearsLater() throws Exception { assertEquals(bas.toString(), ""); try ( - PrintStream out = new PrintStream(new FileOutputStream(file)); + PrintStream out = new PrintStream(Files.newOutputStream(file.toPath())); ) { out.println("log line"); assertTrue(file.exists()); @@ -76,7 +75,7 @@ public void handleExistingInitiallyEmptyFile() throws Exception{ assertEquals(bas.toString(), ""); try ( - PrintStream out = new PrintStream(new FileOutputStream(file)); + PrintStream out = new PrintStream(Files.newOutputStream(file.toPath())); ) { out.println("log line"); assertTrue(file.exists()); @@ -95,7 +94,7 @@ public void handleExistingFileWithOldContent() throws Exception{ assertTrue(file.exists()); try ( - PrintStream out = new PrintStream(new FileOutputStream(file)); + PrintStream out = new PrintStream(Files.newOutputStream(file.toPath())); ) { out.println("old line 1"); out.println("old line 2"); @@ -130,7 +129,7 @@ public void handleExistingEmptyFileWhichDisappearsAndComesBack() throws Exceptio assertEquals(bas.toString(), ""); try ( - PrintStream out = new PrintStream(new FileOutputStream(file)); + PrintStream out = new PrintStream(Files.newOutputStream(file.toPath())); ) { out.println("log line 1"); out.println("log line 2"); @@ -146,7 +145,7 @@ public void handleExistingEmptyFileWhichDisappearsAndComesBack() throws Exceptio Thread.sleep(100); try ( - PrintStream out = new PrintStream(new FileOutputStream(file)); + PrintStream out = new PrintStream(Files.newOutputStream(file.toPath())); ) { out.println("new line"); assertTrue(file.exists()); diff --git a/main/util/src/mill/util/Util.scala b/main/util/src/mill/util/Util.scala index fce44696b84..4f3c7c20568 100644 --- a/main/util/src/mill/util/Util.scala +++ b/main/util/src/mill/util/Util.scala @@ -1,5 +1,7 @@ package mill.util +import java.nio.file.Files +import java.nio.file.Paths import coursier.Repository import mill.api.Loose.Agg import mill.api.{BuildInfo, Ctx, IO, PathRef, Result} @@ -19,7 +21,7 @@ object Util { { val millOptionsPath = sys.props("MILL_OPTIONS_PATH") if (millOptionsPath != null) - LongMillProps.load(new java.io.FileInputStream(millOptionsPath)) + LongMillProps.load(Files.newInputStream(Paths.get(millOptionsPath))) } def cleanupScaladoc(v: String): Array[String] = { @@ -45,19 +47,13 @@ object Util { ctx: Ctx.Dest ): PathRef = { val out = ctx.dest / dest - val website = new java.net.URI(url).toURL - val rbc = java.nio.channels.Channels.newChannel(website.openStream) + val websiteInputStream = website.openStream try { - val fos = new java.io.FileOutputStream(out.toIO) - try { - fos.getChannel.transferFrom(rbc, 0, java.lang.Long.MAX_VALUE) - PathRef(out) - } finally { - fos.close() - } + Files.copy(websiteInputStream, out.toNIO) + PathRef(out) } finally { - rbc.close() + websiteInputStream.close() } } diff --git a/runner/client/src/mill/runner/client/MillProcessLauncher.java b/runner/client/src/mill/runner/client/MillProcessLauncher.java index d4459898385..5e900502da8 100644 --- a/runner/client/src/mill/runner/client/MillProcessLauncher.java +++ b/runner/client/src/mill/runner/client/MillProcessLauncher.java @@ -3,7 +3,7 @@ import static mill.main.client.OutFiles.*; import java.io.File; -import java.io.FileInputStream; +import java.io.InputStream; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -145,7 +145,7 @@ static String[] millClasspath() throws Exception { // read MILL_CLASSPATH from file MILL_OPTIONS_PATH Properties millProps = new Properties(); - try (FileInputStream is = new FileInputStream(millOptionsPath)) { + try (InputStream is = Files.newInputStream(Paths.get(millOptionsPath))) { millProps.load(is); } catch (IOException e) { throw new RuntimeException("Could not load '" + millOptionsPath + "'", e); diff --git a/runner/src/mill/runner/MillMain.scala b/runner/src/mill/runner/MillMain.scala index 606c4ca2fd0..a43f6a07893 100644 --- a/runner/src/mill/runner/MillMain.scala +++ b/runner/src/mill/runner/MillMain.scala @@ -1,6 +1,8 @@ package mill.runner -import java.io.{FileOutputStream, PipedInputStream, PrintStream} +import java.io.{PipedInputStream, PrintStream} +import java.nio.file.Files +import java.nio.file.StandardOpenOption import java.util.Locale import scala.jdk.CollectionConverters._ import scala.util.Properties @@ -45,7 +47,8 @@ object MillMain { // and all Mill output (stdout and stderr) goes to a dedicated file val stderrFile = WorkspaceRoot.workspaceRoot / ".bsp/mill-bsp.stderr" os.makeDir.all(stderrFile / os.up) - val errFile = new PrintStream(new FileOutputStream(stderrFile.toIO, true)) + val errFile = + new PrintStream(Files.newOutputStream(stderrFile.toNIO, StandardOpenOption.APPEND)) val errTee = new TeePrintStream(initialSystemStreams.err, errFile) val msg = s"Mill in BSP mode, version ${BuildInfo.millVersion}, ${new java.util.Date()}" errTee.println(msg) diff --git a/testrunner/src/mill/testrunner/TestRunnerUtils.scala b/testrunner/src/mill/testrunner/TestRunnerUtils.scala index dfd60c038b8..954bbbe43fa 100644 --- a/testrunner/src/mill/testrunner/TestRunnerUtils.scala +++ b/testrunner/src/mill/testrunner/TestRunnerUtils.scala @@ -4,7 +4,7 @@ import mill.api.{Ctx, Loose, TestReporter, internal} import os.Path import sbt.testing._ -import java.io.FileInputStream +import java.nio.file.Files import java.lang.annotation.Annotation import java.lang.reflect.Modifier import java.util.concurrent.ConcurrentLinkedQueue @@ -19,7 +19,7 @@ import scala.jdk.CollectionConverters.IteratorHasAsScala if (os.isDir(base)) { os.walk.stream(base).filter(_.ext == "class").map(_.relativeTo(base).toString) } else { - val zip = new ZipInputStream(new FileInputStream(base.toIO)) + val zip = new ZipInputStream(Files.newInputStream(base.toNIO)) geny.Generator.selfClosing( ( Iterator.continually(zip.getNextEntry)