Skip to content

Commit

Permalink
Merge pull request #238 from eatkins/m3-fixes
Browse files Browse the repository at this point in the history
M3 fixes
  • Loading branch information
eed3si9n authored May 11, 2019
2 parents e5f1ece + 05ff469 commit 13becc5
Show file tree
Hide file tree
Showing 52 changed files with 1,054 additions and 330 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ matrix:
include:
- scala: 2.12.8
env:
CMD="mimaReportBinaryIssues scalafmtCheckAll test whitesourceCheckPolicies doc"
CMD="mimaReportBinaryIssues scalafmtCheckAll headerCheck Test/headerCheck test whitesourceCheckPolicies doc"
TRAVIS_SCALA_VERSION=2.12.8
[email protected]
os: linux
Expand Down
3 changes: 1 addition & 2 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,13 @@ ThisBuild / headerLicense := Some(HeaderLicense.Custom(
|(http://www.apache.org/licenses/LICENSE-2.0).
|""".stripMargin
))
ThisBuild / scalafmtOnCompile := true

def commonSettings: Seq[Setting[_]] = Seq(
scalaVersion := scala212,
javacOptions in compile ++= Seq("-Xlint", "-Xlint:-serial"),
crossScalaVersions := Seq(scala212, scala213),
headerLicense := (ThisBuild / headerLicense).value,
scalafmtOnCompile := true,
Test / scalafmtOnCompile := true,
)

lazy val ioRoot = (project in file("."))
Expand Down
82 changes: 71 additions & 11 deletions io/src/main/scala/sbt/internal/io/MacOSXWatchService.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@
package sbt.internal.io

import java.io.IOException
import java.nio.file.{ WatchEvent, WatchKey, Path => JPath }
import java.nio.file.{ ClosedWatchServiceException, WatchEvent, WatchKey, Path => JPath }
import java.util.Collections
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.{ ConcurrentHashMap, TimeUnit }

import sbt.io.{ Unregisterable, WatchService }

import scala.annotation.tailrec
import scala.collection.JavaConverters._
import scala.collection.{ immutable, mutable }
import scala.concurrent.duration._
Expand All @@ -26,28 +27,87 @@ private[sbt] class MacOSXWatchService extends WatchService with Unregisterable {
private val underlying = com.swoval.files.RegisterableWatchServices.get()
private val keys: mutable.Map[JPath, WatchKey] =
Collections.synchronizedMap(new ConcurrentHashMap[JPath, WatchKey]()).asScala
private val parentKeys: mutable.Map[JPath, WatchKey] =
Collections.synchronizedMap(new ConcurrentHashMap[JPath, WatchKey]()).asScala
private val isClosed = new AtomicBoolean(false)
def isOpen: Boolean = !isClosed.get

override def init(): Unit = {}

override def pollEvents(): Map[WatchKey, immutable.Seq[WatchEvent[JPath]]] =
override def pollEvents(): Map[WatchKey, immutable.Seq[WatchEvent[JPath]]] = {
underlying.poll() match {
case null => Map.empty
case k =>
Map(k -> k.pollEvents().asScala.view.map(_.asInstanceOf[WatchEvent[JPath]]).toIndexedSeq)
k.watchable() match {
case p: JPath if keys.contains(p) =>
val res = k -> k
.pollEvents()
.asScala
.view
.map(_.asInstanceOf[WatchEvent[JPath]])
.toIndexedSeq
Map(res)
case _ => null
}
}
}

override def poll(timeout: Duration): WatchKey =
underlying.poll(timeout.toNanos, TimeUnit.NANOSECONDS)

override def register(path: JPath, events: WatchEvent.Kind[JPath]*): WatchKey = {
val resolved = resolve(path)
val key = underlying.register(resolved, events: _*)
keys.put(resolved, key)
key
override def poll(timeout: Duration): WatchKey = {
val finiteDuration: FiniteDuration = timeout match {
case d: FiniteDuration => d
case _ => new FiniteDuration(Int.MaxValue, SECONDS)
}
val limit = finiteDuration.fromNow
@tailrec def impl(): WatchKey = {
val remaining = limit - Deadline.now
if (remaining > 0.seconds) {
underlying.poll((limit - Deadline.now).toNanos, TimeUnit.NANOSECONDS) match {
case null => null
case k =>
k.watchable match {
case p: JPath if keys.contains(p) => k
case _ => impl()
}
}
} else null
}
impl()
}

override def register(path: JPath, events: WatchEvent.Kind[JPath]*): WatchKey =
if (!isClosed.get()) {
keys.get(path) match {
case Some(k) => k
case _ =>
val resolved = resolve(path)
val parent = path.getParent
if (!keys.contains(parent)) {
// workaround for https://github.com/sbt/sbt/issues/4603
if (keys.keys.exists(
p =>
p.getParent == parent && {
val leftFileName = p.getFileName.toString
val rightFileName = path.getFileName.toString
leftFileName != rightFileName && (leftFileName
.startsWith(rightFileName) || rightFileName.startsWith(leftFileName))
}
)) {
parentKeys.put(parent, underlying.register(parent, events: _*))
}
}
val key =
parentKeys.remove(resolved) match {
case Some(k) => k
case _ =>
underlying.register(resolved, events: _*)
}
keys.put(resolved, key)
key
}
} else {
throw new ClosedWatchServiceException
}

override def unregister(path: JPath): Unit = {
keys.remove(resolve(path)) foreach (_.cancel())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import java.util.concurrent.atomic.AtomicBoolean
import sbt.internal.nio._
import sbt.io._
import sbt.io.syntax._
import sbt.nio.file.{ AnyPath, FileAttributes, Glob, RecursiveGlob }
import sbt.nio.file.{ AnyPath, FileAttributes, FileTreeView, Glob, RecursiveGlob }

import scala.annotation.tailrec
import scala.collection.JavaConverters._
Expand Down Expand Up @@ -217,10 +217,8 @@ final class Source(
* Gathers all the paths from this source without applying filters.
* @return A sequence of all the paths collected from this source.
*/
private[sbt] def getUnfilteredPaths(): Seq[Path] = {
val pathFinder: Glob = if (recursive) base.allPaths else base.glob(AllPassFilter)
pathFinder.get().map(_.toPath)
}
private[sbt] def getUnfilteredPaths(): Seq[Path] =
FileTreeView.default.list(Glob(base, if (recursive) RecursiveGlob else AnyPath)).map(_._1)

def withRecursive(recursive: Boolean): Source =
new Source(base, includeFilter, excludeFilter, recursive)
Expand Down
13 changes: 12 additions & 1 deletion io/src/main/scala/sbt/internal/nio/FileCache.scala
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
/*
* sbt IO
*
* Copyright 2011 - 2019, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
*
* Licensed under Apache License 2.0
* (http://www.apache.org/licenses/LICENSE-2.0).
*/

package sbt.internal.nio

import java.nio.file.{ Path, Paths }
Expand Down Expand Up @@ -137,8 +147,9 @@ private[nio] class FileCache[+T](converter: Path => T, globs: mutable.Set[Glob])
case d => (1 to d).foldLeft(Glob(path)) { case (g, _) => g / AnyPath }
}
}
private[this] val ceilingChar = (java.io.File.separatorChar.toInt + 1).toChar
// This is a mildly hacky way of specifying an upper bound for children of a path
private[this] def ceiling(path: Path): Path = Paths.get(path.toString + Char.MaxValue)
private[this] def ceiling(path: Path): Path = Paths.get(path.toString + ceilingChar)
}
private[nio] object FileCache {
private implicit class GlobOps(val glob: Glob) extends AnyVal {
Expand Down
28 changes: 26 additions & 2 deletions io/src/main/scala/sbt/internal/nio/FileTreeRepositoryImpl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ package sbt.internal.nio

import java.io.IOException
import java.nio.file.{ Path => NioPath }
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.atomic.AtomicBoolean

import com.swoval.files.FileTreeDataViews.CacheObserver
Expand All @@ -23,6 +24,7 @@ import sbt.nio.file.{ FileAttributes, Glob }

import scala.collection.JavaConverters._
import scala.collection.immutable.VectorBuilder
import scala.util.Properties

/**
* The default implemenation of [[FileTreeRepository]]. It delegates all of its methods to the
Expand All @@ -44,6 +46,8 @@ private[sbt] class FileTreeRepositoryImpl[T] extends FileTreeRepository[FileAttr
true
)
private[this] val observers = new Observers[FileEvent[FileAttributes]]
private[this] val registered = ConcurrentHashMap.newKeySet[NioPath].asScala
private[this] val isMac = Properties.isMac

underlying.addCacheObserver(new CacheObserver[FileAttributes] {
override def onCreate(newEntry: FileTreeDataViews.Entry[FileAttributes]): Unit = {
Expand Down Expand Up @@ -105,8 +109,28 @@ private[sbt] class FileTreeRepositoryImpl[T] extends FileTreeRepository[FileAttr
}
override def register(glob: Glob): Either[IOException, Observable[FileEvent[FileAttributes]]] = {
throwIfClosed("register")
underlying.register(glob.base, glob.range.toSwovalDepth).asScala match {
case Right(_) => new RegisterableObservable(observers).register(glob)
val base = glob.base
if (isMac) {
// workaround for https://github.com/sbt/sbt/issues/4603
val parent = glob.base.getParent
if (!registered.contains(parent)) {
if (registered.exists(
path =>
path.getParent == parent && {
val leftFileName = path.getFileName.toString
val rightFileName = base.getFileName.toString
leftFileName != rightFileName && (leftFileName
.startsWith(rightFileName) || rightFileName.startsWith(leftFileName))
}
)) {
register(Glob(parent))
}
}
}
underlying.register(base, glob.range.toSwovalDepth).asScala match {
case Right(_) =>
registered.add(base)
new RegisterableObservable(observers).register(glob)
case Left(ex) => Left(ex)
}
}
Expand Down
117 changes: 117 additions & 0 deletions io/src/main/scala/sbt/internal/nio/Globs.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/*
* sbt IO
*
* Copyright 2011 - 2019, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
*
* Licensed under Apache License 2.0
* (http://www.apache.org/licenses/LICENSE-2.0).
*/

package sbt.internal.nio

import java.nio.file.Path

import sbt.io._
import sbt.nio.file.Glob.FullFileGlob
import sbt.nio.file.RelativeGlob.{ Matcher, NoPath }
import sbt.nio.file.{ AnyPath, Glob, RecursiveGlob, RelativeGlob }

private[sbt] object Globs {
private[sbt] def apply(base: Path, recursive: Boolean, fileFilter: FileFilter): Glob = {
fileFilterToRelativeGlob(fileFilter) match {
case Some(relativeGlob) =>
Glob(base, if (recursive) RecursiveGlob / relativeGlob else relativeGlob)
case None =>
FullFileGlob(base, recursive, fileFilter)
}
}
private[sbt] def fileFilterToRelativeGlob(fileFilter: FileFilter): Option[RelativeGlob] =
fileFilter match {
case nameFilter: NameFilter => nameFilterToRelativeGlob(nameFilter)
case af: AndFilter =>
if (af.left == NothingFilter || af.right == NothingFilter) Some(NoPath)
else if (af.left == AllPassFilter) fileFilterToRelativeGlob(af.right)
else if (af.right == AllPassFilter) fileFilterToRelativeGlob(af.left)
else {
(fileFilterToRelativeGlob(af.left), fileFilterToRelativeGlob(af.right)) match {
case (no @ Some(NoPath), _) => no
case (_, no @ Some(NoPath)) => no
case (Some(AnyPath), right) => right
case (left, Some(AnyPath)) => left
case (Some(left: Matcher), Some(right: Matcher)) => Some(Matcher.and(left, right))
case _ => None
}
}
case nf: NotFilter =>
nf.fileFilter match {
case NothingFilter => Some(AnyPath)
case AllPassFilter => Some(NoPath)
case f =>
fileFilterToRelativeGlob(f).collect {
case m: Matcher => Matcher.not(m)
}
}
case of: OrFilter =>
if (of.left == AllPassFilter || of.right == AllPassFilter) Some(AnyPath)
else if (of.left == NothingFilter) fileFilterToRelativeGlob(of.right)
else if (of.right == NothingFilter) fileFilterToRelativeGlob(of.left)
else {
(fileFilterToRelativeGlob(of.left), fileFilterToRelativeGlob(of.right)) match {
case (Some(NoPath), right) => right
case (left, Some(NoPath)) => left
case (any @ Some(AnyPath), _) => any
case (_, any @ Some(AnyPath)) => any
case (Some(left: Matcher), Some(right: Matcher)) => Some(Matcher.and(left, right))
case _ => None
}
}
case AllPassFilter | HiddenFileFilter => Some(AnyPath)
case NothingFilter => Some(NoPath)
case _ => None
}
private[sbt] def nameFilterToRelativeGlob(nameFilter: NameFilter): Option[Matcher] =
nameFilter match {
case AllPassFilter => Some(AnyPath)
case af: AndNameFilter =>
if (af.left == NothingFilter || af.right == NothingFilter) Some(NoPath)
else if (af.left == AllPassFilter) nameFilterToRelativeGlob(af.right)
else if (af.right == AllPassFilter) nameFilterToRelativeGlob(af.left)
else
(nameFilterToRelativeGlob(af.left), nameFilterToRelativeGlob(af.right)) match {
case (Some(AnyPath), right) => right
case (left, Some(AnyPath)) => left
case (no @ Some(NoPath), _) => no
case (_, no @ Some(NoPath)) => no
case (Some(l), Some(r)) => Some(Matcher.and(l, r))
case _ => None
}
case ef: ExactFilter => Some(Matcher(ef.matchName))
case ef: ExtensionFilter =>
ef.extensions match {
case extensions if extensions.length == 1 => Some(Matcher(s"*.${extensions.head}"))
case extensions => Some(Matcher(s"*.${extensions.mkString("{", ",", "}")}"))
}
case NothingFilter => Some(NoPath)
case nf: NotNameFilter =>
if (nf.fileFilter == NothingFilter) Some(AnyPath)
else nameFilterToRelativeGlob(nf.fileFilter).map(Matcher.not)
case of: OrNameFilter =>
if (of.left == AllPassFilter || of.right == AllPassFilter) Some(AnyPath)
else if (of.left == NothingFilter) nameFilterToRelativeGlob(of.right)
else if (of.right == NothingFilter) nameFilterToRelativeGlob(of.left)
else
(nameFilterToRelativeGlob(of.left), nameFilterToRelativeGlob(of.right)) match {
case (any @ Some(AnyPath), _) => any
case (_, any @ Some(AnyPath)) => any
case (Some(NoPath), right) => right
case (left, Some(NoPath)) => left
case (Some(l), Some(r)) => Some(Matcher.or(l, r))
case _ => None
}
case pf: PatternFilter => Some(Matcher(s"${pf.parts.mkString("*")}"))
case pf: PrefixFilter => Some(Matcher(s"${pf.prefix}*"))
case sf: SimpleFilter => Some(Matcher(sf.acceptFunction))
case sf: SuffixFilter => Some(Matcher(s"*${sf.suffix}"))
}
}
10 changes: 10 additions & 0 deletions io/src/main/scala/sbt/internal/nio/TimeSource.scala
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
/*
* sbt IO
*
* Copyright 2011 - 2019, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
*
* Licensed under Apache License 2.0
* (http://www.apache.org/licenses/LICENSE-2.0).
*/

package sbt.internal.nio

import scala.concurrent.duration._
Expand Down
Loading

0 comments on commit 13becc5

Please sign in to comment.