Improve IO.toFile's handling for Windows

Improve IO.toFile's handling for Windows, and use it for identifying resolvers.

This adds support to convert URL to File on Windows in several ways:

```
val u0 = new URL("file:C:\\Users\\foo/.sbt/preloaded")
val u1 = new URL("file:/C:\\Users\\foo/.sbt/preloaded")
val u2 = new URL("file://unc/Users/foo/.sbt/preloaded")
val u3 = new URL("file:///C:\\Users\\foo/.sbt/preloaded")
val u4 = new URL("file:////unc/Users/foo/.sbt/preloaded")
```

Note that `u0` and `u2` are something `new File(u.toURI)` won't handle. This also correctly handles UNC convention specified by Microsoft in https://blogs.msdn.microsoft.com/ie/2006/12/06/file-uris-in-windows/.

Fixes #3086
Fixes #2150
This commit is contained in:
Eugene Yokota 2017-04-08 02:30:16 -04:00
parent 3247e69cc8
commit dea267c224
2 changed files with 28 additions and 14 deletions

View File

@ -1939,7 +1939,7 @@ object Classpaths {
i.url.getProtocol match {
case "file" =>
// This hackery is to deal suitably with UNC paths on Windows. Once we can assume Java7, Paths should save us from this.
val file = try { new File(i.url.toURI) } catch { case e: java.net.URISyntaxException => new File(i.url.getPath) }
val file = IO.toFile(i.url)
Resolver.file(i.id, file)(patterns)
case _ => Resolver.url(i.id, i.url)(patterns)
}

View File

@ -122,9 +122,17 @@ object IO {
/**
* Constructs a File corresponding to `url`, which must have a scheme of `file`.
* This method properly works around an issue with a simple conversion to URI and then to a File.
*
* On Windows this can accept the following patterns of URLs:
*
* `val u0 = new URL("file:C:\\Users\\foo/.sbt/preloaded")`,
* `val u1 = new URL("file:/C:\\Users\\foo/.sbt/preloaded")`,
* `val u2 = new URL("file://unc/Users/foo/.sbt/preloaded")`,
* `val u3 = new URL("file:///C:\\Users\\foo/.sbt/preloaded")`, and
* `val u4 = new URL("file:////unc/Users/foo/.sbt/preloaded")`.
*/
def toFile(url: URL): File =
try { new File(url.toURI) }
try { uriToFile(url.toURI) }
catch { case _: URISyntaxException => new File(url.getPath) }
/** Converts the given URL to a File. If the URL is for an entry in a jar, the File for the jar is returned. */
@ -139,20 +147,26 @@ object IO {
case _ => None
}
private[this] def uriToFile(uriString: String): File =
private[this] def uriToFile(uriString: String): File = uriToFile(new URI(uriString))
/**
* Converts the given file URI to a File.
*/
private[this] def uriToFile(uri: URI): File =
{
val uri = new URI(uriString)
assert(uri.getScheme == FileScheme, "Expected protocol to be '" + FileScheme + "' in URI " + uri)
if (uri.getAuthority eq null)
new File(uri)
else {
/* https://github.com/sbt/sbt/issues/564
* http://blogs.msdn.com/b/ie/archive/2006/12/06/file-uris-in-windows.aspx
* http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5086147
* The specific problem here is that `uri` will have a defined authority component for UNC names like //foo/bar/some/path.jar
* but the File constructor requires URIs with an undefined authority component.
*/
new File(uri.getSchemeSpecificPart)
val part = uri.getSchemeSpecificPart
Option(uri.getAuthority) match {
case None if part startsWith "/" => new File(uri)
case _ =>
// https://github.com/sbt/sbt/issues/564
// https://github.com/sbt/sbt/issues/3086
// http://blogs.msdn.com/b/ie/archive/2006/12/06/file-uris-in-windows.aspx
// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5086147
// The specific problem here is that `uri` will have a defined authority component for UNC names like //foo/bar/some/path.jar
// but the File constructor requires URIs with an undefined authority component.
if (part startsWith "/") new File(part)
else new File("//" + part)
}
}