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 { i.url.getProtocol match {
case "file" => case "file" =>
// This hackery is to deal suitably with UNC paths on Windows. Once we can assume Java7, Paths should save us from this. // 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) Resolver.file(i.id, file)(patterns)
case _ => Resolver.url(i.id, i.url)(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`. * 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. * 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 = def toFile(url: URL): File =
try { new File(url.toURI) } try { uriToFile(url.toURI) }
catch { case _: URISyntaxException => new File(url.getPath) } 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. */ /** 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 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) assert(uri.getScheme == FileScheme, "Expected protocol to be '" + FileScheme + "' in URI " + uri)
if (uri.getAuthority eq null) val part = uri.getSchemeSpecificPart
new File(uri) Option(uri.getAuthority) match {
else { case None if part startsWith "/" => new File(uri)
/* https://github.com/sbt/sbt/issues/564 case _ =>
* http://blogs.msdn.com/b/ie/archive/2006/12/06/file-uris-in-windows.aspx // https://github.com/sbt/sbt/issues/564
* http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5086147 // https://github.com/sbt/sbt/issues/3086
* The specific problem here is that `uri` will have a defined authority component for UNC names like //foo/bar/some/path.jar // http://blogs.msdn.com/b/ie/archive/2006/12/06/file-uris-in-windows.aspx
* but the File constructor requires URIs with an undefined authority component. // 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
new File(uri.getSchemeSpecificPart) // but the File constructor requires URIs with an undefined authority component.
if (part startsWith "/") new File(part)
else new File("//" + part)
} }
} }